前面已经比较详尽的分析了系统调用引发的内核执行过程,本文将继续分析一下linux2.6.38内核源码中poll函数(与select实现类似)的实现。

通过前面的分析,我们知道,应用程序中的open、read、write函数系统调用都会触发软中断异常,从而进入异常处理,在异常处理中将会获取用户态传入的系统调用号,根据系统调用号在系统调用表中索引出实际的系统调用处理函数,如内核里的sys_open、sys_read、sys_write函数,而内核里的这些函数又会对应到驱动程序里的open、read、write函数。

poll机制也不例外,用户空间里调用poll函数或者select函数时,都会调用到内核空间的sys_poll或者sys_select函数。下面以sys_poll来分析用户空间poll的实现:

用户空间调用:poll
内核: asmlinkage long sys_poll(struct pollfd __user *ufds, unsigned int nfds,long timeout);
即SYSCALL_DEFINE3(poll, struct pollfd __user *, ufds, unsigned int, nfds,long, timeout_msecs) //以前分析过,实际为宏

其实现如下:

\fs\select.c

SYSCALL_DEFINE3(poll, struct pollfd __user *, ufds, unsigned int, nfds,
long, timeout_msecs)
{
struct timespec end_time, *to = NULL;
int ret; if (timeout_msecs >= 0) {
to = &end_time;
poll_select_set_timeout(to, timeout_msecs / MSEC_PER_SEC,
NSEC_PER_MSEC * (timeout_msecs % MSEC_PER_SEC));
} ret = do_sys_poll(ufds, nfds, to); if (ret == -EINTR) {
struct restart_block *restart_block; restart_block = ¤t_thread_info()->restart_block;
restart_block->fn = do_restart_poll;
restart_block->poll.ufds = ufds;
restart_block->poll.nfds = nfds; if (timeout_msecs >= 0) {
restart_block->poll.tv_sec = end_time.tv_sec;
restart_block->poll.tv_nsec = end_time.tv_nsec;
restart_block->poll.has_timeout = 1;
} else
restart_block->poll.has_timeout = 0; ret = -ERESTART_RESTARTBLOCK;
}
return ret;
}

为了使结构简要直观,我们只列出调用关系框架:

用户空间:poll
内核: asmlinkage long sys_poll(struct pollfd __user *ufds, unsigned int nfds,long timeout);
即SYSCALL_DEFINE3(poll, struct pollfd __user *, ufds, unsigned int, nfds,long, timeout_msecs) //以前分析过,实际为宏
poll_select_set_timeout(to, timeout_msecs / MSEC_PER_SEC,NSEC_PER_MSEC * (timeout_msecs % MSEC_PER_SEC));//配置超时时间
do_sys_poll(ufds, nfds, to);
poll_initwait(&table);//初始化等待队列
init_poll_funcptr(&pwq->pt, __pollwait);
pt->qproc = qproc; /*table->pt->qproc= __pollwait;详见注释1-2*/
do_poll(nfds, head, &table, end_time);
for (;;)
{
for (; pfd != pfd_end; pfd++)//针对多个进程
{
if (do_pollfd(pfd, pt)) /*do_pollfd会调用驱动poll函数,poll里面的poll_wait最终调用pt->qproc函数(即__pollwait)将进程中可能引起待监测内容状态变化的等待队列链表头挂载到查询表中,除此以外,poll还会根据条件判断事件是否发生,发生则返回真,详见注释1-1*/
{
count++; //记录等待事件发生的进程数
pt = NULL;
}
}
if (!count) /*若count为0(表示无等待的事件发生)*/
{
count = wait->error;
if (signal_pending(current)) /*判断是否为信号唤醒*/
count = -EINTR;
}
/*若count不为0(有等待的事件发生了)或者timed_out不为0(有信号发生或超时),则推出休眠*/
if (count || timed_out)
break ;
/*上述条件不满足时进入休眠,若有等待的事件发生了,超时或收到信号则唤醒*/
if (!poll_schedule_timeout(wait, TASK_INTERRUPTIBLE, to, slack))
timed_out = 1;
}
poll_freewait(&table); //清除poll_wqueues占用的资源,包括加入到查询表中的等待队列链表头

