Linux IO的五种模型 ongoing

服务器端编程经常需要构造高性能的IO模型,常见的IO模型:

  • 阻塞I/O模型  (Blocking IO) ------------(同步)(阻塞)
  • 非阻塞I/O模型 (Non-Blocking IO)---------(同步)(非阻塞)
  • I/O多路复用模型 (IO Multiplexing)---------(异步)(阻塞)
  • 异步I/O模型(Asynchronous IO) -----------(异步)(非阻塞)

阻塞和非阻塞的概念描述的是用户线程调用内核IO操作的方式:阻塞是指IO操作需要彻底完成后才返回到用户空间;而非阻塞是指IO操作被调用后立即返回给用户一个状态值,无需等到IO操作彻底完成。

同步和异步的概念描述的是用户线程与内核的交互方式:同步是指用户线程发起IO请求后需要等待或者轮询内核IO操作完成后才能继续执行;而异步是指用户线程发起IO请求后仍继续执行,当内核IO操作完成后会通知用户线程,或者调用用户线程注册的回调函数。----????


1. (同步)阻塞I/O模型  (Blocking IO

当read或write一个文件时,如果read/write不能立即返回,那么调用者就会进入睡眠状态,用户线程执行到read/write就不动了,开始等待内核空间,直到该文件变成可读/可写。这种模式就是阻塞模式。

以TCP连接为例,用户空间read/write是与TCP缓冲区交互的,而不是直接同网卡驱动交互。当接收缓冲区内无数据时,read操作就无法立即返回,因此就会阻塞住调用线程,直到对方发送数据过来并由网卡驱动拷贝到接收缓冲区;同理,当网络延时过大或接收方问题导致本端(发送方)发送缓冲区满时,调用write无法将数据写入到发送缓冲区(一个字节都没法写入),调用进程也会阻塞。

整个IO请求的过程中,用户线程是被阻塞的,这导致用户在发起IO请求时,不能做任何事情,对CPU的资源利用率不够。


2. (同步)非阻塞I/O模型 (Non-Blocking IO
默认创建的socket都是阻塞的,非阻塞IO类型的socket需要调用设置函数,例如fcntl等,被设置为NONBLOCK。
用户线程可以在发起IO请求后可以立即返回。

用户需要不断地调用read,尝试读取socket中的数据,直到读取成功后,才继续处理接收的数据。整个IO请求的过程中,虽然用户线程每次发起IO请求后可以立即返回,但是为了等到数据,仍需要不断地轮询、重复请求,消耗了大量的CPU的资源。用户需要不断地调用read,尝试读取socket中的数据,直到读取成功后,才继续处理接收的数据。很少直接使用这种模型,而是在其他IO模型中使用非阻塞IO这一特性。

3. I/O多路复用模型 (IO Multiplexing)
有时也称为异步阻塞IO. 是经典的Reactor设计模式--Java中的Selector和Linux中的epoll都是这种模型。

1) 它的形成原因
如果一个I/O流进来,我们就开启一个线程处理这个I/O流。那么假设现在有一百万个I/O流进来,那我们就需要开启一百万个线程一一对应处理这些I/O流(这就是传统意义下的多线程并发处理)。一百万个进程的CPU占有率高,及其的不合理。所以人们提出了I/O多路复用这个模型,一个线程,通过记录I/O流的状态来同时管理多个I/O,可以提高服务器的吞吐能力。


2) 通过它的英文单词来理解一下I/O多路复用
I/O multiplexing 也就是我们所说的I/O多路复用,multi意味着多,而plex意味着丛(丛:聚集,许多事物凑在一起。),那么字面上来看I/O multiplexing 就是将多个I/O凑在一起。就像下面这张图的前半部分一样,中间的那条线就是我们的单个线程,它通过记录传入的每一个I/O流的状态来同时管理多个IO。


3)I/O多路复用的实现

IO多路复用模型是建立在内核提供的多路分离函数select基础之上的
如下图所示,用户首先将需要进行IO操作的socket添加到select中,然后阻塞,等待select系统调用返回。当数据到达时,select函数返回。用户线程才发起read请求,读取数据并继续执行。
---- select函数亦可等待固定时间后,即使没有检测到数据 也返回。 下面讲的select函数都是指的没有数据返回就无限等待

从流程上来看,使用select函数进行IO请求和 阻塞模型没有太大的区别,甚至还多了添加监视socket,以及调用select函数的额外操作,效率更差。但是,使用select以后最大的优势是用户可以在一个线程内 同时 处理多个socket的IO请求。用户可以注册多个socket,然后不断地调用select读取被激活的socket,即可达到在同一个线程内同时处理多个IO请求的目的,。有任何一个socket口的消息来了,都可以让停在select处的用户线程被开启, 而在 阻塞模型中,必须通过多线程的方式才能达到这个目的。


在代码中:在while死循环中调用select,select监视的是一组你感兴趣的文件,有文件被激活后,可以继续往下执行select后面的语句了,先判断是哪一个文件被激活,然后马上读取该文件的数据,然后继续循环。

使用IO多路复用时,一般使用Reactor设计模式:------????



IO多路复用是最常使用的IO模型,但是其异步程度还不够“彻底”,因为它使用了会阻塞线程的select系统调用。因此IO多路复用只能称为异步阻塞IO,而非真正的异步IO。


1)select/poll---??


2)epoll---??



4.异步I/O模型(Asynchronous IO)

经典的Proactor设计模式,也称为异步非阻塞IO。----??


5.信号驱动I/O模型



Ref:

https://www.jianshu.com/p/6a6845464770
https://blog.csdn.net/shuxiaogd/article/details/50366039
https://blog.csdn.net/baixiaoshi/article/details/48708347


猜你喜欢

转载自blog.csdn.net/qq_35865125/article/details/80849884