4. 事件循环框架搭建
任务定义
本案例要求搭建一个最小可用的嵌入式事件循环框架,支持事件注册、事件投递和事件分发处理。框架运行在单线程环境中,通过一个循环不断从事件队列中取出事件并调用对应的处理函数。完成目标是:提供一组C语言接口,使得业务模块可以注册事件类型及其回调函数,并能在主循环中异步处理投递的事件。
框架接口设计
/* event_loop.h */
#ifndef EVENT_LOOP_H
#define EVENT_LOOP_H
#include <stdint.h>
#define EV_MAX_EVENTS 16 /* 最大事件类型数 */
#define EV_QUEUE_SIZE 32 /* 事件队列容量 */
typedef enum {
EV_SYS_TIMER = 0, /* 示例事件类型 */
EV_KEY_PRESS,
EV_UART_RX,
EV_USER_START = 10 /* 用户自定义事件从此开始 */
} ev_type_t;
typedef struct {
uint16_t type; /* 事件类型 */
uint16_t param; /* 事件参数(简单场景) */
void *data; /* 可选数据指针 */
} ev_msg_t;
typedef void (*ev_handler_t)(const ev_msg_t *msg);
int ev_init(void);
int ev_register(uint16_t type, ev_handler_t handler);
int ev_post(uint16_t type, uint16_t param, void *data);
void ev_loop(void); /* 永不返回,在主函数中调用 */
#endif /* EVENT_LOOP_H */
框架实现
/* event_loop.c */
#include "event_loop.h"
#include <string.h>
static ev_handler_t handlers[EV_MAX_EVENTS];
static ev_msg_t queue[EV_QUEUE_SIZE];
static volatile uint8_t head = 0;
static volatile uint8_t tail = 0;
static uint8_t count = 0;
int ev_init(void)
{
memset(handlers, 0, sizeof(handlers));
head = 0;
tail = 0;
count = 0;
return 0;
}
int ev_register(uint16_t type, ev_handler_t handler)
{
if (type >= EV_MAX_EVENTS || handler == 0)
return -1;
handlers[type] = handler;
return 0;
}
int ev_post(uint16_t type, uint16_t param, void *data)
{
if (type >= EV_MAX_EVENTS)
return -1;
if (count >= EV_QUEUE_SIZE)
return -1; /* 队列满 */
queue[tail].type = type;
queue[tail].param = param;
queue[tail].data = data;
tail = (tail + 1) % EV_QUEUE_SIZE;
count++;
return 0;
}
void ev_loop(void)
{
while (1) {
if (count == 0)
continue; /* 忙等待,实际可配合休眠 */
ev_msg_t msg = queue[head];
head = (head + 1) % EV_QUEUE_SIZE;
count--;
if (handlers[msg.type] != 0)
handlers[msg.type](&msg);
}
}
使用示例
/* main.c */
#include "event_loop.h"
#include <stdio.h>
static void on_timer(const ev_msg_t *msg)
{
printf("timer fired, param=%u\n", msg->param);
}
static void on_key(const ev_msg_t *msg)
{
printf("key pressed, code=%u\n", msg->param);
}
int main(void)
{
ev_init();
ev_register(EV_SYS_TIMER, on_timer);
ev_register(EV_KEY_PRESS, on_key);
/* 模拟外部中断/外设投递事件 */
ev_post(EV_SYS_TIMER, 100, 0);
ev_post(EV_KEY_PRESS, 1, 0);
ev_post(EV_SYS_TIMER, 200, 0);
ev_loop(); /* 进入事件循环 */
return 0;
}
编译与运行
gcc -Wall -o event_demo main.c event_loop.c
./event_demo
验证方法
程序启动后,事件循环依次取出队列中的3个事件并分发。由于ev_loop()为无限循环,队列清空后程序不会退出。预期在终端看到以下输出(顺序固定,由投递顺序决定):
timer fired, param=100
key pressed, code=1
timer fired, param=200
判定标准:输出与上述顺序和内容一致,且程序保持运行不退出,说明事件注册、队列存取和回调分发均工作正常。若需退出测试,可在main()中投递事件后直接调用ev_loop()前加一个计数退出条件,或使用Ctrl+C终止进程。
框架结构示意
边界与限制
本框架为教学最小实现:事件类型上限16种,队列深度32条,采用忙等待方式无休眠策略。实际产品中可增加优先级、动态内存事件、多消费者或接入RTOS信号量。当前实现已完整覆盖事件注册、投递、分发三个核心环节,可直接编译运行验证。