Linux I/O多路复用:
- select
- poll
- epoll
select/poll 缺点:
- 每次查询的时候都要将fd set从用户空间拷贝到内核空间
- 每次select/poll时都会对fd set中所有的fd进行轮询,在fd set很大但是活跃的fd数量很少时,会大幅降低性能
- 监控的fd数量有限
epoll的基本实现原理:
- 三大接口:
- int epoll_create(int size);//创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大
- int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
- int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
- 数据结构
(图片来源:http://blog.csdn.net/tianmohust/article/details/6743987)
- 工作原理:
- epoll_create: 在eventpollfs(epoll模块自建)中创建文件并返回该文件fd(epollfd),创建对应struct file与eventpoll并初始化。
- epoll_ctl: 主要是针对ADD操作,就是为要监控的fd创建对应的epitem结构,然后插入到rbr指向的红黑树中(不可以重复插入),然后最关键的是调用poll系统调用,为fd指向的文件,I/O设备或者socket(Linux 一切皆是文件)注册回调函数,这样在fd对应的文件发生状态变化时,就会调用callback, 执行一些指定的操作,比如说将fd插入到eventpoll的就绪队列中,发出信号唤醒等待队列中的进程等等……基于回调的epoll远比基于轮询的select/poll高效!
- epoll_wait: 检测就绪队列是否为空,如果不为空,就将就绪队列中的信息拷贝到用户空间。如果为空,进入eventpoll的等待队列,设置进程为睡眠状态,等待被唤醒。
说明:
上面的讨论省略了很多细节,比如内核编程中等待队列的相关认识,poll机制,操作数据结构时的加锁保护,边界情况,边缘触发与水平触发,slab等等内核编程中的细节。但是能够帮助你理清epoll的基本脉络,详细的讨论可以访问以下链接。