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实验

有几个需要注意的地方:

  1. 阻塞非阻塞是文件本身的特性,不是系统调用read/write本身可以控制的。
  1. 终端默认是阻塞的,我们可以重新 open 设备文件 /dev/tty(表示当前终端),打开的时候指定 O_NONBLOCK 标志就行了。
  2. 非阻塞 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相关推荐

  1. 文件异步io java,漫画图解java开发之IO阻塞、非阻塞、同步、异步

    周末在家加班,正在疯狂的撸代码,女朋友很开心的跑过来,手里拿着他刚刚画好的一副漫画. 同步.异步.阻塞.非阻塞都是和IO(输入输出)有关的概念.最简单的文件读取就是IO操作.而在文件读取这件事儿上,可 ...

  2. 阻塞与非阻塞的IO网络读写

    看我之前的文章就知道,一般对于网络读的socket,都会加上O_NONBLOCK,非阻塞的选项. int setnonblocking(int fd) {int old_option = fcntl( ...

  3. python网络编程基础(线程与进程、并行与并发、同步与异步、阻塞与非阻塞、CPU密集型与IO密集型)...

    python网络编程基础(线程与进程.并行与并发.同步与异步.阻塞与非阻塞.CPU密集型与IO密集型) 目录 线程与进程并行与并发同步与异步阻塞与非阻塞CPU密集型与IO密集型 线程与进程 进程 前言 ...

  4. IO:同步,异步,阻塞,非阻塞

    IO - 同步,异步,阻塞,非阻塞 都是老生常谈的东西,多通读几遍,理解透彻! 实际上同步与异步是针对应用程序与内核的交互而言的.同步过程中进程触发IO操作并等待(也就是我们说的阻塞)或者轮询的去查看 ...

  5. linux函数的阻塞与非阻塞IO及错误处理

    linux函数的阻塞与非阻塞IO及错误处理 1.阻塞是指进程等待某一个事件的发生而处于等待状态不往下执行,如果等待的事件发生了则会继续执行该进程.调用系统阻塞函数可能会导致进程阻塞进入睡眠状态. 2. ...

  6. IO中的阻塞、非阻塞、同步、异步概念分析详解

    目录 1.什么是I/O编程 2.阻塞.非阻塞.同步.异步分析 2.1 什么是同步.异步 2.2 什么是阻塞和非阻塞 2.3 阻塞.非阻塞和同步.异步的区别 2.4 编程实现 3.参考文献 1.什么是I ...

  7. Linux 阻塞和非阻塞IO 实验

    目录 阻塞和非阻塞IO 阻塞和非阻塞简介 等待队列 轮询 Linux 驱动下的poll 操作函数 阻塞IO 实验 硬件原理图分析 实验程序编写 运行测试 非阻塞IO 实验 硬件原理图分析 实验程序编写 ...

  8. Linux IO - 同步,异步,阻塞,非阻塞

    From:http://blog.csdn.net/historyasamirror/article/details/5778378 同步/异步,阻塞/非阻塞概念深度解析:http://blog.cs ...

  9. IO模式设置,阻塞与非阻塞的比较,recv参数对性能的影响—O_NONBLOCK(open使用)、IPC_NOWAIT(msgrcv)、MSG_DONTWAIT

    非阻塞IO 和阻塞IO: 在网络编程中对于一个网络句柄会遇到阻塞IO 和非阻塞IO 的概念, 这里对于这两种socket 先做一下说明:        基本概念: 阻塞IO:: socket 的阻塞模 ...

  10. 阻塞和非阻塞、同步和异步 、五种IO模型

    阻塞和非阻塞,同步和异步 1 例子 故事:老王烧开水. 出场人物:老张,水壶两把(普通水壶,简称水壶:会响的水壶,简称响水壶). 老王想了想,有好几种等待方式 1.老王用水壶煮水,并且站在那里,不管水 ...

最新文章

  1. 转学美本半年,我眼里的中美高等教育
  2. HTML5获取autoComplete属性:告诉浏览器是否记录之前的输入值
  3. 程序员,你是真的该养生了
  4. 【POJ - 1087】A Plug for UNIX(建图,网络流最大流)
  5. java mysql读取多条数据_myeclipse 使用Java访问mysql数据库,数据库中有多条记录,为何只能读出一条数据??...
  6. python美化输出模块_python日志处理模块
  7. 财务人毕业三年薪资翻了三倍,学会数据分析竟然这么吃香
  8. ModuleNotFoundError: No module named ‘librosa‘
  9. 虚拟机服务器ip端口映射,VMware虚拟机配置端口转发(端口映射),实现远程访问【转】...
  10. PySerial库的简单用法
  11. nodejs 点击按钮下载_从服务器下载文件使用NodeJS表达
  12. 画PCB必备的3D元器件模型下载地址整理
  13. Jenkins 插件中心国内镜像源发布
  14. 医院在线预约挂号系统 jsp+mysql+maven
  15. 手机流量不清零这个便宜不好占
  16. 在微信H5网页中获取用户基本信息
  17. Python3---站在大佬肩膀写爬虫-爬取网易云音乐热歌榜歌曲热评(精彩评论)
  18. 前端开发学习及工具网站(持续更新)
  19. Java 超.简易RPG游戏
  20. mysql数据库的空间释放

热门文章

  1. 什么是运动控制,工业控制与自动化领域中运动控制器的作用是什么?- 顶控科技
  2. 数学运算符和运算符的优先级
  3. 万字长文详解 Go 程序是怎样跑起来的?| CSDN 博文精选
  4. 高通量测序数据分析:RNA-seq
  5. jpg转换成word(如何把jpg转换成word文档)
  6. 一正云系统服务器搭建,腾正云服务器
  7. 好的用户界面-界面设计的一些技巧
  8. 有意思的张飞日记-_-
  9. 变量提升和函数提升哪个优先
  10. arm+linux+分辨率无效,Arm NPU的超分辨率!