您的位置:首页 > 其它

I/O多路复用之总结

2015-08-08 23:55 281 查看

poll() 和 select() 的区别

虽然 poll() 和select() 完成相同的工作,但 poll() 调用在很多方面仍然优于 select() 调用:

poll() 不需要用户计算最大文件描述符值加 1 作为参数传递给它。

poll() 对于值最大的文件描述符,效率最高。试想一下,要通过 select() 监视一个值为900的文件描述符,内核需要检查每个集合中的每个位,一直检查900个位。

select() 的文件描述符集合是静态的,需要对大小设置进行权衡:如果值很小,会限制 select() 可监视的最大文件描述符值;如果值很大,效率会很低。当值很大时,大的位掩码操作效率不高,尤其是当无法确定集合是否稀疏集合。对于 poll(),可以准备创建大小合适的数组。如果只需要监视一项,则仅传递一个结构体。

对于 select() 调用,返回时会重新创建文件描述符集,因此每次调用都必须重新初始化。poll() 系统调用会把输入(events变量)和输出(revents变量) 分离开,支持无需改变数组就可以重新使用。

select() 的调用的 timeout 参数在返回时是未定义的。代码要支持可移植,需要重新对它初始化。而对于 pselect(),不存在这些问题。

不过,select() 系统调用也有些优点:

select() 可移植更好,因为有些UNIX系统不支持 poll()。

select() 提供了更高的超时精度:select() 支持微秒级,poll() 支持毫秒级。ppoll() 和 pselect() 理论上都提供了纳秒级的超时精度,但是实际上,这两个调用的毫秒级精度都不可靠。

比起 poll() 调用和 select() 调用,epoll() 调用更优,epoll() 是Linux特有的I/O多路复用解决方案。

边缘触发事件和条件触发事件

select() 操作和 poll() 操作都是条件触发(level-triggered),而不是边缘触发(edge-triggered)。epoll() 两种模式都支持。边缘触发更简单,但是不注意的话很容易丢失 I/O 事件。
注:条件触发是指当条件满足时发生一个 I/O 事件,边缘触发是指当状态改变时发生一个 I/O 事件。

如果 epoll_ctl() 的参数 event 中的 events 项设置为 EPOLLET, fd 上的监听方式为边缘触发(Edge-triggered),否则为条件触发(Level—triggered)。

考虑下面的生产者和消费者在通过UNIX管道通信时的情况。
1. 生产者向管道写入1KB数据。
2.消费者在管道上调用 epoll_wait(),等待管道上有数据并可读。

通过条件触发监视时,在步骤2中 epoll_wait() 调用会立即返回,表示管道可读。通过边缘触发监视时,需要步骤1发生后,步骤2的 epoll_wait() 调用才会返回。也就是说,对于边缘触发,在调用 epoll_wait() 时,即使管道已经可读,也只有当数据写入之后,调用才会返回。

条件触发是默认行为,poll() 和 select() 就是采用这种模式,也是大多数开发者所期望的。边缘触发需要不同的编程方案,通常是使用非阻塞 I/O,而且需要仔细检查 EAGAIN。

边缘触发
“边缘触发”这个术语来源于电子工程领域。条件触发是只要有状态发生就触发。边缘触发是只有在状态改变的时候才会发生。条件触发关心的是事件状态,边缘触发关心的是事件本身。

举个例子,对于一个读操作的文件描述符,如果是条件触发,只要文件描述符可读了,就会收到通知,是“可读”这个条件触发了通知。如果是边缘触发,当数据可读时,会接收到通知,而且通知有且仅有一次:是“有数据”这个变化本身触发了通知。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: