转载:http://blog.csdn.net/mo_hui123456/article/details/7961619

select系统调用用于多路监控,为什么称为多路呢,它可监控多个文件,例如我监控3个文件是否可读,如果这3个文件都不可读,它将在timeout阻塞进程(调用它的那个进程),如果有满足要求的文件(有文件可读),它将唤醒该进程。

它的原型:int select(int maxfd, fd_set *readfds, fd_set *writefds, fe_set  *exceptfds, const struct  timeval *timeout);当在timeout时间内不满足条件时,是会阻塞的,所以当有条件满足时,就会唤醒进程,从而进行下面的操作(这里为读操作)。

关于timeout的取值不同,其调用有不同的表现:

timeout为0时(指的是*timeout), 不管是否有文件满足要求,都立刻返回,无文件满足要求返回0,有文件满足要求返回一个正值.

Timeout为NULL,select将阻塞进程,直到某个文件满足要求

Timeout值为正整数,就是等待的最长时间,即select在timeout时间内阻塞进程。当超过这个数时,它就不会再阻塞了,返回0, 从而该进程继续向下工作。

Select调用返回时,返回值有如下情况:
1.正常情况下返回满足要求的文件描述符个数;
2.经过了timeout等待后仍无文件满足要求,返回值为0;
3.如果select被某个信号中断,(说明该进程阻塞可被信号打断,TASK_INTERRUPTIBLE)它将返回-1并设置errno为EINTR。
4.如果出错,返回-1并设置相应的errno。

select的使用方法:

1. 将要监控的文件添加到文件描述符集
2. 调用Select开始监控
3. 判断文件是否发生变化

它的一般模型如下:

fd_set fds;

FD_ZERO(&fds); //清空集合
FD_SET(fd1,&fds); //设置描述符
FD_SET(fd2,&fds); //设置描述符
maxfdp=fd1+; //描述符最大值加1,假设fd1>fd2
switch(select(maxfdp,&fds,NULL,NULL,&timeout)) //这里监控两个文件是否可读。
case -: exit(-);break; //select错误,退出程序
case :break; //超时退出
default:
if(FD_ISSET(fd1,&fds)) //测试fd1是否可读
{
}

select与poll调用最终会引发设备驱动中的poll()被执行。好像还有个扩展的poll,叫epoll.(这个不懂,高手请指导)

驱动中poll()原型:unsigned int (*poll)(struct file *filp,poll_table *wait);

Poll设备方法负责完成:
1.  使用poll_wait()将等待队列添加到poll_table中。
2.  返回描述设备是否可读或可写的掩码。

poll_wait原型:void poll_wait(struct file *filp, wait_queue_head_t
*queue, poll_table *wait);
这个函数是不会引起阻塞的,它所做的工作就是将可能引起设备文件状态变化的等待队列头添加到poll_table中。那么这里就有个问题了,既然驱动中的
poll()不会引起阻塞,那阻塞是发生在哪里呢?(下面说)

poll()的典型模板:

static unsigned int mem_poll(struct file *filp,poll_table *wait)
{
struct xxx_dev *dev =filp->private_data; //获得设备结构体指针,(从私有数据获得,前提私有数据已被赋值)
unsigned int mask =;
/* 把等待队列添加到poll_table*/
poll_wait(filp,&dev->r_wait,wait); //加读等待队列头
/*返回掩码*/
if (有数据可读)
mask = POLLIN |POLLRDNORM;/*设备可读*/
return mask;
}

设备可读通常返回(POLLIN|POLLRDNORM )
设备可写通常返回(POLLOUT|POLLWRNORM )

Poll方法只是做一个登记,真正的阻塞发生在select.c 中的 do_select函数。查看内核代码:

