epoll 是 Linux 中用于 I/O 多路复用的机制之一,它允许程序监视多个文件描述符,看它们是否就绪(例如,是否可以读取、写入或接受新的连接等)
epoll 是一个高效的事件轮询机制,主要用于处理大量并发连接的情况
epoll 的使用通常包括以下步骤:
- 使用 epoll_create 创建一个 epoll 实例
- 使用 epoll_ctl 注册需要监视的文件描述符及其对应的事件
- 使用 epoll_wait 等待事件的发生。当有事件发生时,epoll_wait 会返回,并告诉你哪些文件描述符就绪了
- 处理这些就绪的文件描述符
- 重复步骤 3 和 4,直到所有文件描述符都处理完毕
epoll 有两种工作方式:LT(level triggered)和 ET(edge triggered)
- LT 是缺省的工作方式,在这种模式下,内核会告诉进程一个文件描述符是否就绪,然后进程可以对就绪的 fd 进行 I/O 操作
- ET 模式则只告诉进程哪些文件描述符刚刚变为就绪状态,如果进程没有采取行动,那么内核将不会再次告知
epoll 函数
epoll_create:这个函数用于创建一个新的 epoll 实例,并返回一个文件描述符。这个文件描述符可以用于后续的 epoll_ctl 和 epoll_wait 调用
#include <sys/epoll.h>
int epoll_create(int size);
epoll_ctl:这个函数用于修改已存在的 epoll 实例。你可以通过这个函数来注册、修改或取消监视某个文件描述符的事件
#include <sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
- epfd 是 epoll 实例的文件描述符。
- op 表示操作类型,可以是以下几种值之一:
EPOLL_CTL_ADD
:向epoll实例中添加一个文件描述符和对应的事件EPOLL_CTL_MOD
:修改已经添加到epoll实例中的文件描述符的事件EPOLL_CTL_DEL
:从epoll实例中删除一个文件描述符
- fd 是需要进行操作的文件描述符
- event 是一个指向 epoll_event 结构体的指针,用于指定事件的类型和其他属性
epoll_wait:这个函数用于等待事件的发生。它会阻塞当前线程,直到至少有一个文件描述符就绪(即有事件发生)
#include <sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
- epfd 是 epoll 实例的文件描述符
- events 是一个指向 epoll_event 结构体数组的指针,用于存储触发的事件
- maxevents 表示events数组的大小,即最多可以存储多少个事件
- timeout 用于指定等待的超时时间,单位是毫秒。传递负数表示无限等待,传递0表示立即返回
select/poll/epoll 对比
select | poll | epoll | |
---|---|---|---|
底层数据结构 | 数组存储文件描述符 | 链表存储文件描述符 | 红黑树存储监控的文件描述符 双链表存储就绪的文件描述符 |
如何从fd数据中获取就绪的fd | 遍历 | 遍历 | 回调 |
时间复杂度 | O(n) 获得就绪的文件描述符需要遍历fd数组 |
O(n) 获得就绪的文件描述符需要遍历fd链表 |
O(1) 当有就绪事件时,系统注册的回调函数就会被调用,将就绪的fd放入到就绪链表中 |
FD数据拷贝 | 每次调用select,需要将fd数据从用户空间拷贝到内核空间 | 每次调用poll,需要将fd数据从用户空间拷贝到内核空间 | 使用内存映射(mmap),不需要从用户空间频繁拷贝fd数据到内核空间 |
最大连接数 | 有限制,一般为1024 | 无限制 | 无限制 |
连接数较少的应用场景并且都很活跃,用
select
和poll
效率更高;连接数较多的应用场景并且都不很活跃,使用epoll
效率更高