注释1-1:

do_pollfd(pfd, pt)

mask = file->f_op->poll(file, pwait);/*调用驱动程序中的poll函数,若poll驱动函数返回值不为0则会使count++*/

调用关系如下:

do_pollfd(pfd, pt)
mask = file->f_op->poll(file, pwait); //即调用驱动中的poll函数(在写驱动程序时编写poll函数)
poll_wait //其作用为挂载当前进程中可能引起待监测内容状态变化的等待队列链表头到poll_table查询表中,具体实现如下
p->qproc(filp, wait_address, p);(p->qproc = __pollwait前面已有初始化)
__pollwait(struct file *filp, wait_queue_head_t *wait_address,poll_table *p)(上面展开即为本函数调用)
{
entry->wait_address = wait_address; //挂载进程等待队列链表头到查询表
add_wait_queue(wait_address, &entry->wait);
}

除此以外,poll驱动函数还会根据条件判断事件是否发生,发生则返回真,退出休眠;

下面是一个poll驱动函数的例子:

static unsigned forth_drv_poll(struct file *file, poll_table *wait)
{
unsigned int mask = 0;
poll_wait(file, &button_waitq, wait); // 将button_waitq等待链表头加入wait查询表
if (ev_press) //判断事件是否发生,发生了则返回真;
mask |= POLLIN | POLLRDNORM;
return mask;
}

注释1-2:

1. 此处主要目的为初始化函数指针,使其指向__pollwait函数,在poll驱动函数中将调用poll_wait,而该函数中将调用__pollwait函数将当前进程挂到等待队列中,

2. __pollwait函数中有这么两句:entry->wait_address = wait_address; add_wait_queue(wait_address, &entry->wait);用于将当前进程中可能引起监测内容变化的等待队列链表头挂到查询表中,table->pt->qproc= __pollwait就就是初始化table->pt->qproc函数指针 。

从上述分析可知,poll即使只有一个描述符就绪,也要遍历整个集合。如果集合中活跃的描述符很少,遍历过程的开销就会变得很大,而如果集合中大部分的描述符都是活跃的,遍历过程的开销又可以忽略。因此集合中大部分的描述符都是活跃的情况下,poll的使用效率高。每次用户空间调用poll函数时,都要将全部的数据复制到内核,复制数据增加了开销

本文参考:http://watter1985.iteye.com/blog/1614039

