您的位置:首页 > 大数据 > 人工智能

poll函数源码详细分析 http://blog.csdn.net/lmh12506/article/details/7556297

2015-10-04 14:14 543 查看
废话不多说,相信看这篇文章的都用过poll和epoll,先来看poll系统调用的源码。

[cpp] view
plaincopyprint?

asmlinkage long sys_poll(struct pollfd __user * ufds, unsigned int nfds, long timeout)

{

struct poll_wqueues table;

int fdcount, err;

unsigned int i;

struct poll_list *head;

struct poll_list *walk;

/* Do a sanity check on nfds ... */

if (nfds > current->files->max_fdset && nfds > OPEN_MAX) //(1)

return -EINVAL;

if (timeout) { //(2)

/* Careful about overflow in the intermediate values */

if ((unsigned long) timeout < MAX_SCHEDULE_TIMEOUT / HZ)

timeout = (unsigned long)(timeout*HZ+999)/1000+1;

else /* Negative or overflow */

timeout = MAX_SCHEDULE_TIMEOUT;

}

poll_initwait(&table); //(3)

head = NULL;

walk = NULL;

i = nfds;

err = -ENOMEM;

while(i!=0) { //(4)

struct poll_list *pp;

pp = kmalloc(sizeof(struct poll_list)+

sizeof(struct pollfd)*

(i>POLLFD_PER_PAGE?POLLFD_PER_PAGE:i),

GFP_KERNEL);

if(pp==NULL)

goto out_fds;

pp->next=NULL;

pp->len = (i>POLLFD_PER_PAGE?POLLFD_PER_PAGE:i);

if (head == NULL)

head = pp;

else

walk->next = pp;

walk = pp;

if (copy_from_user(pp->entries, ufds + nfds-i,

sizeof(struct pollfd)*pp->len)) {

err = -EFAULT;

goto out_fds;

}

i -= pp->len;

}

fdcount = do_poll(nfds, head, &table, timeout);

(5)

/* OK, now copy the revents fields back to user space. */

walk = head;

err = -EFAULT;

while(walk != NULL) {

(6)

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;

}

walk = walk->next;

}

err = fdcount;

if (!fdcount && signal_pending(current))

err = -EINTR;

out_fds:

walk = head;

while(walk!=NULL) {

struct poll_list *pp = walk->next;

kfree(walk);

walk = pp;

}

poll_freewait(&table);

return err;

}

(1)这里是检查文件描述符集的最大个数是否符合要求。

(2)这里检查timeout并对他进行一些处理。

(3)这里的函数初始化类型为poll_wqueues的table,

1. void poll_initwait(structpoll_wqueues *pwq)

2. {

3. init_poll_funcptr(&pwq->pt, __pollwait);

4. pwq->error = 0;

5. pwq->table = NULL;

6. }

可以看到,函数里面初始化了poll_wqueues结构体,我们接着看他有什么内涵。

1. struct poll_wqueues {

2. poll_table pt;

3. struct poll_table_page * table;

4. int error;

5. };

每一个poll_wqueue对应每一个poll调用。pt是对外的接口。

接着看poll_table:

1. typedef struct poll_table_struct {

2. poll_queue_proc qproc;

3. } poll_table;

可以看到,初始化的时候把__pollwait函数赋给poll_table里面的poll队列处理函数(字面翻译)。这个函数在poll_wait函数里面调用,在驱动里面的poll函数就会调用到poll_wait函数。那__pollwait是干什么的呢?这里引用一下别人的图:

这里有人会问,为什么要一个数目为0的数组,那是方便找到邻接着的下一个结构体。

我们回到sys_poll接着往下看。

(4)这个循环的作用就是把要监听的文件描述符从用户态拷到内核态中。注意到因为这里建立了一个链表,如果要监听的描述符很多,超过一页的话,需要分配多个页,可能会影响性能的。

(5)这里有个do_poll函数。看源码:

[cpp] view
plaincopyprint?

static int do_poll(unsigned int nfds, struct poll_list *list,

struct poll_wqueues *wait, long timeout)

{

int count = 0;

poll_table* pt = &wait->pt;

if (!timeout)

pt = NULL;

for (;;) {

struct poll_list *walk;

set_current_state(TASK_INTERRUPTIBLE);

walk = list;

while(walk != NULL) {

do_pollfd( walk->len, walk->entries, &pt, &count);

walk = walk->next;

}

pt = NULL;

if (count || !timeout || signal_pending(current))

break;

count = wait->error;

if (count)

break;

timeout = schedule_timeout(timeout);

}

__set_current_state(TASK_RUNNING);

return count;

}

do_pollfd就是针对每个传进来的fd,调用它们各自对应的poll函数,简化一下调用过程,如下:

struct file* file = fget(fd);

file->f_op->poll(file, &(table->pt));

“如果fd对应的是某个socket,do_pollfd调用的就是网络设备驱动实现的poll;如果fd对应的是某个ext3文

件系统上的一个打开文件,那do_pollfd调用的就是ext3文件系统驱动实现的poll。一句话,这个file-

>f_op->poll是设备驱动程序实现的,那设备驱动程序的poll实现通常又是什么样子呢?其实,设备驱动

程序的标准实现是:调用poll_wait,即以设备自己的等待队列为参数(通常设备都有自己的等待队列,不

然一个不支持异步操作的设备会让人很郁闷)调用structpoll_table的回调函数。”

(6)就是把结果拷贝回用户态。

版权声明:本文为博主原创文章,未经博主允许不得转载。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: