文件IO——如何实现非阻塞式IO?

以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。

一、阻塞式IO

1、阻塞式的概念

我们知道,有些函数在调用时(比如网络编程中的recv函数),如果某些条件不满足,则会进入等待状态,直到条件满足才去执行操作并返回。这种函数就叫做阻塞式函数。在文件IO中,阻塞式函数(或者API)有wait、pause、sleep等函数。

另外,使用read或write某些文件时(read和write函数本身不是阻塞式的),文件对应的对象可能是阻塞式的,比如键盘和鼠标就是阻塞式设备

Linux系统默认使用阻塞式。

2、阻塞式的缺点

阻塞式的设计,既有它的好处,也有它的困境。它的好处是简单容易实现,有利于充分发挥CPU的性能。

它的缺点,我们用一个例子说明。上面提及,键盘和鼠标都是阻塞式设备。

1、在程序中读取键盘

比如执行下面这段代码,read函数会等待键盘输入完成(因为shell是行缓冲的,输入回车时表示输入完成,不按下回车就表示输入还没有结束),从而阻塞。

int main(void)
{// 读取键盘// 键盘就是标准输入,stdinchar buf[100];memset(buf, 0, sizeof(buf));printf("before read.\n");read(0, buf, 5);printf("读出的内容是:[%s].\n", buf);return 0;
}

2、 在程序中读取鼠标

比如执行下面这段代码。(这里我们先使用cat命令来查看使用的鼠标对应哪个文件,以便在代码中open时使用。)代码执行后,移动鼠标屏幕有数据输出。

int main(void)
{// 读取鼠标int fd = -1;char buf[200];fd = open("/dev/input/mouse1", O_RDONLY);if (fd < 0){perror("open:");return -1;}memset(buf, 0, sizeof(buf));printf("before read.\n");read(fd, buf, 50);printf("读出的内容是:[%s].\n", buf);return 0;
}

3、 在程序中同时读取鼠标与键盘

我们希望实现动键盘就显示键盘的内容,动鼠标就显示鼠标的内容。

但是下面的程序的现象却是:如果顺着程序,先鼠标再键盘则顺利显示;如果先键盘的话,会被鼠标阻塞住。

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main(void)
{// 读取鼠标int fd = -1;char buf[200];fd = open("/dev/input/mouse1", O_RDONLY);if (fd < 0){perror("open:");return -1;}memset(buf, 0, sizeof(buf));printf("before 鼠标 read.\n");read(fd, buf, 50);printf("鼠标读出的内容是:[%s].\n", buf);// 读键盘memset(buf, 0, sizeof(buf));printf("before 键盘 read.\n");read(0, buf, 5);//键盘就是标准输入,stdin,用0表示,它是默认打开的。printf("键盘读出的内容是:[%s].\n", buf);return 0;
}

二、非阻塞式IO

1、实现非阻塞式IO的两种方法

如何实现非阻塞式IO呢?有两种方法:

(1)方法一:打开文件时添加O_NONBLOCK标志。

(2)方法二:普通打开文件后,使用fcntl函数设置文件描述符的属性。

2、代码示例

分别利用O_NONBLOCK标志、fcntl函数, 将上节中阻塞式的鼠标和键盘读取改为非阻塞式的。

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main(void)
{// 读取鼠标int fd = -1;int flag = -1;char buf[200];int ret = -1;//方法一:利用O_NONBLOCK标志将鼠标设置为非阻塞式的  fd = open("/dev/input/mouse1", O_RDONLY | O_NONBLOCK);if (fd < 0){perror("open:");return -1;}//方法二:利用fcntl函数设置键盘为非阻塞式的                                   // 把0号文件描述符(stdin)变成非阻塞式的    flag = fcntl(0, F_GETFL);		// 先获取原来的flagflag |= O_NONBLOCK;				// 添加非阻塞属性fcntl(0, F_SETFL, flag);		// 更新flag// 这3步之后,0就变成了非阻塞式的了while (1){// 读鼠标memset(buf, 0, sizeof(buf));ret = read(fd, buf, 50);if (ret > 0){printf("鼠标读出的内容是:[%s].\n", buf);}// 读键盘memset(buf, 0, sizeof(buf));ret = read(0, buf, 5);if (ret > 0){printf("键盘读出的内容是:[%s].\n", buf);}}return 0;
}

三、并发式IO的解决方案(解决多路阻塞式IO的方案)

由上面的例子引出话题:并发式IO的解决方案,或者说多路阻塞式IO的解决方案。

