什么是epoll
epoll是linux内核的可扩展I/O事件通知机制,在linux2.5首次登场,它的目标是取代既有的posix select与poll系统函数,让需要大量操作文件描述符的程序得以发挥更优异的性能,epoll实现的功能与poll类似,都是监听多个文件描述符上的事件。epoll通过使用红黑树搜索被监控的文件描述符。
为什么会出现epoll
出现epoll是因为现有的select以及poll不能够满足系统的需求,并且两者的开销在进行大量IO操作的时候都很大。首先看一下select的问题:
每次调用select都要将fd_set集合从用户态拷贝到内核态,当fd_set集合很大时,开销很大,每次调用select都需要在内核遍历传递来的所有的fd_set,如果fd_set很大,开销会很大,fd_set的大小由限制,系统中通过宏来控制而且不可改变(限制为1024)。Poll机制和select机制差不多,但是poll机制解决了文件描述符数量的限制但是没有解决当文件描述符过多的时候开销过大的问题。epoll是基于事件驱动的IO方式,epoll没有描述符个数限制。
epoll触发模式
epoll提供了边缘触发和水平触发模式。在ET(边缘)模式中,epoll检测到有IO事件时,通过epoll_wait调用会得到有事件通知的文件描述符,如果不处理这个文件模式符,那么下次调用epoll_wait的时候就不会再次通知此事件(直到有某些操作将就绪变成未就绪状态,也就是说边缘模式只通知一次);而LT(水平触发)模式时默认的工作模式,即当epoll_wait检查到文件描述符事件时会立即通知应用程序,如果应用程序不处理,下次调用epoll_wait的时候还会再次通知。
epoll的系统调用(要包含sys/epoll.h头文件)
1 | int epoll_create(int size); |
\1. epoll_create函数创建一个epoll句柄,参数size表明内核要监听的描述符数量,调用成功返回一个epoll句柄描述符。epoll_create已经被弃用,更多推荐使用epoll_create1函数来代替,
\2. epoll_ctl函数用来注册要监听的事件类型,参数解释如下:
epfd :表示epoll句柄
op : 表示fd的操作类型,有一下三种
- EPOLL_CTL_ADD 注册新的fd到epfd中
- EPOLL_CTL_MOD 修改已注册的fd的监听事件
- EPOLL_CTL_DEL 从epfd中删除一个fd
fd: 要监听的描述符
event 表示要监听的事件。epoll_event的定义如下:
1 | typedef union epoll_data { |
其中epoll_event中的events成员由以下可用事件类型的零个或多个组合在一起形成的掩码:
- EPOLLIN : 关联的文件描述符可以读,
- EPOLLOUT : 关联的文件描述符可以写;
- EPOLLPRI : 关联的文件描述符由紧急的数据可以读
- EPOLLERR: 关联的文件描述符发生错误
- EPOLLHUP : 关联的文件描述符被挂断
- EPOLLRDHUP : 流套接字对等关闭连接,或半关闭写
- EPOLLET: 设置为边缘触发模式
- EPOLLONESHOT : 只监听一次事件,如果要继续监听这个事件,那么需要再次把这个fd加入到epoll队列中。
\3. epoll_wait函数等待事件的就绪,成功时返回就绪事件数目,调用失败时返回-1,等待超时返回0.其中参数含义如下:
- epfd : epoll句柄
- events :表示从内核的多的就绪事件集合
- maxevents : 告诉内核events的大小
- timeout : 表示等待的时间
它会阻塞到一个文件描述符有事件发生;信号中断处理或者超时。
epoll的优点
支持一个进程打开大数目的文件描述符,select对于文件描述符的大小由一定的限制,epoll则没有这个限制;IO效率不会随着文件描述符数目的增加而线性下降,select/poll都会随着文件描述符的数量增加而性能降低,因为在有事件发生的时候select和poll都要遍历所有的文件描述符才能确定是哪一个文件描述符由事件,而epoll则只会对活跃的文件描述符进行操作。epoll的边缘触发模式效率高,系统不会充斥大量不关心的就绪文件描述符。epoll使用mmap进行内核与用户空间消息传递。
epoll实例
下面学习了这些理论的基础,还是要实践一下的。示例代码如下:
1 |
|