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 系统调用家族允许这类应用程序建立内部的内核数
据结构只一次, 并且多次使用它们.

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Nessus详细安装-windows (保姆级教程)

Nessus描述 Nessus 是一款广泛使用的网络漏洞扫描工具。它由 Tenable Network Security 公司开发&#xff0c;旨在帮助组织评估其计算机系统和网络的安全性。 Nessus 可以执行自动化的漏洞扫描&#xff0c;通过扫描目标系统、识别和评估可能存在的安全漏洞和弱点。它可以检测…

使用 Spring Boot + MyBatis开发需要注意的事项以及开发模版

前言&#xff1a; 注意&#xff0c;本篇不适用于有相关开发经验的开发者&#xff0c;作为一个在职开发者&#xff0c;我经常在完成从0-1的模块&#xff0c;也就是从数据库表开始到创建实体类&#xff0c;以及dao层&#xff0c;Service层等业务需要添加相关注解&#xff0c;这样…

使用office打开word文档时候提示错误:0x426-0x0的解决方案

在使用office打开word文档时候提示错误&#xff1a;0x426-0x0。如下图&#xff1a; 昨天还用的好好的&#xff0c;怎么今天就不行了&#xff1f;为什么呢&#xff1f; 更多工作中遇到问题见&#xff1a;凯哥BK 这个错误导致office无法启动通常是由于office软件所依赖的服务无…

vue的表单收集案例

Vue的表单收集案例 这只是最基础的表单收集&#xff0c;并未涉及到element-ui。 <!DOCTYPE html> <html><head><meta charset"UTF-8" /><title>收集表单数据</title><script type"text/javascript" src"../js…

Hago 的 Spark on ACK 实践

作者&#xff1a;华相 Hago 于 2018 年 4 月上线&#xff0c;是欢聚集团旗下的一款多人互动社交明星产品。Hago 融合优质的匹配能力和多样化的垂类场景&#xff0c;提供互动游戏、多人语音、视频直播、 3D 虚拟形象互动等多种社交玩法&#xff0c;致力于为用户打造高效、多样、…

物理模拟重力 斜抛运动计算 抛物线计算

物理模拟重力 斜抛运动计算 抛物线计算 一、介绍二、原理三、实现如下PhysicsUtil.cs 工具类Missile.cs 四、资源分享 一、介绍 模拟Unity原始重力系统进行重写&#xff0c;可是实现发射到指定目标位置并能继续当前力进行自身的弹力与摩擦继续运动 二、原理 将Unity原始不受控…

word2003 open word2007+

Win 7 C:\Documents and Settings\Administrator\Application Data\Microsoft\Templates 还是不行&#xff0c;重装office2003吧&#xff0c;再安装转换插件&#xff0c;但是再高版本好像没转换工具

【Linux】进程管理

ps&#xff1a;报告当前进程快照。top&#xff1a;显示任务。kill&#xff1a;给一个进程发送信号。shutdown&#xff1a;关机或重启系统。 一个程序可以发动另一个程序被表述为一个父进程可以产生一个子进程&#xff0c;内核维护每个进程的信息&#xff0c;以此来保持事情有序…

【新版】软考 - 系统架构设计师(总结笔记)

个人总结学习笔记&#xff0c;仅供参考&#xff01;&#xff01;&#xff01;! →点击 笔者主页&#xff0c;欢迎关注哦&#xff08;互相学习&#xff0c;共同成长&#xff09; 笔记目录 &#x1f4e2;【系统架构设计系列】系统架构设计专业技能 计算机组成与结构操作系统信…

如何快速实现地源热泵远程监控

地源热泵远程监控解决方案 一、项目背景 山东省潍坊市盛世花园小区地源热泵项目是一个先进的供暖与制冷系统&#xff0c;旨在为整个小区提供高效且节能的温控服务。该系统主要由地下管道网络、地源热泵单元以及室内分配系统组成。 针对现有的地源热泵系统的管理和监控问题&a…

1162字符串逆序

一&#xff1a;题目 二.思路分析 1.如果不用递归&#xff0c;可以输入字符串后&#xff0c;再逆序输出&#xff0c;但是题目要求使用递归 2.使用递归&#xff1a; 2.1输入字符&#xff0c;直到输入的字符是‘&#xff01;’&#xff0c;停止输入&#xff0c;否则继续输入&…

Redis数据一致解决方案

文章目录 前言技术积累查询缓存业务流程更新缓存业务流程 更新缓存问题解决方案写在最后 前言 当前的应用服务很多都有着高并发的业务场景&#xff0c;对于高并发的解决方案一般会用到缓存来降低数据库压力&#xff0c;并且还能够提高系统性能减少请求耗时&#xff0c;比如我们…

AndroidStudio无法新建Java工程解决办法

我用的 AS 版本是 Android Studio Giraffe | 2022.3.1 Build #AI-223.8836.35.2231.10406996, built on June 29, 2023 以往新建工程都是 New project >> Empty Activity &#xff0c; 有个选择 Java 还是 Kotlin 语言的选项&#xff0c; 之后会默认生成一个 MainActi…

系列三、安装RocketMQ(单机版)

一、安装RocketMQ&#xff08;单机版&#xff09; 1.1、前置准备 通过前面系列一、MQ简介、系列二、RocketMQ简介的文章我们知道RocketMQ是用Java语言编写的&#xff0c;所以在安装RocketMQ之前&#xff0c;需要保证Linux中的JDK是已经安装好了的&#xff0c;要不然无法安装&a…

java流浪动物保护系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 java Web 流浪动物保护系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql…

3.java——继承及拓展(保姆级别教程,万字解析,匠心制作)

三.继承——节省了共有属性和方法的代码&#xff1a;语法 class Student extends Person 1.继承基础 1.继承首先是面向对象中非常强的一种机制&#xff0c;他首先可以复用代码&#xff08;name ,age&#xff09;&#xff0c;让我们的获得了Person全部功能和属性&#xff0c;只…

使用keytool查看Android APK签名

文章目录 一、找到JDK位置二、使用方法2.1 打开windows命令行工具2.2 查看签名 三、如何给APK做系统签名呢? 一、找到JDK位置 安卓AS之后&#xff0c;可选择继续安装JDK&#xff0c;如本文使用amazon版本默认位置&#xff1a;C:\Users\66176.jdks\corretto-1.8.0_342可通过自…

智能优化算法应用:基于北方苍鹰算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于北方苍鹰算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于北方苍鹰算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.北方苍鹰算法4.实验参数设定5.算法结果6.…

数据库之MySQL的介绍

操作系统&#xff1a; windows&#xff1a;win10、win11、win7、windows Server2016 Linux/Unix &#xff1a;红帽&#xff08;RedHat&#xff09;、Bebian、SUSE MacOS Linux系统&#xff1a;CantOS&#xff08;yum、dnf&#xff09;、Ubuntu&#xff08;apt、apt—get&am…

2024 年 22 款顶级免费数据恢复软件比较 [Windows 和 Mac]

适用于 Windows 和 Mac 用户的最佳数据恢复软件下载列表和比较&#xff0c;可快速恢复丢失的数据、已删除的文件、照片或格式化的分区数据&#xff1a; 数据恢复软件是一种从任何存储介质恢复丢失文件的应用程序。它可以恢复由于病毒攻击、硬盘故障或任何其他原因而意外删除或…