谈到IO,阻塞、非阻塞,异步、同步是绕不开的话题。说实话,我也没搞清楚,网上查了许多资料,大家众说纷纭,一种比较靠谱的说法是:”在处理 IO 的时候,阻塞和非阻塞都是同步 IO,使用使用了特殊的API才是异步IO“。知乎的回答相对来说可信度高点,大家姑且可以先看着:
http://www.zhihu.com/question/19732473
这些资料大多说理,我还是想通过一些例子,以我们看到到摸得着的方式,慢慢搞懂阻塞、非阻塞以及异步同步间的关系。所以这一系列将是我的读书笔记,因为我也摸着石头过河,等过完河我再整理IO这一个系列的文章。
阻塞和非阻塞
阻塞与非阻塞是个概念,概念背后肯定有很多内容嘛,我们一一展开。首先,主语是操作系统(OS),宾语是进程,也就是说,阻不阻塞都是针对进程的;我们知道阻塞与不阻塞是针对于同一问题的两种处理方式,那么这个问题又是什么呢?答,是当我进程要读数据时,数据还没准备好,在这种情况下操作系统的策略。用户态进程想走下去,需要读取硬件上收集的数据,用户态到硬件间的距离,隔着一个操作系统,操作系统此时向用户态提供了两种解决方案:①阻塞,进程在等待队列上睡觉,到时叫醒你;②非阻塞,返回给你个码字,告诉你数据没准备好,你可能还要再来问一遍。
来点接地气的。下面这个程序很简单,不断从标准输入里读出数据,然后显示。
33 while(1){ 37 ntowrite = read(0, buf, sizeof(buf)); 39 printf("ntowrite:%d\n", ntowrite); 40 sleep(2); 42 };
默认情况下,io是阻塞的,所以你会发现,程序会傻傻地卡在那里,等着输入:
[email protected]:~/f2fs/share_aarch64/filemap$ ./block_test
但是把上面的程序作如下修改,即通过fctnl将对stdin的读操作变成非阻塞的,结果就会变得大不一样。
25 flag = fcntl(0, F_GETFL, 0); 26 flag |= O_NONBLOCK; 27 error = fcntl(0, F_SETFL, flag); 28 if (error < 0) 29 printf("std stdion to non-block fails\n"); 30 while(1){ 34 ntowrite = read(0, buf, sizeof(buf)); 36 printf("ntowrite:%d\n", ntowrite); 37 sleep(2); 38 39 };
这次的结果是,程序不再傻等了,-1 就是操作系统给你的信息了:当前并没什么卵数据。
[email protected]:~/f2fs/share_aarch64/filemap$ ./block_test ntowrite:-1 ntowrite:-1 ntowrite:-1 ntowrite:-1 ntowrite:-1 ntowrite:-1.....
上面就是不是对阻塞非阻塞有了个感性的认识呢?阻塞时,程序在 read 调用处傻傻等着,因为操作系统让你睡眠了;非阻塞时,操作系统不会让你等,有数据就返回数据,没数据就直白告诉你,但是总会返回。