前面已经比较详尽的分析了系统调用引发的内核执行过程,本文将继续分析一下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. 深入浅出—JAVA(5)

    5.编写程序

  2. js求指定时间的周一和周日

    /*计算指定时间的的周一和周日 return=>{mondy:Date,sundy:Date} parms:{ date:指定时间,如果不指定则取当前时间 } */ function getWe ...

  3. MVC项目中如何判断用户是在用什么设备进行访问

    使用UAParser在C#MVC项目中如何判断用户是在用什么设备进行访问(手机,平板还是普通的电脑) 现在我们开发的很多web应用都要支持手机等移动设备.为了让手机用户能有更加好的用户体验,我们经常为 ...

  4. poj 2245 Lotto(dfs)

    题目链接:http://poj.org/problem?id=2245 思路分析:无重复元素组合组合问题,使用暴力枚举法,注意剪枝条件. 代码如下: #include <iostream> ...

  5. 基于物品的协同过滤推荐算法——读“Item-Based Collaborative Filtering Recommendation Algorithms” .

    ligh@local-host$ ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.168.0.3 基于物品的协同过滤推荐算法--读"Item-Based ...

  6. 【充电器】小米手机2S电池座充——小米手机官网

    ligh@local-host$ ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.168.0.3 [充电器]小米手机2S电池座充--小米手机官网 小米手机2S电池座 ...

  7. Ubuntu12.04 cuda5.5安装

    预处理步骤: 首先确认你的电脑装了一个可以运行CUDA程序的GPU. lspci | grep -i nvidia 另外要确认linux版本和gcc版本 具体参考链接:http://docs.nvid ...

  8. Android短彩信源码解析-短信发送流程(二)

    转载请注明出处:http://blog.csdn.net/droyon/article/details/11699935 2,短彩信发送framework逻辑 短信在SmsSingleRecipien ...

  9. 一、cocos2dx之如何优化内存使用(高级篇)

    本文由qinning199原创,转载请注明:http://www.cocos2dx.net/?p=93 一.内存优化原则 为了优化应用内存,你应该知道是什么消耗了你应用的大部分内存,答案就是Textu ...

  10. nodejs笔记2——请求路由

    对于不同的URL请求,服务器应该有不同的反应.我们要为路由提供请求的URL和其他需要的GET及POST参数,随后路由需要根据这些数据来执行相应的代码.我们需要的所有数据都会包含在request对象中, ...