各自支持的最大fd数上限以及原因
select支持的fd数是固定的,默认一般为1024,用一个常数FD_SETSIZE定义,如果要monitor更多的fd,需要修改这个常数,并且重新编译(?),一般如果要monitor超过1024的fd,推荐使用poll/epoll
poll和epoll,因为他们monitor的fd list是通过数组或者说链表来保存,所以没有数量限制
区别
select
select是三者中最早被使用的多路复用机制,因此可移植性最好
通过给定三个fd set(bit mask),分别对应关注的是否可读,可写,是否出现异常三种类别,在调用select的时候,这三个集合被传给内核,内核逐一检测这些fd是否ready,然后把三个集合中ready的fd对应的bit置为1,返回
然后用户再逐一检测set中的fd是否处于ready,如果是,就对对应的fd执行相应的操作
注意到,在上面的描述中,用户始终需要扫描整个集合,也就是说扫描的个数和io事件的个数无关,而是始终是FD_SETSIZE,为了提高效率,我们可以使用一个maxfd,这样可以只检测maxfd以下的,从而扫描的个数正比于监听的fd个数,而不是始终是固定的FD_SETSIZE
缺点:
1.每次调用select都需要把三个集合传给内核,然后内核又把修改过的三个集合传回来,耗费时间
2.扫描时耗时与monitor的fd个数成正比,所以如果监听的个数很多,就会很慢
3.监听个数有上限
poll
基本和select相同,优点是监听的fd没有数量限制(因为poll不是用bit mask保存关注的fd,而是用pollfd 结构体数组
epoll
首先他和poll一样,监听的fd数没有限制(或者说只受硬件配置限制
为什么epoll的性能更好:
1.每次有io event让fd编程ready,内核都把该ready fd加入到epoll ready list,执行wait的时候直接从ready list里取而select每次调用都要扫描所有的fd
2.每次调用select/poll,都把三个set传给内核,然后内核把ready的用三个set传回来而epoll,使用epoll_ctl在内核里创建一个数据结构,之后epoll_wait时不需要传什么数据
返回的时候,也只返回ready的(事实上因为返回时我们也必须检查所有的位,判断是否ready,而epoll只返回ready的,所以还是有区别的
3.每次调用select之前,都必须初始化三个集合,还有返回之后必须检查所有的位(不过貌似这些是小头
我们可以说select/poll的耗时和N(fd数)呈线性关系,epoll的耗时和发生的io事件呈线性关系
所以epoll十分实用下面这种情况:monitor大量的fd,但是其中大部分是空闲的(因此io事件相对较少
此外epoll还提供edge trigger和level trigger两种形式的通知,而select/poll 都只支持level trigger
这两种的区别在于:
level trigger:只要fd处于ready(可读可写),就会报告给用户
edge trigger:仅当上一次调用epoll_wait之后有新的io事件,才报告给用户(如果是之前没有使用epoll_wait,那么就看创建fd之后有没有新的io事件)
举例说明edge trigger和level trigger的区别
假设现在一个fd处于ready,可读,我们执行一次epoll wait,此时一定是ready,不管采用level trigger还是edge trigger
再执行一次,如果是level trigger,那么仍然报告ready
但如果是edge trigger,那么就不会报告ready,因为从上次调用epoll wait以来,没有发生io事件