epoll的用法
epoll的作用
epoll 是 Linux 内核提供的一种 I/O 多路复用 机制。它的核心作用是:让单个进程(或线程)能够同时监听大量文件描述符(如 socket、管道、文件等),并在这些描述符就绪(可读、可写、出错)时得到通知,从而高效地处理 I/O 事件。
epoll的工作原理
1、epoll 的三个核心数据结构
当调用 epoll_create() 时,内核会创建一个 struct eventpoll 对象,它主要包含两个核心成员:
| 成员 | 类型 | 作用 |
|---|---|---|
rbr |
红黑树根节点 | 存放所有被监听的文件描述符及其关注的事件(epoll_event)。查找、插入、删除都是 O(log n)。 |
rdlist |
双向链表 | 存放已经就绪(有事件发生)的文件描述符。epoll_wait 直接从这里取数据,O(1) 返回。 |
wq |
等待队列 | 当进程调用 epoll_wait 且当前没有就绪事件时,进程会挂载到这里,直到被唤醒。 |
2、事件注册:epoll_ctl 做了什么
调用 epoll_ctl(EPOLL_CTL_ADD, fd, &ev) 时:
-
检查
fd是否已经在红黑树中(避免重复添加)。 -
如果没有,就创建一个
epitem结构(代表一个被监听的事件节点),包含:-
指向目标文件对象
struct file *的指针 -
用户关注的事件类型(
EPOLLIN、EPOLLOUT等) -
回调函数指针(关键)
-
-
将这个
epitem插入到红黑树rbr中。 -
最重要的一步:向目标文件(如 socket)的等待队列中添加一个回调入口。这个回调函数就是
ep_poll_callback。
至此,内核已经“告诉”文件系统:当这个
fd上发生任何事件(数据到达、写缓冲区空闲等),请立即调用ep_poll_callback。
3、事件发生:回调驱动
当某个被监听的 fd 上有事件发生时(比如网卡收到数据,TCP 栈把数据放入 socket 接收缓冲区):
-
文件系统(如 socket 的
tcp_data_ready函数)会调用之前注册的ep_poll_callback。 -
这个回调函数会做三件事:
-
将对应的
epitem添加到eventpoll的 就绪链表rdlist中(如果还没在链表里)。 -
如果当前有进程正阻塞在
epoll_wait上(即挂在wq等待队列中),则唤醒该进程。
-
-
整个过程不涉及轮询,完全由事件驱动。
4、获取就绪事件:epoll_wait 如何工作
调用 epoll_wait(events, maxevents, timeout) 时:
-
检查
rdlist是否为空。-
如果不为空:将
rdlist中的事件复制到用户提供的events数组,并返回事件个数。复制后可以选择是否从链表中移除(取决于触发模式)。 -
如果为空:将当前进程加入
wq等待队列,然后调用schedule_timeout()让出 CPU。当有事件触发回调时,进程会被唤醒,重新检查rdlist。
-
-
边缘触发(ET)与水平触发(LT)的区别:
-
LT(水平触发,默认):内核在返回事件后,不会从
rdlist中删除该epitem,只要文件描述符依然处于就绪状态,下次epoll_wait还会再次返回它。 -
ET(边缘触发):内核在返回事件后立即从
rdlist中删除该epitem,应用程序必须一次性处理完所有数据,否则不会再次通知。
-
一.重要数据结构
struct eventpoll{
//...
struct rb_root rbr; //红黑树,管理epoll的新事件
struct list_head rdlist; //就绪列表,保存epoll_wait返回满足条件的事件
};
//一个连接的io事件
struct epitem {
struct rb_node rbn; //红黑树节点
struct list_head rdllist; //双向链表节点
struct epoll_filefd ffd; //事件句柄信息
struct eventpoll *ep; //指向所属的eventpo11对象
struct epoll_event event; //注册的事件类型
};
struct epoll_event {
uint32_t events; //感兴趣的事件掩码(EPOLLIN、EPOLLOUT 等)
epoll_data_t data; //用户数据,可以是 int 或 void* 等
};
typedef union epoll_data {
void *ptr; // 可以指向任意用户数据(如连接对象)
int fd; // 直接存放文件描述符
uint32_t u32;
uint64_t u64;
} epoll_data_t;
二.主要函数
1.epoll_creat系统调用
int epoll_creat(int flags);
-
功能:创建一个 epoll 实例,返回一个文件描述符(epfd),后续操作都通过它进行。
-
参数
flags:-
0:默认行为(兼容旧版epoll_create(int size),size 参数已忽略)。 -
EPOLL_CLOEXEC:当进程执行exec系列函数时,自动关闭这个 epoll 文件描述符,防止子进程继承。
-
-
返回值:成功返回非负整数(epfd);失败返回 -1,并设置
errno。
2.epoll_ctl系统调用
int epoll_ctl(int epfd,int op,int fd,struct epoll_event* event);
-
功能:向 epoll 实例中添加、修改或删除要监控的文件描述符。
-
参数详解:
| 参数 | 含义 |
|---|---|
epfd |
epoll_create1() 返回的 epoll 实例文件描述符 |
op |
操作类型:EPOLL_CTL_ADD(添加)、EPOLL_CTL_MOD(修改)、EPOLL_CTL_DEL(删除) |
fd |
要监控的文件描述符(如 socket) |
event |
指向 struct epoll_event 的指针,描述感兴趣的事件及用户数据。当 op 为 EPOLL_CTL_DEL 时可为 NULL |
常用 events 标志:
| 标志 | 含义 |
|---|---|
EPOLLIN |
可读事件(socket 接收缓冲区有数据,或监听 socket 有新连接) |
EPOLLOUT |
可写事件(socket 发送缓冲区有空闲,可以发送数据) |
EPOLLRDHUP |
对端关闭连接(TCP 连接被对端关闭,某些内核版本支持) |
EPOLLERR |
错误事件(如连接被重置) |
EPOLLHUP |
挂起事件(对端挂断,通常与 EPOLLERR 一起出现) |
EPOLLET |
边缘触发模式(Edge Triggered),默认是水平触发(Level Triggered) |
EPOLLONESHOT |
一次性触发,事件发生后自动移除,需重新添加才能再次监控 |
3. epoll_wait()
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
-
功能:等待 epoll 实例上的事件发生,返回就绪的事件列表。
-
参数详解:
| 参数 | 含义 |
|---|---|
epfd |
epoll 实例文件描述符 |
events |
输出参数,指向 struct epoll_event 数组,用于接收就绪的事件 |
maxevents |
events 数组的大小(最多返回多少个事件) |
timeout |
超时时间(毫秒)。-1 表示阻塞直到有事件;0 表示立即返回(非阻塞);>0 表示等待指定毫秒数后超时返回 0 |
-
返回值:
-
成功:返回就绪的事件个数(0 表示超时)。
-
失败:返回 -1,设置
errno。
-
三、触发模式详解
水平触发(Level Triggered, LT,默认)
-
只要文件描述符处于可读/可写状态,
epoll_wait就会一直返回该事件。 -
优点:编程简单,不易遗漏数据。
-
缺点:可能多次唤醒,效率略低。
边缘触发(Edge Triggered, ET,高性能推荐)
-
仅当文件描述符的状态发生变化(从无数据变为有数据,或发送缓冲区从满变为不满)时,才返回一次事件。
-
必须以非阻塞方式循环读/写,直到系统调用返回
EAGAIN或EWOULDBLOCK。 -
优点:减少系统调用次数,效率更高。
-
缺点:编程复杂,必须一次处理完所有数据。
设置 ET 模式:
ev.events = EPOLLIN | EPOLLET; // 添加 EPOLLET 标志
四.扩展
fcntl 函数族
-
fcntl(fd, F_GETFL, 0):获取文件描述符fd当前的状态标志(如是否非阻塞)。 -
fcntl(fd, F_SETFL, flags):设置文件描述符的状态标志。 -
O_NONBLOCK:非阻塞标志。当文件描述符被设置为非阻塞时,recv/send等操作不会阻塞进程,如果没有数据可读或缓冲区已满,会立即返回-1并设置errno为EAGAIN或EWOULDBLOCK。
EAGAIN 和 EWOULDBLOCK
-
在使用非阻塞 socket 时,如果
recv没有数据可读,或send的发送缓冲区已满,系统调用会返回-1,并设置errno为EAGAIN或EWOULDBLOCK(这两个值在 Linux 上通常相同)。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)