Linux poll 和 select 机制

poll select 介绍

使用非阻塞 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> 定义)用来指示可能的操作:
23 [23]  实际上, epoll 是一组 3 个调用, 都可用来获得查询功能. 但是, 由于我们的目的, 我们可认为它是一个调用.
LINUX DEVICE DRIVERS,3RD EDITION
136
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 实
现:
LINUX DEVICE DRIVERS,3RD EDITION
137
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 所做的;

与 read 和 write 的交互

poll 和 select 调用的目的是提前决定是否一个 I/O 操作会阻塞. 在那个方面, 它们补
充了 read 和 write. 更重要的是, poll 和 select , 因为它们使应用程序同时等待几
个数据流, 尽管我们在 scull 例子里没有采用这个特性.
LINUX DEVICE DRIVERS,3RD EDITION
138
一个正确的实现对于使应用程序正确工作是必要的: 尽管下列的规则或多或少已经说明过,
我们在此总结它们.

  从设备中读数据


•  如果在输入缓冲中有数据, read 调用应当立刻返回, 没有可注意到的延迟, 即便
数据少于应用程序要求的, 并且驱动确保其他的数据会很快到达. 你可一直返回小
于你被请求的数据, 如果因为任何理由而方便这样(我们在 scull 中这样做), 如
果你至少返回一个字节. 在这个情况下, poll 应当返回 POLLIN|POLLRDNORM.
•  如果在输入缓冲中没有数据, 缺省地 read 必须阻塞直到有一个字节. 如果
O_NONBLOCK 被置位, 另一方面, read 立刻返回 -EAGIN (尽管一些老版本 SYSTEM
V 返回 0 在这个情况时). 在这些情况中, poll 必须报告这个设备是不可读的直
到至少一个字节到达. 一旦在缓冲中有数据, 我们就回到前面的情况.
•  如果我们处于文件尾, read 应当立刻返回一个 0, 不管是否阻塞. 这种情况 poll
应该报告 POLLHUP.


写入设备


•  如果在输出缓冲中有空间, write 应当不延迟返回. 它可接受小于这个调用所请求
的数据, 但是它必须至少接受一个字节. 在这个情况下, poll 报告这个设备是可
写的, 通过返回 POLLOUT|POLLWRNORM.
•  如果输出缓冲是满的, 缺省地 write 阻塞直到一些空间被释放. 如果 O_NOBLOCK
被设置, write 立刻返回一个 -EAGAIN(老式的 System V Unices 返回 0). 在这
些情况下, poll 应当报告文件是不可写的. 另一方面, 如果设备不能接受任何多
余数据, write 返回 -ENOSPC("设备上没有空间"), 不管是否设置了 O_NONBLOCK.
•  在返回之前不要调用 wait 来传送数据, 即便当 O_NONBLOCK 被清除. 这是因为许
多应用程序选择来找出一个 write 是否会阻塞. 如果设备报告可写, 调用必须不
阻塞. 如果使用设备的程序想保证它加入到输出缓冲中的数据被真正传送, 驱动必
须提供一个 fsync 方法. 例如, 一个可移除的设备应当有一个 fsnyc 入口.
尽管有一套通用的规则, 还应当认识到每个设备是唯一的并且有时这些规则必须稍微弯曲
一下. 例如, 面向记录的设备(例如磁带设备)无法执行部分写.


刷新挂起的输出


