linux高级IO

同步、异步,阻塞和非阻塞等概念

同步和异步

  • 同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了。换句话说,就是由调用者主动等待这个调用的结果。而异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用

阻塞与非阻塞

  • 阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态。阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。

所以说,同步和异步针对的是消息通知机制,我要得到这个消息是自己一直等待还是被调用者在处理完后通知调用者。而阻塞非阻塞是针对本身线程或进程来说。线程或进程要一直等待则是阻塞,可以不等待处理其他任务为非阻塞

以下来源网络:

出场人物:老张,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)。

  1. 老张把水壶放到火上,立等水开。(同步阻塞)
  2. 老张把水壶放到火上,去客厅看电视,时不时去厨房看看水开没有。(同步非阻塞)
  3. 老张还是觉得自己有点傻,于是变高端了,买了把会响笛的那种水壶。水开之后,能大声发出嘀~~~~的噪音。
  4. 老张把响水壶放到火上,立等水开。(异步阻塞)老张觉得这样傻等意义不大
  5. 老张把响水壶放到火上,去客厅看电视,水壶响之前不再去看它了,响了再去拿壶。(异步非阻塞)。

本篇索引:

  1. 引言
  2. 非阻塞IO
  3. 记录锁(文件锁)
  4. io多路复用(I/O multiplexing )
  5. 异步IO
  6. 存储映射IO

1 引言

我们第三篇学习了对IO的open、read、write等的操作,这一篇我们将会学习对IO的一些高级操作,实际上这一篇的内容是对第三篇内容的进一步升华,主要的内容如下:

  • 非阻塞IO:对文件实现非阻塞操作。
  • 记录锁:利用文件实现锁的机制。
  • IO多路复用:实现单线同时操作多个阻塞IO,分select和poll两种的操作。
  • 存储映射IO:mmap

本篇不重理论,重点是列举各种例子代码,教会大家如何使用这些高级IO的设置和使用。

2 非阻塞IO

2.1 低速系统调用之阻塞

所有系统调用被分为两类,一类是低速系统调用,另一类是其它系统调用

1)低速系统调用:可能会使进程永远阻塞的一类系统调用,系统调用导致进程阻塞的原因有两种。

a)函数操作文件时,因文件类型而阻塞,阻塞与函数本身无关

读某些文件由于数据不存在会导致调用者永远阻塞

  •  读管道文件:管道是进程间通信用的特殊文件,读管道时,如果管道中并无数据会导致对管道的读操作会阻塞。
  •  读终端设备:如读鼠标、键盘等字符设备类文件,以及网络设备文件时,如果没有数据的话,读操作也会阻塞。
  •  注意:值得强调的是低速系统调用读磁盘I/O(普通文件)并非是阻塞的,如果有数据会带着数据正常返回,如果没有数据则也会返回,所以不会阻塞。

写某些文件:在写某些文件时,当文件不能立即接收写入的数据时,也可能会导致写操作长期的阻塞下去。

打开文件:在某些条件成立之前可能有些文件是无法打开的,这同样可能会导致打开操作长期的阻塞下去。

  •  情况1:如果想要成功打开某些终端设备,那么你就必须等到某些调制解调器应答后才能打开,否者会一直阻塞下去。
  •  情况2:如果某个管道的读端没打开时,而你又想以写方式打开该管道,那么这个以写 方式打开的操作会一直阻赛直到某个地方以读打开这个管道为止。

b)某些函数本身就是阻塞的

pause函数,wait函数,sleep函数,某些ioctl操作,以及某些进程间通信的函数(如当消息队列的消息接受函数设置了阻塞时),这些函数调用本身就是阻塞的。

2)其它系统调用:

2.2 如何设置和修改阻塞为非阻塞

前面说过,某些文件默认打开后默认对文件的操作就是阻塞的,但是利用对文件描述符设置,可将其操作设置为非阻塞的,主要的方法有如下两种。

1)打开文件时指定非阻塞,例子如下:

以非阻塞的方式打开标准输入文件

/dev/stdin是标准输入文件,对应着键盘输入,一般情况下默认就是以阻塞方式打开的,但如果我们在打开时指定O_NONBLOCK参数的话,就指定为了非阻塞,当我们去read该文件时就不会再阻塞了。

2) 用fcntl函数进行设置

上例中我们重新打开了标准输入文件,新返回的描述符(3)和描述符0同时指向了标准输入文件,虽然fd被设置为了非阻塞,但是描述符0任然是阻塞的。因为0和3这两个文件描述符是分别各自调用open函数打开/dev/stdin文件而返回得到的,这种情况下各个文件描述符指向的文件结构关系如下:

linux高级IO

从上图我们很明显的看到0,3这两个描述符各自有一个文件表,可以设置自己的文件状态标志,所以0是阻塞的而3却是非阻塞的就不难理解了。那么如何将已经打开了的文件描述符设置为非阻塞的呢就又要使用到fcntl函数了,比如我们可以将已经打开了的0设置为非阻塞,代码实现如下:

2.3 非阻塞举例

1)同时阻塞读键盘和鼠标

我们在一个单进程里面同时实现键盘的输入和鼠标的输入,但是对于这两个低速系统调用默认情况的读都是阻塞的,所以这两个的输入会相互阻塞,如下例:

该程序必须在超级用户下运行,因为鼠标文件只能在超级用户下才能被打开,运行程序时由于先读的是鼠标,它阻塞了键盘,所以我们先输入键盘是没有用的,当从鼠标输入数据后,鼠标数据打印出来,这时进程又阻塞在了读键盘处,所以此时从鼠标输入数据是没有用的,这时必须敲键盘输入数据,才能回到读鼠标处,由于鼠标数据是整形的坐标值,所打印出来是乱码

2)非阻塞地实现读键盘和鼠标

从前面我们知道,键盘和鼠标的读导致了相互的阻塞,我们输入时并不通畅,现在我们将它们都改为非阻塞的,那么它们就不会相互阻塞,输入就会变得通畅,对上例修改后的代码如下:

来源:小虾米_2018

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2019年5月1日
下一篇 2019年5月1日

相关推荐