一.select

  select采用的是集合的方式,最多只能访问1024个套接字。可读,可写,异常,三种访问,并且采用的是轮训的方式进行每次访问都需要从内核向用户空间拷贝

二.poll

  poll采用的是轮训的方式对给定大小的文件描述符进行轮询,并且有pollfd结构体,事件的返回时存储在结构体中的revents中,与select不相同的是内核对fd_set的参数直接进行修改,所以在每次进行调用时,需要对三个参数进行重新定义;而poll是在pollfd结构体中的revents钟存储返回的结果,每次调用时,不需要对其重新定义。

三.epoll_wait

  epoll_wait是将内核关心的文件描述符,放在内核的事件表中,无需象select和epoll那样,每次从内核空间往用户空间调用,epoll_wait如果监测到事件就将所有就绪的事件传送到第二个struct epoll_event 结构体中,这个结构体适用于输出epoll_wait检测到内核中监测到的就绪文件描述符,这就极大地提高了应用程序索引就绪文件描述符的效率。不需要从内核传送到用户空间,内部相当于红黑树和链表,红黑树中存储的是内核关心的文件描述符,而在epoll_wait()监测到就绪事件时,就会触发回调,这个描述符加入链表中,每次调用epoll_wait返回链表是从内核返回给用户空间的,每次从内核传送到用户空间的描述符并不是很多。select和poll的时间复杂度是O(n),而epoll的时间复杂度是O(1).

  epoll_wait存在两种触发,水平触发和边缘触发。

  水平触发和边缘触发的区别是当一段数据没有读完,水平触发会触发,而边缘触发不会响应,只有当事件改变时,边缘触发才会触发。

区别:

  select的参数类型是fd_set没有将文件描述符和事件绑定,仅仅是一个文件描述符的集合,并且只能处理可读,可写,异常,不能处理更多类型的数据。并且fd_set的修改是在线修改,所以在下次调用select时,要对三个参数进行重置。pollfd参数是pollfd结构体,它是统一管理,并且是在结构体中的revents中存储修改,所以在下次调用时,不需要对pollfd结构体进行重置。由于每次select和poll调用逗返回整个用户注册的事件集合,所以应用程序索引就绪文件描述符的时间复杂度就是O(n),而epoll是在内核中维护一个事件表,采用一个独立的系统调用epoll_ctl来控制往其中添加,删除和修改时间,这样每次epol_wait调用都是直接从内核事件表中取得用户注册的时间,而无需反复的从用户空间读入一些事件,epoll_wait系统调用的event参数仅用来返回就绪事件,这使得应用程序索引就绪文件描述符的时间复杂度为O(1).

  从原理上,select和poll采用的都是轮询的访问方式,每次调用都要访问整个文件描述符集,并将其中准备好的文件描述符返回给用户程序,因此时间复杂度是O(n),epoll_wait采用回调的方式,内核检测到就绪文件描述符时,将触发回调函数,回调函数将文件描述符上对应的事件插入内核就绪队列,内核最后在适合的时机将就绪队列中的内容拷贝到用户空间,因此epoll_wait无需轮询整个文件描述符集合来检测那些事件已经就绪,其算法事件复杂度是O(1).

04-26 13:11