当前位置: 首页 > news >正文

初识Linux · 五种IO模型和非阻塞IO

目录

前言:

五种IO模型

什么是IO

IO模型

非阻塞IO


前言:

前文我们已经将网络的基本原理介绍完了,都是通过围绕TCP/IP四层协议,将应用层,传输层,网络层,数据链路层全部介绍完毕,至于部分小主题,比如ARP欺骗,HTTP协议的原理,cookie以及session等内容,我们放在后面介绍。

本文以及之后的内容,主要通过是介绍IO相关的问题,通过IO模型的介绍,从而引出多路复用的具体内容,通过介绍select,poll,epoll,最后再单独介绍一下Reactor,网络的基本原理我们就介绍完了。

那么废话不多说,我们直接进入五种IO模型。


五种IO模型

什么是IO

对于IO这个话题,我们从语言阶段,一直到了现在都经常谈论,因为IO问题不管在哪个阶段都是非常重要的,从C语言阶段的printf scanf,到文件操作的read和write涉及到了IO,即便是Linux,我们从一开始的文件系统,引出了文件描述符,到后面的网络也是一直使用文件描述符的概念,足以看出IO在编程中的重要性了。

那么问题来了,一开始我们只是粗略的将IO认定为是输入输出,今天我们进一步探讨,当我们第一次使用scanf的时候,我们发现只要我们不输入,系统就会阻塞住,系统在干什么呢?

系统在等我们输入,所以IO有一个非常重要的过程是:等待。当我们输入数据之后,系统也收到了数据也就不再等待,就执行了下一步操作了。这个过程还有读取的操作,或者是写入的操作,但是不管怎么说,都是从发送缓冲区/接收缓冲区拷贝数据到应用层的缓冲区。

所以实际上的IO = 等待 + 拷贝

而我们从一开始的学习到现在,使用到的基本上都是阻塞IO,自然也就存在非阻塞的IO,我们放在IO模型里面介绍。那么我们在MySQL中介绍索引的时候,我们提及到了IO次数如果多了,就一定会降低效率,所以我们就要使用高效的IO,那么高效的IO实际上减少了等待操作在IO中的比例

IO模型

对于IO模型我们拿钓鱼举例子,假设有五个人,分别在鱼塘中钓鱼,张三拿个鱼竿就坐在那里一直等待,当别人问他什么事,他也不理睬,这是第一种IO模型;李四拿个鱼竿放在那里,就不管了,转身去做自己的事儿了,然后定期来查看鱼竿的情况,这是第二种IO模型;王五拿个鱼竿,钓鱼的时候放个铃铛,当鱼上钩了,就放在自己手里的事儿,然后钓鱼,这是第三种IO模型;赵六就很有说法了,拿了一卡车的鱼竿过来了,同时使用很多的鱼竿,然后一个一个的检查鱼竿情况,这是第四种IO模型;田七的钓鱼操作是派遣一个人,让这个人像张三一样,完成钓鱼的等待和钓操作,自己就转身去干其他事儿了,也就是田七只负责吃的操作,这是第五种IO模型。

那么上述都是一个口语化的介绍,实际上的名称叫做:
第一种,阻塞IO,比如我们常见的read recvfrom scanf都是阻塞IO的代表,如果系统的数据还没有准备好,强行非阻塞的话就会返回错误码11,一会儿我们可以实验。

第二种,非阻塞IO,非阻塞IO顾名思义,IO的同时干自己的事儿,那么这意味着需要反反复复的尝试读写文件描述符,这个过程称为轮询,但是因为会反反复复的操作文件操作符,所以会对CPU资源造成较大的浪费,一般根据特定情况使用:

第三种,信号驱动IO,同王五一样,铃铛就像信号,内核将数据处理好之后,就使用信号SIGIO通知应用程序,这种IO模型实际上也是非阻塞IO模型:

第四种,IO多路转接,它实际上是阻塞IO的plus版本,也就是说张三一个人进行阻塞IO的时候,赵六已经创建了100个张三的分身了,所以实际上的流程图也就是IO的流程图,那么多路转接也是我们后面的主题,介绍select poll epoll就是从这里引出的:

