【Linux C | I/O模型】Unix / Linux系统的5种IO模型 | 图文详解

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍 Unix / Linux系统的5种IO模型🍭
😎金句分享😎:🍭你不能选择最好的,但最好的会来选择你------泰戈尔🍭
🕛发布时间🕛:2024-02-01 00:09:45

本文未经允许,不得转发!!!

目录


🎄一、概述

✨1.1 什么是 I/O

I/OInput/Output 的缩写,意思是输入输出,一般指操作系统与硬件设备(磁盘、网卡)的数据写入或读取。常见的IO有以下两种:

  • 磁盘I/O:与磁盘的数据交互,读取磁盘数据写入数据到磁盘;在Linux编程中,可以使用open、read、write等函数来操作文件描述符实现与磁盘的数据交互。
  • 网络I/O:通过网卡与其他主机或本机其他进程的数据交互;在Linux编程中,可以使用socket、read、write等函数来操作套接字描述符实现网络I/O。

✨1.2 五种 I/O 模型介绍

在了解 I/O 模型之前,先看看操作系统是怎样与磁盘或网卡交换数据(操作I/O)的,一般分为两个步骤,下面以数据输入为例:

  • 1、等待数据到达网卡(或数据已存在与磁盘),将数据复制到内核的某个缓冲区;
  • 2、再将数据复制到应用层的进程的缓冲区。

之所以这样要分成这两个步骤去操作IO,是因为操作系统是分层的,有内核空间 和 用户空间,操作硬件设备都需要通过内核去操作,用户空间一般不直接操作硬件。

正是因为,数据从硬件设备到应用层缓冲区需要经过内核,所以存在一个等待的过程,这个过程被称为阻塞。由于这个阻塞的情况,再加上内核一些避免阻塞的机制,使 Unix / Linux 系统有5种 I/O 模型:

  • 阻塞I/O模型:一直等待数据到应用层缓冲区;
  • 非阻塞I/O模型:不等待数据完成,如果数据没到应用层就返回错误;
  • 复用式I/O模型:不等待数据完成,让其他系统调用帮忙等着;
  • 信号驱动式I/O模型:不等待数据完成,让内核准备好数据后就通知进程;
  • 异步I/O模型:告诉内核要读取哪些数据,让内读取完数据后再通知进程。

🎄二、阻塞I/O模型

阻塞I/O模型是使用得最多的IO模型,默认情况下,大部分IO操作都是阻塞的。

阻塞是指调用该系统调用后,需要等待内核获取完数据,再把数据复制到应用层缓冲区后,该系统调用才回返回。

如下图:应用层的进程调用recvfrom后,告诉内核要读取某个套接字的数据,内核就开始等待数据,等到数据准备好了,再将数据复制到应用层的用户空间,复制完成后,recvfrom函数读取到数据并返回。


阻塞I/O模型,就像你要去买奶茶,但前面很多人在买,你的奶茶没做好,你就一直在奶茶店等着你的奶茶,直到做好拿走。


🎄三、非阻塞I/O模型

进程把一个描述符fd设置成非阻塞是在通知内核:当所请求的I/O操作非得把本进程投入睡眠,才能完成时,不要把本进程投入睡眠,而是返回一个错误(EWOULDBLOCK)。EWOULDBLOCK这个单词由would block组成,表示告诉进程会阻塞

看下图,应用层的进程调用recvfrom获取数据时,因为内核准备好的数据,直接返回EWOULDBLOCK,紧接着,进程又调用recvfrom获取数据,内核仍没有准备好的数据,返回EWOULDBLOCK,前面三次都没有数据,知道第四次调用recvfrom获取数据时,内核的数据准备好了,将数据复制到用户空间,返回给应用的调用进程。

轮询:当一个进程对一个非阻塞的描述符循环调用recvfrom,称为轮询。应用程序持续轮询内核,会消耗大量的CPU时间,所以这样轮询访问数据的模型比较少用。轮询访问就类似下面伪代码。

c 复制代码
while(1)
{
	recvfrom(...);
	usleep(1000);
}


非阻塞I/O模型,就像你要去买奶茶,但前面很多人在买,你来拿奶茶时,奶茶店没做好,你就马上走了,过一会又来拿,又没做好,你又走了,过一会又来拿。。。这样循环来查看奶茶做好了没,直到某一次,奶茶做好了,你就拿走。