比如上面的例子中,鼠标与键盘都是阻塞式的IO设备(即多路阻塞),它们可能同时启动与输入数据(即并发式)。在上面例子中,我们使用fcntl函数和O_NONBLOCK标志解决了阻塞的问题,但是性能不是很好。其实还可以利用多路复用IO、异步IO、存储映射IO来解决阻塞的问题。

1、多路复用IO

(1)方法原理

多路复用IO英文为“ IO multiplexing ”,主要用于解决多路阻塞式IO的问题。

它涉及select函数、poll函数,这两个函数的设计思想一样,只是外部特征不一样,我们可以任选其一来完成功能。

它的实现原理是:外部阻塞式(select函数本身是阻塞式的),内部自动轮询多路阻塞式IO(键盘A,鼠标B,这两者本身是阻塞式的),只要AB至少有一个输入,则select由阻塞返回。

(2)代码示例

1)比如使用select函数实现同时读取鼠标键盘。

 int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);

在代码中设置了超时时间,即阻塞时间不能太长,如果很久没有IO来激活select,则表明超时了。

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>int main(void)
{// 读取鼠标int fd = -1, ret = -1;char buf[200];fd_set myset;struct timeval tm;//设置超时时间,即阻塞的时间不能太长,//如果很久都没有IO来激活select,则表明超时了。fd = open("/dev/input/mouse1", O_RDONLY);if (fd < 0){perror("open:");return -1;}// 当前有2个fd,一共是fd一个是0// 处理mysetFD_ZERO(&myset);FD_SET(fd, &myset);FD_SET(0, &myset);tm.tv_sec = 10;tm.tv_usec = 0;//+1,是因为0~fd,则共有fd+1个文件描述符ret = select(fd+1, &myset, NULL, NULL, &tm);if (ret < 0)//错误{perror("select: ");return -1;}else if (ret == 0)//表明超时{printf("超时了\n");}else//>0表示有一路IO激活了{// 等到了一路IO,然后去监测到底是哪个IO到了,处理之if (FD_ISSET(0, &myset)){// 这里处理键盘memset(buf, 0, sizeof(buf));read(0, buf, 5);printf("键盘读出的内容是:[%s].\n", buf);}if (FD_ISSET(fd, &myset)){// 这里处理鼠标memset(buf, 0, sizeof(buf));read(fd, buf, 50);printf("鼠标读出的内容是:[%s].\n", buf);}}return 0;
}

2)用poll函数实现同时读取键盘鼠标。

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>int main(void)
{// 读取鼠标int fd = -1, ret = -1;char buf[200];struct pollfd myfds[2] = {0};fd = open("/dev/input/mouse1", O_RDONLY);if (fd < 0){perror("open:");return -1;}// 初始化我们的pollfdmyfds[0].fd = 0;		// 键盘myfds[0].events = POLLIN;	// 等待读操作myfds[1].fd = fd;		// 鼠标myfds[1].events = POLLIN;	// 等待读操作ret = poll(myfds, fd+1, 10000);if (ret < 0){perror("poll: ");return -1;}else if (ret == 0){printf("超时了\n");}else{// 等到了一路IO,然后去监测到底是哪个IO到了,处理之if (myfds[0].events == myfds[0].revents){// 这里处理键盘memset(buf, 0, sizeof(buf));read(0, buf, 5);printf("键盘读出的内容是:[%s].\n", buf);}if (myfds[1].events == myfds[1].revents){// 这里处理鼠标memset(buf, 0, sizeof(buf));read(fd, buf, 50);printf("鼠标读出的内容是:[%s].\n", buf);}}return 0;
}

2、异步IO

(1)方法原理

几乎可以认为,异步IO就是操作系统用软件实现的一套中断响应系统。

异步IO的工作方法是:我们当前进程注册一个异步IO事件(使用signal注册一个信号SIGIO的处理函数),然后当前进程可以正常处理自己的事情,当异步事件发生后当前进程会收到一个SIGIO信号从而执行绑定的处理函数去处理这个异步事件。

主要涉及两个函数:fcntl函数(F_GETFL(获取flag)、F_SETFL、O_ASYNC(表明可以接收异步通知)、F_SETOWN(设置通知谁,一般都是通知当前进程)),以及signal或者sigaction(SIGIO)函数。

