目录
一、poll函数介绍
poll 函数是 Linux 系统中用于实现 I/O 多路复用的函数,可用来监控多个文件描述符,检查它们的状态是否发生变化,如是否可读、可写或产生错误等,常用于同时处理多个网络连接或文件操作的场景。
int poll(struct pollfd*fds,nfds_t nfds,int timeout);
/*
fds:指向struct pollfd结构类型的数组指针,每个数组元素指定一个被监视的文件描述符及其相关事件
struct pollfd{
int fd;//文件描述符
short events;//等待的需要测试事件,由用户设置
short revents;//实际发生了的事件,由内核在函数返回时设置
};
nfds:nfds_t类型,用于标记fds数组中的结构体元素的总数量
timeout:指定poll函数调用的阻塞时间,单位为毫秒
-1:一直阻塞,直到所检测的文件描述符上感兴趣的事件发生,或捕获到信号
0:立即返回,不阻塞线程
>0:等待指定的毫秒数,如果在这段时间内没有感兴趣的事件发生,函数超时返回
*/
二、poll原码的刨析
poll系统调用的原理:先注册回调函数__poll_wait,再初始化table变量,接着拷贝用户传入的struct pollfd(主要是fd),然后轮流调用所用的fd对应的poll(把current挂在各个fd对应的设备等待队列上)
1.分析table
在第19行定义了一个poll_queue_proc的函数指针类型;在第21-23行,结构体成员qproc是一个函数指针类型,即struct poll_table结构体包含了一个函数指针
2.分析poll_initwait
poll_initwait把table变量的成员poll_table对应的回调函数设置为__pollwait
__pollwait是操作系统的异步操作的“御用”回调函数,但是epoll并不使用此,它另增了一个回调函数,达到高速运转
3.分析sys_poll
此代码中链表节点由一个指向struct poll_list的指针控制,而众多的struct pollfd就通过struct_list的entires成员访问。循环就是把用户态的struct pollfd拷进这些entries
当用户传入的fd很多时,由于poll系统调用每次都要把所有struct pollfd拷进内核,所以参数传递和页分配此时就成了poll系统调用的性能瓶颈(问题一)
4.分析do_poll
注意438行的set_current_state和445行的signal_pending,它们两句保障了当用户程序在调用poll后挂起时,发信号可以让程序迅速推出poll调用,而通常的系统调用是不会被信号打断的
440-443行,当用户传入的fd很多时,对do_pollfd就会调用很多次(问题二)
do_pollfd就是针对每个传进来的fd,调用他们各自对应的poll函数(do_pollfd调用的是网络设备驱动实现的poll)
5.驱动程序
设备驱动程序的标准实现:调用poll_wait,即以设备自己的等待队列为参数,调用struct poll_table的回调函数
6.分析__poll_wait
__poll_wait的作用就是创建所示的数据结构,并通过struct poll_table_entry的wait成员,把current挂在了设备的等待队列上,此处的等待队列是wait_address
内容参考文章:http://donghao.org/uii/
后续文章对epoll进行刨析...