在I/O多路复用中主要有两个模式select,epoll,说道它们的优劣时,都会说epoll好过select,但为什么好呢?我尝试粗浅地来说下原因,起到一个启发的作用,如有错误的地方请指正。
select
关键过程如下:
1.select把fd传入内核,这里花费的时间假设为O(n),n指的是fd的个数
2.等待io,这里的花费的时间假设为m
3.当有某个io请求时,select要扫描一遍fd,找出请求的fd,这里花费的时间假设为O(n),n指的是fd的个数
4.io结束后,又回到步骤1,重新把需要监听的fd传入内核
epoll
epoll对select进行了改进,关键过程如下:
epoll多了几个api,其中有两个关键api: epoll_ctl 和 epoll_wait
epoll_ctl : 负责fd的管理,执行add,mod,del操作,执行add时,会向内核注册该fd的回调函数,内核检测到该fd可读可写时,内核调用该回调函数,回调函数将fd放到就绪链表
epoll_wait : 监控就绪链表,有fd时,返回到用户态
看下整个过程:
1.epoll_ctl初始化fd,这里花费的时间为O(n)
2.进入while死循环,执行epoll_wait
3.等待io,这里的花费的时间假设为m
4.当有某个io请求时,内核通过回调函数,将对应的fd直接放到就绪链表,epoll_wait检测到就绪列表的fd,将该fd返回用户态,这里花费的时间假设为1
总结
在select模式中,一次io需要时间为O(n) + m + O(n),如果有N次io请求,则需要的时间为:
N*(O(n) + m + O(n))。
在epoll模式中,一次io需要时间为O(n) + m +1,如果有N次io请求,则需要的时间为:
O(n) + N*( m + 1)。
通过这样粗浅地计算,可以看出epoll是明显优于select的,它只需要花费一次初始化fd的时间,之后fd由epoll_ctl接管,epoll_wait只负责监听。而select则每次都需要重新遍历fd,并且要遍历两次,一次用于扫描fd,一次用于找出需要用到的fd,整个时间复杂度是远远高于epoll,所以现在的高并发程序基本都用epoll,比如nginx。