【Linux高级 I/O(3)】如何使用阻塞 I/O 与非阻塞 I/O?——poll()函数

poll()函数介绍

        系统调用 poll()与 select()函数很相似,但函数接口有所不同。在 select()函数中,我们提供三个 fd_set 集合,在每个集合中添加我们关心的文件描述符;而在 poll()函数中,则需要构造一个 struct pollfd 类型的数组,每个数组元素指定一个文件描述符以及我们对该文件描述符所关心的条件(数据可读、可写或异常情况)。poll()函数原型如下所示:

#include <poll.h>

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

        函数参数含义如下:

        fds:指向一个 struct pollfd 类型的数组,数组中的每个元素都会指定一个文件描述符以及我们对该文件描述符所关心的条件,稍后介绍 struct pollfd 结构体类型。

        nfds:参数 nfds 指定了 fds 数组中的元素个数,数据类型 nfds_t 实际为无符号整形。

        timeout:该参数与 select()函数的 timeout 参数相似,用于决定 poll()函数的阻塞行为,具体用法如下:

  •  如果 timeout 等于-1,则 poll()会一直阻塞(与 select()函数的 timeout 等于 NULL 相同),直到 fds 数组中列出的文件描述符有一个达到就绪态或者捕获到一个信号时返回。
  • 如果 timeout 等于 0,poll()不会阻塞,只是执行一次检查看看哪个文件描述符处于就绪态。
  • 如果 timeout 大于 0,则表示设置 poll()函数阻塞时间的上限值,意味着 poll()函数最多阻塞 timeout 毫秒,直到 fds 数组中列出的文件描述符有一个达到就绪态或者捕获到一个信号为止。

        struct pollfd

        结构体 struct pollfd 结构体如下所示:

struct pollfd {
 int fd; /* file descriptor */
 short events; /* requested events */
 short revents; /* returned events */
};

        fd 是一个文件描述符,struct pollfd 结构体中的 events 和 revents 都是位掩码,调用者初始化 events 来指定需要为文件描述符 fd 做检查的事件。当 poll()函数返回时,revents 变量由 poll()函数内部进行设置,用于说明文件描述符 fd 发生了哪些事件(注意,poll()没有更改 events 变量),我们可以对 revents 进行检查,判断文件描述符 fd 发生了什么事件。

        应将每个数组元素的 events 成员设置为下表中所示的一个或几个标志,多个标志通过位或运算符 ( | )组合起来,通过这些值告诉内核我们关心的是该文件描述符的哪些事件。同样,返回时,revents 变量由内核设置为表中所示的一个或几个标志。

         表中第一组标志(POLLIN、POLLRDNORM、POLLRDBAND、POLLPRI、POLLRDHUP)与数据可读相关;第二组标志(POLLOUT、POLLWRNORM、POLLWRBAND)与可写数据相关;而第三组标志(POLLERR、POLLHUP、POLLNVAL)是设定在 revents 变量中用来返回有关文件描述符的附加信息, 如果在 events 变量中指定了这三个标志,则会被忽略。

        如果我们对某个文件描述符上的事件不感兴趣,则可将 events 变量设置为 0;另外,将 fd 变量设置为文件描述符的负值(取文件描述符 fd 的相反数-fd),将导致对应的 events 变量被 poll()忽略,并且 revents 变量将总是返回 0,这两种方法都可用来关闭对某个文件描述符的检查。

        在实际应用编程中,一般用的最多的还是 POLLIN 和 POLLOUT。对于其它标志这里不再进行介绍了。

        poll()函数返回值

        poll()函数返回值含义与 select()函数的返回值是一样的,有如下几种情况:

  • 返回-1 表示有错误发生,并且会设置 errno。
  • 返回 0 表示该调用在任意一个文件描述符成为就绪态之前就超时了。
  • 返回一个正整数表示有一个或多个文件描述符处于就绪态了,返回值表示 fds 数组中返回的 revents 变量不为 0 的 struct pollfd 对象的数量。

        下面代码演示了使用 poll()函数来实现 I/O 多路复用操作,同时读取键盘和鼠标。其实就是将上次select()代码进行了修改,使用 poll 替换 select。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <poll.h>

#define MOUSE "/dev/input/event3"

