Selector和Epoll区别
select原理
原理还是轮询所有文件描述符
- 将文件描述符集合fd_set从用户空间拷贝到内核空间,进入内核态
- 遍历所有文件描述符,对每个文件描述符调用poll(),该函数返回一组标准的掩码,其各个位指示相应的文件描述符的就绪状态(全为0表示没有任何事件就绪)
- 根据掩码对fd_set进行赋值,如果没有文件就绪且时间未到则会进入短暂的睡眠
- 然后恢复并再次对fd_set轮询调用poll(),直到有文件就绪或者时间到达。
- 如果有文件就绪,函数就返回,将fd_set再次从内核空间拷贝到用户空间,系统返回用户态,用户就可以对相应的文件进行读和写了
epoll原理
epoll_create()
该函数建立一个epoll句柄
内核在epoll文件系统中建立了一个 file节点
在内核缓冲区 Cache 中建立一棵红黑树和一条双向链表。
红黑树用来存储以后 epoll_ctl() 注册的文件描述符
双向链表用来存放就绪的文件描述符。
epoll_ctl()
该函数是功能是将被监听的文件描述符添加到epoll句柄,或者从epoll句柄中删除,或者更改文件描述符的监听状态
将对应的文件描述符添加到红黑树中,或者从红黑树中删除,当将文件描述添加到红黑树时,并给内核中断函数注册一条回调函数,告诉内核,如果该文件描述符就绪,将其添加到就绪链表中
epoll_wait()
该函数返回就绪的文件描述和就绪数目的大小
该函数观察就绪链表中的数据,有数据就返回,没数据就sleep,直到有事件发生或者事件到达
select 和 epoll对比
-
select有最大并发数量限制,默认数量是1024个
-
select 每次调用需要复制全部的fd,文件描述符集合到内核态,并进行线性遍历。
消耗时间多,且随着文件描述符集合增加,速度越慢,效率延展性差 -
epoll 没有最大并发数量限制,依赖于系统所能打开的最大文件数目限制
-
只有已经就绪的文件才会主动触发回调函数,效率和文件数目大小无关,性能延展性好
-
epoll中的epoll_ctl contol控制
- 可以对文件描述符进行添加和删除操作
- 这样就避免了像select那样每次都要复制全部的fd到内核空间
- 但是增加,删除和修改也要考虑时间复杂度,明显红黑树就是一个非常好的数据结构。
总结
(1)select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而epoll其实也需要调用epoll_wait不断轮询就绪链表,期间也可能多次睡眠和唤醒交替,但是它是设备就绪时,调用回调函数,把就绪fd放入就绪链表中,并唤醒在epoll_wait中进入睡眠的进程。虽然都要睡眠和交替,但是select和poll在“醒着”的时候要遍历整个fd集合,而epoll在“醒着”的时候只要判断一下就绪链表是否为空就行了,这节省了大量的CPU时间。这就是回调机制带来的性能提升。
(2)select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,并且要把current往设备等待队列中挂一次,而epoll只要一次拷贝,而且把current往等待队列(红黑树)上挂也只挂一次(在epoll_wait的开始,注意这里的等待队列并不是设备等待队列,只是一个epoll内部定义的等待队列)。这也能节省不少的开销。