使用非阻塞 I/O 的应用程序常常使用 poll, select, 和 epoll 系统调用. poll, select 和 epoll 本质上有相同的功能: 每个允许一个进程来决定它是否可读或者写一个 或多个文件而不阻塞. 这些调用也可阻塞进程直到任何一个给定集合的文件描述符可用来 读或写. 因此, 它们常常用在必须使用多输入输出流的应用程序, 而不必粘连在它们任何 一个上. 相同的功能常常由多个函数提供, 因为 2 个是由不同的团队在几乎相同时间完 成的: select 在 BSD Unix 中引入, 而 poll 是 System V 的解决方案. epoll 调用[23]23 添加在 2.5.45, 作为使查询函数扩展到几千个文件描述符的方法.

支持任何一个这些调用都需要来自设备驱动的支持. 这个支持(对所有 3 个调用)由驱动 的 poll 方法调用. 这个方法由下列的原型:

unsigned int (*poll) (struct file *filp, poll_table *wait);

这个驱动方法被调用, 无论何时用户空间程序进行一个 poll, select, 或者 epoll 系统 调用, 涉及一个和驱动相关的文件描述符. 这个设备方法负责这 2 步:

  • 1. 在一个或多个可指示查询状态变化的等待队列上调用 poll_wait. 如果没有文 件描述符可用作 I/O, 内核使这个进程在等待队列上等待所有的传递给系统调用的 文件描述符.
  • 2. 返回一个位掩码, 描述可能不必阻塞就立刻进行的操作.

这 2 个操作常常是直接的, 并且趋向与各个驱动看起来类似. 但是, 它们依赖只能由驱 动提供的信息, 因此, 必须由每个驱动单独实现.

poll_table 结构, 给 poll 方法的第 2 个参数, 在内核中用来实现 poll, select, 和 epoll 调用; 它在 <linux/poll.h>中声明, 这个文件必须被驱动源码包含. 驱动编写者 不必要知道所有它内容并且必须作为一个不透明的对象使用它; 它被传递给驱动方法以便 驱动可用每个能唤醒进程的等待队列来加载它, 并且可改变 poll 操作状态. 驱动增加一 个等待队列到 poll_table 结构通过调用函数 poll_wait:

void poll_wait (struct file *, wait_queue_head_t *, poll_table *);

poll 方法的第 2 个任务是返回位掩码, 它描述哪个操作可马上被实现; 这也是直接的. 例如, 如果设备有数据可用, 一个读可能不必睡眠而完成; poll 方法应当指示这个时间 状态. 几个标志(通过 <linux/poll.h> 定义)用来指示可能的操作:

实际上, epoll 是一组 3 个调用, 都可用来获得查询功能. 但是, 由于我们的目的, 我们可认为它是一个调用.

POLLIN

如果设备可被不阻塞地读, 这个位必须设置.

POLLRDNORM

这个位必须设置, 如果"正常"数据可用来读. 一个可读的设备返回 ( POLLIN|POLLRDNORM ).

POLLRDBAND

这个位指示带外数据可用来从设备中读取. 当前只用在 Linux 内核的一个地方 ( DECnet 代码 )并且通常对设备驱动不可用.

POLLPRI

高优先级数据(带外)可不阻塞地读取. 这个位使 select 报告在文件上遇到一个异 常情况, 因为 selct 报告带外数据作为一个异常情况.

POLLHUP

当读这个设备的进程见到文件尾, 驱动必须设置 POLLUP(hang-up). 一个调用 select 的进程被告知设备是可读的, 如同 selcet 功能所规定的.

POLLERR

一个错误情况已在设备上发生. 当调用 poll, 设备被报告位可读可写, 因为读写 都返回一个错误码而不阻塞.

POLLOUT

这个位在返回值中设置, 如果设备可被写入而不阻塞.

POLLWRNORM

这个位和 POLLOUT 有相同的含义, 并且有时它确实是相同的数. 一个可写的设备 返回( POLLOUT|POLLWRNORM).

POLLWRBAND

如同 POLLRDBAND , 这个位意思是带有零优先级的数据可写入设备. 只有 poll 的 数据报实现使用这个位, 因为一个数据报看传送带外数据.

应当重复一下 POLLRDBAND 和 POLLWRBAND 仅仅对关联到 socket 的文件描述符有意义: 通常设备驱动不使用这些标志.

poll 的描述使用了大量在实际使用中相对简单的东西. 考虑 poll 方法的 scullpipe 实 现:

static unsigned int
scull_p_poll(struct file *filp, poll_table *wait)

{

struct scull_pipe *dev = filp->private_data;
unsigned int mask = 0;

/*

*  The buffer is
circular; it is considered full

*  if "wp"
is right behind "rp" and empty if the

*  two are equal.

*/

down(&dev->sem);

poll_wait(filp,
&dev->inq, wait); poll_wait(filp, &dev->outq, wait); if
(dev->rp != dev->wp)

mask |=
POLLIN | POLLRDNORM;  /* readable */ if
(spacefree(dev))

mask |= POLLOUT | POLLWRNORM;  /* writable */

up(&dev->sem); return mask;

}

这个代码简单地增加了 2
个 scullpipe 等待队列到 poll_table, 接着设置正确的掩码
位, 根据数据是否可以读或写.

所示的
poll 代码缺乏文件尾支持, 因为 scullpipe 不支持文件尾情况. 对大部分真实 的设备, poll 方法应当返回 POLLHUP 如果没有更多数据(或者将)可用.
如果调用者使用 select 系统调用, 文件被报告为可读. 不管是使用 poll 还是 select, 应用程序知道它 能够调用 read 而不必永远等待,
并且 read 方法返回 0 来指示文件尾.

