水平触发(Level Triggered):将就绪的fd告诉进程后,若进程没有对其进行IO操作,则下次执行检测时会再次报告这些fd,select、poll、epoll均支持;
边缘触发(Edge Triggered):只告诉一遍,下次就不在告诉了,仅epoll支持;
select
1 |
|
思路
对所有的fd进行线性扫描,即轮询一次
缺点
-
单个进程可监视的fd数量被限制,即能监听端口的大小有限
一般来说这个数目和系统内存关系很大,具体数目可以
cat /proc/sys/fs/file-max
查看。32位机默认是1024个。64位机默认是2048 -
对socket进行扫描时是线性扫描,即采用轮询的方法,效率较低
当套接字比较多的时候,每次select()都要通过遍历FD_SETSIZE个Socket来完成调度,不管哪个Socket是活跃的,都遍历一遍。这会浪费很多CPU时间
-
每次调用select,需要把fd集合从用户态拷贝到内核态,然后在内核遍历fd集合,当fd集合很大时,开销会比较大
poll
1 |
|
思路
poll本质上和select没有区别,但它没有最大连接数的限制,原因是它是基于链表来存储的
缺点
- 它没有最大连接数的限制,原因是它是基于链表来存储的
- poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd
epoll
1 |
|
思路
epoll支持水平触发和边缘触发,会把监控的fd放在内核中的一个事件表中,使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核将触发回调函数,该回调函数将就绪的文件描述符和事件拷贝到用户空间events所管理的内存,epoll_wait便可以收到通知
优点
- 没有最大并发连接的限制
- 效率提升
- 内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减少复制开销
三种比较
比较点 | select | poll | epoll |
---|---|---|---|
一个进程最大连接数 | 由FD_SETSIZE宏定义,其大小是32个整数的大小,可以对进行修改,然后重新编译内核,但是性能可能会受到影响,这需要进一步的测试 | 基于链接存储,无限制 | 上限很大,1G内存的可以打开10万左右的连接 |
效率 | 因为每次调用时都会对连接进行线性遍历,所以随着FD的增加会造成遍历速度慢的“线性下降性能问题”。 | 同select | 不是轮询的方式,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数;即Epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,Epoll的效率就会远远高于select和poll |
消息传递方式 | 内核需要将消息传递到用户空间,都需要内核拷贝动作 | 同select | 内核需要将消息传递到用户空间,都需要内核拷贝动作 |
事件集合 | 内核会修改fd_set,五哦一每次调用都需要重置fd_set | events传入revents反馈 | 使用内核事件表管理;epoll_eait的events仅用来保存就绪事件 |
索引就绪fd的时间复杂度 | |||
内核实现 | 轮询 | 轮询 | 回调 |
工作模式 | LT | LT | LT和ET |