第五种,异步IO,异步IO实际上就是先让内核吧数据报准备好,再进行拷贝的,最后OS给应用程序发信号通知准备好了,这个过程田七只是作为发起IO,但是等的过程交给了内核,然后他再来负责拷贝,那么被称为异步IO就是因为发出 I/O 请求的线程不会阻塞等待,后续的 I/O 完成由内核负责异步处理,应用程序通过信号、回调或轮询某种状态(如 aio_error())来获取 I/O 是否完成,所以它是异步的。

那么以上五种IO中,第四种IO多路复用的效率是最高的,因为大大减少了等待了时间,这也是之后我们为什么围绕它展开的理由。


非阻塞IO

对于阻塞IO来说,是我们最常见,也是最经常使用的IO模型,但是如果我们今天想尝试一下非阻塞IO怎么办呢?我们可以使用函数fcntl

第一个参数是对应的文件描述符,第二个参数是执行的控制命令,第三个参数取决于第三个参数,可以忽略。

一般常用的cmd有F_SETFL,F_GETFL,GET用来获取状态标志,SET用来获取标志,比如我们想要将一个文件描述符设置为非阻塞的就可以这样操作:

void SetNoBlock()
{int n = ::fcntl(0, F_GETFL);if(n < 0)std::cout << "Error" << std::endl;else   fcntl(0, F_SETFL, n | O_NONBLOCK);
}int main() 
{SetNoBlock();while(true){char buffer[1024];ssize_t n = ::read(0, buffer, sizeof(buffer));if(n > 0){std::string str = buffer;std::cout << str << std::endl;}else if(n == 0){std::cout << "read done!" << std::endl;break;}else{// errno == 11 EWOULDBLOCK EAGAINif(errno == EWOULDBLOCK){continue;}std::cout << "GG" << std::endl;sleep(1);}}return 0;
}

我们会发现如果我们强行让read这种阻塞IO变成非阻塞IO,就会导致返回的值是负数,所以这个时候就有这种情况:发生错误是因为read本身出错还是因为非阻塞下没有数据可读导致read不会阻塞直接返回-1?所以我们可使用EWOULDBLOCK比较一下,当然了,它的本质是11,也是EAGAIN:

那么IO模型先到这里,算是一个开胃小菜~


感谢阅读!

相关文章:

  • 探索Puter:一个基于Web的轻量级“云操作系统”
  • 2025.05.21华为暑期实习机考真题解析第一题
  • 31-35【动手学深度学习】深度学习硬件
  • Nginx核心服务
  • Typescript学习教程,从入门到精通,TypeScript 面向对象编程指南:抽象类、接口及其应用知识点及案例代码(9)
  • 论文阅读:Auto-Encoding Variational Bayes
  • 学习路之uniapp--unipush2.0推送功能--服务端推送消息
  • 【Python】使用 Python 构建 Weaviate 工具类:实现数据插入、语义搜索、混合检索与集合管理
  • 服务器安装xfce桌面环境并通过浏览器操控
  • Vue大数据量前端性能优化策略
  • 为什么服务器突然变慢?从硬件到软件的排查方法
  • 【Linux笔记】防火墙firewall与相关实验(iptables、firewall-cmd、firewalld)
  • 服务器网络配置 netplan一个网口配置两个ip(双ip、辅助ip、别名IP别名)
  • 每日算法刷题计划Day12 5.21:leetcode不定长滑动窗口求最短/最长3道题,,用时1h40min(有点长了)
  • SQLMesh 宏操作符详解:@IF 的条件逻辑与高级应用
  • 使用 Matter.js 创建封闭箱体与里面的小球
  • Python学习Day1:安装
  • 数独求解器3.0 增加latex格式读取
  • 通过TDE透明加密实现SQL Server数据库免改造加密
  • SQL 数值计算全解析:ABS、CEIL、FLOOR与ROUND函数深度精讲