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终止进程。

框架结构示意

事件源 中断/外设/定时器 ev_post() 事件队列 FIFO 环形缓冲 容量 32 条 取出 事件循环 while(1) 分发 ev_loop() 回调 处理函数表 handlers[type] 注册回调函数 ev_register()

边界与限制

本框架为教学最小实现:事件类型上限16种,队列深度32条,采用忙等待方式无休眠策略。实际产品中可增加优先级、动态内存事件、多消费者或接入RTOS信号量。当前实现已完整覆盖事件注册、投递、分发三个核心环节,可直接编译运行验证。