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)就是把结果拷贝回用户态。
版权声明:本文为博主原创文章,未经博主允许不得转载。
[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)就是把结果拷贝回用户态。
版权声明:本文为博主原创文章,未经博主允许不得转载。
相关文章推荐
- CF#323-DIV2-D. Once Again-暴力贪心LIS
- TCP协议发送SKB时ip_summed成员的设置 http://blog.csdn.net/justlinux2010/article/details/8508455
- epoll源码分析---sys_epoll_wait()函数 http://blog.csdn.net/hbhhww/article/details/7746638
- 【bzoj1260】【CQOI2007】【涂色paint】【dp】
- unknown host www.baidu.com network is unreachable
- kernel painic not syncing
- Software caused connection abort: recv failed
- Hadoop Failed to set permissions of path
- Failed to set permissions of path:\tmp\hadoop-yth\mapred\staging\yth-2036315919\.staging to 0700
- Failed to list databases
- nginx:accept() failed (24: Too many open files)
- Codeforces #323 D. Once Again... (LIS)
- 直接使用JDB解决ndk-gdb调试时的“waiting for debugger”弹窗不消失问题
- Mac Please try running this command again as root/Administrator.
- 责任链模式----ChainOfResponsibility
- Facebook如何使用“我们”的数据去构建人工智能
- nasm : UltraIso制作的MBR的逆向整理
- 每次询问求出两个字符串的最长公共前缀的长度 后缀数组+RMQ+lcp UVA 12338 - Anti-Rhyme Pairs
- 深入理解 [NSBundle mainBundle]
- main cannot be resolved or is not a field