(2)代码实践

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>int mousefd = -1;// 绑定到SIGIO信号,在函数内处理异步通知事件
void func(int sig)
{char buf[200] = {0};if (sig != SIGIO)return;read(mousefd, buf, 50);printf("鼠标读出的内容是:[%s].\n", buf);
}int main(void)
{// 读取鼠标char buf[200];int flag = -1;mousefd = open("/dev/input/mouse1", O_RDONLY);if (mousefd < 0){perror("open:");return -1;}	// 把鼠标的文件描述符设置为可以接受异步IOflag = fcntl(mousefd, F_GETFL);flag |= O_ASYNC;fcntl(mousefd, F_SETFL, flag);// 把异步IO事件的接收进程设置为当前进程fcntl(mousefd, F_SETOWN, getpid());// 注册当前进程的SIGIO信号捕获函数signal(SIGIO, func);// 读键盘,在这里是当前进程while (1){memset(buf, 0, sizeof(buf));read(0, buf, 5);printf("键盘读出的内容是:[%s].\n", buf);}return 0;
}

3、存储映射IO

该方法体现在mmap函数,将一个文件和一段内存映射起来,比如LCD设备文件和显存的对应,比如IPC之共享内存。

由于共享而不是复制,减少内存操作,处理大文件时效率高(一般用于视频处理)。

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

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

相关文章

散点画三维曲面图_UG 复杂曲面合金零件的数控加工

随着柔性制造、机床数控技术的飞速发展&#xff0c;具有复杂、 精密、小批量、多品种的曲面零件越来越多&#xff0c;如何利用数控 加工技术高质量、高效率加工该类零部件是很有研究价值 的。 本研究利用 UG 软件对复杂曲面合金零件进行三维实 体建模、设计加工工艺过程&#x…

扫盲:php session缓存至memcached中的方法

memcached是一套分布式的快取系统&#xff0c;当初是Danga Interactive为了LiveJournal所发展的&#xff0c;但被许多软件&#xff08;如MediaWiki&#xff09;所使用。这是一套开放源代码软件&#xff0c;以BSD license授权协议发布。[1]memcached仅支持一些非常简单的命令 比…

使用juniversalchardet做字符编码识别

为什么80%的码农都做不了架构师&#xff1f;>>> 在抓取网站的页面的时候最烦人的一件事情之一就是识别原站点的编码&#xff0c;通常来说只有GBK&#xff08;GB2312&#xff09;和UTF8两种&#xff0c;不过依旧需要读取大量Http头信息来识别&#xff0c;有些网站则…

获取系统信息1——linux系统中的时间

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 一、关于时间的概念 1、GMT时间 GMT是格林尼治时间&#xff0c;即格林尼治地区的当地时间。用格林尼治的当地时间作为全球国际时间&#xff0c;用以描述全球性的事件的时间&#xff0c;方便大家记忆…

判断一个字符串是否为回文-链队(新建,进队,出队),链栈(新建,进栈,出栈)...

回文&#xff1a;字符对称排列的字符串&#xff0c;例如ABCBA 思路&#xff1a;根据队&#xff1a;先进先出和栈: 先进后出的原则&#xff0c;进行比较出队和出栈的字符是否相等。如果相等&#xff0c;则为回文。 创建控制台应用程序。 1 #region 字符节点类 2 …

句法依存分析_复旦大学邱锡鹏教授:词法、句法分析研究进展综述

本文为第十六届自然语言处理青年学者研讨会 YSSNLP2019 报告《词法、句法分析研究进展综述》的简要文字整理&#xff0c;本报告主要回顾词法、句法领域的最新研究进展。 关于报告人&#xff1a;邱锡鹏&#xff0c;复旦大学计算机科学技术学院副教授&#xff0c;博士生导师。于复…

获取系统信息3——proc文件系统介绍和使用

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 一、proc文件系统介绍 1、操作系统级别的调试一般很困难 简单的程序可以单步调试&#xff1b;复杂一点的程序可以printf、cout等打印信息调试&#xff08;即输出信息到控制台&#xff09;&#xff0…

阻止函数源码在控制台输出

这是一个很贱的技能&#xff0c;我在谷歌控制台源码里看到的。相信大家都知道&#xff0c;在控制台里只输入函数名&#xff0c;不输入 () 然后按回车&#xff0c;就可以输出源码。 都不会陌生吧&#xff0c;这也有助于我们调试&#xff0c;是个很棒的技巧。不过系统内置的就会输…

值不值得入手_iPhone11现在还值不值得入手?真实用户说出心里话

iPhone11作为苹果走量的一款机型&#xff0c;自发布以来就备受争议&#xff0c;有的朋友说真香&#xff0c;A13iOS只卖4000多&#xff0c;还有的朋友吐槽大黑边、828P的屏幕、信号不好还有充电太慢&#xff0c;特别是现在同价位能买到的安卓旗舰&#xff0c;要5G有5G、要高刷新…

