当前位置: 首页 > news >正文

架构一个完整的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物联网设备程序提供了全面的测试保障。

http://www.dtcms.com/a/542083.html

相关文章:

  • 计算机视觉:python老照片修复系统 卷积神经网络CNN算法 图片修复 深度学习 Django框架 pytorch 大数据(建议收藏)✅
  • 深圳网站建设公司选全通网络东莞网站建设咨询公
  • C 语言进制转换全景指南
  • 前端速通—Vue_简介 第一个Vue程序 el:挂载点 data:数据对象 Vue指令
  • Vue 3 + Element Plus 动态通用表单组件设计与实现
  • 网站开发用到什么技术wordpress的安装界面
  • c 开发商城网站开发nodejs同时做网站和后台管理
  • 开发中的英语积累 P10:Capability、Enterprise、Transport、Loop、Active、Host
  • 网站建设开发步骤wordpress用旧的编辑器
  • 劳力士手表网站官方网站是 优帮云
  • 中国建设银行网站查询余额网络销售渠道
  • 【FPGA】时序逻辑计数器——仿真验证
  • 2025年--Lc217-145. 二叉树的后序遍历(递归版,带测试用例)-Java版
  • 做直播网站要多少钱北京网页设计
  • 门户网站标题居中加大seo个人博客
  • 【音视频】RTP协议快速上手
  • 阿里云可以做几个网站做网站南昌
  • DM8 分区表学习笔记
  • 做网站有没有免费空间英文网站建设注意什么
  • 3.枚举算法(一)
  • 网站开发需求收集网站建设和维护工作内容
  • 建设银行昆山分行网站wordpress本站导航在哪里
  • 房地产网站建设策划书如何对网站进行维护
  • Lambert W 函数简要探讨
  • 为什么要避免使用 `SELECT *`?
  • 网站怎么做外链接中国建设银行e路护航网银安全组件
  • 网页设计网站名字做网站开发数据库怎么写
  • 创建es索引
  • Spring Boot项目中如何实现接口幂等
  • 深圳高端网站开发网上卖产品怎么推广