当前位置:编程学习 > C/C++ >>

poll调用深入解析

poll调用和select调用实现的功能一样,都是网络IO利用的一种机制。先看一下poll的调用形式

一,poll调用

[cpp] 
#include <poll.h> 
int poll(struct pollfd fds[], nfds_t nfds, int timeout); 
struct pollfd结构如下:【在源码文件poll.h文件中】
[cpp] 
struct pollfd { 
    int fd; 
    short events; 
    short revents; 
}; 
这个结构中fd表示文件描述符,events表示请求检测的事件,revents表示检测之后返回的事件,如果当某个文件描述符有状态变化时,revents的值就不为空。

二,参数说明
fds:存放需要被检测状态的Socket描述符;与select不同(select函数在调用之后,会清空检测socket描述符的数组),每当调用这个函数之后,系统不会清空这个数组,而是将有状态变化的描述符结构的revents变量状态变化,操作起来比较方便;
nfds:用于标记数组fds中的struct pollfd结构元素的总数量;
timeout:poll函数调用阻塞的时间,单位是MS(毫秒)
三,返回值
大于0:表示数组fds中有socket描述符的状态发生变化,或可以读取、或可以写入、或出错。并且返回的值表示这些状态有变化的socket描述符的总数量;此时可以对fds数组进行遍历,以寻找那些revents不空的socket描述符,然后判断这个里面有哪些事件以读取数据。
等于0:表示没有socket描述符有状态变化,并且调用超时。
小于0:此时表示有错误发生,此时全局变量errno保存错误码。
四,内核实现
      poll系统调用的内核实现是sys_poll,其代码如下:
[cpp] 
asmlinkage long sys_poll(struct pollfd __user *ufds, unsigned int nfds, 
            long timeout_msecs) 

    s64 timeout_jiffies; 
    int ret; 
 
    if (timeout_msecs > 0) { 
#if HZ > 1000 
        /* We can only overflow if HZ > 1000 */ 
        if (timeout_msecs / 1000 > (s64)0x7fffffffffffffffULL / (s64)HZ) 
            timeout_jiffies = -1; 
        else 
#endif 
            timeout_jiffies = msecs_to_jiffies(timeout_msecs); 
    } else { 
        /* Infinite (< 0) or no (0) timeout */ 
        timeout_jiffies = timeout_msecs; 
    } 
 
    ret = do_sys_poll(ufds, nfds, &timeout_jiffies); 
    if (ret == -EINTR) { 
        struct restart_block *restart_block; 
        restart_block = ¤t_thread_info()->restart_block; 
        restart_block->fn = do_restart_poll; 
        restart_block->arg0 = (unsigned long)ufds; 
        restart_block->arg1 = nfds; 
        restart_block->arg2 = timeout_jiffies & 0xFFFFFFFF; 
        restart_block->arg3 = (u64)timeout_jiffies >> 32; 
        ret = -ERESTART_RESTARTBLOCK; 
    } 
    return ret; 

这个函数还是比较容易理解,包括三个部分的工作:
函数调用超时阻塞时间转换,根据内核的软时钟设置频率将超时时间设置为jiffies标准时间。
调用do_sys_poll,这里完成主要的工作。
如果当前进程有待处理的信号,则先处理信号,这是根据do_sys_poll返回来决定的,事实上在这个调用中会检查当前的进程是否有未处理信号,如果有,就会返回EINTR以处理信号,然后返回-ERESTART_RESTARTBLOCK,这会导致重新调用。
进入到do_sys_poll函数中
[cpp] 
int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout) 

    struct poll_wqueues table; 
    int err = -EFAULT, fdcount, len, size; 
    /* Allocate small arguments on the stack to save memory and be
       faster - use long to make sure the buffer is aligned properly
       on 64 bit archs to avoid unaligned access */ 
    long stack_pps[POLL_STACK_ALLOC/sizeof(long)]; 
    struct poll_list *const head = (struct poll_list *)stack_pps; 
    struct poll_list *walk = head; 
    unsigned long todo = nfds; 
 
    if (nfds > current->signal->rlim[RLIMIT_NOFILE].rlim_cur) 
        return -EINVAL; 
 
    len = min_t(unsigned int, nfds, N_STACK_PPS); 
    for (;;) { 
        walk->next = NULL; 
        walk->len = len; 
        if (!len) 
            break; 
 
        if (copy_from_user(walk->entries, ufds + nfds-todo, 
                    sizeof(struct pollfd) * walk->len)) 
            goto out_fds; 
 
        todo -= walk->len; 
        if (!todo) 
            break; 
 
        len = min(todo, POLLFD_PER_PAGE); 
        size = sizeof(struct poll_list) + sizeof(struct pollfd) * len; 
        walk = walk->next = kmalloc(size, GFP_KERNEL); 
        if (!walk) { 
            err = -ENOMEM; 
            goto out_fds; 
        } 
    } 
pollfd 
    poll_initwait(&table); 
    fdcount = do_poll(nfds, head, &table, timeout); 
    poll_freewait(&table); 
 
    for (walk = head; walk; walk = walk->next) { 
        struct pollfd *fds = walk->entries; 
        int j; 
 
        for (j = 0; j < walk->len; j++, ufds++) 
            if (__put_user(fds[j].revents, &ufds->revents)) 
                goto out_fds; 
    } 
 
    err = fdcount; 
out_fds: 
    walk = head->next; 
    while (walk) { 
        struct poll_

补充:软件开发 , C++ ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,