现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

IO复用函数poll内核源码剖析

2017-05-31 11:11 工业·编程 ⁄ 共 1259字 ⁄ 字号 暂无评论

应用层调用poll时,内核调用了sys_poll

在sys_poll中首先初始化,判断了nfds是超过struct file支持的最大的fd数(默认256),将传入的timeout时间转化为cpu时钟周期,并且调用了poll_initwait初始化了一个函数指针,用于操作系统异步回调的;

接下来,进行了一个while循环,从0到nfds给每一个struct pollfd对象开辟内存,是以链表节点poll_list大小开辟的(一个链表节点是一个page,链表节点中有struct pollfd结构),然后将用户空间的pollfd拷贝到链表节点中,再将链表节点连接起来。

出了循环所有的pollfd事件用poll_list链接起来了,然后调用do_poll进行处理。

对于do_poll分析

do_poll里有一个for的死循环,轮询检查是否有事件发生,for内部嵌套while循环,遍历链表的每一个节点,调用do_pollfd对于每一个fd进行判断;while循环退出会记录有多少个事件发生的个数,如果count>0就退出for循环,反之继续for循环直到有事件发生poll返回(timewait为-1时)。

ps:可以看出判断是否有事件发生采用的是轮询的方式,遍历每一个链表节点,将最终就绪文件描述符的个数返回

对于do_pollfd分析

在do_pollfd中获取到fd对应的strut file文件指针,然后根据不同类型的fd调用不同的设备驱动程序实现的poll(实际上不同的设备驱动程序的poll都是调用了poll_wait),将设备自己的等待队列作为参数传递过去

对于socket使用tcp驱动实现的poll分析

因为驱动程序实现的poll主要是调用了poll_wait,所以主要分析poll_wait,poll_wait可以从参数获取到设备自身的等待队列以及在sys_poll中定义的回调函数__pollwait,所以在poll_wait中主要是调用了回调函数__pollwait并将设备等待序列以参数传递给它(即设备驱动实现的poll调用了poll_wait,poll_wait又调用了回调__pollwait,将设备的等待队列传给__pollwait)

分析回调函数__pollwait

每一个fd调用一次__pollwait就创建了一个poll_table_entry,然后将当前进程挂在fd对应的设备等待序列上(在poll_table_entry结构体中有一个设备等待队列变量,从参数获取到传入的设备等待序列,赋值给结构体中的等待队列,然后将当前进程挂在结构体中的等待队列);当有事件发生时,会唤醒设备等待序列上的进程。

poll的性能瓶颈

(1) 每次都将用户态的pollfd结构拷贝到内核态的链表节点中,所以参数传递和页分配就是此时poll系统调用的一个性能瓶颈

(2) 当传入的fd很多时,对于每一个fd都需要调用do_pollfd(轮询),那么do_pollfd会调用很多次去进行判断(不论有没有事件发生都要调用),这是另一个瓶颈

给我留言

留言无头像?