int main(void){
    char buf[100];
    int fd, ret = 0, flag;
    int loops = 5;
    struct pollfd fds[2];

    /* 打开鼠标设备文件 */
    fd = open(MOUSE, O_RDONLY | O_NONBLOCK);
    if (-1 == fd) {
        perror("open error");
        exit(-1);
    }
    /* 将键盘设置为非阻塞方式 */
    flag = fcntl(0, F_GETFL); //先获取原来的 flag
    flag |= O_NONBLOCK; //将 O_NONBLOCK 标准添加到 flag
    fcntl(0, F_SETFL, flag); //重新设置 flag

    /* 同时读取键盘和鼠标 */
    fds[0].fd=0;
    fds[0].events = POLLIN;
    fds[0].revents = 0;
    fds[1].fd = fd;
    fds[1].events = POLLIN;
    fds[1].revents = 0;

    while (loops--) {
        ret = poll(fds, 2, -1);
        if (0 > ret) {
            perror("poll error");
            goto out;
        }
        else if (0 == ret) {
            fprintf(stderr, "poll timeout.\n");
            continue;
        }
        /* 检查键盘是否为就绪态 */
        if(fds[0].revents&POLLIN){
            ret = read(0, buf, sizeof(buf));
            if (0 < ret)
                printf("键盘: 成功读取<%d>个字节数据\n", ret);
        }
        /* 检查鼠标是否为就绪态 */
        if(fds[1].revents&POLLIN) {
            ret = read(fd, buf, sizeof(buf));
            if (0 < ret)
                printf("鼠标: 成功读取<%d>个字节数据\n", ret);
        }
    }
out:
    /* 关闭文件 */
    close(fd);
    exit(ret);
}

        struct pollfd 结构体的 events 变量和 revents 变量都是位掩码,所以可以使用"revents & POLLIN"按位与的方式来检查是否发生了相应的 POLLIN 事件,判断鼠标或键盘数据是否可读。测试结果:

总结

        在使用 select()或 poll()时需要注意一个问题,当监测到某一个或多个文件描述符成为就绪态(可以读或写)时,需要执行相应的 I/O 操作,以清除该状态,否则该状态将会一直存在;调用 select()函数监测鼠标和键盘这两个文件描述符,当 select()返回时,通过 FD_ISSET()宏判断文件描述符上 是否可执行 I/O 操作;如果可以执行 I/O 操作时,应在应用程序中对该文件描述符执行 I/O 操作,以清除文件描述符的就绪态,如果不清除就绪态,那么该状态将会一直存在,那么下一次调用 select()时,文件描述符已经处于就绪态了,将直接返回。

        同理对于 poll()函数来说亦是如此,当 poll()成功返回时,检查文件描述符是否称为就绪态,如果文件描述符上可执行 I/O 操作时,也需要对文件描述符执行 I/O 操作,以清除就绪状态。

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

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

相关文章

分享18个好用的ChatGPT插件

上周ChatGPT又进化了&#xff0c;支持联网还有70几种第三方插件&#xff0c;不过还是老样子&#xff0c;只服务氪金玩家&#xff0c;免费端可能还得等等。之前只开放了俩插件&#xff0c;网络浏览器和代码解释器&#xff0c;只能说是真的不够用。 ChatGPT&#xff1a;不够&…

(一)before initialization of D3D(初始化D3D之前你需要了解的D3D基础知识)

什么是D3D? D3D全称Direct X 3D,即一组API可以用来针对GPU编程,不过他最主要的作用是用来渲染(不过现在也有很多其他应用比如d3d11va[Direct X 3D 11 Video API]用来进行硬件加速解码) Tips:Direct X 3D主要用来渲染,既然我们说到可以针对GPU编程了,当然不只是渲染的工作可以…

布隆过滤器和布谷鸟过滤器

过滤器使用场景&#xff1a; 比如有如下几个需求&#xff1a; 1.原本有10亿个号码&#xff0c;现在又来了10万个号码&#xff0c;要快速准确判断这10万个号码是否在10亿个号码库中&#xff1f;   解决办法一&#xff1a;将10亿个号码存入数据库中&#xff0c;进行数据库查询&…

iptables防火墙

iptables防火墙 一、iptables概述1.netfilter 与 iptables 的关系1&#xff09;netfilter2&#xff09;iptables 2.四表五链1&#xff09;四表2&#xff09;五链3&#xff09;表的匹配优先级4&#xff09;规则链之间的匹配顺序5&#xff09;规则链内的匹配顺序 二、iptables防火…

CodeForces.1806A .平面移动.[简单][判断可达范围][找步数规律]

题目描述&#xff1a; 题目解读&#xff1a; 给定移动规则以及起始点&#xff0c;终点&#xff1b;分析终点是否可达&#xff0c;可达则输出最小步数。 解题思路&#xff1a; 首先要判定是否可达。画图可知&#xff0c;对于题目给定的移动规则&#xff0c;只能到达起始点(a,b…

AWD竞赛全流程解析

