Linux下select&poll&epoll的实现原理(一)【转】
转自:http://www.cnblogs.com/lanyuliuyun/p/5011526.html
最近简单看了一把 linux-3.10.25 kernel中select/poll/epoll这个几个IO事件检测API的实现。此处做一些记录。
其基本的原理是相同的,流程如下
- 先依次调用fd对应的struct file.f_op->poll()方法(如果有提供实现的话),尝试检查每个提供待检测IO的fd是否已经有IO事件就绪
- 如果已经有IO事件就绪,则直接所收集到的IO事件返回,本次调用结束
- 如果暂时没有IO事件就绪,则根据所给定的超时参数,选择性地进入等待
- 如果超时参数指示不等待,则本次调用结束,无IO事件返回
- 如果超时参数指示等待(等待一段时间或持续等待),则将当前select/poll/epoll的调用任务挂起
- 当所检测的fd任何一个有新的IO事件发生时,会将上述的处于等待的任务唤醒。任务被唤醒之后,重新执行1中的IO事件收集过程,将此时收集到的IO事件返回,本次的调用过程结束。
可以看出流程并不复杂,本文按照上述流程,先对select/poll的实现做进一步分析,epoll的实现要复杂一些,另外做叙述。
上述比较关键的地方是
1. 起初在无IO事件时,调用任务在被挂起之后,fd上有IO事件发生时,如何将挂起的任务唤醒?
接下来一poll()的实现过程,介绍这一过程的实现原理。
要实现唤醒过程,比较关键的步骤为
- 在上述初次调用 struct file.f_op->poll() 时,除了fd对应的 struct file 指针之外,还需要传入一个 poll_table 类型的指针。 该 poll_table 具体是在 poll() 的实现过程中定义提供的。
- 在各个fd的struct file.f_op->poll()方法中,需要对应调用一下poll_wait()
其中关键
struct file.f_op->poll() 方法的作用是既是直接查询fd上的IO事件
而 poll_wait() 既是实现根据需要将 poll() 的调用任务,选择性的添加到fd的IO事件等待队列中。
注意此处是“选择性的添加”,为何如此说,得看 poll_wait() 的实现,其是一个inline函数,如下

static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
{
if (p && p->_qproc && wait_address)
p->_qproc(filp, wait_address, p);
}

定义在 include/linux/poll.h 中。
可以看出,当调用 struct file.f_op->poll() 时,poll_table 的 _qproc 成员有定义时,poll_wait() 的调用才有实际的效果。
而前面已经说了, “poll_table 具体是在 poll() 的实现过程中定义提供的”,具体是 poll_initwait(&table) 调用对 poll_table 进行初始化的。
再进一步看一下 poll_initwait() 实现,如下

1 void poll_initwait(struct poll_wqueues *pwq)
2 {
3 init_poll_funcptr(&pwq->pt, __pollwait);
4 pwq->polling_task = current;
5 pwq->triggered = 0;
6 pwq->error = 0;
7 pwq->table = NULL;
8 pwq->inline_index = 0;
9 }

