0


【Netty系列】Linux下的5种IO模型大揭秘

一、什么是IO?

I/O即

Input/Output

,翻译为中文为输入/输出。指的是操作程序或设备与计算机之间发生的数据传输的过程,分为IO设备和IO接口两部分:

  • IO设备是可以与计算机进行数据传输的硬件。如打印机、鼠标、键盘等。
  • IO接口是主机和外设之间的交接界面,通过接口可以实现主机与外设之间的信息交换。

在计算机的世界里,IO的本质就是计算机的核心(CPU和内存)与其它设备之间数据转移的过程。比如数据从磁盘读入到内存,或内存的数据写回到磁盘,都是IO操作。

二、Linux下的五种IO模型

Linux中有5种IO模型,分别是阻塞IO模型、非阻塞IO模型、信号驱动IO模型、IO多路复用IO模型、异步IO模型。通常有同步(

Sync

)/异步(

Async

),阻塞(

Block

)/非阻塞(

Unblock

)四种调用方式。它们的关系如下图所示:

在这里插入图片描述

2.1 何为同步与异步?

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

  • 同步调用是指发出一个调用后,必须要等到结果之后才能返回。大白话就是事情一件一件做(死等结果)。
  • 异步调用是指发出一个调用后,调用者不用等待执行结果就可以去做其他事。等到该调用处理完成后会回调通知调用者。
2.2 何为阻塞与非阻塞?

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

  • 阻塞调用指的是调用结果返回前,该线程会被挂起。线程只有得到结果之后才会返回。
  • 非阻塞调用指的是若调用无法立即得到结果,该线程不会被挂起,可以做其他事情。

阻塞IO模型(Blocking I/O)

在这里插入图片描述

同步阻塞 IO 模型是最常用的一个模型,也是最简单的模型。在linux中,默认情况下所有的

socket

都是

blocking

的。它符合人们最常见的思考逻辑。阻塞就是进程 “被” 休息,CPU处理其它进程去了。

当用户进程调用了

recv()/recvfrom()

这个系统调用,

kernel

就开始了IO的第一个阶段:准备数据(对于网络IO来说,很多时候数据在一开始还没有到达。比如,还没有收到一个完整的UDP包。这个时候kernel就要等待足够的数据到来)。这个过程需要等待,也就是说数据被拷贝到操作系统内核的缓冲区中是需要一个过程的。而在用户进程这边,整个进程会被阻塞(当然,是进程自己选择的阻塞)。第二个阶段:数据拷贝,当

kernel

一直等到数据准备好了,它就会将数据从

kernel

中拷贝到用户内存,然后

kernel

返回结果,用户进程才解除

block

的状态,重新运行起来。所以,blocking IO的特点就是在IO执行的两个阶段都被block了

非阻塞IO模型(Nonblocking I/O)

linux 下,可以通过设置

socket

使其变为

non-blocking

。当对一个 non-blocking socket 执行读操作时,流程是这个样子:

在这里插入图片描述

当用户进程发出

read

操作时,如果

kernel

中的数据还没有准备好,那么它并不会

block

用户进程,而是立刻返回一个

error

。从用户进程角度讲 ,它发起一个

read

操作后,并不需要等待,而是马上就得到了一个结果。用户进程判断结果是一个

error

时,它就知道数据还没有准备好,于是它可以再次发送

read

操作。一旦

kernel

中的数据准备好了,并且又再次收到了用户进程的

system call

,那么它马上就将数据拷贝到了用户内存,然后返回;所以,nonblocking IO 的特点是用户进程需要不断的主动询问

kernel

数据好了没有。

I/O复用模型

在这里插入图片描述

当用户进程调用了

select

,那么整个进程会被

block

,而同时,

kernel

会监视所有

select

负责的

socket

,当任何一个

socket

中的数据准备好了,

select

就会返回。这个时候用户进程再调用

read

操作,内核负责将数据从

kernel

拷贝到用户进程;所以,I/O 多路复用的特点是通过一种机制使得一个进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select()函数就可以返回,所以说它最大的优势是系统开销小,系统不需要创建或维护新的进程/线程。另外,从上面比较 IO 复用流程图和阻塞 IO 的图可以发现,多路复用本身也是阻塞的,事实上,其效率可能还更差一些。因为这里需要使用两个

system call

(

select

recvfrom

),而阻塞 IO 只调用了一个

system call

(

recvfrom

)。但是,用

select

的优势在于它可以同时处理多个

connection

。所以,如果处理的连接数不是很高的话,使用

select/epoll

的 web server 不一定比使用阻塞 IO 的 web server 性能更好,可能延迟还更大。

select/epoll

的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。)在 IO 复用模型中,对于每一个

socket

,一般都设置成为

non-blocking

,但是,如上图所示,整个用户的 process 其实是一直被

block

的。只不过 process 是被

select

这个函数

block

,而不是被

socket IO

block

信号驱动IO模型

首先开启套接口信号驱动 I/O 功能,并通过系统调用

sigaction

执行一个信号处理函数(此系统调用立即返回,进程继续工作,它是非阻塞的)。当数据准备就绪时,就为该进程生成一个

SIGIO

信号。随即可以在信号处理程序中调用

recvfrom

来读数据,井通知主循环函数处理数据;一般用的较少。

异步IO模型

异步 IO

模型下,用户进程发起

read

操作之后,立刻就可以开始去做其它的事。而另一方面,从

kernel

的角度,当它收到一个

asynchronous read

之后,首先它会立刻返回,所以不会对用户进程产生任何

block

。然后,

kernel

会等待数据准备完成,然后依然由它将数据拷贝到用户内存,当这一切都完成之后,

kernel

会给用户进程发送一个

signal

,告诉它

read

操作完成了。

在这里插入图片描述

标签: 网络 JAVA NETTY

本文转载自: https://blog.csdn.net/weixin_40909461/article/details/138346274
版权归原作者 快乐早睡 所有, 如有侵权,请联系我们删除。

“【Netty系列】Linux下的5种IO模型大揭秘”的评论:

还没有评论