epoll、poll 和 select

epoll

epoll 要做的事情,就是管理这些文件描述符。或者用一句话来描述:epoll 就是增删改查文件描述符。epoll 里面有两个关键结构。一个是红黑树,每一个节点都代表了一个文件描述符。另外一个是双向链表,也叫做就绪列表。

  • epoll 并不是在我发起 epoll 调用的时候才把文件描述符挪到就绪列表的。而是在 epoll 创建之后,不管你有没有发起 epoll_wait 调用,只要文件描述符满足条件了,就会被挪到就绪列表(需要时会复制到用户空间中)。
  • 每一个和 IO 有关的文件描述符都有一个对应的驱动,这个驱动会告诉 epoll 发生了什么。比如说,当有数据发送到网卡的时候,会触发一个中断。借助这个中断,网卡的驱动会告诉 epoll,这里有数据了。而超时也是利用了中断,不过是时钟中断。时钟中断之后,内核会去检查发起 epoll_wait 的线程有没有超时,如果超时了就会唤醒这个线程。调用者就会得到超时响应。

image-20240112110935211

poll 和 select

发起 select 调用的时候会传给 select 一堆代表连接的文件描述符,内核会帮你检查这些文件描述符,调用时才进行遍历。epoll 会提前帮你准备好符合条件的文件描述符,但是 select 不会。poll 和 select 的基本原理一样,区别在于select存储的文件描述符个数有限。

总结

image-20240112111100038

边缘触发和水平触发:

  • 使用边缘触发模式时,当被监控的 Socket 描述符上有可读事件发生时,服务器端只会从 epoll_wait 中苏醒一次,即使进程没有调用 read 函数从内核读取数据,也依然只苏醒一次,因此我们程序要保证一次性将内核缓冲区的数据读取完;
  • 使用水平触发模式时,当被监控的 Socket 上有可读事件发生时,服务器端不断地从 epoll_wait 中苏醒,直到内核缓冲区数据被 read 函数读完才结束,目的是告诉我们有数据需要读取;

select/poll 只有水平触发模式,epoll 默认的触发模式是水平触发,但是可以根据应用场景设置为边缘触发模式。