IO多路复用-select/poll/epoll

IO 多路复用之 select/poll/epoll 学习

select

基本原理

  select() 的机制中提供一种 fd_set 的数据结构,实际上是一个 long 类型的数组,每一个数组元素都能与一打开的文件句柄(不管是Socket句柄,还是其他文件或命名管道或设备句柄)建立联系,建立联系的工作由程序员完成,当调用 select() 时,由内核根据 IO 状态修改 fd_set 的内容,由此来通知执行了 select() 的进程哪一个 Socket 或文件可读。

优缺点

  select 目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点。但是,select 仅仅知道有 I/O 事件发生了,却并不知道是哪那几个流(可能有一个,多个,甚至全部),因此只能无差别轮询所有流,找出能读出数据,或者写入数据的流,然后进行操作。select 主要有三个缺点:

  • 数据拷贝开销大。每次调用 select 都需要把 fd_set 集合从用户态拷贝到内核态,函数返回时,再次从内核拷贝结构体。fd_set 集合很大时,那么开销也很大。
  • 查找匹配速度慢。每次调用select都需要在内核遍历传递进来的所有 fd_set,如果 fd_set 集合很大时,不仅查询速度慢,开销也很大。
  • 连接数受限。为了减少数据拷贝带来的性能损坏,内核对被监控的 fd_set 集合大小做了限制,并且这个是通过宏控制的,大小不可改变(限制为1024)。

poll

基本原理

  poll 本质上和 select 没有区别,它将用户传入的数据拷贝到内核空间,然后查询每个 fd 对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。

优缺点

  poll 没有最大连接数的限制,原因是它是基于链表来存储的。但是,select 存在的开销问题 poll 依然存在,对 poll 而言传递的结构体是 pollfd,结构体大小随fd个数增多而增加。因为每次调用时都会对连接进行线性遍历,所以随着 fd 的增加会造成遍历速度慢的“线性下降性能问题。

epoll

基本原理

  epoll 可以理解为 event poll,相对于 select 和 poll 来说,epoll 更加灵活,没有描述符限制。在 select/poll 中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而 epoll 事先通过 epoll_ctl() 来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似 callback 的回调机制,迅速激活这个文件描述符,当进程调用 epoll_wait() 时便得到通知。

  epoll 对文件描述符的操作有两种模式:LT(level trigger)和 ET(edge trigger):

  • LT 模式:默认的工作模式,同时支持block和no-block socket。当 epoll_wait 检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。如果你不作任何操作,下次调用epoll_wait时,会再次响应应用程序并通知此事件。
  • ET 模式:高速工作方式,只支持 no-block socket。当 epoll_wait 检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用epoll_wait时,不会再次响应应用程序并通知此事件。ET模式在很大程度上减少了epoll事件被重复触发的次数,因此效率要比LT模式高。
优缺点
  • epoll 没有最大并发连接的限制,能打开的 FD 的上限远大于1024(1G的内存上能监听约10万个端口)。
  • 效率提升,基于事件的方式,无需轮询,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数;即 epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,Epoll的效率就会远远高于select和poll。
  • 内存拷贝,利用 mmap() 文件映射内存加速与内核空间的消息传递,即epoll使用mmap减少复制开销。使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的 copy 只需一次。
  • 如果没有大量的idle-connection或者dead-connection,epoll的效率并不会比select/poll高很多,但是当遇到大量的idle-connection,就会发现epoll的效率大大高于select/poll。

总结

  综上,在选择select,poll,epoll时要根据具体的使用场合以及这三种方式的自身特点。

  • 表面上看 epoll 的性能最好,但是在连接数少并且连接都十分活跃的情况下,select 和 poll 的性能可能比 epoll好,毕竟 epoll 的通知机制需要很多函数回调。
  • select 低效是因为每次它都需要轮询。但低效也是相对的,视情况而定,也可通过良好的设计改善。
坚持原创技术分享,您的支持将鼓励我继续创作!