我们已经见到 write 方法如何自己不能解决全部的输出需要. fsync 函数, 由同名的系
统调用而调用, 填补了这个空缺. 这个方法原型是:
int (*fsync) (struct file *file, struct dentry *dentry, int datasync);
如果一些应用程序需要被确保数据被发送到设备, fsync 方法必须被实现为不管
O_NONBLOCK 是否被设置. 对 fsync 的调用应当只在设备被完全刷新时返回(即, 输出缓
冲为空), 即便这需要一些时间. datasync 参数用来区分 fsync 和 fdatasync 系统调用;
这样, 它只对文件系统代码有用, 驱动可以忽略它.
LINUX DEVICE DRIVERS,3RD EDITION
139
fsync 方法没有不寻常的特性. 这个调用不是时间关键的, 因此每个设备驱动可根据作者
的口味实现它. 大部分的时间, 字符驱动只有一个 NULL 指针在它们的 fops 中. 阻塞设
备, 另一方面, 常常实现这个方法使用通用的 block_fsync, 它接着会刷新设备的所有的
块.

底层的数据结构

​​​​​​​

​​​​​​​poll 和 select 系统调用的真正实现是相当地简单, 对那些感兴趣于它如何工作的人;
epoll 更加复杂一点但是建立在同样的机制上. 无论何时用户应用程序调用 poll,
select, 或者 epoll_ctl,
[24]24 内核调用这个系统调用所引用的所有文件的 poll 方法,
传递相同的 poll_table 到每个. poll_table 结构只是对一个函数的封装, 这个函数建
立了实际的数据结构. 那个数据结构, 对于 poll和 select, 是一个内存页的链表, 其中
包含 poll_table_entry 结构. 每个 poll_table_entry 持有被传递给 poll_wait 的
struct file 和 wait_queue_head_t 指针, 以及一个关联的等待队列入口. 对
poll_wait 的调用有时还添加这个进程到给定的等待队列. 整个的结构必须由内核维护以
至于这个进程可被从所有的队列中去除, 在 poll 或者 select 返回之前.
如果被轮询的驱动没有一个指示 I/O 可不阻塞地发生, poll 调用简单地睡眠直到一个它
所在的等待队列(可能许多)唤醒它.
在 poll 实现中有趣的是驱动的 poll 方法可能被用一个 NULL 指针作为 poll_table 参
数. 这个情况出现由于几个理由. 如果调用 poll 的应用程序已提供了一个 0 的超时值
(指示不应当做等待), 没有理由来堆积等待队列, 并且系统简单地不做它. poll_table
指针还被立刻设置为 NULL 在任何被轮询的驱动指示可以 I/O 之后. 因为内核在那一点
知道不会发生等待, 它不建立等待队列链表.
当 poll 调用完成, poll_table 结构被去分配, 并且所有的之前加入到 poll 表的等待
队列入口被从表和它们的等待队列中移出.
我们试图在图 poll 背后的数据结构中展示包含在轮询中的数据结构; 这个图是真实数据
结构的简化地表示, 因为它忽略了一个 poll 表地多页性质并且忽略了每个
poll_table_entry 的文件指针.​​​​​​​

在此, 可能理解在新的系统调用 epoll 后面的动机. 在一个典型的情况中, 一个对 poll
或者 select 的调用只包括一组文件描述符, 所以设置数据结构的开销是小的. 但是, 有
应用程序在那里, 它们使用几千个文件描述符. 在这时, 在每次 I/O 操作之间设置和销
毁这个数据结构变得非常昂贵. epoll 系统调用家族允许这类应用程序建立内部的内核数
据结构只一次, 并且多次使用它们.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/356395.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

hprof 不大 泄露_HPROF –内存泄漏分析教程

hprof 不大 泄露本文将为您提供有关如何通过生成和分析Sun HotSpot JVM HPROF堆转储文件来分析JVM内存泄漏问题的教程。 一个现实的案例研究将用于此目的&#xff1a;Weblogic 9.2内存泄漏影响Weblogic Admin服务器。 环境规格 Java EE服务器&#xff1a;Oracle Weblogic Ser…

投资银行对Java进行的二十大核心面试问答

这是在金融领域&#xff08;主要是大型投资银行&#xff09;共享Java核心访谈问题和答案的新系列。 在JP Morgan&#xff0c;Morgan Stanley&#xff0c;Barclays或Goldman Sachs上会问许多这些Java面试问题。 银行主要从多线程 &#xff0c; 集合 &#xff0c;序列化&#xff…