int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
{
ktime_t expire, *to = NULL;
struct poll_wqueues table;
poll_table *wait;
int retval, i, timed_out = ;
unsigned long slack = ; rcu_read_lock();
retval = max_select_fd(n, fds);
rcu_read_unlock(); if (retval < )
return retval;
n = retval; poll_initwait(&table);
wait = &table.pt;
if (end_time && !end_time->tv_sec && !end_time->tv_nsec) {
wait = NULL;
timed_out = ;
} if (end_time && !timed_out)
slack = estimate_accuracy(end_time); retval = ;
for (;;) {
unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp; inp = fds->in; outp = fds->out; exp = fds->ex;
rinp = fds->res_in; routp = fds->res_out; rexp = fds->res_ex; for (i = ; i < n; ++rinp, ++routp, ++rexp) {
unsigned long in, out, ex, all_bits, bit = , mask, j;
unsigned long res_in = , res_out = , res_ex = ;
const struct file_operations *f_op = NULL;
struct file *file = NULL; in = *inp++; out = *outp++; ex = *exp++;
all_bits = in | out | ex;
if (all_bits == ) {
i += __NFDBITS;
continue;
}
//从这里开始看
for (j = ; j < __NFDBITS; ++j, ++i, bit <<= ) {
int fput_needed;
if (i >= n)
break;
if (!(bit & all_bits))
continue;
file = fget_light(i, &fput_needed);
if (file) {
f_op = file->f_op;
mask = DEFAULT_POLLMASK;
if (f_op && f_op->poll) {
wait_key_set(wait, in, out, bit);
mask = (*f_op->poll)(file, wait); //调用poll函数,返回掩码
}
fput_light(file, fput_needed); //如果监控的文件满足条件(例如:在读集合里,可读,说明满足条件,在读集合里,可写,这是不满足条件的),retvall++。 判断文件是可读,可写,而且在这个集合里时才retall++,只要retall>0,就会不会阻塞。可见阻塞虽然并没有发生在poll()里面,但是阻塞依赖于poll返回的掩码。如果文件都不满足条件时,那在timeout里发生阻塞。
if ((mask & POLLIN_SET) && (in & bit)) { res_in |= bit;
retval++;
wait = NULL;
}
if ((mask & POLLOUT_SET) && (out & bit)) {
res_out |= bit;
retval++;
wait = NULL;
}
if ((mask & POLLEX_SET) && (ex & bit)) {
res_ex |= bit;
retval++;
wait = NULL;
}
}
}
if (res_in)
*rinp = res_in;
if (res_out)
*routp = res_out;
if (res_ex)
*rexp = res_ex;
cond_resched();
}
wait = NULL;
if (retval || timed_out || signal_pending(current)) //如果retval>0 (意味满足条件)或timeout超时,被信号打断,则跳出循环。则没有阻塞。
break;
if (table.error) {
retval = table.error;
break;
} /*
* If this is the first loop and we have a timeout
* given, then we convert to ktime_t and set the to
* pointer to the expiry value.
*/
if (end_time && !to) {
expire = timespec_to_ktime(*end_time);
to = &expire;
} //如果上面条件不满足,阻塞就发生在这里。阻塞该进程并且在timeout时间里调度其他进程。(阻塞意味着发生调度,将该进程撤换出去,调度其他进程,避免cpu空转)
if (!poll_schedule_timeout(&table, TASK_INTERRUPTIBLE, to, slack))
timed_out = ;
} poll_freewait(&table); return retval;
}

