libevent源码分析--epoll中的几个函数 epoll_init epoll_add epoll_dispatch

本文深入剖析libevent库中与epoll相关的函数,包括epoll_init、epoll_add和epoll_dispatch。首先,文章指出libevent在编译时已选定I/O复用机制,并以epoll为例进行说明。接着,详细解释了结构体的定义及其在初始化过程中的作用,如event_create生成的描述符数组以及如何动态分配内存。最后,重点介绍了epollop结构体的初始化过程,特别是epfd和fds的设置。

这篇文件讲解下上面的几篇文章中欠下的几个函数解释。在看这个之前,请移步这篇文章这里说明了libevent库在编译的时候已经决定了所使用的I/O复用机制,系统中提供了五种机制:在这里指选择一种来说明 epoll

在even-internal.h文件中有一个结构体,

38 struct eventop {
 39     const char *name;
 40     void *(*init)(struct event_base *);
 41     int (*add)(void *, struct event *);
 42     int (*del)(void *, struct event *);
 43     int (*dispatch)(struct event_base *, void *, struct timeval *);
 44     void (*dealloc)(struct event_base *, void *);
 45     /* set if we need to reinitialize the event base */
 46     int need_reinit;
 47 };

这个结构体中的每个字段,都是函数指针,函数指针其实就是C语言中的多态,上面的这五个字段在编译的时候指向某个系统调用中的五个函数。也就是event_base中的evsel指针。在epoll机制中,首先封装了struct event :

59 struct evepoll {
 60     struct event *evread;
 61     struct event *evwrite;
 62 };

接下来又是一个结构体:

64 struct epollop {
 65     struct evepoll *fds;
 66     int nfds;
 67     struct epoll_event *events;
 68     int nevents;
 69     int epfd;
 70 };
然后是五个函数,就是event中需要的五个函数
72 static void *epoll_init (struct event_base *);
 73 static int epoll_add    (void *, struct event *);
 74 static int epoll_del    (void *, struct event *);
 75 static int epoll_dispatch   (struct event_base *, void *, struct timeval *);
 76 static void epoll_dealloc   (struct event_base *, void *);
然后初始化一个结构体, 这个结构体的名字就是epollops

78 const struct eventop epollops = {
 79     "epoll",
 80     epoll_init,
 81     epoll_add,
 82     epoll_del,
 83     epoll_dispatch,
 84     epoll_dealloc,
 85     1 /* need reinit */
 86 };
看在具体的代码中怎么使用上述的内容的:

107 static void *
108 epoll_init(struct event_base *base)
109 {
110     int epfd, nfiles = NEVENT;
111     struct rlimit rl;
112     struct epollop *epollop;
113 
114     /* Disable epollueue when this environment variable is set */
115     if (getenv("EVENT_NOEPOLL"))
116         return (NULL);
117 
118     if (getrlimit(RLIMIT_NOFILE, &rl) == 0 &&
119         rl.rlim_cur != RLIM_INFINITY) {
120         /*
121          * Solaris is somewhat retarded - it's important to drop
122          * backwards compatibility when making changes.  So, don't
123          * dare to put rl.rlim_cur here.
124          */
125         nfiles = rl.rlim_cur - 1;
126     }
127 
128     /* Initalize the kernel queue */
129 
130     if ((epfd = epoll_create(nfiles)) == -1) {
131         if (errno != ENOSYS)
132             event_warn("epoll_create");
133         return (NULL);
134     }
135 
136     FD_CLOSEONEXEC(epfd);
137 
138     if (!(epollop = calloc(1, sizeof(struct epollop))))
139         return (NULL);
140 
141     epollop->epfd = epfd;
142 
143     /* Initalize fields */
144     epollop->events = malloc(nfiles * sizeof(struct epoll_event));
145     if (epollop->events == NULL) {
146         free(epollop);
147         return (NULL);
148     }
149     epollop->nevents = nfiles;
150 
151     epollop->fds = calloc(nfiles, sizeof(struct evepoll));
152     if (epollop->fds == NULL) {
153         free(epollop->events);
154         free(epollop);
155         return (NULL);
156     }
157     epollop->nfds = nfiles;
158 
159     evsignal_init(base);
160 
161     return (epollop);
162 }

 在这篇文章中使用了init,其实如果系统在编译中使用的epol机制,最终调用的就是上面的这个函数。

