7-文件IO-阻塞与非阻塞IO
1. 阻塞 IO
通常来说,从普通文件读数据,无论你是采用 fscanf,fgets 也好,read 也好,一定会在有限的时间内返回。但是如果你从设备,比如终端(标准输入设备)读数据,只要没有遇到换行符(’\n’),read 一定会“堵”在那而不返回。还有比如从网络读数据,如果网络一直没有数据到来,read 函数也会一直堵在那而不返回。
read 的这种行为,称之为 block,一旦发生 block,本进程将会被操作系统投入睡眠,直到等待的事件发生了(比如有数据到来),进程才会被唤醒。
系统调用 write 同样有可能被阻塞,比如向网络写入数据,如果对方一直不接收,本端的缓冲区一旦被写满,就会被阻塞。
1.1 阻塞读终端实验
- 代码
// 文件名:blockdemo.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>int main() {char buf[10];int len;while(1) {// STDIN_FILENO 是标准输入的描述符,它的值是 0. STDOUT_FILENO 是标准输出的描述符,它的值是 1.len = read(STDIN_FILENO, buf, 10);write(STDOUT_FILENO, buf, len);}return 0;
}
- 编译
$ gcc blockdemo.c -o blockdemo
- 运行
$ ./blockdemo
如果你不向终端键入任何字符,程序将永远阻塞在 read 系统调用处。
1.2 阻塞调用面临的问题
假设有这样一个场景,我要从 2 个不同设备读取数据进行数据,分别进行处理。伪代码如下。
while(1) {阻塞 read(设备1);处理设备1数据;阻塞 read(设备2);处理设备2数据;
}
上面有什么问题呢?仔细想想,假如设备1一直没有数据到来,那么程序就一直停在 read(设备1)这一行,即使设备2有数据到来,也将得不到处理。
经验很丰富的同学肯定想出了各种方案,比如什么多进程多线程什么的。抱歉,我们是新手,目前只会单线程单进程。
既然如此,可否有一种方案,让 read 不阻塞?不管有没有数据到来,read 执行完立即返回,然后再通过某种特殊的变量来判断本次调用到底有没有数据到来?
实际上,这个方案是可行的。请续读下文。
2. 非阻塞 IO
- 如何解决从不同设备读数据而造成的干扰
现在,把刚刚上面面临问题的代码改成这样。
while(1) {非阻塞 read(设备1);if (设备1有数据){处理设备1数据;}非阻塞 read(设备2);if (设备2有数据) {处理设备2数据;}
}
且不论这样的代码执行效率如何,我们先看看它是否解决了前面的问题。
如果设备1没有数据到来,read(设备1)也会立即返回,有数据就处理数据,没数据接着执行 read(设备2),有数据就处理数据,没有的话紧接着又去 read(设备1)……如此往复。
我们会发现,设备1和设备2之间,不论有没有数据到来,都不会互相影响,而不像之前阻塞IO那样,如果设备1没有数据,将会影响到设备2的数据处理。
这种方案非常不错,总之目前来说是这样的,先辈们给这种解决方案取了一个很好听的名字——Poll (轮询)。
- 效率
现在,是时候把效率搬上来谈谈了。
如果设备1和设备2一直没有数据到来,这个 while 循环将不断空转,CPU将面临高负荷。这是一种极大的浪费。不像阻塞方式,没有数据,就直接被操作系统投入睡眠。
那么,我们把上面的代码再改改。
- 修改方案
添加 sleep,主动让出 CPU。
while(1) {非阻塞 read(设备1);if (设备1有数据){处理设备1数据;}非阻塞 read(设备2);if (设备2有数据) {处理设备2数据;}sleep(5); // 加了一行
}
这种方案仍然有问题,虽然可以每次让出一定时间的CPU,但是也导致了设备的数据得不到及时处理。可是以目前的知识,我们只能做到这个份上。未来,我们有机会学习更加先进的技术,来完美解决这个问题。提前预告一下,它的大名是——select。
2.1 非阻塞IO实验
有几个需要注意的地方:
- 阻塞非阻塞是文件本身的特性,不是系统调用read/write本身可以控制的。
- 终端默认是阻塞的,我们可以重新 open 设备文件 /dev/tty(表示当前终端),打开的时候指定 O_NONBLOCK 标志就行了。
- 非阻塞 read,如果有数据到到来,返回读取到的数据的字节数。如果没有数据到来,返回 -1,这时候我们没有办法判断到底是因为出错而返回,还是因为没有数据返回。所以需要借助 errno 全局变量,来判断是什么原因。如果 errno 的值为 EWOULDBLOCK或 EAGAIN(这两个宏的值是一样的),表示当前没有数据到达,希望你再尝试一次。因为 read 返回 -1 前,linux 系统会在 read 返回前给 errno 赋值,来告诉应用层,到底是什么原因。
- 非阻塞IO读终端数据
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <errno.h> // errno 变量的头文件
#include <stdlib.h>char MSG_TRY[] = "try again!\n";int main() {char buffer[10];int len;int fd; fd = open("/dev/tty", O_RDONLY | O_NONBLOCK);while(1) {len = read(fd, buffer, 10);if (len < 0) {if (errno == EAGAIN) {write(STDOUT_FILENO, MSG_TRY, strlen(MSG_TRY));sleep(1); // 让出 CPU,避免CPU长时间空转} else {perror("read");exit(1);} } else {break;} }write(STDOUT_FILENO, buffer, len);return 0;
}
- 非阻塞IO读终端数据结合等待超时
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>char MSG_TRY[] = "try again!\n";
char MSG_TMOUT[] = "time out!\n";int main() {char buffer[10];int len;int fd; int i;fd = open("/dev/tty", O_RDONLY | O_NONBLOCK);// 超过 5 秒后,无论有没有数据都退出。for (i = 0; i < 5; ++i) {len = read(fd, buffer, 10);if (len < 0) {if (errno == EAGAIN) {write(STDOUT_FILENO, MSG_TRY, strlen(MSG_TRY));sleep(1);} else {perror("read");exit(1);} } else {break;} }if (i == 5) {write(STDOUT_FILENO, MSG_TMOUT, strlen(MSG_TMOUT)); }else {write(STDOUT_FILENO, buffer, len);}return 0;
}
3. 总结
本文简单介绍了阻塞与非阻塞IO的概念,并给出一个实际生产环境可能遇到的例子,利用单线程来解决多设备数据处理的方法。
因为还没有学习多进程与多线程,我们只能借助非阻塞IO来完成这个功能。在后面的深入学习中,我们将出给出更加完美的解决方案,解决因为没有数据到来而使 CPU 空转的问题。
7-文件IO-阻塞与非阻塞IO相关推荐
- 文件异步io java,漫画图解java开发之IO阻塞、非阻塞、同步、异步
周末在家加班,正在疯狂的撸代码,女朋友很开心的跑过来,手里拿着他刚刚画好的一副漫画. 同步.异步.阻塞.非阻塞都是和IO(输入输出)有关的概念.最简单的文件读取就是IO操作.而在文件读取这件事儿上,可 ...
- 阻塞与非阻塞的IO网络读写
看我之前的文章就知道,一般对于网络读的socket,都会加上O_NONBLOCK,非阻塞的选项. int setnonblocking(int fd) {int old_option = fcntl( ...
- python网络编程基础(线程与进程、并行与并发、同步与异步、阻塞与非阻塞、CPU密集型与IO密集型)...
python网络编程基础(线程与进程.并行与并发.同步与异步.阻塞与非阻塞.CPU密集型与IO密集型) 目录 线程与进程并行与并发同步与异步阻塞与非阻塞CPU密集型与IO密集型 线程与进程 进程 前言 ...
- IO:同步,异步,阻塞,非阻塞
IO - 同步,异步,阻塞,非阻塞 都是老生常谈的东西,多通读几遍,理解透彻! 实际上同步与异步是针对应用程序与内核的交互而言的.同步过程中进程触发IO操作并等待(也就是我们说的阻塞)或者轮询的去查看 ...
- linux函数的阻塞与非阻塞IO及错误处理
linux函数的阻塞与非阻塞IO及错误处理 1.阻塞是指进程等待某一个事件的发生而处于等待状态不往下执行,如果等待的事件发生了则会继续执行该进程.调用系统阻塞函数可能会导致进程阻塞进入睡眠状态. 2. ...
- IO中的阻塞、非阻塞、同步、异步概念分析详解
目录 1.什么是I/O编程 2.阻塞.非阻塞.同步.异步分析 2.1 什么是同步.异步 2.2 什么是阻塞和非阻塞 2.3 阻塞.非阻塞和同步.异步的区别 2.4 编程实现 3.参考文献 1.什么是I ...
- Linux 阻塞和非阻塞IO 实验
目录 阻塞和非阻塞IO 阻塞和非阻塞简介 等待队列 轮询 Linux 驱动下的poll 操作函数 阻塞IO 实验 硬件原理图分析 实验程序编写 运行测试 非阻塞IO 实验 硬件原理图分析 实验程序编写 ...
- Linux IO - 同步,异步,阻塞,非阻塞
From:http://blog.csdn.net/historyasamirror/article/details/5778378 同步/异步,阻塞/非阻塞概念深度解析:http://blog.cs ...
- IO模式设置,阻塞与非阻塞的比较,recv参数对性能的影响—O_NONBLOCK(open使用)、IPC_NOWAIT(msgrcv)、MSG_DONTWAIT
非阻塞IO 和阻塞IO: 在网络编程中对于一个网络句柄会遇到阻塞IO 和非阻塞IO 的概念, 这里对于这两种socket 先做一下说明: 基本概念: 阻塞IO:: socket 的阻塞模 ...
- 阻塞和非阻塞、同步和异步 、五种IO模型
阻塞和非阻塞,同步和异步 1 例子 故事:老王烧开水. 出场人物:老张,水壶两把(普通水壶,简称水壶:会响的水壶,简称响水壶). 老王想了想,有好几种等待方式 1.老王用水壶煮水,并且站在那里,不管水 ...
最新文章
- 转学美本半年,我眼里的中美高等教育
- HTML5获取autoComplete属性:告诉浏览器是否记录之前的输入值
- 程序员,你是真的该养生了
- 【POJ - 1087】A Plug for UNIX(建图,网络流最大流)
- java mysql读取多条数据_myeclipse 使用Java访问mysql数据库,数据库中有多条记录,为何只能读出一条数据??...
- python美化输出模块_python日志处理模块
- 财务人毕业三年薪资翻了三倍,学会数据分析竟然这么吃香
- ModuleNotFoundError: No module named ‘librosa‘
- 虚拟机服务器ip端口映射,VMware虚拟机配置端口转发(端口映射),实现远程访问【转】...
- PySerial库的简单用法
- nodejs 点击按钮下载_从服务器下载文件使用NodeJS表达
- 画PCB必备的3D元器件模型下载地址整理
- Jenkins 插件中心国内镜像源发布
- 医院在线预约挂号系统 jsp+mysql+maven
- 手机流量不清零这个便宜不好占
- 在微信H5网页中获取用户基本信息
- Python3---站在大佬肩膀写爬虫-爬取网易云音乐热歌榜歌曲热评(精彩评论)
- 前端开发学习及工具网站(持续更新)
- Java 超.简易RPG游戏
- mysql数据库的空间释放