其中
1 static inline void init_poll_funcptr(poll_table *pt, poll_queue_proc qproc)
2 {
3 pt->_qproc = qproc;
4 pt->_key = ~0UL; /* all events enabled */
5 }
也就是说初次遍历 poll 给定的诸fd,直至初始收集到IO事件,都会对 __pollwait() 调用一把。
实际上也正是在
__pollwait() 中实现将 poll() 的调用任务添加到 fd
的等待队列中,并且指定了唤醒执行过程中的回调;并且初次对所有的fd调用过 struct file.f_op->poll() 之后,会将
poll_table._qproc 只为NULL,故而后续重新,调用 struct file.f_op->poll()
时不会重复的将调用线程添加到fd的等待队列两种。在 poll() 的实现中,该回调函数是 pollwake(),具体有 pollwake()
调用 kernel 的唤醒API(default_wake_function())将 poll() 的调用任务唤醒。
(由于之前具体是在 poll() 的实现中进行挂起的,所以自然唤醒过程也应该放在 poll() 的实现中了)
上面这些内容是介绍了 poll() 这一侧为等待唤醒,所做的准备工作。接下来看看,当fd上有IO时间就绪时,是如何将那些对自身等待的任务唤醒的。
此处以 eventfd 的实现为例,其实现较为简单,方便叙述。
先简单说一下 eventfd 的特性,其内部维持了一个64位的计数器。当该计数器大于0时,fd上有可读事件;当该计数器值小于 ULLONG_MAX 时,有可写实现
看其实现代码知道,其具体对该计数值更新过程发生在 eventfd_ctx_read() / eventfd_write() 中。
eventfd_ctx_read()操作之后,该内部计数器值减小,结尾如下代码中片段会对的内部 wait_queue 中记录的等待任务进行唤醒操作。
1 if (likely(res == 0)) {
2 eventfd_ctx_do_read(ctx, cnt);
3 if (waitqueue_active(&ctx->wqh))
4 wake_up_locked_poll(&ctx->wqh, POLLOUT);
5 }
而在 eventfd_write() 操作完毕后,内部计数器值增大,结尾如下代码片段会对对的内部 wait_queue 中记录的等待任务进行唤醒操作。
1 if (likely(res > 0)) {
2 ctx->count += ucnt;
3 if (waitqueue_active(&ctx->wqh))
4 wake_up_locked_poll(&ctx->wqh, POLLIN);
5 }
至此, 完整的唤醒通知过程也就介绍完成了。
select() 实现过程与 poll() 完全一致,不同的待检测的fd和结果事件的返回方式不同而已。poll() 内部是使用链表进行记录,而 select() 是使用的bit位序列进行记录的而已。
详细请看 fs/select.c 中看 poll() 和 select() 的实现代码。
epoll() 的事件检查方式与 poll() / select() 类似,一个明显的差别是各个 fd 是注册到 epoll 内部进行记录管理的,而 poll/ select 需要每次从用户态拷贝到 kernel 中,
且 epoll 内部对 fd 的记录是用rbtree,并且还有自己自己的一些独有的特点,这些内容将在另外的文章中进行描述。本文到此为止
Linux下select&poll&epoll的实现原理(一)【转】的更多相关文章
- Linux下select&poll&epoll的实现原理(一)
最近简单看了一把 linux-3.10.25 kernel中select/poll/epoll这个几个IO事件检测API的实现.此处做一些记录.其基本的原理是相同的,流程如下 先依次调用fd对应的st ...
- linux下select/poll/epoll机制的比较
select.poll.epoll简介 epoll跟select都能提供多路I/O复用的解决方案.在现在的Linux内核里有都能够支持,其中epoll是Linux所特有,而select则应该是POSI ...
- linux下select,poll,epoll的使用与重点分析
好久没用I/O复用了,感觉差点儿相同都快忘完了.记得当初刚学I/O复用的时候花了好多时间.可是因为那会不太爱写博客,导致花非常多时间搞明确的东西,依旧非常easy忘记.俗话说眼过千遍不如手过一遍,的确 ...
- (转)Linux下select, poll和epoll IO模型的详解
Linux下select, poll和epoll IO模型的详解 原文:http://blog.csdn.net/tianmohust/article/details/6677985 一).Epoll ...
- Linux下select, poll和epoll IO模型的详解
http://blog.csdn.net/tianmohust/article/details/6677985 一).Epoll 介绍 Epoll 可是当前在 Linux 下开发大规模并发网络程序的热 ...
- Linux 网络编程的5种IO模型:多路复用(select/poll/epoll)
Linux 网络编程的5种IO模型:多路复用(select/poll/epoll) 背景 我们在上一讲 Linux 网络编程的5种IO模型:阻塞IO与非阻塞IO中,对于其中的 阻塞/非阻塞IO 进行了 ...
- Linux I/O复用中select poll epoll模型的介绍及其优缺点的比較
关于I/O多路复用: I/O多路复用(又被称为"事件驱动"),首先要理解的是.操作系统为你提供了一个功能.当你的某个socket可读或者可写的时候.它能够给你一个通知.这样当配合非 ...
- Linux内核中网络数据包的接收-第二部分 select/poll/epoll
和前面文章的第一部分一样,这些文字是为了帮别人或者自己理清思路的.而不是所谓的源代码分析.想分析源代码的,还是直接debug源代码最好,看不论什么文档以及书都是下策. 因此这类帮人理清思路的文章尽可能 ...
- Linux IO模式以及select poll epoll详解
一 背景 同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案是不同的.所以先限定一下本文的上下文. 本文讨论的背景是Linux环境下的network ...
随机推荐
- 睡眠猴子——beta阶段项目总结
Questions: 每个成员在beta 阶段的实践和alpha 阶段有何改进? 团队在beta 阶段吸取了那些alpha 阶段的经验教训? 12 条敏捷开发的原则中, 团队做得最好和最不好的各列 ...
- LeetCode 88. 合并两个有序数组
题目: 给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组. 说明: 初始化 nums1 和 nums2 的元素数量分别为 m ...
- 毕业设计---jQuery动态生成的a标签的事件绑定
这几天在毕业设计的前端设计阶段,准备放弃使用jsp,完全通过html+ajax+SSH进行网站的编写,在前端的页面显示我准备使用jQuery来实现数据的动态绑定.但是遇到动态添加的a标签无法直接通过$ ...
- 用户场景模拟+Spec
场景模拟: Spec: 浏览包车信息-->登录-->选择包车城市-->选择去/回程-->选择路线-->预定-->选择包车日期-->出发时间和地点--> ...
- Arduino与Air800开发板使用UART通信:传输DHT22传感器数据
硬件介绍 Arduino Leonardo在数字引脚0(RX)和1(TX)进行串口通信时是使用“Serial1”,USB的串口通信使用的是“Serial”.在数字引脚0(RX)和1(TX)与USB是相 ...
- Beta 冲刺 四
团队成员 051601135 岳冠宇 031602629 刘意晗 031602248 郑智文 031602330 苏芳锃 031602234 王淇 照片 项目进展 岳冠宇 昨天的困难 数据交换比较复杂 ...
- poi中如何自定义日期格式
1. poi的“Quick Guide”中提供了 “How to create date cells ”例子来说明如何创建日期单元格,代码如下: HSSFCellStyle cellStyle = w ...
- 安装selenium和chromedriver
网上找的算法,在运行爬虫代码时,需要Selenium+Phantomjs实现,我改成了用Selenium+Chrome:针对指定网址,自动打开浏览器,输入关键词搜索,并保存搜索的内容. 1. 安装se ...
- Duplicate spring bean id
问题背景:从本地调用服务器的dubbo接口进行测试 实现思路:基于IDEA+Spring+maven+Dubbo搭建测试项目,从本地直接调用 具体实现思路可参考博客:https://www.cnb ...
- 动态规划DP的优化
写一写要讲什么免得忘记了.DP的优化. 大概围绕着"是什么","有什么用","怎么用"三个方面讲. 主要是<算法竞赛入门经典>里 ...