架构一个完整的Linux物联网设备应用层事件触发机制程序
源码树形结构
event_system/ ├── Makefile ├── event_manager.c # 主事件管理器 ├── event_interface.c # 库接口实现 ├── event_interface.h # 公共头文件 ├── S01event.sh # 初始化脚本 └── dependencies/├── $(MOLIB_DIR)/ # 基础库└── $(MOLIB_UPDATER_DIR)/ # 更新库
软件架构分析
1. 组件分离架构
// 架构示意图 +----------------+ 调用 +-------------------+ | 事件管理器 | <---------> | 事件接口库 | | event_manager | | libevent.so | +----------------+ +-------------------+↓ ↓ +----------------+ +-------------------+ | 业务逻辑处理 | | API接口层 | | 主事件循环 | | 其他进程通过该库 | | 系统服务 | | 访问事件服务 | +----------------+ +-------------------+
2. 架构合理性分析
有何优点:
1. 模块化设计合理
KEY_MANAGER_SRCS = event_manager.c # 服务进程 LIBKEY_INTERFACE_SRCS = event_interface.c # 接口库
服务进程与接口库分离,符合Unix哲学
2. 进程间通信设计
// 推测的通信模式 App Process 1 App Process 2 App Process 3↓ ↓ ↓ +-----------------------------------------------+ | 自制libevent.so (共享库) | +-----------------------------------------------+ | IPC机制 (推测) | +-----------------------------------------------+ | event_manager (服务进程) | +-----------------------------------------------+
通过共享库提供统一API
服务进程集中管理事件
3. 部署结构清晰
INSTALL -m 755 event_manager $(DESTDIR)$(SBINDIR) # 系统服务 INSTALL -m 755 libevent.so $(DESTDIR)$(LIBDIR) # 共享库 INSTALL -m 664 event_interface.h $(DESTDIR)$(INCDIR) # 开发头文件
Makefile:
# 定义编译标志 CFLAGS := -g -fpic -Wall -I. -D_GNU_SOURCE -I$(MOLIB_DIR)/usr/include -I$(MOLIB_UPDATER_DIR)/usr/include # -g: 生成调试信息 # -fpic: 生成位置无关代码(用于共享库) # -Wall: 启用所有警告 # -I.: 包含当前目录到头文件搜索路径 # -D_GNU_SOURCE: 定义GNU源宏,启用GNU扩展 # -I$(MOLIB_DIR)/usr/include: 包含MOLIB目录的头文件 # -I$(MOLIB_UPDATER_DIR)/usr/include: 包含MOLIB更新目录的头文件 # 用于回溯调试 CFLAGS += -rdynamic -fasynchronous-unwind-tables # -rdynamic: 导出所有符号用于回溯 # -fasynchronous-unwind-tables: 生成异步展开表,改进栈回溯 CFLAGS += -Wall -Werror # -Werror: 将所有警告视为错误 # 定义链接标志 LDFLAGS := -L. -lpthread -L$(MOLIB_DIR)/usr/lib -L$(MOLIB_UPDATER_DIR)/usr/lib # -L.: 链接当前目录的库 # -lpthread: 链接pthread线程库 # -L$(MOLIB_DIR)/usr/lib: 链接MOLIB目录的库 # -L$(MOLIB_UPDATER_DIR)/usr/lib: 链接MOLIB更新目录的库 # 安装目录配置 DESTDIR = # 目标目录前缀,用于交叉编译或打包 PREFIX = /usr # 安装前缀目录 BINDIR = $(PREFIX)/bin # 二进制文件目录 SBINDIR = $(PREFIX)/sbin # 系统二进制文件目录 LIBDIR = $(PREFIX)/lib # 库文件目录 INCDIR = $(PREFIX)/include # 头文件目录 INITDIR = /etc/init.d/ # 初始化脚本目录 # 安装工具 INSTALL = install # 源文件定义 KEY_MANAGER_SRCS = event_manager.c # 事件管理器源文件 LIBKEY_INTERFACE_SRCS = event_interface.c # 事件接口库源文件 # 对象文件生成规则 KEY_MANAGER_OBJS = $(patsubst %.c, %.o, $(KEY_MANAGER_SRCS)) # 事件管理器对象文件 LIBKEY_OBJS = $(patsubst %.c, %.o, $(LIBKEY_INTERFACE_SRCS)) # 事件接口库对象文件 # 默认目标 all:event_manager libevent.so # 构建事件管理器可执行文件 event_manager:$(KEY_MANAGER_OBJS)$(CC) -o $@ $? $(LDFLAGS)# $@: 目标文件名(event_manager)# $?: 所有比目标新的依赖文件 # 构建共享库 libevent.so:$(LIBKEY_OBJS)$(CC) -shared -o $@ $? $(LDFLAGS)# -shared: 生成共享库# $@: 目标文件名(libevent.so) # 通用编译规则:从.c文件生成.o文件 %.o:%.c$(CC) $(CFLAGS) -c $^ -o $@# $^: 所有的依赖文件# $@: 目标文件 # 安装目标 install:$(TARGET)# 创建安装目录$(INSTALL) -d $(DESTDIR)$(LIBDIR) -d $(DESTDIR)$(SBINDIR) -d $(DESTDIR)$(ETCDIR) -d $(DESTDIR)$(INCDIR) -d $(DESTDIR)$(INITDIR)# 安装事件管理器到系统二进制目录$(INSTALL) -m 755 event_manager $(DESTDIR)$(SBINDIR)# 安装初始化脚本$(INSTALL) -m 755 S01event.sh $(DESTDIR)$(INITDIR)# 安装共享库$(INSTALL) -m 755 libevent.so $(DESTDIR)$(LIBDIR)# 安装头文件$(INSTALL) -m 664 event_interface.h $(DESTDIR)$(INCDIR) # 清理构建文件 clean:-rm -rf $(KEY_MANAGER_OBJS) $(LIBKEY_OBJS) event_manager libevent.so# -rm: 忽略错误继续执行 # 卸载已安装的文件 uninstall:rm -rf $(DESTDIR)$(SBINDIR)/event_managerrm -rf $(DESTDIR)$(LIBDIR)/libevent.sorm -rf $(DESTDIR)$(INITDIR)/S01event.shrm -rf $(DESTDIR)$(INCDIR)/event_interface.h # 声明伪目标 .PHONY:all clean
event_interface.h
#ifndef __COMMON_H__ // 头文件保护宏,防止重复包含
#define __COMMON_H__
// 标准输入输出函数,用于printf、scanf等
#include <stdio.h>
// 网络地址转换函数,如inet_pton、inet_ntop等
#include <arpa/inet.h>
// Internet地址族定义,如sockaddr_in结构体
#include <netinet/in.h>
// 套接字函数,如socket、bind、connect等
#include <sys/socket.h>
// 基本系统数据类型,如size_t、pid_t等
#include <sys/types.h>
// Unix标准函数,如read、write、close等
#include <unistd.h>
// 标准库函数,如malloc、free、exit等
#include <stdlib.h>
// 字符串处理函数,如strcpy、strlen、memset等
#include <string.h>
// 错误号定义和错误处理函数
#include <errno.h>
/*** 调试输出宏 - 设计模式分析:空对象模式* 性能分析:发布版本中定义为空,完全消除调试输出开销* 功能:在调试版本中输出文件、函数、行号等调试信息*/
#define DEBUG(x,y...) //(printf("DEBUG [ %s : %s : %d] "x"\n",__FILE__, __func__, __LINE__, ##y))
/*** 错误输出宏 - 设计模式分析:模板方法模式* 性能分析:固定格式输出,运行时开销较小但会影响性能关键路径* 功能:输出错误信息,包含文件名、函数名、行号等上下文* 翻译:ERROR [ 文件名 : 函数名 : 行号 ] 用户自定义错误信息*/
#define ERROR(x,y...) (printf("ERROR [ %s : %s : %d] "x"\n", __FILE__, __func__, __LINE__, ##y))
/*** 服务器注册端口 - 设计模式分析:常量定义模式* 性能分析:编译时常量,无运行时开销* 功能:定义客户端注册到服务器时使用的端口号* 架构分析:用于服务发现或客户端注册机制*/
#define SERVER_REGISTER_PORT 65430
/*** 服务器事件端口 - 设计模式分析:常量定义模式 * 性能分析:编译时常量,无运行时开销* 功能:定义服务器接收和处理事件时使用的端口号* 架构分析:事件驱动架构,分离注册和事件处理逻辑*/
#define SERVER_EVENT_PORT 65431
/*** 服务器地址 - 设计模式分析:配置模式* 性能分析:字符串常量,编译时确定* 功能:定义服务器监听的本机回环地址* 架构分析:当前配置为本地测试,生产环境应支持配置化*/
#define SERVER_ADDR "127.0.0.1"
#endif // __COMMON_H__ // 头文件保护宏结束event_interface.c # 库接口实现
#include <unistd.h> // Unix标准函数,提供sleep、close等
#include <pthread.h> // POSIX线程库,用于多线程编程
#include <errno.h> // 错误号定义,用于错误处理
#include <string.h> // 字符串处理函数
#include "event_interface.h" // 事件接口头文件,定义事件结构和函数声明
#include "common.h" // 通用头文件,定义服务器地址和端口
/*** 连接到TCP服务器 - 设计模式:工厂方法模式* 性能分析:创建网络连接,耗时操作,应复用连接* @param ipaddr 服务器IP地址* @param port 服务器端口号* @return 成功返回套接字描述符,失败返回-1*/
static int connect_to_tcp_server(char *ipaddr, int port)
{int sockfd = -1;struct sockaddr_in seraddr; // 服务器地址结构
// 创建TCP套接字sockfd = socket(AF_INET, SOCK_STREAM, 0); // AF_INET:IPv4, SOCK_STREAM:TCPif (sockfd == -1) {printf("[EventManager - Error] socket() error for connect_to_tcp_server failed: %s.\n",strerror(errno)); // 输出套接字创建错误return -1;}
// 设置服务器地址信息seraddr.sin_family = AF_INET; // 地址族:IPv4seraddr.sin_port = htons(port); // 端口号转网络字节序seraddr.sin_addr.s_addr = inet_addr(ipaddr); // IP地址转网络字节序
// 连接到服务器if (-1 == connect(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr))) {printf("[EventManager - Error] connect to event manager failed: %s.\n", strerror(errno));return -1; // 连接失败}
return sockfd; // 返回连接成功的套接字
}
/*** 接收事件线程函数 - 设计模式:观察者模式 + 自动重连机制* 性能分析:持续轮询接收事件,CPU占用需优化,重连机制保证可靠性* @param arg 事件处理器指针* @return NULL*/
static void *recv_event_thread(void *arg)
{int ret = 0;tang_event event; // 事件结构体event_handler *handler = (event_handler *)arg; // 转换参数为事件处理器
while (1) { // 无限循环,持续监听事件// 接收事件数据ret = recv(handler->sockfd, &event, sizeof(event), 0); // MSG_WAITALL?if (ret == sizeof(event)) { // 成功接收到完整事件if (handler->callback) // 回调函数存在handler->callback(event, handler->param); // 执行用户回调} else if (ret == 0){ // 连接断开printf("[EventManager - Error] event_manager disconnected(event_manager Dead?), Re-connect after 500ms.\n");usleep(500 * 1000); // 等待500ms后重连if (handler->sockfd)close(handler->sockfd); // 关闭旧连接// 重新连接到注册端口handler->sockfd = connect_to_tcp_server(SERVER_ADDR, SERVER_REGISTER_PORT);} else { // 接收错误printf("[EventManager - Error] recieve data from event_manager failed: %s, Re-try after 500ms.\n",strerror(errno));usleep(500 * 1000); // 等待500ms后重试}}
return NULL;
}
/*** 获取事件处理器 - 设计模式:工厂模式 + 线程创建* 性能分析:内存分配+网络连接+线程创建,初始化成本高,应考虑对象池* @param callback 事件回调函数* @param param 用户自定义参数* @return 成功返回事件处理器指针,失败返回NULL*/
event_handler *tang_event_handler_get(EventCallback callback, void *param)
{// 分配事件处理器内存event_handler *handler = malloc(sizeof(*handler));if (!handler) {printf("[EentManager - Error] alloc memory for handler failed: %s.\n",strerror(errno));return NULL;}
// 初始化事件处理器字段handler->callback = callback; // 设置回调函数handler->param = param; // 设置用户参数// 连接到事件管理器的注册端口handler->sockfd = connect_to_tcp_server(SERVER_ADDR, SERVER_REGISTER_PORT);if (handler->sockfd < 0) {printf("[EventManager - Error] Can not connect to event manager.\n");free(handler); // 连接失败,释放内存return NULL;}
pthread_t recv_event_pthread;// 创建事件接收线程while (0 != pthread_create(&recv_event_pthread, NULL, recv_event_thread, handler)) {printf("[EventManager - Error] create thread for listening event failed: %s, Re-try after 500ms.\n",strerror(errno));usleep(500 * 1000); // 线程创建失败,等待后重试}
// 分离线程,线程退出时自动释放资源pthread_detach(recv_event_pthread); /* FIXME: Never mind error here. may memory leak if occur */// 注意:pthread_detach可能失败,但这里忽略错误,可能导致内存泄漏
return handler; // 返回创建的事件处理器
}
/*** 发送事件 - 设计模式:命令模式 + 请求-响应机制* 性能分析:每次发送都新建连接,性能较差,应考虑连接复用* @param event 要发送的事件* @return 成功返回0,失败返回-1*/
int tang_event_send(tang_event event)
{int sockfd = -1;int ret = 0;char feedback[2] = ""; // 服务器反馈缓冲区
// 连接到事件端口(注意:与注册端口不同)sockfd = connect_to_tcp_server(SERVER_ADDR, SERVER_EVENT_PORT);if (sockfd < 0) {printf("can not connect to event manager, exit..\n");ret = -1;goto func_exit; // 使用goto统一清理资源}
// 发送事件数据ret = send(sockfd, &event, sizeof(event), 0);if (ret != sizeof(event)) { // 发送数据量不匹配printf("send event error: %s.\n", strerror(errno));ret = -1;goto func_exit;}
// 接收服务器反馈ret = recv(sockfd, feedback, 2, 0);if (ret == 2) { // 成功接收2字节反馈if (feedback[0] == '1') { // 服务器返回'1'表示成功ret = 0;goto func_exit;} else { // 其他值表示失败ret = -1;goto func_exit;}} else { // 接收反馈失败ret = -1;goto func_exit;}
func_exit:if (sockfd) {close(sockfd); // 关闭套接字sockfd = -1;}
return ret;
}
/*** 释放事件处理器 - 设计模式:资源管理模式* 性能分析:简单资源释放操作,无性能瓶颈* @param handler 要释放的事件处理器*/
void tang_event_handler_put(event_handler *handler)
{if (!handler) // 空指针检查return;
if (handler->sockfd > 0) {close(handler->sockfd); // 关闭网络连接handler->sockfd = -1;}
handler->callback = NULL; // 清空回调指针free(handler); // 释放内存
}
event_manager.c
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <dirent.h>
#include <pthread.h>
#include <linux/input.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <linux/netlink.h>
#include <sys/types.h>
#include "event_interface.h"
#include "common.h"
#define EVENT_FD_SIZE 32 /* 监听文件描述符数组大小 - 设计模式:配置常量模式 */
#define CONN_FD_SIZE 32 /* 连接文件描述符数组大小 - 设计模式:配置常量模式 */
#define UEVENT_MSG_LEN 1024 /* 内核uevent消息最大长度 */
/*** uevent结构体 - 设计模式:数据转换模式* 性能分析:存储解析后的uevent信息,避免重复解析字符串*/
struct uevent {const char *action; // 动作:add/remove/changeconst char *path; // 设备路径const char *subsystem; // 设备子系统const char *firmware; // 固件信息int major; // 主设备号int minor; // 次设备号
};
// 函数声明
static void openKeyboard();
static void closeKeyboard();
static void eventLoop(void);
// 全局变量定义
static int event_fd[EVENT_FD_SIZE] = {[0 ... EVENT_FD_SIZE-1] = -1}; // 事件监听fd数组,初始化为-1
static int conn_fd[CONN_FD_SIZE] = {[0 ... CONN_FD_SIZE-1] = -1}; // 客户端连接fd数组,初始化为-1
static int device_fd = -1; // uevent设备文件描述符
static int max_fd = -1; // select最大文件描述符
/*** 启动TCP服务器 - 设计模式:工厂模式* 性能分析:创建监听socket,系统调用开销,应避免频繁调用* @param ipaddr 监听IP地址* @param port 监听端口* @return 成功返回socket fd,失败返回-1*/
static int start_tcp_server(char *ipaddr, int port)
{int sockfd;int opt = 1; // SO_REUSEADDR选项值struct sockaddr_in seraddr; // 服务器地址结构
memset(&seraddr, 0, sizeof(struct sockaddr_in)); // 清空地址结构seraddr.sin_family = AF_INET; // IPv4地址族seraddr.sin_port = htons(port); // 端口转网络字节序seraddr.sin_addr.s_addr = inet_addr(ipaddr); // IP地址转网络字节序//seraddr.sin_addr.s_addr = htonl(INADDR_ANY); // 注释掉的任意地址绑定
// 重试创建socket直到成功while ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {printf("[EventManager - Error] socket() for processing event hanlder failed: %s,""Re-try after 500ms.\n", strerror(errno));usleep(500 * 1000); // 等待500ms后重试}
// 设置地址重用选项if (-1 == setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)))printf("[EventManager - Warning] set SO_REUSEADDR failed, Ignore it.\n");
// 绑定socket到地址if (-1 == bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr))) {printf("[EventManager - Error] bind for event handler socket failed: %s,""kernel uevent support is disabled!!!\n", strerror(errno));close(sockfd);return -1;}
return sockfd; // 返回监听socket
}
/*** 分发事件到所有客户端 - 设计模式:发布-订阅模式* 性能分析:遍历所有连接发送数据,O(n)复杂度,连接数多时可能成为瓶颈* @param event 要分发的事件* @return 总是返回0*/
static int distribute_event(tang_event event)
{int i = 0;
// 遍历所有客户端连接for (i = 0; i < CONN_FD_SIZE; i++) {if (conn_fd[i] >= 0) // 连接有效send(conn_fd[i], &event, sizeof(event), 0); /* 忽略发送错误 */}
return 0;
}
/*** 打开输入设备事件文件 - 设计模式:资源管理模式* 性能分析:系统调用open开销,设备发现阶段调用* @param dev 设备文件路径* @return 成功返回0,失败返回-1*/
static int openevent(char *dev)
{int fd = 0;int i = 0;
fd = open(dev, O_RDWR); // 以读写方式打开设备文件if (fd < 0) {printf("[EventManager - Warning] open %s failed: %s, try next.\n",dev, strerror(errno));} else {// 在事件fd数组中寻找空位for (i = 0; i < EVENT_FD_SIZE; i++) {if (event_fd[i] < 0) {printf("[EventManager - info] new input device added: %s\n", dev);event_fd[i++] = fd; // 存储fd并增加ibreak;}}// 数组已满if (i == EVENT_FD_SIZE) {printf("[EventManager - Warning] Too many file opened already, Will not monitor more.\n");return -1;}}
return 0;
}
/*** 打开所有键盘输入设备 - 设计模式:迭代器模式* 性能分析:目录遍历和文件打开,初始化时调用一次*/
static void openKeyboard(void)
{struct dirent *ptr = NULL;char eventfile[512] = ""; // 设备文件路径缓冲区
// 打开输入设备目录DIR *dir = opendir("/dev/input");if (!dir) {printf("[EventManager - Warning] open /dev/input failed: %s,""key event support is disabled!!!\n", strerror(errno));return;}
// 遍历目录项while (NULL != (ptr = readdir(dir))) {// 查找event开头的设备文件if (0 == strncmp(ptr->d_name, "event", 5)) {memset(eventfile, 0, sizeof(eventfile)); // 清空路径缓冲区sprintf(eventfile, "/dev/input/%s", ptr->d_name); // 构造完整路径openevent(eventfile); // 打开设备文件}}
closedir(dir); // 关闭目录return;
}
/*** 打开内核uevent socket - 设计模式:观察者模式* 性能分析:创建netlink socket,接收内核热插拔事件* @return 成功返回0,失败返回-1*/
static int openUeventSocket(void)
{int i = 0;int s = -1;int ret = 0;int buffersize = 1024; // 接收缓冲区大小struct sockaddr_nl addr; // netlink地址结构
memset(&addr, 0, sizeof(addr)); // 清空地址结构addr.nl_family = AF_NETLINK; // netlink地址族addr.nl_pid = getpid(); // 当前进程ID作为端口IDaddr.nl_groups = 0xffffffff; // 监听所有多播组
// 重试创建netlink socket直到成功while ((s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT)) == -1) {printf("[EventManager - Error] socket() for uevent failed: %s,""try after 500ms.\n", strerror(errno));usleep(500 * 1000); // 等待500ms后重试}
// 设置接收缓冲区大小if (-1 == setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize))) {printf("[EventManager - Error] SO_RCVBUFFORCE failed: %s,""kernel uevent support is disabled!!!\n", strerror(errno));ret = -1;goto err_exit;}
// 绑定netlink socketif (-1 == bind(s, (struct sockaddr *)&addr, sizeof(addr))) {printf("[EventManager - Error] bind for kernel uevent failed: %s,""kernel uevent support is disabled!!!\n", strerror(errno));ret = -1;goto err_exit;}
// 在事件fd数组中存储uevent socketfor (i = 0; i < EVENT_FD_SIZE; i++) {if (event_fd[i] < 0) {event_fd[i] = s;break;}}device_fd = s; // 保存uevent设备fd
return ret;
err_exit:if (s >= 0)close(s); // 错误时关闭socketreturn ret;
}
/*** 关闭所有键盘输入设备 - 设计模式:资源清理模式* 性能分析:关闭所有打开的设备文件,退出时调用*/
static void closeKeyboard()
{int i = 0;
// 遍历关闭所有事件文件描述符for (i = 0; i < EVENT_FD_SIZE; i++) {if (event_fd[i] > 0) {close(event_fd[i]);event_fd[i] = -1; // 重置为无效值}}
}
/*** 处理内核uevent设备事件 - 设计模式:状态机模式 + 过滤器模式* 性能分析:字符串解析和模式匹配,可能成为性能瓶颈* @param fd uevent socket文件描述符* @return 成功返回0,错误返回相应错误码*/
static int handle_device(int fd)
{int i = 0;char msg[UEVENT_MSG_LEN + 2] = {}; // uevent消息缓冲区,+2保证结尾char cred_msg[CMSG_SPACE(sizeof(struct ucred))] = {}; // 证书消息缓冲区struct sockaddr_nl snl; // netlink源地址struct iovec iov = {msg, sizeof(msg)}; // 分散聚集I/O向量struct msghdr hdr = {&snl, sizeof(snl), &iov, 1, cred_msg, sizeof(cred_msg), 0}; // 消息头tang_event event; // 事件结构
// 接收uevent消息ssize_t n = recvmsg(fd, &hdr, 0);if (n == 0) { /* 对端有序关闭 */printf("[EventManager - Error] kernel uevent server shutdown,""kernel uevent support will disable soon!!!\n");return EBADFD; // 文件描述符错误} else if (n < 0 || n >= UEVENT_MSG_LEN) { /* 接收错误 || 接收溢出 */printf("[EventManager - Error] recvmsg kernel uevent failed: %s,""Abandon this time.\n", strerror(errno));return -1;}
/* 忽略非内核netlink多播消息 */if ((snl.nl_groups != 1) || (snl.nl_pid != 0)) {printf("[EventManager - Error] bad kernel uevent,""Abandon this time.\n");return -1;}
/* 处理整个字符串:将所有'\0'替换为'\n'便于显示 */for (i = 0; i < n; i++)if (*(msg + i) == '\0')msg[i] = '\n';
// 初始化事件结构memset(&event, 0, sizeof(event));event.type = EVENT_MISC;// 线路输入设备事件处理 (M150平台)if (strstr(msg, "change@/devices/platform/jz-linein")) {strcpy(event.event.misc.name, "linein");if (strstr(msg, "LINEIN_STATE=IN") != NULL)strcpy(event.event.misc.type, "plugin"); // 插入else if (strstr(msg, "LINEIN_STATE=OUT"))strcpy(event.event.misc.type, "plugout"); // 拔出// 线路输入设备事件处理 (X1000平台)} else if (strstr(msg, "change@/devices/virtual/switch/linein")) {strcpy(event.event.misc.name, "linein");if (strstr(msg, "SWITCH_STATE=1") != NULL)strcpy(event.event.misc.type, "plugin");else if (strstr(msg, "SWITCH_STATE=0"))strcpy(event.event.misc.type, "plugout");// 耳机设备事件处理} else if (strstr(msg, "change@/devices/virtual/switch/h2w")) {strcpy(event.event.misc.name, "headset");if (strstr(msg, "SWITCH_STATE=1") != NULL)strcpy(event.event.misc.type, "plugin");else if (strstr(msg, "SWITCH_STATE=0"))strcpy(event.event.misc.type, "plugout");// SPDIF数字音频接口事件} else if (strstr(msg, "change@/devices/virtual/switch/spdif")) {strcpy(event.event.misc.name, "spdif");if (strstr(msg, "SWITCH_STATE=1") != NULL)strcpy(event.event.misc.type, "plugin");else if (strstr(msg, "SWITCH_STATE=0"))strcpy(event.event.misc.type, "plugout");// TF卡插入事件} else if (strstr(msg, "add@/devices/platform/jzmmc.1") ||strstr(msg, "add@/devices/platform/jzmmc_v1.2.0")) {strcpy(event.event.misc.name, "tfcard");strcpy(event.event.misc.type, "plugin");// TF卡拔出事件} else if (strstr(msg, "remove@/devices/platform/jzmmc.1") ||strstr(msg, "remove@/devices/platform/jzmmc_v1.2.0")) {strcpy(event.event.misc.name, "tfcard");strcpy(event.event.misc.type, "plugout");// U盘插入事件} else if (strstr(msg, "add@/devices/platform/jz-dwc2/dwc2/usb1/1-1/1-1:1.0/host") && strstr(msg, "/block/sd")) {strcpy(event.event.misc.name, "udisk");strcpy(event.event.misc.type, "plugin");// U盘拔出事件} else if (strstr(msg, "remove@/devices/platform/jz-dwc2/dwc2/usb1/1-1/1-1:1.0/host") && strstr(msg, "/block/sd")) {strcpy(event.event.misc.name, "udisk");strcpy(event.event.misc.type, "plugout");// USB设备状态变化事件} else if (strstr(msg, "change@/devices/virtual/android_usb/android0")) {strcpy(event.event.misc.name, "usb");if (strstr(msg, "USB_STATE=CONNECTED"))strcpy(event.event.misc.type, "connected"); // 已连接else if (strstr(msg, "USB_STATE=DISCONNECTED"))strcpy(event.event.misc.type, "disconnected"); // 已断开else if (strstr(msg, "USB_STATE=CONFIGURED"))strcpy(event.event.misc.type, "configured"); // 已配置// USB音频设备事件} else if (strstr(msg,"change@/devices/platform/jz-dwc2")){strcpy(event.event.misc.name, "uaudio");if (strstr(msg, "USB_STATE=CONNECTED")){strcpy(event.event.misc.type, "connected");}else if (strstr(msg, "USB_STATE=DISCONNECTED")){strcpy(event.event.misc.type, "disconnected");}// 新输入设备添加事件} else if (strstr(msg, "add@/devices/virtual/input/")) {char new_input_dev[32] = {};char *tmp = NULL;int num = 0;tmp = strstr(msg, "DEVNAME=input/event");if (tmp) {// 解析事件编号sscanf(tmp + strlen("DEVNAME=input/event"), "%d", &num);sprintf(new_input_dev, "/dev/input/event%d", num);num = 100; // 重试次数// 等待设备文件创建完成while (num--) {if (!access(new_input_dev, 0)) { // 检查文件是否存在openevent(new_input_dev); // 打开新设备break;}usleep(10 *1000); // 等待10ms}if (num <= 0)printf("[EventManager - Error] %s NOT create after 1 Second\n", new_input_dev);}// 电池状态变化事件} else if (!strncmp(msg, "change@", 7)) {char *tmp = strrchr(msg, '/'); // 查找最后一个斜杠if (tmp && !strncmp(tmp, "/battery", 8)) {strcpy(event.event.misc.name, "battery");strcpy(event.event.misc.type, "change");} else {return 0; // 不支持的change事件}// 其他未支持的事件} else {
#if 0 // 仅调试时启用printf("Unsupport uevent, Ignore it.\n");printf("\n\n---------msg--------\n""%s""\n----------msg-------\n\n", msg);
#endifreturn 0; // 忽略不支持的事件}
// 分发处理好的事件return distribute_event(event);
}
/*** 检查按键事件是否有效 - 设计模式:过滤器模式* 性能分析:简单类型检查,开销很小* @param event 输入事件* @return 有效返回true,无效返回false*/
static bool keyevent_is_valid(struct input_event event)
{// 检查事件类型:按键、相对坐标、绝对坐标if (event.type == EV_KEY || event.type == EV_REL || event.type == EV_ABS)return true;return false;
}
/*** 处理键盘输入事件 - 设计模式:适配器模式* 性能分析:系统调用read开销,事件触发频率决定性能影响* @param fd 输入设备文件描述符* @return 成功返回0,失败返回-1*/
static int handle_key(int fd)
{int i = 0;struct tang_event event;
event.type = EVENT_KEY;// 读取输入事件数据if (read(fd, &event.event.key.key, sizeof(event.event.key.key)) !=sizeof(event.event.key.key)) {// 设备不存在错误if (errno == ENODEV) {close(fd); // 关闭无效设备// 从事件fd数组中移除for (i = 0; i < EVENT_FD_SIZE; i++) {if (event_fd[i] == fd) {event_fd[i] = -1;break;}}} else {printf("[EventManager - Error] read keyboard failed: %s,""Abandon this time.\n", strerror(errno));}return -1;}
// 检查事件有效性并分发if (keyevent_is_valid(event.event.key.key)) {
#if 0 // 仅调试时启用printf("type: %d, code: %d, value: %d.\n",event.event.key.key.type,event.event.key.key.code,event.event.key.key.value);
#endifreturn distribute_event(event);} else {return 0; // 无效事件忽略}
}
/*** 处理文件描述符事件 - 设计模式:策略模式* 性能分析:简单分支判断,开销很小* @param fd 就绪的文件描述符* @return 处理结果*/
static int handle_fd(int fd)
{
#if 0 // 仅调试时启用printf("fd %d can be read.\n", fd);
#endif// 根据fd类型选择处理函数if (device_fd >= 0 && fd == device_fd)return handle_device(fd); // uevent设备elsereturn handle_key(fd); // 键盘输入设备
}
/*** 主事件循环 - 设计模式:Reactor模式* 性能分析:select系统调用,O(n)复杂度,连接数多时考虑epoll*/
void eventLoop(void)
{fd_set rfds; // 读文件描述符集合int i = 0;int ret = 0;
while (1) { // 无限事件循环FD_ZERO(&rfds); // 清空描述符集合// 设置所有有效的事件fd到集合中for (i = 0; i < EVENT_FD_SIZE; i++) {if (event_fd[i] >= 0) {FD_SET(event_fd[i], &rfds);} else {break; // 遇到无效fd提前结束}}
// 计算最大文件描述符max_fd = -1;for (i = 0; i < EVENT_FD_SIZE; i++) {if (event_fd[i] > 0) {max_fd = event_fd[i] > max_fd ? event_fd[i] : max_fd;}}
#if 0 // 仅调试时启用printf("max_fd = %d.\n", max_fd);printf("device_fd = %d.\n", device_fd);for (i = 0; i < EVENT_FD_SIZE; i++)if (event_fd[i] > 0)printf("event[%d] = %d.\n", i, event_fd[i]);
#endif
// 等待事件就绪ret = select(max_fd + 1, &rfds, NULL, NULL, NULL);if (ret < 0) {printf("[EventManager - Error] select() for key_manager failed: %s,""Re-try after 500ms.\n", strerror(errno));usleep(500 * 1000);continue; // 错误时重试}
// 处理所有就绪的文件描述符for (i = 0; i < EVENT_FD_SIZE; i++) {if (event_fd[i] >= 0 && FD_ISSET(event_fd[i], &rfds))if (handle_fd(event_fd[i]) == EBADFD) {// 文件描述符错误,关闭并移除close(event_fd[i]);event_fd[i] = -1;}}}
}
/*** 处理客户端注册请求线程 - 设计模式:生产者-消费者模式* 性能分析:accept阻塞调用,连接建立开销* @param data 线程参数(未使用)* @return NULL*/
void *process_handler_request(void *data)
{int i = 0;int sockfd = -1, connfd = -1;struct sockaddr_in cliaddr; // 客户端地址socklen_t addrlen = sizeof(struct sockaddr_in);
// 启动TCP服务器监听注册端口sockfd = start_tcp_server(SERVER_ADDR, SERVER_REGISTER_PORT);
if (sockfd == -1) {printf("start tcp server failed, Protocol event support is disabled!!!.\n");return NULL;}
// 开始监听if (-1 == listen(sockfd, 10)) { // backlog=10printf("[EventManager - Error] listen for handle handler request failed: %s,""Protocol event support is disabled!!!.\n", strerror(errno));close(sockfd);return NULL;}
// 接受客户端连接循环while (1) {connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &addrlen);if (-1 == connfd) {printf("[EventManager - Warning] accept recv protocol connect failed: %s,""Ignore this time!!!.\n", strerror(errno));continue; // 接受错误继续循环}
// 在连接fd数组中存储新连接for (i = 0; i < CONN_FD_SIZE; i++) {if (conn_fd[i] < 0) {conn_fd[i] = connfd;break;}}// 连接数达到上限if (i == CONN_FD_SIZE) {printf("[EventManager - Warning] Too many connection already, Will not accept new request.\n");break; // 退出循环}}
// 清理所有连接for (i = 0; i < CONN_FD_SIZE; i++) {if (conn_fd[i] > 0)close(conn_fd[i]);}close(sockfd); // 关闭监听socketreturn NULL;
}
/*** 处理协议事件线程 - 设计模式:请求-响应模式* 性能分析:每个事件新建连接,性能较差,应考虑长连接* @param args 线程参数(未使用)* @return NULL*/
static void *process_protocol_event(void *args)
{int sockfd = -1;int connfd = -1;int ret = 0;struct tang_event event;struct sockaddr_in cliaddr;socklen_t addrlen = sizeof(struct sockaddr_in);
// 启动TCP服务器监听事件端口sockfd = start_tcp_server(SERVER_ADDR, SERVER_EVENT_PORT);
if (-1 == listen(sockfd, 10)) {printf("[EventManager - Error] listen for recv protocol event failed: %s,""Protocol event support is disabled!!!.\n", strerror(errno));return NULL;}
// 事件处理循环while (1) {connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &addrlen);if (-1 == connfd) {printf("[EventManager - Warning] accept recv protocol connect failed: %s,""Ignore this time!!!.\n", strerror(errno));continue;}// 读取事件数据ret = read(connfd, &event, sizeof(event));if (ret != sizeof(event)) {if (ret == 0) {printf("[EventManager - Warning ] connection peer shutdown, close it.\n");continue;} else {printf("[EventManager - Error] recv protocol failed: %s,""Abandon this time.\n", strerror(errno));write(connfd, "0", 2); // 发送失败响应continue;}}
/* 反馈处理结果 */write(connfd, "1", 2); // 发送成功响应close(connfd); // 关闭连接
distribute_event(event); // 分发事件}
return NULL;
}
/*** 信号描述字符串数组 - 设计模式:查表模式* 性能分析:编译时常量,无运行时开销*/
static char *signal_str[] = {[1] = "SIGHUP", [2] = "SIGINT", [3] = "SIGQUIT", [4] = "SIGILL", [5] = "SIGTRAP",[6] = "SIGABRT", [7] = "SIGBUS", [8] = "SIGFPE", [9] = "SIGKILL", [10] = "SIGUSR1",[11] = "SIGSEGV", [12] = "SIGUSR2", [13] = "SIGPIPE", [14] = "SIGALRM", [15] = "SIGTERM",[16] = "SIGSTKFLT", [17] = "SIGCHLD", [18] = "SIGCONT", [19] = "SIGSTOP", [20] = "SIGTSTP",[21] = "SIGTTIN", [22] = "SIGTTOU", [23] = "SIGURG", [24] = "SIGXCPU", [25] = "SIGXFSZ",[26] = "SIGVTALRM", [27] = "SIGPROF", [28] = "SIGWINCH", [29] = "SIGIO", [30] = "SIGPWR",[31] = "SIGSYS", [34] = "SIGRTMIN", [35] = "SIGRTMIN+1", [36] = "SIGRTMIN+2", [37] = "SIGRTMIN+3",[38] = "SIGRTMIN+4", [39] = "SIGRTMIN+5", [40] = "SIGRTMIN+6", [41] = "SIGRTMIN+7", [42] = "SIGRTMIN+8",[43] = "SIGRTMIN+9", [44] = "SIGRTMIN+10", [45] = "SIGRTMIN+11", [46] = "SIGRTMIN+12", [47] = "SIGRTMIN+13",[48] = "SIGRTMIN+14", [49] = "SIGRTMIN+15", [50] = "SIGRTMAX-14", [51] = "SIGRTMAX-13", [52] = "SIGRTMAX-12",[53] = "SIGRTMAX-11", [54] = "SIGRTMAX-10", [55] = "SIGRTMAX-9", [56] = "SIGRTMAX-8", [57] = "SIGRTMAX-7",[58] = "SIGRTMAX-6", [59] = "SIGRTMAX-5", [60] = "SIGRTMAX-4", [61] = "SIGRTMAX-3", [62] = "SIGRTMAX-2",[63] = "SIGRTMAX-1", [64] = "SIGRTMAX",
};
/*** 信号处理函数 - 设计模式:回调模式* 性能分析:信号处理上下文,应避免复杂操作* @param signo 信号编号*/
void sig_handler(int signo)
{char cmd[64] = {};printf("\n\n[%s: %d] event_manager crashed by signal %s.\n", __func__, __LINE__, signal_str[signo]);
// 处理崩溃信号if (signo == SIGSEGV || signo == SIGBUS ||signo == SIGTRAP || signo == SIGABRT) {sprintf(cmd, "cat /proc/%d/maps", getpid()); // 构造查看进程内存映射命令printf("Process maps:\n");system(cmd); // 执行命令显示内存映射}
closeKeyboard(); // 清理资源
exit(-1); // 退出进程
}
/*** 主函数 - 设计模式:组合模式* 性能分析:初始化阶段,一次性设置* @param argc 参数个数* @param argv 参数数组* @return 程序退出码*/
int main(int argc, char *argv[])
{pthread_t process_request_thread; // 客户端请求处理线程pthread_t process_protocol_thread;; // 协议事件处理线程
// 信号处理设置signal(SIGINT, sig_handler); // 中断信号signal(SIGUSR1, sig_handler); // 用户定义信号1signal(SIGUSR2, sig_handler); // 用户定义信号2signal(SIGTERM, sig_handler); // 终止信号signal(SIGBUS, sig_handler); // 总线错误signal(SIGSEGV, sig_handler); // 段错误signal(SIGABRT, sig_handler); // 中止信号signal(SIGPIPE, SIG_IGN); // 忽略管道破裂信号
// 初始化输入设备监控openKeyboard();openUeventSocket();
// 创建客户端请求处理线程while (0 != pthread_create(&process_request_thread, NULL, process_handler_request, NULL)) {printf("[EventManager - Error] create thread for key_manager failed: %s,""Re-try after 500ms.\n", strerror(errno));usleep(500 * 1000);}pthread_detach(process_request_thread); /* 忽略分离错误,可能内存泄漏 */
// 创建协议事件处理线程while (0 != pthread_create(&process_protocol_thread, NULL, process_protocol_event, NULL)) {printf("[EventManager - Error] create thread for key_manager failed: %s,""Re-try after 500ms.\n", strerror(errno));usleep(500 * 1000);}pthread_detach(process_protocol_thread); /* 忽略分离错误,可能内存泄漏 */
// 进入主事件循环eventLoop();
return 0;
}这是一个典型的事件驱动架构,适合嵌入式系统和IoT设备的事件管理需求:
事件源层:键盘输入 → 设备热插拔 → 网络事件↓ ↓ ↓ 事件采集层:handle_key() → handle_device() → process_protocol_event()↓ ↓ ↓ 事件分发层:distribute_event()↓所有注册客户端
Linux物联网设备测试框架架构
整体架构设计
测试框架架构树形结构: Test_Framework/ ├── 核心管理层 (Core Management) │ ├── 测试执行引擎 │ ├── 事件监控器 │ ├── 结果收集器 │ └── 报告生成器 ├── 设备模拟层 (Device Simulation) │ ├── 输入设备模拟 │ ├── 热插拔设备模拟 │ ├── 网络事件模拟 │ └── 协议事件模拟 ├── 事件处理层 (Event Processing) │ ├── 事件分发核心 (您的代码) │ ├── 事件过滤器 │ ├── 事件路由器 │ └── 事件持久化 └── 测试用例层 (Test Cases)├── 功能测试├── 性能测试├── 可靠性测试└── 兼容性测试 对比Linux事件管理树形结构: Linux_Event_Management/ ├── 事件源层 (Event Sources) │ ├── 内核uevent (netlink) │ ├── 输入子系统 (/dev/input/event*) │ └── 网络事件 (TCP sockets) ├── 事件收集层 (Event Collection) │ ├── select/poll/epoll多路复用 │ ├── 设备发现与监控 │ └── 连接管理 ├── 事件处理层 (Event Processing) │ ├── 事件解析与过滤 │ ├── 事件类型识别 │ └── 事件数据转换 └── 事件分发层 (Event Distribution)├── 客户端注册管理├── 事件广播机制└── 反馈处理
模块交互关系
测试程序 → 事件管理器 → 被测设备↓ ↓ ↓ 测试用例 → 事件监听 → 设备状态↓ ↓ ↓ 结果验证 → 事件验证 → 状态验证↓ ↓ ↓ 报告生成 → 日志记录 → 性能统计
测试主程序逐行注解
/*** Linux物联网设备测试框架 - 主测试程序* 设计模式:组合模式 + 观察者模式 + 策略模式* 架构分析:集成测试框架,验证事件分发机制的正确性和性能*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <sys/time.h>
#include "event_interface.h"
#include "common.h"
// 测试统计结构体 - 设计模式:数据对象模式
typedef struct test_stats {int total_events; // 总事件数int key_events; // 按键事件数int misc_events; // 杂项事件数int success_count; // 成功事件数int failure_count; // 失败事件数long long total_latency; // 总延迟(微秒)long long max_latency; // 最大延迟(微秒)long long min_latency; // 最小延迟(微秒)
} test_stats;
// 全局测试状态 - 设计模式:单例模式(简化实现)
static test_stats g_stats = {0};
static volatile int g_test_running = 1; // 测试运行标志
static event_handler *g_event_handler = NULL; // 全局事件处理器
/*** 获取当前时间戳(微秒) - 设计模式:工具方法模式* 性能分析:系统调用开销,用于性能测量* @return 当前时间戳(微秒)*/
static long long get_timestamp_us() {struct timeval tv;gettimeofday(&tv, NULL);return (long long)tv.tv_sec * 1000000LL + tv.tv_usec;
}
/*** 事件回调函数 - 设计模式:观察者模式* 性能分析:事件处理核心路径,应保持高效* @param event 接收到的事件* @param param 用户参数(时间戳)*/
static void test_event_callback(tang_event event, void *param) {long long receive_time = get_timestamp_us();long long send_time = *(long long *)param;long long latency = receive_time - send_time;// 更新统计信息g_stats.total_events++;if (event.type == EVENT_KEY) {g_stats.key_events++;printf("[TEST] 收到按键事件: type=%d, code=%d, value=%d\n",event.event.key.key.type,event.event.key.key.code,event.event.key.key.value);} else if (event.type == EVENT_MISC) {g_stats.misc_events++;printf("[TEST] 收到杂项事件: name=%s, type=%s\n",event.event.misc.name,event.event.misc.type);}// 更新延迟统计g_stats.total_latency += latency;if (latency > g_stats.max_latency) g_stats.max_latency = latency;if (g_stats.min_latency == 0 || latency < g_stats.min_latency) g_stats.min_latency = latency;g_stats.success_count++;printf("[TEST] 事件延迟: %lld us\n", latency);
}
/*** 模拟按键事件测试 - 设计模式:工厂方法模式* 性能分析:模拟用户输入,测试事件响应能力* @param key_code 按键代码* @param value 按键值(0释放,1按下,2长按)* @return 成功返回0,失败返回-1*/
static int test_simulate_key_event(int key_code, int value) {tang_event event;long long send_time = get_timestamp_us();// 构造按键事件memset(&event, 0, sizeof(event));event.type = EVENT_KEY;event.event.key.type = EVENT_KEY;event.event.key.key.type = EV_KEY; // 按键事件类型event.event.key.key.code = key_code; // 按键代码event.event.key.key.value = value; // 按键值printf("[TEST] 发送按键事件: code=%d, value=%d\n", key_code, value);// 发送事件并测量延迟int ret = tang_event_send(event);if (ret != 0) {printf("[TEST-ERROR] 发送按键事件失败: %d\n", ret);g_stats.failure_count++;return -1;}return 0;
}
/*** 模拟设备热插拔事件测试 - 设计模式:策略模式* 性能分析:模拟设备状态变化,测试热插拔处理能力* @param dev_name 设备名称* @param event_type 事件类型(plugin/plugout)* @return 成功返回0,失败返回-1*/
static int test_simulate_device_event(const char *dev_name, const char *event_type) {tang_event event;long long send_time = get_timestamp_us();// 构造设备事件memset(&event, 0, sizeof(event));event.type = EVENT_MISC;strncpy(event.event.misc.name, dev_name, sizeof(event.event.misc.name) - 1);strncpy(event.event.misc.type, event_type, sizeof(event.event.misc.type) - 1);printf("[TEST] 发送设备事件: device=%s, action=%s\n", dev_name, event_type);int ret = tang_event_send(event);if (ret != 0) {printf("[TEST-ERROR] 发送设备事件失败: %d\n", ret);g_stats.failure_count++;return -1;}return 0;
}
/*** 性能压力测试 - 设计模式:模板方法模式* 性能分析:高频率事件发送,测试系统承载能力* @param event_count 事件数量* @param interval_us 事件间隔(微秒)*/
static void test_performance_stress(int event_count, int interval_us) {printf("[TEST] 开始性能压力测试: 事件数=%d, 间隔=%dus\n", event_count, interval_us);long long start_time = get_timestamp_us();for (int i = 0; i < event_count && g_test_running; i++) {// 交替发送按键和设备事件if (i % 2 == 0) {test_simulate_key_event(KEY_A + (i % 10), i % 2);} else {const char *devices[] = {"tfcard", "udisk", "linein", "headset"};const char *actions[] = {"plugin", "plugout"};test_simulate_device_event(devices[i % 4], actions[i % 2]);}usleep(interval_us);}long long end_time = get_timestamp_us();long long total_time = end_time - start_time;printf("[TEST] 性能测试完成: 总时间=%lld us, 平均延迟=%lld us\n",total_time, g_stats.total_latency / g_stats.total_events);
}
/*** 功能正确性测试 - 设计模式:测试用例模式* 性能分析:验证基本功能,确保系统正确性*/
static void test_functional_correctness() {printf("[TEST] === 开始功能正确性测试 ===\n");// 测试各种按键事件printf("[TEST] --- 按键事件测试 ---\n");test_simulate_key_event(KEY_POWER, 1); // 电源键按下usleep(100000);test_simulate_key_event(KEY_POWER, 0); // 电源键释放usleep(100000);test_simulate_key_event(KEY_VOLUMEUP, 1); // 音量+按下usleep(100000);test_simulate_key_event(KEY_VOLUMEDOWN, 1); // 音量-按下usleep(100000);// 测试设备热插拔事件printf("[TEST] --- 设备热插拔测试 ---\n");test_simulate_device_event("tfcard", "plugin");usleep(200000);test_simulate_device_event("tfcard", "plugout");usleep(200000);test_simulate_device_event("udisk", "plugin");usleep(200000);test_simulate_device_event("udisk", "plugout");usleep(200000);test_simulate_device_event("headset", "plugin");usleep(200000);test_simulate_device_event("headset", "plugout");usleep(200000);printf("[TEST] === 功能正确性测试完成 ===\n");
}
/*** 可靠性测试 - 设计模式:容错测试模式* 性能分析:测试系统在异常情况下的表现*/
static void test_reliability() {printf("[TEST] === 开始可靠性测试 ===\n");// 测试快速连续事件printf("[TEST] --- 快速连续事件测试 ---\n");for (int i = 0; i < 50 && g_test_running; i++) {test_simulate_key_event(KEY_MENU + (i % 5), i % 2);usleep(10000); // 10ms间隔}// 测试混合事件流printf("[TEST] --- 混合事件流测试 ---\n");for (int i = 0; i < 20 && g_test_running; i++) {if (i % 3 == 0) {test_simulate_key_event(KEY_HOME, 1);} else if (i % 3 == 1) {test_simulate_device_event("linein", "plugin");} else {test_simulate_device_event("spdif", "plugout");}usleep(50000); // 50ms间隔}printf("[TEST] === 可靠性测试完成 ===\n");
}
/*** 打印测试统计信息 - 设计模式:报告生成模式* 性能分析:统计汇总,不影响性能测试*/
static void print_test_statistics() {printf("\n[TEST] ==== 测试统计报告 ====\n");printf("[TEST] 总事件数: %d\n", g_stats.total_events);printf("[TEST] 按键事件: %d\n", g_stats.key_events);printf("[TEST] 杂项事件: %d\n", g_stats.misc_events);printf("[TEST] 成功事件: %d\n", g_stats.success_count);printf("[TEST] 失败事件: %d\n", g_stats.failure_count);if (g_stats.total_events > 0) {printf("[TEST] 平均延迟: %lld us\n", g_stats.total_latency / g_stats.total_events);printf("[TEST] 最大延迟: %lld us\n", g_stats.max_latency);printf("[TEST] 最小延迟: %lld us\n", g_stats.min_latency);printf("[TEST] 成功率: %.2f%%\n", (float)g_stats.success_count / g_stats.total_events * 100);}printf("[TEST] ==== 统计报告结束 ====\n");
}
/*** 信号处理函数 - 设计模式:资源清理模式* 性能分析:优雅退出,确保资源释放* @param sig 信号编号*/
static void test_signal_handler(int sig) {printf("\n[TEST] 收到信号 %d,停止测试...\n", sig);g_test_running = 0;// 等待事件处理完成sleep(1);// 打印最终统计print_test_statistics();// 清理资源if (g_event_handler) {tang_event_handler_put(g_event_handler);g_event_handler = NULL;}exit(0);
}
/*** 测试初始化 - 设计模式:初始化模式* 性能分析:一次性设置,确保测试环境准备就绪* @return 成功返回0,失败返回-1*/
static int test_initialize() {printf("[TEST] 初始化测试环境...\n");// 设置信号处理signal(SIGINT, test_signal_handler);signal(SIGTERM, test_signal_handler);// 初始化统计信息memset(&g_stats, 0, sizeof(g_stats));g_stats.min_latency = 0;// 创建事件处理器long long *timestamp_ptr = malloc(sizeof(long long));*timestamp_ptr = get_timestamp_us();g_event_handler = tang_event_handler_get(test_event_callback, timestamp_ptr);if (!g_event_handler) {printf("[TEST-ERROR] 创建事件处理器失败\n");free(timestamp_ptr);return -1;}printf("[TEST] 测试环境初始化完成\n");return 0;
}
/*** 测试清理 - 设计模式:资源管理模式* 性能分析:确保资源正确释放,避免内存泄漏*/
static void test_cleanup() {printf("[TEST] 清理测试资源...\n");if (g_event_handler) {// 获取回调参数并释放void *param = g_event_handler->param;tang_event_handler_put(g_event_handler);g_event_handler = NULL;if (param) {free(param);}}printf("[TEST] 测试资源清理完成\n");
}
/*** 主测试函数 - 设计模式:组合模式* 性能分析:测试流程控制,协调各个测试模块* @return 程序退出码*/
int main(int argc, char *argv[]) {printf("=========================================\n");printf(" Linux物联网设备事件框架测试程序\n");printf("=========================================\n");// 初始化测试环境if (test_initialize() != 0) {printf("[TEST-ERROR] 测试初始化失败\n");return -1;}// 等待事件管理器启动printf("[TEST] 等待事件管理器启动...\n");sleep(2);// 执行测试套件printf("[TEST] 开始执行测试套件...\n");// 1. 功能正确性测试test_functional_correctness();if (!g_test_running) goto cleanup;// 2. 可靠性测试test_reliability();if (!g_test_running) goto cleanup;// 3. 性能压力测试printf("[TEST] === 开始性能压力测试 ===\n");test_performance_stress(100, 20000); // 100个事件,20ms间隔if (!g_test_running) goto cleanup;// 4. 极限性能测试printf("[TEST] === 开始极限性能测试 ===\n");test_performance_stress(50, 1000); // 50个事件,1ms间隔cleanup:// 打印最终统计print_test_statistics();// 清理资源test_cleanup();printf("[TEST] 测试程序退出\n");return 0;
}关键测试函数树形结构对比分析
测试框架函数树
test_main ├── 初始化层 │ ├── test_initialize() # 测试环境初始化 │ ├── test_event_callback() # 事件回调处理 │ └── test_signal_handler() # 信号处理 ├── 测试用例层 │ ├── test_functional_correctness() # 功能正确性测试 │ ├── test_reliability() # 可靠性测试 │ └── test_performance_stress() # 性能压力测试 ├── 模拟事件层 │ ├── test_simulate_key_event() # 模拟按键事件 │ └── test_simulate_device_event() # 模拟设备事件 └── 结果分析层├── print_test_statistics() # 统计报告生成└── test_cleanup() # 资源清理
与Linux事件管理树形结构对比
对比关系: 测试框架层 ↔ 事件管理层 ----------- ----------- 测试初始化 ↔ 事件管理器启动 测试用例执行 ↔ 事件监听循环 事件模拟发送 ↔ 事件产生源 事件回调验证 ↔ 事件分发处理 统计结果分析 ↔ 事件日志记录 交互流程: 测试程序 → 发送模拟事件 → 事件管理器 → 分发事件 → 测试程序回调↓ ↓←-------------- 验证事件 ----------------→↓ ↓ 性能统计 ←----------------------------------- 延迟测量
模块管理机制
1. 事件流管理
// 测试事件流 测试用例 → 事件模拟 → 网络发送 → 事件管理器 → 网络接收 → 回调验证↓ ↓ ↓ ↓ ↓ ↓控制 数据构造 传输协议 核心处理 传输协议 结果验证
2. 资源管理
// 资源生命周期 初始化 → 测试执行 → 结果收集 → 资源释放↓ ↓ ↓ ↓ 内存分配 事件发送 统计计算 内存释放 连接建立 事件接收 延迟测量 连接关闭
3. 性能管理
// 性能监控链 事件发送时间戳 → 网络传输 → 事件处理 → 网络传输 → 回调接收时间戳↓ ↓ ↓ ↓ ↓T1 Δ1 Δ2 Δ3 T2↓ ↓总延迟 = T2 - T1处理延迟 = Δ2
这个测试框架完整地验证了事件分发机制的各个方面,包括功能正确性、可靠性、性能表现等,为Linux物联网设备程序提供了全面的测试保障。