🎄四、复用式I/O模型

I/O复用(I/O multiplexing),可以让我们阻塞在其他系统调用函数上,而不是阻塞在真正的I/O系统调用上。但是阻塞在其他系统调用函数也还是阻塞啊,有什么区别吗?区别在于,如果需要对多个(大于1个)描述符进行IO操作时,select或poll系统调用可以同时监控多个描述符是否可读、可写,而阻塞IO只要阻塞在那就干不了其他事情了。

下图使用select函数演示复用I/O模型,应用层进程调用select系统调用,并将感兴趣的描述符通知内核,内核等待数据到达,发现select函数感兴趣的描述符的数据准备好了,就马上让select返回该描述符可读,然后进程再调用recvfrom函数来获取数据,因为此时数据已准备好,所以内核直接将数据复制到用户空间,返回给recvfrom的调用进程。

复用式I/O模型,就像你要去买奶茶,但前面很多人在买,你就花钱找了个黄牛,帮你在那里等,奶茶做好之后,黄牛就打电话让你来拿。


🎄五、信号驱动式I/O模型

信号驱动式I/O模型,让内核在描述符的数据准备好的时候发SIGIO信号给进程,进程再调用recvfrom函数来获取数据。

信号驱动式IO是指进程预先告知内核,使得当某个描述符上发生某事时,内核使用信号通知相关进程。

针对一个套接字使用信号驱动式IO (SIGIO)要求进程执行以下3个步骤。

(1)建立SIGIO信号的信号处理函数。

(2)设置该套接字的属主,通常使用fcntl的F_SETOWN命令设置。

(3)开启该套接字的信号驱动式IO,通常通过使用fcnt1的F_SETFL命令打开O_ASYNC标志完成。

下图:先建立了SIGIO信号处理函数并设置其他配置,然后使用sigaction注册SIGIO的处理函数到内核,内核数据准备好了就会给该进程发送SIGIO信号,进程接收SIGIO信号后,进入SIGIO的信号处理函数,然后会调用recvfrom函数,此时数据已准备好,所以内核直接将数据复制到用户空间,返回给recvfrom的调用进程。

信号驱动式I/O模型,就像你要去买奶茶,但前面很多人在买,你不想等,于是,你把手机号码给到奶茶店,等奶茶做好了,店主给你打电话,你再去拿奶茶。


🎄六、异步I/O模型

异步I/O(asynchronous I/O)的工作机制是:告知内核启动某个I/O操作,并让内核在整个操作(包括将数据从内核复制到我们自己的缓冲区)完成后通知我们。这种模型与信号驱动式I/O模型的主要区别在于:信号驱动式IO是由内核通知我数据准备好了,可以读取了;而异步I/O通知到进程时,数据已经读取完了。


异步I/O模型,就像你要去买奶茶,但前面很多人在买,你不想等,就干脆点外卖,等奶茶做好了,直接送到你手里,不需要你再去店里拿了。


如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

参考资料:

《Unix网络编程卷1》

https://blog.csdn.net/qq_54015483/article/details/131013426

https://blog.csdn.net/qq_54015483/article/details/130943574

https://blog.csdn.net/Bb15070047748/article/details/124699009

相关推荐
FoldWinCard6 小时前
D5 Linux 网络及端口命令
linux·运维·服务器
零涂毕业设计7 小时前
毕业设计模块开发-OLED显示屏(IIC协议0.96寸)STM32 ESP32 FPGA Linux驱动免费分享
linux·stm32·单片机·嵌入式·esp32·fpga·oled
tedcloud1238 小时前
OmniRoute怎么部署?开源AI模型路由平台Linux部署教程
linux·服务器·人工智能·开源·音视频
霸道流氓气质8 小时前
KMS 密钥管理服务(Key Management Service)原理与实践
linux·服务器·数据库
SakitamaX8 小时前
LVS(Linux Virtual Server)概念详解
linux·运维·lvs
小张同学a.8 小时前
Linux系统管理
linux·运维
阿龙的工作记录9 小时前
深入理解Linux设备驱动模型
linux·嵌入式硬件·工作记录
代码村新手9 小时前
Linux的基本指令
linux·运维·服务器
hkj88089 小时前
Ubuntu 切换java版本
java·linux·ubuntu
撩得Android一次心动11 小时前
Linux编程笔记3【个人用】
linux·笔记