Linux下的I/O模型

目录

一、什么是IO?

二、IO操作的两个阶段

三、五种I/O模型

[1、阻塞I/O(blocking I/O)](#1、阻塞I/O(blocking I/O))

[2、非阻塞I/O(non-blocking I/O)](#2、非阻塞I/O(non-blocking I/O))

[3、多路复用I/O(multiplexing I/O)](#3、多路复用I/O(multiplexing I/O))

[4、信号驱动I/O(signal-driven I/O)](#4、信号驱动I/O(signal-driven I/O))

[5、异步I/O(asynchronous I/O)](#5、异步I/O(asynchronous I/O))

四、五种I/O模型比较


一、什么是IO?

IO 是 Input/Output 的缩写,指的是输入和输出。在计算机当中,IO 操作通常指将数据从一个设备或文件中读取到计算机内存中,或将内存中的数据写入设备或文件中。这些设备可以包括硬盘驱动器、网卡、键盘、屏幕等。

通常用户进程中的一个完整I/O分为两个阶段:用户进程空间→内核空间→设备空间

I/O分为内存I/O、网络I/O和磁盘I/O三种

二、IO操作的两个阶段

Linux中进程无法直接操作I/O设备,其必须通过系统调用请求内核来协助完成I/O操作。

内核会为每个I/O设备维护一个缓冲区。

对于一个输入操作来说,进程I/O系统调用后,内核会先看缓冲区中有没有相应的缓存数据,没有的话再到设备(比如网卡设备)中读取(因为设备I/O一般速度较慢,需要等待);

内核缓冲区有数据则直接复制到用户进程空间。所以,对于一个网络输入操作通常包括两个不同阶段:

1、等待网络数据到达网卡,把数据从网卡读取到内核缓冲区,准备好数据。

2、从内核缓冲区复制数据到用户进程空间。

网络I/O的本质是对socket的读取,socket在Linux系统中被抽象为流,I/O可以理解为对流的操作。

网络I/O的模型可分为两种:

●异步I/O(asynchronous I/O)

●同步I/O(synchronous I/O)

同步I/O又包括

●阻塞I/O(blocking I/O)

●非阻塞I/O(non-blocking I/O)

●多路复用I/O(multiplexing I/O)

●信号驱动I/O(signal-driven I/O)

强调一下:信号驱动I/O属于同步I/O,原因往后看。

三、五种I/O模型

1、阻塞I/O(blocking I/O)

对于一个套接字上的输入操作,第一步通常涉及等待数据从网络中到达,当所有等待分组到达时,它被复制到内核中的某个缓冲区。第二步是把数据从内核缓冲区复制到应用程序缓冲区。

同步阻塞I/O模型是最常用、最简单的模型。在Linux中,默认情况下,所有套接字都是阻塞的。下面我们以阻塞套接字的recvfrom的调用图来说明阻塞,如图所示

2、非阻塞I/O(non-blocking I/O)

非阻塞的recvform系统调用之后,进程并没有被阻塞,内核马上返回给进程,如果数据还没准备好,此时会返回一个error(EAGAIN或EWOULDBLOCK)。

进程在返回之后,可以先处理其他的业务逻辑,稍后再发起recvform系统调用。采用轮询的方式检查内核数据,直到数据准备好。再拷贝数据到进程,进行数据处理。

在Linux下,可以通过设置套接字选项使其变为非阻塞。非阻塞的套接字的recvfrom操作如图所示

可以看到前三次调用recvfrom请求时,并没有数据返回,内核返回errno(EWOULDBLOCK),并不会阻塞进程。当第四次调用recvfrom时,数据已经准备好了,于是将它从内核空间拷贝到程序空间,处理数据。

在非阻塞状态下,I/O执行的等待阶段并不是完全阻塞的,但是第二个阶段依然处于一个阻塞状态(调用者将数据从内核拷贝到用户空间,这个阶段阻塞)。

3、多路复用I/O(multiplexing I/O)

I/O多路复用的好处在于单个进程就可以同时处理多个网络连接的I/O。它的基本原理是不再由应用程序自己监视连接,而由内核替应用程序监视文件描述符。

以select函数为例,当用户进程调用了select,那么整个进程会被阻塞,而同时,kernel会"监视"所有select负责的socket,当任何一个socket中的数据准备好,select就会返回。

这个时候用户进程再调用read操作,将数据从内核拷贝到用户进程,如下图所示。

4、信号驱动I/O(signal-driven I/O)

该模型允许socket进行信号驱动I/O,并注册一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据,如图所示

注意:虽然信号驱动IO在注册完信号处理函数以后,就可以做其他事情了。但是第二阶段拷贝数据的过程当中进程依然是被阻塞的,而后要介绍的异步IO是完全不会阻塞进程的,所以信号驱动虽然具有异步的特点,但依然属于同步IO, 原因就是其在从内核中将数据拷贝到用户空间的时候时阻塞的。

5、异步I/O(asynchronous I/O)

相对于同步I/O,异步I/O不是按顺序执行。用户进程进行aio_read系统调用之后,就可以去处理其他逻辑了,无论内核数据是否准备好,都会直接返回给用户进程,不会对进程造成阻塞。这是因为

aio_read只向内核递交申请,并不关心有没有数据。

等到数据准备好了,内核直接复制数据到进程空间,然后内核向进程发送通知,此时数据已经在用户空间了,可以对数据进行处理。

四、五种I/O模型比较

前四种I/O模型都是同步I/O操作,它们的区别在于第一阶段,而第二阶段是一样的:在数据从内核复制到应用缓冲区期间(用户空间),进程阻塞于recvfrom调用。

相反,异步I/O模型在等待数据和接收数据的这两个阶段都是非阻塞的,可以处理其他的逻辑,用户进程将整个I/O操作交由内核完成,内核完成后会发送通知。在此期间,用户进程不需要检查I/O操作的状态,也不需要主动拷贝数据。

总结一下:其实只要是涉及到用户进程到内核空间进行数据拷贝这一个动作的都属于是同步IO, 像aio_read这样的系统调用连数据的拷贝都是内核帮我们完成的, 这种才是真正意义上的异步。

好了, 分享到此结束了, 希望对需要的人有启发和帮助。

相关推荐
wespaul4 分钟前
VeapAI 自然语言操作实战|第 07 篇:执行层——编排引擎与全局语义命令
服务器·人工智能·milvus
牧羊人.3335 分钟前
自然语言处理基础 04|长短期记忆网络 LSTM
网络·人工智能·rnn·深度学习·学习·自然语言处理·lstm
lambo mercy13 分钟前
Day4:流式传输
运维·服务器·网络
狂奔的sherry14 分钟前
JRuby 多线程反引号偶发 127:GC 提前释放了 posix_spawn 的 argv
java·linux·ruby·gc·jruby
I'm Jie22 分钟前
Rhost v2.0.0 版本发布!!!不止 SSH 终端,内置 AI Agent 支持自然语言操作远程服务器
运维·服务器·rust·开源·ssh·tauri·finalshell
ESDWAN24 分钟前
跨境网络延迟、抖动和丢包有什么区别?如何判断业务影响
开发语言·网络·计算机网络·php
广州灵眸科技有限公司28 分钟前
瑞芯微(EASY EAI)RV1126B DeepSeek R1部署教程
linux·数据库·yolo
Spatial3Ddddd30 分钟前
2026空间三维重建服务商横向对比
java·服务器·人工智能
沙老师31 分钟前
linux比windows死板的地方
linux·运维·windows
比兔代理33 分钟前
HTTP 代理与 TLS 指纹:如何避免被识别为代理流量?
网络·网络协议·http