高效Linux嵌入式事件驱动框架
本文介绍了一个基于epoll的Linux嵌入式事件驱动框架C语言模板。该框架采用I/O多路复用机制,能够高效监听多个文件描述符事件,支持标准输入、定时器和网络Socket等多种事件源。框架采用模块化设计,包含事件循环核心(event_loop)、回调处理(handlers)和主程序(main)三个主要模块,提供事件注册/移除接口和回调调度机制。核心功能包括:创建/销毁事件循环、事件注册与移除、事件
Linux 嵌入式事件驱动框架(C 语言模板)
本模板提供一个基于 epoll 的事件驱动框架,用于 Linux 嵌入式系统开发。主要特点包括:
-
使用 epoll 实现高效的 I/O 多路复用,能够同时监听多个文件描述符上的可读/可写事件man7.org 。
-
支持 标准输入(STDIN)、定时器(timerfd)、网络 Socket 等事件源,可通过 event_loop_add 注册并指定回调函数。
-
模块化设计,代码分为 event_loop.c/h(事件循环核心)、handlers.c/h(事件处理回调示例)、main.c(主程序),每个模块均有注释和使用说明。
-
提供事件注册/移除接口以及事件回调调度机制。
-
使用 Makefile 构建,可直接编译。
-
易于扩展:未来可以加入 GPIO、signalfd、eventfd 等事件源,例如 signalfd 提供基于文件描述符的信号通知,也能被 epoll 监测reddit.com。
以下是完整的目录结构:
project/
├── event_loop.h
├── event_loop.c
├── handlers.h
├── handlers.c
├── main.c
└── Makefile
1. 事件循环模块(event_loop.c/h)
event_loop 模块实现事件循环的核心逻辑,使用 epoll 管理多个事件源,并在事件发生时调用注册的回调函数。主要功能包括:
- 创建/销毁事件循环:event_loop_create(int max_events) 创建 epoll 实例,并分配最大事件数数组;event_loop_destroy 关闭 epoll 并释放资源。
- 事件注册/移除:event_loop_add 将文件描述符及其事件(例如 EPOLLIN 可读)注册到 epoll,同时保存回调函数和参数;event_loop_remove 从 epoll 和内部列表中移除指定 fd。
- 事件循环执行:event_loop_run 调用 epoll_wait 等待事件发生,并遍历返回事件列表,逐个触发对应的回调函数。event_loop_stop 可用于终止循环(例如在回调中调用以退出)。
代码示例(含注释):
/* event_loop.h: 事件循环框架核心头文件,定义EventLoop结构和API */
#ifndef EVENT_LOOP_H
#define EVENT_LOOP_H
#include <stdint.h>
#include <stdbool.h>
#include <sys/epoll.h>
typedef void (*event_callback_t)(int fd, uint32_t events, void *arg);
typedef struct EventHandler {
int fd; /* 文件描述符 */
event_callback_t callback; /* 事件回调函数 */
void *arg; /* 回调参数 */
struct EventHandler *next;
} EventHandler;
/* EventLoop: 包含 epoll 实例和注册的事件 */
typedef struct {
int epoll_fd; /* epoll 文件描述符 */
struct epoll_event *events; /* 用于存放 epoll_wait 返回事件的数组 */
int max_events; /* 最大事件数 */
EventHandler *handlers; /* 已注册的事件处理链表 */
bool running; /* 事件循环运行标志 */
} EventLoop;
/* 创建事件循环 */
EventLoop *event_loop_create(int max_events);
/* 销毁事件循环,释放资源 */
void event_loop_destroy(EventLoop *loop);
/* 添加事件监听 */
int event_loop_add(EventLoop *loop, int fd, uint32_t events, event_callback_t cb, void *arg);
/* 移除事件监听 */
int event_loop_remove(EventLoop *loop, int fd);
/* 开始事件循环 */
void event_loop_run(EventLoop *loop);
/* 停止事件循环 */
void event_loop_stop(EventLoop *loop);
#endif /* EVENT_LOOP_H */
c
Copy
Edit
/* event_loop.c: 事件循环框架核心实现,使用 epoll */
#include <sys/epoll.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include "event_loop.h"
/* 创建并初始化事件循环 */
EventLoop *event_loop_create(int max_events) {
EventLoop *loop = malloc(sizeof(EventLoop));
if (!loop) return NULL;
loop->epoll_fd = epoll_create1(0);
if (loop->epoll_fd == -1) {
perror("epoll_create1");
free(loop);
return NULL;
}
loop->max_events = max_events;
loop->events = malloc(sizeof(struct epoll_event) * max_events);
if (!loop->events) {
perror("malloc events");
close(loop->epoll_fd);
free(loop);
return NULL;
}
loop->handlers = NULL;
loop->running = false;
return loop;
}
/* 销毁事件循环及其资源 */
void event_loop_destroy(EventLoop *loop) {
if (!loop) return;
close(loop->epoll_fd);
free(loop->events);
EventHandler *handler = loop->handlers;
while (handler) {
EventHandler *tmp = handler;
handler = handler->next;
free(tmp);
}
free(loop);
}
/* 注册事件处理 */
int event_loop_add(EventLoop *loop, int fd, uint32_t events, event_callback_t cb, void *arg) {
if (!loop || fd < 0) return -1;
EventHandler *handler = malloc(sizeof(EventHandler));
if (!handler) return -1;
handler->fd = fd;
handler->callback = cb;
handler->arg = arg;
/* 添加到链表头 */
handler->next = loop->handlers;
loop->handlers = handler;
struct epoll_event ev;
ev.events = events;
ev.data.ptr = handler; /* 将 handler 指针存入 epoll 事件数据 */
if (epoll_ctl(loop->epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
perror("epoll_ctl: add");
/* 失败时,移除并释放 handler */
loop->handlers = handler->next;
free(handler);
return -1;
}
return 0;
}
/* 移除事件处理 */
int event_loop_remove(EventLoop *loop, int fd) {
if (!loop || fd < 0) return -1;
EventHandler *prev = NULL;
EventHandler *handler = loop->handlers;
while (handler) {
if (handler->fd == fd) {
epoll_ctl(loop->epoll_fd, EPOLL_CTL_DEL, fd, NULL);
if (prev) prev->next = handler->next;
else loop->handlers = handler->next;
free(handler);
return 0;
}
prev = handler;
handler = handler->next;
}
return -1;
}
/* 运行事件循环 */
void event_loop_run(EventLoop *loop) {
if (!loop) return;
loop->running = true;
while (loop->running) {
int n = epoll_wait(loop->epoll_fd, loop->events, loop->max_events, -1);
if (n == -1) {
if (errno == EINTR) continue; /* 被信号中断时重试 */
perror("epoll_wait");
break;
}
for (int i = 0; i < n; i++) {
EventHandler *handler = (EventHandler *)loop->events[i].data.ptr;
if (handler && handler->callback) {
handler->callback(handler->fd, loop->events[i].events, handler->arg);
}
}
}
}
/* 停止事件循环 */
void event_loop_stop(EventLoop *loop) {
if (!loop) return;
loop->running = false;
}
2. 事件处理模块(handlers.c/h)
handlers 模块给出了事件回调函数的示例实现。包括:
- handlers.h:声明回调函数原型,如 handle_stdin、handle_timer、handle_socket。
- handlers.c:实现相应事件的处理逻辑。
示例回调说明:handle_stdin 读取标准输入并打印内容;当输入含有 “exit” 时,调用 event_loop_stop 退出循环。handle_timer 读取定时器文件描述符并统计触发次数。handle_socket 接受新的 TCP 连接并发送示例消息。
/* handlers.h: 事件回调函数原型 */
#ifndef HANDLERS_H
#define HANDLERS_H
#include <stdint.h>
void handle_stdin(int fd, uint32_t events, void *arg);
void handle_timer(int fd, uint32_t events, void *arg);
void handle_socket(int fd, uint32_t events, void *arg);
#endif /* HANDLERS_H */
/* handlers.c: 示例事件回调函数 */
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include "event_loop.h"
#include "handlers.h"
/* 处理标准输入事件 */
void handle_stdin(int fd, uint32_t events, void *arg) {
(void)events; /* 未使用参数 */
EventLoop *loop = (EventLoop *)arg;
char buffer[256];
ssize_t count = read(fd, buffer, sizeof(buffer) - 1);
if (count <= 0) return;
buffer[count] = '\0';
printf("STDIN: %s", buffer);
if (strncmp(buffer, "exit", 4) == 0) {
printf("Exit command received, stopping event loop.\n");
event_loop_stop(loop);
}
}
/* 处理定时器事件 */
void handle_timer(int fd, uint32_t events, void *arg) {
(void)events; (void)arg;
uint64_t expirations;
ssize_t s = read(fd, &expirations, sizeof(expirations));
if (s != sizeof(expirations)) {
perror("read timerfd");
return;
}
static int count = 0;
count += expirations;
printf("Timer expired %d times\n", count);
}
/* 处理网络连接事件 */
void handle_socket(int fd, uint32_t events, void *arg) {
(void)events; (void)arg;
int client_fd = accept(fd, NULL, NULL);
if (client_fd == -1) {
perror("accept");
return;
}
printf("Accepted new connection: fd=%d\n", client_fd);
const char *msg = "Hello from server!\n";
send(client_fd, msg, strlen(msg), 0);
close(client_fd);
}
3. 主程序(main.c)
main.c 演示如何使用上述事件循环框架:
- 调用 event_loop_create 创建事件循环。
- 注册标准输入(STDIN_FILENO)的可读事件,回调 handle_stdin。
- 创建 timerfd 定时器(周期 1 秒),并将其注册到事件循环。Linux 的 timerfd 通过文件描述符提供定时器通知,可被 epoll 监测man7.org 。
- (可选)创建一个 TCP 监听 Socket,将其注册到事件循环,回调 handle_socket 处理新连接。
- 调用 event_loop_run 进入循环,直至通过输入或其他方式调用 event_loop_stop 退出。
- 最后调用 event_loop_destroy 清理资源。
/* main.c: 程序入口,初始化事件循环并注册事件 */
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/timerfd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include "event_loop.h"
#include "handlers.h"
#define PORT 12345
int main() {
/* 创建事件循环 */
EventLoop *loop = event_loop_create(10);
if (!loop) {
fprintf(stderr, "Failed to create event loop\n");
return EXIT_FAILURE;
}
/* 注册标准输入事件 (EPOLLIN) */
event_loop_add(loop, STDIN_FILENO, EPOLLIN, handle_stdin, loop);
/* 创建1秒定时器 (timerfd) */
int timer_fd = timerfd_create(CLOCK_MONOTONIC, 0);
if (timer_fd == -1) {
perror("timerfd_create");
} else {
struct itimerspec timerSpec = {0};
timerSpec.it_value.tv_sec = 1;
timerSpec.it_interval.tv_sec = 1;
timerfd_settime(timer_fd, 0, &timerSpec, NULL);
event_loop_add(loop, timer_fd, EPOLLIN, handle_timer, NULL);
}
/* 可选: 创建TCP服务器socket */
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd != -1) {
int flags = fcntl(server_fd, F_GETFL, 0);
fcntl(server_fd, F_SETFL, flags | O_NONBLOCK);
int opt = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(PORT);
if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) == 0 &&
listen(server_fd, 5) == 0) {
printf("Listening on port %d\n", PORT);
event_loop_add(loop, server_fd, EPOLLIN, handle_socket, NULL);
} else {
perror("socket bind/listen");
close(server_fd);
}
}
/* 运行事件循环 */
event_loop_run(loop);
/* 清理资源 */
event_loop_destroy(loop);
return EXIT_SUCCESS;
}
上述代码完成后,可通过执行 make 编译生成 main 可执行文件。下例为 Makefile 内容:
# Makefile: 构建事件驱动框架
CC=gcc
CFLAGS=-Wall -Wextra -std=c99
TARGET=main
SRCS=main.c event_loop.c handlers.c
OBJS=$(SRCS:.c=.o)
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $(TARGET) $(OBJS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f $(TARGET) $(OBJS)
4. 扩展与使用说明
- 本模板演示了基础的事件循环和常见事件源的处理。开发者可在 handlers.c 中添加更多回调函数,根据需要注册新的事件源。
- 例如,可以使用 Linux 的 signalfd 创建一个信号文件描述符,同样通过 event_loop_add 注册到循环。reddit.com指出 signalfd 提供了基于文件描述符的信号通知机制,可由 select/poll/epoll 监测。类似地,eventfd、GPIO(通过 /sys/class/gpio 或字符设备)等也可以作为事件源扩展到此框架中。
- 各模块均配有注释说明,开发者可直接参考此模板进行扩展开发,并按照自己的需求注册和处理其它事件。
5、参考文献:
- Linux 手册等资料、man7.org 、reddit.com提到,timerfd、signalfd 等接口生成的文件描述符都可以被 epoll 所监测,使其非常适合用作事件驱动框架中的事件源。
更多推荐
所有评论(0)