poll系统调用的内核态实现机制分析的更多相关文章

  1. Linux内核态抢占机制分析(转)

    Linux内核态抢占机制分析  http://blog.sina.com.cn/s/blog_502c8cc401012pxj.html 摘 要]本文首先介绍非抢占式内核(Non-Preemptive ...

  2. Linux内核态抢占机制分析

    http://blog.sina.com.cn/s/blog_502c8cc401012pxj.html [摘要]本文首先介绍非抢占式内核(Non-Preemptive Kernel)和可抢占式内核( ...

  3. Linux内核态抢占机制分析【转】

    转自:http://blog.csdn.net/yiyeguzhou100/article/details/53097665 目录(?)[-] 1非抢占式和可抢占式内核的区别 21 用户态抢占User ...

  4. Linux内核抢占实现机制分析【转】

    Linux内核抢占实现机制分析 转自:http://blog.chinaunix.net/uid-24227137-id-3050754.html [摘要]本文详解了Linux内核抢占实现机制.首先介 ...

  5. linux内核剖析(六)Linux系统调用详解(实现机制分析)

    本文介绍了系统调用的一些实现细节.首先分析了系统调用的意义,它们与库函数和应用程序接口(API)有怎样的关系.然后,我们考察了Linux内核如何实现系统调用,以及执行系统调用的连锁反应:陷入内核,传递 ...

  6. poll机制分析

    更多文档:http://pan.baidu.com/s/1sjzzlDF linux poll/select用法及在字符驱动中的简单实现 1.poll和select 使用非阻塞I/O 的应用程序常常使 ...

  7. 系统调用syscall---用户态切换到内核态的唯一途径

    1.应用程序有时需要内核协助完成一些处理,但是应用程序不可能执行内核代码(主要是安全性考虑), 那么,应用程序需要有一种机制告诉内核,它现在需要内核的帮助,这个机制就是系统调用. 2.系统调用的本质是 ...

  8. linux 用户态和内核态以及进程上下文、中断上下文 内核空间用户空间理解

    1.特权级         Intel x86架构的cpu一共有0-4四个特权级,0级最高,3级最低,ARM架构也有不同的特权级,硬件上在执行每条指令时都会对指令所具有的特权级做相应的检查.硬件已经提 ...

  9. Linux操作系统学习_用户态与内核态之切换过程

    因为操作系统的很多操作会消耗系统的物理资源,例如创建一个新进程时,要做很多底层的细致工作,如分配物理内存,从父进程拷贝相关信息,拷贝设置页目录.页表等,这些操作显然不能随便让任何程序都可以做,于是就产 ...

随机推荐

  1. 「OC」类和对象

    一.面向对象 OC语言是面向对象的,c语言是面向过程的,面向对象和面向过程只是解决问题的两种思考方式,面向过程关注的是解决问题涉及的步骤,面向对象关注的是设计能够实现解决问题所需功能的类. 术语:OO ...

  2. cocos2d-x游戏开发系列教程-超级玛丽07-CMGameMap(六)-马里奥跳跃

    当w键按下时,马里奥执行跳跃动作 执行跳跃动作也是在MarioMove函数中调用的

  3. 将HDC保存为BMP文件

    HDC在MSDN中的全称为:The handle of device context.通常,我们都是用来做相应的显示操作.        熟悉WIN32的朋友对于其应该不会陌生,经常采用GetDC,G ...

  4. Uva 167 The Sultan's Successors(dfs)

    题目链接:Uva 167 思路分析:八皇后问题,采用回溯法解决问题. 代码如下: #include <iostream> #include <string.h> using n ...

  5. 孙弘与Masa Maso 做互联网最贵的衬衫(2)_人物对话_中国时尚品牌网

    孙弘与Masa Maso 做互联网最贵的衬衫(2)_人物对话_中国时尚品牌网 孙弘与Masa Maso 做互联网最贵的衬衫(2)

  6. 修改linux多系统启动顺序

    Ubuntu和XP双系统grub2默认启动项设置为XP 装了双系统后,在开机时总会有想让一个系统默认启动的时候,一般安装完Ubuntu和XP双系统后,开机时默认的是启动Ubuntu系统,但是当想让XP ...

  7. js 实现图片间隔循环轮播以及没有间隔的循环轮播

    链接地址:http://blog.sina.com.cn/s/blog_75cf5f32010199dn.html 最近做了个图片循环轮播的功能.就是几张图片不断的循环滚动显示. 感觉这个方法不错所以 ...

  8. Irrlicht学习之光照的研究

    Irrlicht学习之光照的研究 最近研究一下Irrlicht的光照.发现Irrlicht的光照还是比较简单的,相比低于它的OpenGL和Direct3D,设置光源以及设置光照的参数更加人性化(可能是 ...

  9. hdu1284经典钱币兑换问题

    钱币兑换问题. 题目 http://acm.hdu.edu.cn/showproblem.php?pid=1284 完全背包. 这种是求背包问题最多的组合方案 参考了一些资料   http://blo ...

  10. 在树莓派上设置无线静态IP

    修改文件: /etc/network/interfaces,命令如下 sudo nano /etc/network/interfaces 将最后一句iface default inet dhcp,替换 ...