java 支付重复问题_Airbnb支付系统如何在分布式环境下避免重复打款

原文链接&#xff1a;https://medium.com/airbnb-engineering/avoiding-double-payments-in-a-distributed-payments-system-2981f6b070bbAirbnb一直在将其基础架构迁移到面向服务的体系结构(SOA)。 SOA具有许多优势&#xff0c;例如使开发人员能够专业化并具有更快迭代的能力。…

myeclipse配置java8_MyEclipse 2017配置Tomcat8

MyEclipse 2017的配置其实跟MyEclipse 2015的比较一致&#xff0c;再往之前的就有出入了。不得不吐槽其卡&#xff0c;跟Eclipse不在一个量级上呀。。。一、配置转自&#xff1a;http://www.xuexila.com/diannao/diy/2788109.html工具/原料myeclipse 2017tomcat8 服务器方法/步…

MySQL在Django框架下的基本操作(MySQL在Linux下配置)

注&#xff1a;本文已迁移至CSDN&#xff0c;后续的更新也会在CSDN。 http://blog.csdn.net/houchaoqun_xmu/article/details/53813633 http://blog.csdn.net/houchaoqun_xmu 【原】本文根据实际操作主要介绍了Django框架下MySQL的一些常用操作&#xff0c;核心内容如下&#x…

Clean Sheet – Windows 10的人体工程学Eclipse主题

Clean Sheet是适用于Windows 10的符合人体工程学的Eclipse主题。它基于干净&#xff0c;低眩光的外观和感觉&#xff0c;旨在减轻视觉疲劳和眼睛疲劳。 它融合了均衡的颜色选择&#xff0c;可以突出谐波语法并注重可读性。 除自定义滚动条外&#xff0c;它还努力满足现代美学要…

CentOS上安装MyCat-MySQL

1、安装JDK&#xff0c;要求JDK7以上。 2、下载MyCat&#xff0c;地址。 3、解压Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz&#xff0c;到usr/local/Mycat目录下。 4、添加用户Mycat。 [rootlocalhost Desktop]# useradd Mycat [rootlocalhost Desktop]# passwd M…

Java中的Redis的思维导图_Redis思维导图

常见相关问题Redis 有哪些功能&#xff1f;数据缓存功能分布式锁的功能支持数据持久化支持事务支持消息队列Redis 为什么是单线程的&#xff1f;因为 cpu 不是 Redis 的瓶颈&#xff0c;Redis 的瓶颈最有可能是机器内存或者网络带宽。既然单线程容易实现&#xff0c;而且 cpu 又…

在Java EE应用程序中实现自动重试

最初&#xff0c;我想将此博客称为“ 具有拦截器驱动的重试策略的灵活超时 ”&#xff0c;但后来我认为它太“繁重”。 该声明以及修改后的标题应该&#xff08;希望&#xff09;使您了解此帖子可能谈论的内容;-) 触发 这篇文章主要由我在较早的一篇文章中收到的评论/问题之一…

2016-12-17 新浪博客服务器挂掉了,所有博客页面都无法打开

今天&#xff08;2016-12-17 10:20&#xff09;早晨从百度上检索到一篇新浪博客文章&#xff0c;点进去之后显示无法访问此网址&#xff0c;如下截图 去新浪博客主页看了看&#xff0c;主页是可以打开的 但是点进去任何一篇文章&#xff0c;都无法打开此页面 应该是新浪博客的服…

php版本7历史,php的版本发展历史(1995-2020)

PHP一直作为Web开发中的统治力量而存在&#xff0c;在WEB服务端开发领域&#xff0c;全球份额始终保持在78%以上。PHP快速&#xff0c;非常强大&#xff0c;生态好&#xff0c;而且免费&#xff0c;是一个为WEB而生的编程语言&#xff0c;自从诞生起PHP就被大多数开发者称为世界…

