源码树形结构

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_manager
    rm -rf $(DESTDIR)$(LIBDIR)/libevent.so
    rm -rf $(DESTDIR)$(INITDIR)/S01event.sh
    rm -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:TCP
    if (sockfd == -1) {
        printf("[EventManager - Error] socket() error for connect_to_tcp_server failed: %s.\n",
               strerror(errno));  // 输出套接字创建错误
        return -1;
    }
​
    // 设置服务器地址信息
    seraddr.sin_family = AF_INET;              // 地址族:IPv4
    seraddr.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/change
    const 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并增加i
                break;
            }
        }
        // 数组已满
        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作为端口ID
    addr.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 socket
    if (-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 socket
    for (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);  // 错误时关闭socket
    return 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);
#endif
        return 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);
#endif
        return 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设备
    else
        return 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=10
        printf("[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);  // 关闭监听socket
    return 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);  // 用户定义信号1
    signal(SIGUSR2, sig_handler);  // 用户定义信号2
    signal(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物联网设备程序提供了全面的测试保障。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