四种主要IO模型

四种主要IO模型

同步IO,是指用户空间的线程是主动发起IO请求的一方,内核空间是被动接收方。

异步IO,是指系统内核是主动发起IO请求的一方,用户空间的线程是被动接收方。

阻塞IO,是指需要内核IO操作彻底完成后,才返回到用户空间执行用户的操作。

非阻塞IO,是指用户空间的程序不需要等待内核IO操作彻底完成,可以立即返回用户空间执行用户的操作,即处于非阻塞的状态,并且内核会立即返回给用户一个状态值。

可以这样理解:阻塞是指用户空间一直处于等待状态,不能去处理其他事件;非阻塞指的是用户空间获取到内核返回的状态就返回当自己的空间去干别的事情去了。

同步阻塞IO(Blocking IO)

在Java应用程序中,默认情况下,所有的socket连接的IO操作都是同步阻塞IO。在阻塞式IO模型中,Java应用程序从IO调用开始,直到系统调用返回都处于阻塞状态。当返回数据之后,应用程序开始处理用户空间的缓存区数据。

 以Java发起一个Socket的read读操作的系统调用为例:

  1. Java进程启动IO读的read系统调用之后,便进入了阻塞状态。

  2. 当系统收到read系统调用之后,便开始准备数据。在数据没有完全到达缓冲区时,内核需要等待数据(比如还没有收到一个完整的Socket数据包)

  3. 内核接收到完整的数据之后,内核便会将数据从内核缓冲区复制到用户缓冲区(用户空间的内存),然后内核返回结果(例如返回复制到用户缓冲区中的字节数)

  4. 在内核返回后,进程解除阻塞状态,重新运行起来。

阻塞IO的特点:内核在等待数据以及复制数据的过程中,用户的进程或线程都处于阻塞状态。

阻塞IO的优点:应用程序开发简单,在阻塞等待数据期间,用户进行请求IO的线程挂起。在阻塞期间,请求IO的用户线程基本不会占用CPU资源。

阻塞IO的缺点:一个线程只能维护一个连接的IO操作,在高并发的应用场景下,需要大量的线程来维护大量的网络连接,内存、线程切换开销会非常大,因此在高并发场景下不会使用阻塞IO。

同步非阻塞NIO(None Blocking IO)

socket连接默认是阻塞模式,在Linux系统中可以讲Socket设置为同步非阻塞模式,在NIO模式下,会出现两种情形:

  1. 在内核缓冲区中没有数据的情况下,系统调用会立刻返回一个调用失败的信息。

  2. 在内核缓冲区存在数据的情况下,会阻塞请求IO的线程,直至数据从内核缓冲区复制到用户进程缓冲区,复制完成后系统调用返回成功,请求IO线程开始处理用户空间的缓存数据。

以Java中的一个线程请求非阻塞socket的read读操作为例:

  1. 在内核数据没有准备好的阶段,用户线程发起IO请求时,立即返回。为了读取到最终的数据,用户线程需要不断发起IO请求。

  2. 内核数据到达后,用户线程发起IO请求,用户线程会被阻塞,内核开始复制数据,将数据从内核缓冲区复制到用户缓冲区,然后内核返回结果(例如复制到的用户缓冲区的字节数)。

  3. 用户线程获取到数据之后解除阻塞状态,重新运行起来。换句话来讲,其实就是用户线程不断地循环区请求数据,直到拿到内核数据之后去执行其他的业务逻辑。

同步非阻塞IO的特点:应用程序的线程需要不断进行IO系统调用,直到拿取到数据完成IO系统调用为止。

同步非阻塞IO的优点:每次用户线程发起的IO请求,在内核等待数据的过程中可以立即获得结果,用户线程不会阻塞,实时性比较好。

同步非阻塞IO的缺点:为了获取数据需要不断的去轮询内核,这将占用大量的CPU时间,效率低下。

由于需要不断去轮询获取数据,这种NIO模式很少在实际项目中使用。

IO多路复用模型(IO Multiplexing)