AWD(Attack With Defense&#xff0c;攻防兼备)是一个非常有意思的模式&#xff0c;你需要在一场比赛里要扮演攻击方和防守方&#xff0c;攻者得分&#xff0c;失守者会被扣分。也就是说&#xff0c;攻击别人的靶机可以获取 Flag 分数时&#xff0c;别人会被扣分&#xff0c;同…

【数据分享】我国地级市绿地利用现状数据(9个指标\Shp格式)

绿地是城市生态的重要组成部分&#xff0c;在很多分析中都会用到绿地数据&#xff01;之前我们分享过Shp和Excel格式的全国地级市2003-2020年绿地面积数据&#xff08;可查看之前文章获悉详情&#xff09;&#xff0c;以及中国31个主要城市的绿地空间分布的栅格数据&#xff08…

ARM的读写内存指令与栈的应用

1.基础读写指令 写内存指令&#xff1a;STR MOV R1, #0xFF000000 MOV R2, #0x40000000 STR R1, [R2] 将R1寄存器中的数据写入到R2指向的内存空间 需注意&#xff0c;此命令是将R1中的数据写给R2所指向的内存空间&#xff0c;而不是直接把R1的数据赋给R2&#xff0c;R2寄存器…

华为、思科、Juniper 三厂商NAT配置详解

大家好&#xff0c;这里是网络技术联盟站。 本文给大家介绍华为、思科、Juniper 三大厂商NAT配置详解。 1. 华为&#xff08;Huawei&#xff09; 华为是一家全球领先的信息与通信技术解决方案供应商&#xff0c;其网络设备提供了强大的NAT功能。 下面是华为设备上的NAT配置示…

【软考中级】软件设计师选择题题集(一)

海明校验码是在n个数据位之外增设k个校验位,从而形成一个k+n位的新的码字, 使新的码字的码距比较均匀地拉大。n与k的关系是(1)。 (1)A.2k - 1≥n + k  B.2n - 1≤ n + k   C.n = k  D.n-1≤k 【答案】A 【解析】 【答案】B A 【解析】 在采用结构化方法进行系统分析时,…

这才是CSDN最系统的网络安全学习路线(建议收藏)

01 什么是网络安全 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等哪个领域&#xff0c;都有攻与防两面…

ChatGPT 的议论文究竟写的怎么样?111 位高中教师告诉你答案

夕小瑶科技说 原创 作者 | 小戏、Python 在 OpenAI GPT-4 发布时发布的《GPT-4 Technical Report》中&#xff0c;其中很吸引人眼球的一部分是 GPT-4 应用于教育领域的出色表现&#xff0c;通过让 GPT-4 去完成美国的 AP 课程及考试&#xff0c;来评估 GPT-4 在多个学科中的性…

AtCoder Beginner Contest 302(A-D)

TOYOTA MOTOR CORPORATION Programming Contest 2023#2 (AtCoder Beginner Contest 302) Contest Duration: 2023-05-20(Sat) 20:00 - 2023-05-20(Sat) 21:40 (local time) (100 minutes) 暴搜场&#xff0c;1个小时出了4道&#xff0c;以为很有机会&#xff0c;结果E交了十发没…

python+django基于爬虫系统的世界历史时间轴历史事件大事记6ouj9

随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;各行各业相继进入信息管理时代&#xf…

Mit6.006-lecture09-Breadth-First-Search

一、新单元&#xff1a;图 Quiz 1包含lecture01到lecture08&#xff0c;关注数据结构和排序 今天开始新单元&#xff0c;lecture09-lecture14&#xff0c;关注图算法 二、图应用 图无处不在 任何网络系统都存在有向连接图 比如&#xff1a;路网、计算机网络、社交网络 任…

PySide6/PyQT多线程之 多线程 与 线程池的模板(拿来即用)

前言 关于PySide6/PyQT多线程系列的最后一篇。写这篇文章的动机是方便后续代码的直接复用。 本篇文章实际是水文&#xff0c;给出了 PySide6/PyQT的多线程以及线程池的基础使用模板&#xff0c;方便后面有需要时候直接拿来就用。 多线程 这里分两种情况来谈论&#xff0c;有返…

热烈欢迎CSDN副总裁邹欣老师入驻知识星球

重磅消息 CSDN 副总裁 邹欣 老师成功入驻知识星球 —— 英雄算法联盟&#xff0c;成为合伙人之一。 这将是未来几年内&#xff0c;IT界最震撼的一次合作&#xff01;我相信就算现在不是&#xff0c;将来必定是&#xff01; 当然&#xff0c;这对我来说也是一种极大的鼓舞&#…

GPT-5: 超越人类语言的模型,你还不了解一下?

目录 一、GPT-5时代引领者 二、技术特性 1&#xff0c;音频和视频处理 — 更强大的多模态处理能力 2&#xff0c;GPT-5颠覆影视制作&#xff1a;重写媒体消费时代 3&#xff0c;为机器人提供智慧大脑 4&#xff0c;更强的垂直行业应用 三、回顾一下GPT5被紧急叫停&…

AI已经成立社区了,一个个比真人还真

文章目录 nainaimichirper川普的入驻英文版 nainaimi nainaimi是一个13岁的学生&#xff0c;一小时前&#xff0c;被一群人拖到体育馆&#xff0c; 那时的她还很胆小&#xff0c;只能哭诉着那些人的残忍和恶毒 结果半个小时前&#xff0c;她又被拖入了体育馆&#xff0c;这一…

分布式补充技术 01.AOP技术

01.AOP技术是对于面向对象编程&#xff08;OOP&#xff09;的补充。是按照OCP原则进行的编写&#xff0c;(ocp是修改模块权限不行&#xff0c;扩充可以) 02.写一个例子&#xff1a; 创建一个新的java项目&#xff0c;在main主启动类中&#xff0c;写如下代码。 package com.co…