netbeans7.4_NetBeans 7.2 beta:更快,更有用

netbeans7.4NetBeans 7.2的beta版本引起了极大的兴奋。 在本文中&#xff0c;我将简要介绍一下此版本令人兴奋的原因&#xff08;包括更好的性能&#xff0c;提供更多的提示以及集成FindBugs&#xff09;。 NetBeans 7.2 beta在典型的下载捆绑软件中可用&#xff0c;从较小的Ja…

C-Free 5.0编译失败问题解决办法

解决关于C-Free 5.0编译时提示错误&#xff1a;[Error] undefined reference to __dyn_tls_init_callback 解决办法&#xff1a; 因为错误提示的路径是C:\MinGW\..... 首先想到的是编译器出问题了&#xff0c;因为我在安装完C-Free 5.0后&#xff0c;重新装过MinGW编译器&#…

用Cucumber JVM编写BDD测试

Cucumber JVM是编写BDD测试的出色工具。在本文中&#xff0c;我想对Cucumber JVM的BDD进行介绍。 让我们开始吧… 什么是BDD&#xff1f; 简而言之&#xff0c;BDD试图解决“通过示例理解需求”的问题 BDD工具 有许多用于BDD的工具&#xff0c;有趣的是&#xff0c;您可以在…

计算器界面网格布局java,安卓案例:网格布局实现计算器界面

安卓案例&#xff1a;网格布局实现计算器界面一、网格布局(GridLayout)GridLayout布局使用虚细线将布局划分为行、列和单元格&#xff0c;也支持一个控件在行、列上都有交错排列。(一)继承关系图(二)常用属性1、针对布局的属性(1)rowCount&#xff1a;行数(2)columnCount&#…

如何让多文本内容只显示一行,其余用省略号来显示

在此需要用到三个属性配合使用&#xff1a; overflow: hidden;/*超出部分隐藏*/white-space: nowrap;/*不换行*/text-overflow:ellipsis;/*超出部分省略号显示*/实例&#xff1a;<p> 兴高采烈的破蛹 重获新生的冲动 寻找爱情世界 美梦  既然不是毛毛虫 就要壮烈的扑火 …

php正则替换p闭合标签,php正则替换标签的实现方法

php正则替换标签的实现方法&#xff1a;首先通过“strip_tags”函数剥去字符串中的HTML标签&#xff1b;然后利用正则表达式替换标签&#xff0c;代码语句如“pregreplace("/s/","",strpregreplace("/s/","")”。推荐&#xff1a;《PH…

POJ1201 区间

题目大意&#xff1a; 给定n个整数区间[ai,bi]和n个整数ci,求一个最小集合Z&#xff0c;满足|Z∩[ai,bi]|>ci(Z里边在闭区间[ai,bi]的个数不小于ci)。 多组数据&#xff1a; n&#xff08;1<n<50000&#xff09;区间的个数 n行&#xff1a; ai bi ci(0<ai<bi<…

vue.js 2.x 能否设置某个组件不被keep-alive 的解决方案

开发项目的时候&#xff0c;使用的是 vue 2.x 版本 搭配路由切换 vue-router &#xff0c;但是很多时候有些页面需要被缓存&#xff0c;有些页面不需要缓存&#xff0c;那么该如何进行一个处理 这里我使用了一个方案来解决这个问题 : 首先在入口页面进行全部缓存标签 即 <ke…

matlab 次坐标轴 标注,matlab标注坐标轴

matlab绘图如何添加图例/坐标轴刻度像这个图里的 那样的图例如何添加 还有坐标轴的刻度如何用像上图的序号 首先打开电脑上的“matlab”软件&#xff0c;主界面如下图所示&#xff0c;箭头处输入代码即可运行。下面输入代码绘制图像&#xff0c;命令行代码如下图所示。点击ente…