
select,poll,epoll都是IO多路复用的机制。I/O多路复用就通过一种机制,
可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),
能够通知程序进行相应的读写 *** 作,读写过程是阻塞的,
select的几大缺点:监视的文件描述符的数量存在最大限制
1 单个进程可监视的fd数量被限制
2 需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大
3 对socket进行扫描时是线性扫描
//select的3个缺点:1 连接数受限 2 查找配对速度慢 3 数据由内核拷贝到用户态。
poll的实现和select非常相似,pollfd并没有最大数量限制(但是数量过大后性能也是会下降)
select、poll的内部实现机制相似,它没有最大连接数的限制,原因是它是基于链表来存储的,
但是同样有一个缺点:大量的fd的数组被整体复制于用户态和内核地址空间之间,
而不管这样的复制是不是有意义。它将用户传入的数组拷贝到内核空间,
然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,这过程经历了多次无谓的遍历
epoll是对select和poll的改进
epoll:IO的效率不会随着监视fd的数量的增长而下降。epoll不同于select和poll轮询的方式,
而是通过每个fd定义的回调函数来实现的。只有就绪的fd才会执行回调函数。
一般情况下fd数量较少的时候poll略优于epoll,但是当fd增大到某个阈值时,
poll性能急剧下降。而epoll始终保持的稳定的性能。
希望采纳
本序列涉及的 Linux 源码都是基于 linux-4.14.143 。
1.1 文件抽象
在 Linux 内核里,文件是一个抽象,设备是个文件,网络套接字也是个文件。
文件抽象必须支持的能力定义在 file_operations 结构体里。
在 Linux 里,一个打开的文件对应一个文件描述符 file descriptor/FD,FD 其实是一个整数,内核把进程打开的文件维护在一个数组里,FD 对应的是数组的下标。
文件抽象的能力定义:
1.2 文件 poll *** 作
poll 函数的原型:
文件抽象 poll 函数的具体实现必须完成两件事(这两点算是规范了):
1. 在 poll 函数敢兴趣的等待队列上调用 poll_wait 函数,以接收到唤醒;具体的实现必须把 poll_table 类型的参数作为透明对象来使用,不需要知道它的具体结构。
2. 返回比特掩码,表示当前可立即执行而不会阻塞的 *** 作。
下面是某个驱动的 poll 实现示例,来自:https://www.oreilly.com/library/view/linux-device-drivers/0596000081/ch05s03.html:
poll 函数接收的 poll_table 只有一个队列处理函数 _qproc 和感兴趣的事件属性 _key。
文件抽象的具体实现在构建时会初始化一个或多个 wait_queue_head_t 类型的事件等待队列 。
poll 等待的过程:
事件发生时的唤醒过程:
一个小困惑:
fds[0].fd 与 fds[1].fd 同样是sock这个描述符, 其中在fds[0] 中注册POLLIN和POLLRDNORM事件,
在fds[1]中监听POLLOUT和POLLWRNORM事件, 这样做就成功了, 不会出现上述 "同时在一个sock描述符注册可读可写事件,
导致监听结果与实际不符"的现象。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)