设备驱动,字符设备驱动、(总线)设备驱动模型、sysfs文件系统、平台设备驱动

以下内容转载于微信公众号&#xff1a;嵌入式企鹅圈。如有侵权&#xff0c;请告知删除。 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动、平台设备驱动、设备驱动模型和sysfs等相关概念和技术。 对于初学者来说会非常困惑&#xff0c;甚至对Linux有一定基础的工程师而言…

对于局部变量_对于SQL常用查询优化方法的整理

查询进行优化&#xff0c;应尽量避免全表扫描&#xff0c;首先应考虑在where 及order by 涉及的列上建立索引:尝试下面的技巧以避免优化器错选了表扫描&#xff1a;使用ANALYZE TABLE tbl_name为扫描的表更新关键字分布。对扫描的表使用FORCE INDEX告知MySQL&#xff0c;相对于…

黄聪:如何使用CodeSmith批量生成代码(原创系列教程)

在上一篇我们已经用PowerDesigner创建好了需要的测试数据库,下面就可以开始用它完成批量代码生成的工作啦. 下面我会一步步的解释如何用CodeSmith实现预期的结果的,事先声明一下,在此只做一个简单的Demo,并不详细的讲解CodeSmith各个强大的功能,有兴趣的朋友可以打开CodeSmith的…

c语音异或运算符_C语言中的按位异或运算符有什么用处?

原标题&#xff1a;C语言中的按位异或运算符有什么用处&#xff1f;想知道C语言中的按位异、运算符有什么用处&#xff0c;首先C语言中^为按位异或运算符&#xff0c;若两个二进制位相同&#xff0c;则结果为0&#xff0c;不同为1例&#xff1a;#include "stdio.h"ma…

HDU2201

水&#xff5e; 分析&#xff1a;n&#xff0c;m。对于第一个人不抽到m号座位概率为&#xff08;n-1&#xff09;/n&#xff0c;第二个人为&#xff08;n-2&#xff09;/&#xff08;n-1&#xff09;.。。。第m个人为1/&#xff08;n-m1&#xff09;.。。。 相乘之后则为 1/n V…

字符设备驱动基础1——简单的驱动源代码分析

以下内容源于朱有鹏嵌入式课程的学习&#xff0c;如有侵权请告知删除。 参考博客&#xff1a;linux驱动开发&#xff08;一&#xff09; - biaohc - 博客园 一、驱动源代码示例 /********module_test.c代码*********/#include <linux/module.h> // module_init module…

字符设备驱动基础3——使用register_chrdev()函数注册字符设备

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 一、系统工作原理 1、工作流程 系统的整体工作流程是&#xff1a;应用层—>API—>设备驱动—>硬件。 操作系统提供的API包括open、read、write、close等函数&#xff0c;它们只是一种操作逻…

win7在未关闭vmware情况下直接关机,导致虚拟机无法克隆

今天有点小激动啊&#xff0c;着急关机&#xff0c;结果发现重启之后的虚拟机不能进行克隆操作。系统提示如下&#xff1a;the Specific Virtual Disk Needs Repair .查询“度娘”&#xff0c;突然看到一篇文章说&#xff0c;删除这啊&#xff0c;修改那的....特别复杂&#xf…

hdu 1159(最长公共子序列)

题目链接&#xff1a;http://acm.hdu.edu.cn/showproblem.php?pid1159 思路&#xff1a;dp[i][j]表示s1从0~i-1,s2从0~j-1的最长公共子序列&#xff1b; 递推方程为&#xff1a;dp[i][j](s1[i-1]s2[j-1])?d[i-1][j-1]1:max(dp[i-1][j],dp[i][j-1]); View Code 1 #include<…

mysql applier_MySQL推出Applier,可实时复制数据到Hadoop-阿里云开发者社区

http://labs.mysql.comMySQL复制操作可以将数据从一个MySQL服务器(主)复制到其他的一个或多个MySQL服务器(从)。试想一下&#xff0c;如果从服务器不再局限为一个MySQL服务器&#xff0c;而是其他任何数据库服务器或平台&#xff0c;并且复制事件要求实时进行&#xff0c;是否可…

字符设备驱动基础4——读写接口的操作实践

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 一、细节提要 1、与用户与内核数据交换有关的函数 &#xff08;1&#xff09;copy_from_user()函数 该将数据从用户空间复制到内核空间。 如果成功复制则返回0&#xff0c;如果不成功复制则返回尚未…