为了解决同步非阻塞IO模型中轮询的问题,提出了一种新的模型,便是IO多路复用模型。在IO多路复用模型中,引入了一种新的系统调用,查询IO的就绪状态。在Linux系统中,对应的系统调用为select/epoll系统调用等等。通过系统调用,一个进程可以监视多个文件描述符,一旦某个文件描述符就绪(一般为内核缓冲区可读/可写),内核能够将就绪的状态返回给应用程序。之后应用程序根据就绪的状态进行相应的IO系统调用。

在IO多路复用模型中通过select/epoll系统调用,单个应用程序的线程,可以不断轮询多个socket连接,当某个socket网络连接有了就绪状态,就返回对应的可以执行的读写操作。

发起一个多路复用IO的read读操作的系统调用如下:

  1. 选择器注册。这种模式下,需要read操作的目标socket网络连接,提前注册到select/epoll选择器中,Java中对应的选择器类是Selector类。

  2. 就绪状态的轮询。通过选择器的查询方法,查询注册过的所有socket连接的接续状态,通过查询的系统调用,内核会返回一个就虚的socket列表,当任何一个注册过的socket中的数据准备好,内核缓冲区中有数据之后,内核就将该socket加入到就绪的列表中。

  3. 用户线程获得了就绪状态的列表后,根据其中的socket连接,发起read系统调用,用户线程阻塞,内核开始复制数据,将数据从内核缓冲区复制到用户缓冲区。

  4. 复制完成后,内核返回结果,用户线程才会解除阻塞的状态,用户线程读取到数据。流程图

  5. 如下所示:

从网上https://blog.csdn.net/huang_wu_yao_xin/article/details/80886847?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_title~default-1.control&spm=1001.2101.3001.4242看到了这个图感觉更详细:Java中对应的选择器类是Selector类。

IO多路复用模型的特点:IO多路复用模型的IO涉及两种系统调用:一种是select/epoll,一种是IO操作。IO多路复用模型建立在操作系统 的基础设施之上,即操作系统的内核必须能够提供多路分离的系统调用select/epoll。

其实IO多路复用模型就是用一个线程来解决了同步非阻塞IO中,多个用户线程进行轮询获取结果。在IO多路复用模型中,对于注册在选择器上的每一个可以查询的socket连接,一般都设置成为同步非阻塞模型。

IO多路复用模型的优点:与一个线程维护一个连接的阻塞IO模式相比,使用select/epoll的最大优势在于,一个选择器查询线程可以同时处理成千上万个连接。系统不必创建大量的线程,大大减小了系统的开销。

IO多路复用模型的缺点:本质上,select/epoll系统调用是阻塞的,属于同步IO。都需要在读写事件就绪后,由系统调用本身负责进行读写,这个读写过程也是阻塞的。

异步IO模型(Asynchronous IO)

异步IO模型的基本流程是:用户线程通过系统调用,内核注册某个IO操作。内核在整个IO操作完成后,通知用户程序,用户执行后续的业务操作。

在异步IO模型中,在整个内核的数据处理过程中,包括内核将数据读取到内核缓冲区,将内核缓冲区数据复制到用户缓冲区,用户线程都不会阻塞。

发起一个异步IO的read读操作的系统调用,流程如下:

  1. 当用户线程发起了read系统调用,立刻就可以开始做其他的事,用户线程不阻塞。

  2. 接着内核开始等待、准备数据,将数据从内核缓冲区复制到用户缓冲区。

  3. 之后内核会给用户线程发一个信号,或者回调用户线程注册的回调接口,告诉用户线程read操作完成

  4. 用户线程读取缓冲区的数据,完成后续操作。

异步IO模型的特点:在内核等待数据和复制数据的两个阶段,用户线程都不是阻塞的。用户线程需要接收内核的IO操作完成的事件,或者用户线程需要注册一个IO操作完成的回调函数。

异步IO模型的缺点:应用程序仅需要进行事件的注册与接收,其余的工作都交付给系统来完成。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值