linux设备驱动之select的更多相关文章

  1. linux设备驱动归纳总结(十一):写个简单的看门狗驱动【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-112879.html linux设备驱动归纳总结(十一):写个简单的看门狗驱动 xxxxxxxxxxx ...

  2. linux设备驱动归纳总结(三):6.poll和sellct【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-61749.html linux设备驱动归纳总结(三):6.poll和sellct xxxxxxxxxx ...

  3. Linux 设备驱动 Edition 3

    原文网址:http://oss.org.cn/kernel-book/ldd3/index.html Linux 设备驱动 Edition 3 By Jonathan Corbet, Alessand ...

  4. Linux设备驱动中的阻塞和非阻塞I/O

    [基本概念] 1.阻塞 阻塞操作是指在执行设备操作时,托不能获得资源,则挂起进程直到满足操作所需的条件后再进行操作.被挂起的进程进入休眠状态(不占用cpu资源),从调度器的运行队列转移到等待队列,直到 ...

  5. 《Linux设备驱动开发具体解释(第3版)》进展同步更新

    本博实时更新<Linux设备驱动开发具体解释(第3版)>的最新进展. 2015.2.26 差点儿完毕初稿. 本书已经rebase到开发中的Linux 4.0内核,案例多数基于多核CORTE ...

  6. linux 设备驱动概述

    linux 设备驱动概述 目前,Linux软件工程师大致可分为两个层次: (1)Linux应用软件工程师(Application Software Engineer):       主要利用C库函数和 ...

  7. 蜕变成蝶~Linux设备驱动中的阻塞和非阻塞I/O

    今天意外收到一个消息,真是惊呆我了,博客轩给我发了信息,说是俺的博客文章有特色可以出本书,,这简直让我受宠若惊,俺只是个大三的技术宅,写的博客也是自己所学的一些见解和在网上看到我一些博文以及帖子里综合 ...

  8. 华清远见Linux设备驱动(每章小结)

    1.  linux设备驱动是以内核模块的方式而存在的,在具体的驱动开发中将驱动编译为模块具有很到的工程意义.因为如果将正在开发中的驱动编译如内核,而开发过程中会不断修改驱动代码,则需要不断的编译和重启 ...

  9. (笔记)linux设备驱动--LED驱动

    linux设备驱动--LED驱动 最近正在学习设备驱动开发,因此打算写一个系列博客,即是对自己学习的一个总结,也是对自己的一个督促,有不对,不足,需要改正的地方还望大家指出,而且希望结识志同道合的朋友 ...

随机推荐

  1. emacs 操作集锦

    1.C-k 的功能并不是剪切当前行,而是剪切当前行从光标到行末的内容. Emacs 中的剪切不叫剪切(cut),叫kill,复制(copy)不叫copy ,叫kill-ring-save (这个可以理 ...

  2. 第三百三十二天 how can I 坚持

    今天一大早,住的这就施工了,被吵醒了.. 下午去了趟小米之家,小米5还行,黑科技不黑,哈哈. 小米5黑科技不太黑,就知道造词,整体感觉还行,就是感觉屏幕有点长,小米之家人倒是不少,还有老太太去小米之家 ...

  3. 【128】Word中的VBA

    通过查找关键字,然后删除整段文字的实现! Sub 删除查找含关键词的行() Dim KeyWord As String KeyWord = InputBox("请输入关键词(词长不限,中英均 ...

  4. TreeSet介绍

    一.TreeSet原理: 1.TreeSet存储对象的时候, 可以排序, 但是需要指定排序的算法 2.Integer能排序(有默认顺序), String能排序(有默认顺序), 自定义的类存储的时候出现 ...

  5. shell输出调试信息

    [shell输出调试信息] 1.使用trap命令 trap命令用于捕获指定的信号并执行预定义的命令. 其基本的语法是: trap 'command' signal 其中signal是要捕获的信号,co ...

  6. flask中的session对象方法

    'clear', 'copy', 'fromkeys', 'get', 'has_key', 'items', 'iteritems', 'iterkeys', 'itervalues', 'keys ...

  7. HDU 1312 Red and Black DFS(深度优先搜索) 和 BFS(广度优先搜索)

    Red and Black Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total ...

  8. How Tomcat Works(四)

    Servlet容器有两个主要的模块,即连接器(connector)与容器(container),本文接下来创建一个连接器来增强前面文章中的应用程序的功能,以一种更优雅的方式来创建request对象和r ...

  9. Lotus 迁移到Exchange POC 之 新建2007 服务器!

    我们登录到Exchange 2007 服务器,由于需要对AD进行扩展,我们首先必须完成架构扩展,由于默认没有ldifde工具,所以我们需要执行servermanagercmd –I rsat-adds ...

  10. String.Format格式说明(转)

    C#格式化数值结果表 字符 说明 示例 输出 C 货币 string.Format("{0:C3}", 2) $2.000 D 十进制 string.Format("{0 ...