Linux中一切皆文件,不论是我们存储在磁盘上的字符文件,可执行文件还是我们的接入电脑的I/O设备等都被VFS抽象成了文件,比如标准输入设备默认是键盘,我们在操作标准输入设备的时候,其实操作的是默认打开的一个文件描述符是0的文件,而一切软件操作硬件都需要通过OS,而OS操作一切硬件都需要相应的驱动程序,这个驱动程序里配置了这个硬件的相应配置和使用方法。Linux的I/O分为阻塞I/O,非阻塞I/O,I/O多路复用,信号驱动I/O四种。对于I/O设备的驱动,一般都会提供关于阻塞非阻塞两种配置。我们最常见的I/O设备之一--键盘(标准输入设备)的驱动程序默认是阻塞的。
多路复用就是为了使进程能够从多个阻塞I/O中获得自己想要的数据并继续执行接下来的任务。其主要的思路就是同时监视多个文件描述符,如果有文件描述符的设定状态的被触发,就继续执行进程,如果没有任何一个文件描述符的设定状态被触发,进程进入sleep
多路复用的一个主要用途就是实现"I/O多路复用并发服务器",和多线程并发或者多进程并发相比,这种服务器的系统开销更低,更适合做web服务器,但是由于其并没有实现真正的多任务,所以当压力大的时候,部分用户的请求响应会较慢

阻塞I/O

阻塞I/O,就是当进程试图访问这个I/O设备而这个设备并没有准备好的时候,设备的驱动程序会通过内核让这个试图访问的进程进入sleep状态。阻塞I/O的一个好处就是可以大大的节约CPU时间,因为一旦一个进程试图访问一个没有准备好的阻塞I/O,就会进入sleep状态,而进入sleep状态的进程是不在内核的进程调度链表中,直到目标I/O准备好了将其唤醒并加入调度链表,这样就可以节约CPU时间。当然阻塞I/O也有其固有的缺点,如果进程试图访问一个阻塞I/O,但是否访问成功并不对接下来的任务有决定性影响,那么直接使其进入sleep状态显然会延误其任务的完成。

  • 典型的默认阻塞IO有标准输入设备,socket设备,管道设备等,当我们使用gets(),scanf(),read()等操作请求这些IO时而IO并没有数据流入,就会造成进程的sleep。 进程会一直阻塞下去直到接收缓冲区中有数据可读,此时内核再去唤醒该进程,通过相应的函数从中获取数据。如果阻塞过程中对方发生故障,那么这个进程将会永远阻塞下去。
  • 写操作时发生阻塞的情况要比读操作少,主要发生在要写入的缓冲区的大小小于要写入的数据量的情况下,这时写操作将不进行任何任何拷贝工作,将发生阻塞。一旦发送缓冲区内有足够的空间,内核将唤醒进程,将数据从用户缓冲区中拷贝到相应的发送数据缓冲区。udp不用等待确认,没有实际的发送缓冲区,所以udp协议中不存在发送缓冲区满的情况,在udp套接字上执行的写操作永远都不会阻塞

现假设一个进程希望通过三个管道中任意一个中读取数据并显示,伪代码如下

read(pipe_0,buf,sizeof(buf));       //sleep print buf; read(pipe_1,buf,sizeof(buf)); print buf; read(pipe_2,buf,sizeof(buf)); print buf;

由于管道是阻塞I/O,所以如果pipe

网友评论