在130行调用了系统调用event_create,生成的32000个套接字描述符,从143行开始初始化,也就是32000个描述符,每个描述符都是一个struct epoll_event,在这个函数体中最终生成的一些列东西都是返回一个struct epollop,也就是上面其中一个结构体的封装。函数最后的返回值也是epollop.下面来看看这个函数式怎么初始化struct epollops的。在130行event_create返回值的epfd最终赋值给了epollop->epfd,而epollop->fds在151行是动态申请可空间,大小也是nfiles=NEVENT = 32000,epollop中的nevents和nfds都是NEVENT.这里暂时不讨论关于信号的任何内容。

这个函数也就是相当于初始化了一个struct epollop.



### ✅ 简要回答: **libevent** 是一个基于事件驱动的高性能网络库,它封装了底层 I/O 多路复用机制(如 `epoll`、`kqueue`、`select` 等),提供统一的事件注册和回调接口。它的工作机制与你描述的 **事件循环线程 + `epoll_wait` 等待事件 + 串行执行回调** 是一致的。 --- ## 🧠 详细解析: ### 🔹 1. libevent 是什么? **libevent** 是一个开源的 C 语言事件通知库,用于开发高性能的网络服务器或异步 I/O 程序。 它主要特性包括: - 跨平台支持(Linux、BSD、macOS、Windows) - 支持多种 I/O 多路复用后端(`epoll`、`kqueue`、`select`、`IOCP`) - 支持定时器、信号、文件 I/O 等事件 - 提供统一的事件注册和回调处理接口 --- ### 🔹 2. libevent 的基本工作流程 #### ✅ 核心组件: | 组件 | 说明 | |------|------| | `event_base` | 事件循环的核心结构,相当于一个事件循环实例(类似 `io_context`) | | `event` | 事件结构体,表示一个 I/O、定时器或信号事件 | | `event_dispatch()` / `event_base_loop()` | 启动事件循环,相当于 `epoll_wait()` 的封装 | | 回调函数 | 事件触发时调用的用户定义函数 | #### ✅ 基本流程如下: 1. 创建 `event_base`:`struct event_base *base = event_base_new();` 2. 创建事件并绑定回调函数:`event_assign()` / `event_new()` 3. 注册事件(如监听某个 socket 可读、定时器触发等) 4. 启动事件循环:`event_base_loop(base, 0);` 5. 事件发生后,回调函数被调用 6. 循环持续运行,直到退出 --- ### 🔹 3. libevent 是不是类似你描述的机制? | 描述 | 对应 libevent 的行为 | |------|----------------------| | 事件循环线程 | `event_base_loop()` 相当于事件循环主线程 | | `epoll_wait` 等待事件 | libevent 底层根据平台自动选择 `epoll` / `kqueue` / `select` | | 串行执行回调 | 所有事件回调都在事件循环线程中串行执行 | | 支持定时器 | `event_new()` 可绑定定时器事件,支持一次性或周期性触发 | | 多线程支持 | 默认单线程,但可以通过多 `event_base` 实现多线程模型 | --- ### 🔹 4. 示例:libevent 实现一个定时器回调 ```c #include <event2/event.h> #include <stdio.h> void timer_cb(evutil_socket_t fd, short event, void *arg) { printf("定时器触发!\n"); } int main() { struct event_base *base = event_base_new(); struct event *timer = evtimer_new(base, timer_cb, NULL); struct timeval tv = {1, 0}; // 1秒后触发 evtimer_add(timer, &tv); printf("开始事件循环...\n"); event_base_dispatch(base); // 进入事件循环 event_free(timer); event_base_free(base); return 0; } ``` --- ### ✅ 总结:libevent 与你描述机制的对比 | 特性 | libevent 表现 | |------|----------------| | 是否基于事件循环? | ✅ 是 | | 是否使用 `epoll` 等机制? | ✅ 是,自动选择 | | 是否串行执行回调? | ✅ 是,默认在事件循环线程中执行 | | 是否支持定时器? | ✅ 是 | | 是否支持多线程? | ✅ 可以通过多个 `event_base` 实现 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值