Linux下的五种I/O通信模型

2019-07-13 06:06发布

同步,异步,阻塞,非阻塞的概念

首先,需要明确的一个问题就是,通常程序是运行在用户态下,如果需要进行I/O操作,那么就会发出系统调用(System call),由用户态转变为内核态,由操作系统去完成I/O操作,实际上应用程序本身没有权利操作I/O。
  • 同步和异步:都是针对应用程序和操作系统之间交互而言的。同步的话,如果上层应用程序不主动询问操作系统的话,操作系统不会主动通知应用程序。异步的话,操作系统主动通知应用程序。
  • 阻塞:如果条件不满足的话,操作系统将应用线程挂起或者休眠,此时应用线程处于阻塞状态,只有当数据准备好之后或者数据已经从内核空间拷贝到用户空间,操作系统通知线程,此为唤醒线程。
  • 非阻塞:如果条件不满足的话,该方法不会阻塞当前线程,而是立即返回一个标志信息(数据还没有准备好),或者timeout之后返回,此时应用线程还可以进行其他的操作。
要注意的问题:同步不等同于阻塞,同步调用的时候,即便调用不返回,但是应用线程还处于运行态,还可以做一些其它的事情。而阻塞调用时,应用线程处于阻塞的状态。

阻塞I/O模型

应用程序调用I/O函数,应用线程会阻塞,直到数据准备好为止。如果数据没有准备好,则应用线程一直等待。
这里写图片描述

非阻塞I/O模型

非阻塞I/O通过应用线程反复调用I/O函数(多次调用,立即返回);在数据拷贝阶段,线程还是阻塞的。
我们把socket套接字接口设置为非阻塞的时候,就是告诉操作系统,在没有数据的时候,不要把我挂起或者休眠,而是返回一个错误信号。
这里写图片描述

多路复用I/O模型

多路复用I/O模型是目前使用比较多的模型。Java nio实际上就是这种模型。所谓的多路复用,可以理解为在一个应用线程里可以处理多个socket连接,或者同一个端口接受来自多个客户端的请求(连接,读,写)
这里写图片描述 当然在java nio中对多路复用支持阻塞和非阻塞两种模式。 多路复用IO与传统的非阻塞IO的区别在于,在传统的非阻塞IO中,由用户线程去轮询socket的状态,而在多路复用IO中,由内核去轮询向select注册的socket的状态。所以多路复用IO需要操作系统的支持。

信号驱动I/O

在信号驱动IO模型中,当用户线程发起一个IO请求操作的时候,会给对应的socket注册一个信号函数,然后用户线程继续执行,当内核数据准备就绪时,会给用户线程发送一个信号。用户线程接收到信号之后,便在信号函数中调用IO读写操作进行实际的IO请求操作。
这里写图片描述

异步I/O模型

异步IO模型才是最理想的IO模型,在异步IO模型中,当用户线程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从内核的角度,当它受到一个asynchronous read之后,它会立刻返回,说明read请求已经成功发起了,因此不会对用户线程产生任何block。然后,内核会等待数据准备完成,然后将数据拷贝到用户线程,当这一切都完成之后,内核会给用户线程发送一个信号,告诉它read操作完成了。也就说用户线程完全不需要实际的整个IO操作是如何进行的,只需要先发起一个请求,当接收内核返回的成功信号时表示IO操作已经完成,可以直接去使用数据了。
异步IO模型中,由操作系统内核去等待数据就绪,copy数据到用户空间,用户线程始终没有阻塞。 注:linux中并不存在真正的异步IO,而是通过多路复用的方式去模拟异步IO。