例如, 对于 真正的 FIFO, 读者见到一个文件尾当所有的写者关闭文件, 而在 scullpipe 中读者永远见不到文件尾. 这个做法不同是因为 FIFO 是用作一个 2 个进程的通讯通道, 而 scullpipe 是一个垃圾桶,
人人都可以放数据只要至少有一个读者. 更多地, 重新实
现内核中已有的东西是没有意义的, 因此我们选择在我们的例子里实现一个不同的做法.

与 FIFO 相同的方式实现文件尾就意味着检查 dev->nwwriters, 在 read 和 poll 中, 并且报告文件尾(如刚刚描述过的)如果没有进程使设备写打开. 不幸的是,
使用这个实现 方法, 如果一个读者打开 scullpipe 设备在写者之前, 它可能见到文件尾而没有机会来 等待数据. 解决这个问题的最好的方式是在 open 中实现阻塞, 如同真正的
FIFO 所做的; 这个任务留作读者的一个练习.

linux poll 和 select的更多相关文章

  1. linux epoll,poll,select

    epoll函数用法,还有点poll和select 1,LT的epoll是select和poll函数的改进版. 特点是,读完缓冲区后,如果缓冲区还有内容的话,epoll_wait函数还会返回,直到把缓冲 ...

  2. poll和select

    都允许进程决定是否可以对一个或者多个打开的文件做非阻塞的读取或写入.这些调用也会阻塞进程,直到给定的文件描述符集合中的任何一个可读取或写入.常常用于那些要使用多个输入或输出流而又不会阻塞与其中任意一个 ...

  3. Linux poll机制

    1.用户空间调用(参考 poll(2) - Linux man page) int poll(struct pollfd *fds, nfds_t nfds, int timeout); it wai ...

  4. 四、poll()、select()和epoll()

    在用户程序中,poll()和select()系统调用用于对设备进行无阻塞访问.poll()和select()最终会调用设备驱动中的poll()函数,在我所使用的Linux内核中,还有扩展的poll() ...

  5. linux poll函数

    poll函数与select函数差不多 函数原型: #include <poll.h> int poll(struct pollfd fd[], nfds_t nfds, int timeo ...

  6. 【Pyhton Network】使用poll()或select()实现非阻塞传输

    通常情况下,socket上的I/O会阻塞.即除非操作结束,否则程序不会照常进行.而以下集中情况需要在非阻塞模式下进行:1. 网络接口在等待数据时是活动的,可以做出相应:2. 在不使用线程或进程的情况下 ...

  7. linux c中select使用技巧

    1.select函数作为定时器使用    it_value.tv_sec = 0;    it_value.tv_usec = 100000:    select(1,NULL,NULL,NULL,& ...

  8. linux c语言 select函数用法

    linux c语言 select函数用法 表头文件 #i nclude<sys/time.h> #i nclude<sys/types.h> #i nclude<unis ...

  9. linux c中select使用方法

    1.select函数作为定时器使用    it_value.tv_sec = 0;    it_value.tv_usec = 100000:    select(1,NULL,NULL,NULL,& ...

随机推荐

  1. 【JZOJ3886】【长郡NOIP2014模拟10.22】道路维护

    CCC 最近徆多人投诉说C国的道路破损程度太大,以至亍无法通行 C国的政府徆重视这件事,但是最近财政有点紧,丌可能将所有的道路都进行维护,所以他们决定按照下述方案进行维护 将C国抽象成一个无向图,定义 ...

  2. 大数据技术之HBase

    第1章 HBase简介 1.1 什么是HBase HBase的原型是Google的BigTable论文,受到了该论文思想的启发,目前作为Hadoop的子项目来开发维护,用于支持结构化的数据存储. 官方 ...

  3. oracle加锁

    锁 insert into TEST values(5); 加row exclusive锁,和row share相同,但也禁止用share方式加锁. Create index idx_test on ...

  4. 微服务开源生态报告 No.4

    「微服务开源生态报告」,汇集各个开源项目近期的社区动态,帮助开发者们更高效的了解到各开源项目的最新进展. 社区动态包括,但不限于:版本发布.人员动态.项目动态和规划.培训和活动. 非常欢迎国内其他微服 ...

  5. TCPThree_C杯 Day1

    题解 或 正规题解 已经很详细,不再赘述. 跟着wjx打代码,不怕卡题. 忘开long long智障错误第四次左偏树

  6. hdu1527 威佐夫博奕

    有2堆石子,有2个人,每个人可以从一堆取或从2堆取一样的个数的石子,至少取1个.问先手的是胜或输.设(ak,bk)我么成为局势. (0,0)(1,2)(3,5)(4,7)..这种先手必输的叫奇异局势. ...

  7. Java面向对象----接口概念

    接口语法 interface 接口名{ //静态常量,抽象方法 } 特点 接口中只能存放静态常量和抽象方法 java接口是对功能的扩展 通过实现接口,java类可以实现多实现 一个类可以同时继承(ex ...

  8. 记一次Celery的仇

    背景:项目在公司的一台虚拟机上运行(32核+32G).其他人的项目也在这台物理机上运行..我的训练代码是单进程的,跑完一次需要大约10h(数据量大逮着一个核使劲跑..):训练是一个Celery定时任务 ...

  9. 【算法】BSGS算法

    BSGS算法 BSGS算法用于求解关于x的模方程\(A^x\equiv B\mod P\)(P为质数),相当于求模意义下的对数. 思想: 由费马小定理,\(A^{p-1}\equiv 1\mod P\ ...

  10. 1176. Two Ends

    题目链接地址:http://soj.me/1176 题目大意:两头取数.第一个人随机取,第二个人用贪婪算法(每次都取大的),求两人取数在第一个人赢的情况下的最大分差.使用贪婪算法时,如果左右两边相等, ...