IO多路复用的两种模式:水平触发和边缘触发

在 linux 的 IO 多路复用中有水平触发,边缘触发两种模式

  • 水平触发 (level-trggered)
    只要文件描述符关联的读内核缓冲区非空,有数据可以读取,就一直发出可读信号进行通知,
    当文件描述符关联的内核写缓冲区不满,有空间可以写入,就一直发出可写信号进行通知
    LT 模式支持阻塞和非阻塞两种方式。epoll 默认的模式是 LT。

  • 边缘触发 (edge-triggered)
    当文件描述符关联的读内核缓冲区由空转化为非空的时候,则发出可读信号进行通知,
    当文件描述符关联的内核写缓冲区由满转化为不满的时候,则发出可写信号进行通知

两者的区别在哪里呢?

水平触发是只要读缓冲区有数据,就会一直触发可读信号,而边缘触发仅仅在空变为非空的时候通知一次,LT (level triggered) 是缺省的工作方式,并且同时支持 block 和 no-block socket. 在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的 fd 进行 IO 操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的 select/poll 都是这种模型的代表.

  • 水平触发:如果有文件描述符已经就绪可以非阻塞的执行 IO 操作了,此时会触发通知。允许在任意时刻重复检测 IO 的状态,没有必要等待每次描述符就绪后才检测,目的是尽可能多的执行 IO。

  • 边缘触发:如果文件描述符自上次状态改变后有新的 IO 活动到来,此时会触发通知。在收到一个 IO 事件通知后要尽可能多的执行 IO 操作,因为如果在一次通知中没有执行完 IO 那么就需要等到下一次新的 IO 活动到来才能获取到就绪的描述符。信号驱动式 IO 就属于边缘触发.

  • select,poll 就属于水平触发。epoll 既可以采用水平触发,也可以采用边缘触发。

举个栗子

  • 第一个栗子:一个管道收到了 1kb 的数据,这时如果是水平触发的,epoll 会立即返回,因为有数据准备好了。如果是边缘触发的不会立即返回,因为此时虽然有数据可读但是已经触发了一次通知,在这次通知到现在还没有新的数据到来,直到有新的数据到来 epoll 才会返回,此时老的数据和新的数据都可以读取到 (当然是需要这次你尽可能的多读取).

  • 在举个读 socket 的例子:现在来了 100 个字节,这时无论边缘触发和水平触发都会产生一个 read ready notification 通知应用程序可读。应用程序读了 50 个字节,然后重新调用 api 等待 io 事件。这时水平触发的 api 会因为还有 50 个字节可读从 而立即返回用户一个 read ready notification。而边缘触发的 api 会因为可读这个状态没有发生变化而陷入长期等待。 因此在使用边缘触发的 api 时,要注意每次都要读到 socket 返回 EWOULDBLOCK 为止,否则这个 socket 就算废了。而使用条件触发的 api 时,如果应用程序不需要写就不要关注 socket 可写的事件,否则就会无限次的立即返回一个 write ready notification。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章