一、select系统调用
(1)select系统调用的用途是:在一段指定的时间内,监听用户感兴趣的文件描述符上的可读、可写和异常事件。
(2)select的实现
select的参数类型fd_set没有将文件描述符和事件绑定,它仅仅是一个文件描述符集合,因此select需要提供3个这种类型的参数来分别传入和输出可读、可写及异常等事件。这一方面使得select不能处理更多类型事件,另一方面由于内核对fd_set集合在线修改,应用程序下次调用select前不得不重置这3个fd_set集合。
二、poll
poll函数将用户关注的文件描述符以及其关注的事件、内核返回的文件描述符上发生的事件分离开表示,并且通过一个用户数组将所有的文件描述符传递给内核。因此,poll函数能关注的事件类型更多,每次调用也不需要重新设置
三、epoll
(1)epoll通过一组函数来完成的,epoll通过epoll_create创建一个内核事件表,通过epoll_ctl函数添加、删除、修改事件,epoll_wait只需要从内核事件表中读取用户的注册事件。当某个描述符有事件发生的时候,就会触发回调函数call_back()
(2)epoll的ET/LT模式在实现上有什么区别?
a:LT模式下:描述符上事件就绪后,如果没有把数据处理完成,或者没有处理,下一次epoll会继续提醒应用程序。
LT是默认的工作模式,这种模式下epoll相当于一个效率较高的poll
b:ET模式下:描述符上事件就绪后,如果没有把数据处理完或者没有处理,下一次epoll不会提醒应用程序,所以要求应用程序在收到一次提醒时,必须把当前的所有数据处理完成
(3)内核上两种模式的实现
epoll_wait每次将收集到的就绪事件和描述符返回给应用程序,过程是这样的,当检测到就绪队列不为空时,就说明有事件就绪,使用ep_collect_ready_items方法将就绪队列rdlist中的数据挪到txlist中,此时,就绪队列为空。再通过ep_send_events方法将就绪事件返回给应用程序,同时又会调用ep_reinject_items方法将一些有设置EPOLLET的事件的描述符又放回到就绪队列中,这样下一轮,epoll_wait会发现有就绪事件,但会再次检查是否有数据,有就返回,没有就继续阻塞。但设置了EPOLLET事件,相应的描述符在ep_reinject_items方法中不会被放回到就绪队列中,只有等设备驱动程序检查到有事件产生才会再次将事件放到rdlist。所以,如果ET模式数据没有读完不会放回rdlist,那么也就不会再去检查是否有数据,提醒应用程序了,直到下次设备驱动程序检查到了,事件才会放入就绪队列中
四、select、poll、epoll的区别(加上上面一、二、三)
(1)使用限制:
a:select所使用的fd_set结构实际上是一个整型数组,32位系统上关注的文件描述符最多1024个,最大文件描述符数是1023
b:poll和epoll的最大文件文件描述符个数是系统允许打开的最大文件描述符65535
(2)使用效率
a:select、poll每次调用都需要将用户空间的文件描述符拷贝到内核空间,epoll则直接从内核读取,效率更高
b:select、poll每次都将所有的文件描述符(就绪和未就绪的)返回,所以应用程序检索就绪文件描述符的事件复杂度位O(n),epoll通过events参数返回所有就绪的文件描述符,应用程序检索就绪文件描述符的时间复杂度位O(1)
c:select、poll只能工作在效率较低的LT模式下,而epoll则能工作在ET高效模式,并且epoll还支持EPOLLONSHOT事件,从而进一步减少事件被触发的次数
(3)内核效率
Select和poll采用轮询的方式检测就绪事件,即每次都需要扫描整个注册的文件描述符集合,并将其中就绪的文件描述符返回给用户程序,因此,内核中检测就绪文件描述符的算法事件复杂度位O(n),epoll则采用回调的方式检测就绪事件,内核检测到就绪的文件描述符,就触发回调函数,将文件描述符及发生的事件插入到内核就绪事件队列中,因此,epoll在内核中检测就绪文件描述符的算法事件复杂度位O(1),但是,当链接的活动比较频繁时,select和poll的效率要比epoll高,因此epoll的回掉函数调用过去频繁,所以,epoll适用于链接较多,但是活动不频繁的情况
(4)工作模式
select、poll使用LT,epoll使用ET模式
五、epoll的内核实现,及哪些数据结构?
Epoll功能的实现分了三个系统调用完成
(1)epoll_create:创建内核事件表用于存放描述符和关注的事件,主要数据结构有:struct eventpoll;其中两个重要成员红黑树也就是内核事件表,和就绪事件存放的队列
(2)epoll_ctl:为红黑树添加、移除、修改节点的操作,红黑树的一个节点就是一个描述符和事件的结构体
(3)epoll_wait:负责收集就绪事件,注意是收集,所以它并没有太多的事情做,效率自然就高,那数据是怎么就绪的呢?第二步在添加数据的时候会注册回调函数,当关注的描述符上有事件就绪,就将该描述符和事件放到就绪队列,也就是第一步创建的就绪队列