Linux:(五种IO模型)

目录

一、对IO的重新认识

二、IO的五种模型

1.阻塞IO

2.非阻塞IO

3.信号驱动IO

4.IO多路转接

5.异步IO

6.一些概念的解释

三、非阻塞IO的代码实现

1.fcntl

2.实现主程序


一、对IO的重新认识

如果有人问你IO是什么,你该怎么回答呢?

你可能会说,IO不就是input和output表示输入和输出,输入表示把数据从硬盘等外设拷贝到内存,而输出表示把数据从内存拷贝到其他外设。

虽然这样说没什么大问题,但还不够深刻。

我们不妨设想下面的现象,有一个进程,调用read/recv这样的系统调用读取数据。如果此时读取条件不满足,那就没有数据可供进程读取,进程只就会一直等待数据准备好。

IO除了拷贝数据需要消耗时间,还包含这个等待的过程所以我们使用的系统调用除了拷贝代码,也包含了等的这部分代码。

也就是说,IO=等+数据拷贝

那什么是高效IO呢?

我们知道,IO过程我们在意的是拷贝,而不是等待。而拷贝需要的时间是由电路还有系统实现等保证的。随着科技的发展,拷贝本身花费的时间已经基本没有提升空间了,所以拷贝本身的效率已经很难再有提升了。那么等待时间的长度就决定了IO的效率。

换句话说,单位时间内,等待的比重越低,IO效率越高。

二、IO的五种模型

1.阻塞IO

在内核将数据准备好之前,系统调用会一直等待。我们之前写代码使用的IO接口读取文件描述符,默认都使用阻塞IO方式。

下图就是阻塞IO的示意图,进程调用recvfrom这样的IO接口从内核中读取数据。如果数据没有准备好,进程就会阻塞在调用处等待,数据准备好后,才会将内核中的数据拷贝到用户缓冲区,并给出返回值。

阻塞IO是最常见的IO模型,也最简单,我们之前写的所有代码,IO都是阻塞式的。

2.非阻塞IO

如果内核还未将数据准备好,系统调用仍然会直接返回,并且返回EAGAN或者EWOULDBLOCK错误码。

如图所示,进程调用recvfrom从内核缓冲区中读取数据。如果数据没有准备好,就会给进程返回一个EWOULDBLOCK错误码,告诉进程数据还没准备好,进程就会接着去干自己的事情。

过了一段时间,进程还会调用recvfrom读取数据,不断反复,直到数据准备好。接着系统调用完成拷贝并返回成功的返回值。

非阻塞IO需要程序员设计循环代码,反复尝试读写文件描述符,这个过程称为轮询。但轮询对CPU有一定的性能浪费,只有特定场景下才使用。

3.信号驱动IO

信号驱动IO会在内核将数据准备好的时候,发送SIGIO信号通知进程进行IO操作。

如图所示,信号驱动IO模型,该模式使用信号处理函数执行IO。

首先使用signal注册信号处理函数为包含IO系统调用的函数。所以只要进程收到信号,就可以在处理函数中调用recvfrom拷贝已经准备好的数据。

也就是说,只要数据准备完成了,进程就会收到信号,进程直接来拷贝就可以了。其余时间进程还可以继续执行自己的代码。

但是我们之前也说过,如果我们给一个进程同时发很多信号,只有两个能被最终递达。而这里的信号丢失就相当于读取次数减少,就相当于数据丢失。所以,这种很少有符合这种模式的IO状态。

4.IO多路转接

IO多路转接可以理解为多个阻塞IO同时进行,并不断遍历检测哪个IO的文件描述符准备好了,准备好了就会执行拷贝。

如图所示为IO多路转接模式,它将IO的等待和拷贝分开了。

进程调用select系统调用等待内核中的数据就绪,就绪以后会通知进程调用recvfrom来将数据拷贝到用户缓冲区中。

由于多路转接可以同时等待多个文件描述符。所以,当一个或者多个文件的缓冲区中数据就绪时,都会通知上层用户读取。而且每个拷贝过程也是并行的,还是免不了等,但是等的比重降低了很多,从而提高了IO的效率。

多路转接既是效率最高的IO模式,也是我们以后讲解的重点。

5.异步IO

当一个异步IO调用发出后,调用者不会立刻得到结果,而是在调用发出后,被调用者通过状态、信号等来通知调用者,或通过回调函数处理这个调用。

下图表示异步IO,进程调用aio_read,将等待数据就绪和将数据拷贝到用户缓冲区两个步骤的工作全部交给操作系统来完成。当操作系统完成两个步骤以后,直接通知上层用户去用户缓冲区中读取数据即可。

也就是,进程不需要再考虑数据的IO,而是将其全权交给操作系统完成。

6.一些概念的解释

什么是同步IO和异步IO?

同步和异步的区别在于消息通信的机制。

所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回,但是一旦调用返回,就得到返回值了。

换句话说就是,调用者在主动等待这个调用的结果。

而异步则是调用开始执行后直接返回,调用者不会立刻得到结果,而是在调用返回后,被调用者通过状态、信号等通知调用者或通过回调函数处理。

话句话说,就是把事情交给了其他应用去做,自己只根据通信数据接收处理结果。

线程同步和同步IO有什么关系?

我们在讲解Linux线程时也提到了同步。

线程同步表示多个线程同时对临界资源进行操作时,系统为了保证没有线程处于饥饿状态,会以一定的顺序安排各个线程的执行顺序。

而同步IO表示处理数据的进程本身是否全权参与IO过程。

也就是,同步IO和线程同步之间,除了都有同步这个词之外没有任何关系。

三、非阻塞IO的代码实现

1.fcntl

int fcntl(int fd, int cmd, ... /* arg */ );
  • 头文件unistd.h、fcntl.h

  • 功能:修改文件描述符的属性或对其进行其他操作。

  • 参数:int fd需要操作的文件描述符。int cmd表示对描述符的操作。...表示可变参数列表,传入的cmd不同,参数也不同

  • 返回值:成功返回非-1的值,失败返回-1。

fcntl函数有5种功能:

  • 复制一个现有的描述符(cmd=F_DUPFD).

  • 获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD).

  • 获得/设置文件状态标记(cmd=F_GETFL或F_SETFL).

  • 获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN).

  • 获得/设置记录锁(cmd=F_GETLK,F_SETLK或F_SETLKW).

我们只使用第三个功能,即获取/设置文件状态标记,可将一个文件描述符设置为非阻塞。我们写一个SetNonBlock函数支持该功能。

//将文件描述符设为非阻塞
void SetNonBlock(int fd)
{int fl = fcntl(fd, F_GETFL);//获取文件描述符的标志,该标志是一个位图结构if(fl < 0)//获取失败{std::cerr << "fctnl:" << strerror(errno) << std::endl;//打印错误码}else{fcntl(fd, F_SETFL, fl | O_NONBLOCK);//将该文件描述符设为非阻塞}
}

2.实现主程序

由于我们从标准输入流(文件描述符为0)中读取数据,所以只要我们敲击键盘输入文字,就相当于向标准输入流中写入数据。

main.cc

#include"util.hpp"
#include<iostream>int main()
{SetNonBlock(0);//设置文件描述符为非阻塞while(1){char buffer[1024];ssize_t n = read(0, buffer, sizeof(buffer)-1);//读取数据if(n > 0)//读到了数据{buffer[n] = '\0';std::cout << buffer << std::endl;}else if(n == 0)//读到了结尾{std::cout << "read end" << std::endl;break;}else//n等于-1有两种情况,一种是读取出错,另一种是数据还没准备好,read只能按-1返回{if (errno == EAGAIN)//错误码为EAGAIN表示数据还没有准备好{//std::cout << "我没错, 只是没有数据" << std::endl;print_work();//程序继续执行自己的事}else if (errno == EINTR)//错误码为EINTR表示读取时进程收到了信号,需要进行处理,读取就被暂时打断了。{continue;//继续循环}else//这次就是出错了,打印错误码就可以了{std::cout << " errno: " << strerror(errno) << std::endl;break;}}sleep(1);}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/900249.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

将电脑控制手机编写为MCP server

文章目录 电脑控制手机后,截屏代码复习MCP server构建修改MCP的config文件测试效果困惑电脑控制手机后,截屏代码复习 def capture_window(hwnd: int, filename: str = None) -> dict:""&

[ctfshow web入门] web6

前置知识 入口点(目录)爆破 还记得之前说过网站的入口的吗&#xff0c;我们输入url/xxx&#xff0c;其中如果url/xxx存在&#xff0c;那么访问成功&#xff0c;证明存在这样一个入口点&#xff1b;如果访问失败则证明不存在此入口点。所以我们可以通过遍历url/xxx&#xff0c;…

【计算机网络】Linux配置SNAT策略

什么是NAT&#xff1f; NAT 全称是 Network Address Translation&#xff08;网络地址转换&#xff09;&#xff0c;是一个用来在多个设备共享一个公网 IP上网的技术。 NAT 的核心作用&#xff1a;将一个网络中的私有 IP 地址&#xff0c;转换为公网 IP 地址&#xff0c;从而…

Mathematics | Branch

注&#xff1a;本文为“遇见数学”翻译的 “数学分支概览” 两篇文章合辑。 数学世界的版图&#xff1a;主要分支概览&#xff08;上&#xff09; 原创 遇见数学 2025 年 04 月 03 日 12:02 河南 数学的分支&#xff08;Areas of Mathematics&#xff09; 在文艺复兴之前&am…

Ubuntu(CentOS、Rockylinux等)快速进入深度学习pytorch环境

这里写自定义目录标题 安装进入系统&#xff08;如Ubuntu22.04&#xff09;安装anacondapip、conda换源pip换源conda换源 安装nvidia安装pytorch环境针对于wsl的优化 安装进入系统&#xff08;如Ubuntu22.04&#xff09; docker 、 wsl 、 双系统 、服务器系统 推荐 Ubuntu 20…

什么是混杂模式?为什么 macvlan 依赖它

在 macvlan 场景中&#xff0c;物理网络是否支持混杂模式&#xff08;Promiscuous Mode&#xff09; 直接影响 macvlan 虚拟接口的通信能力。以下是详细解释和操作指南&#xff1a; 一、什么是混杂模式&#xff1f;为什么 macvlan 依赖它&#xff1f; 混杂模式的定义 当物理网络…

物理数据流图

物理数据流图&#xff08;Physical Data Flow Diagram, PDFD&#xff09;详解 物理数据流图是结构化系统分析中的一种建模工具&#xff0c;用于描述系统在物理环境下的具体实现方式&#xff0c;包括硬件、软件、人工操作和物理文件等实际组成部分。它与**逻辑数据流图&#xf…

Linux开发工具——vim

&#x1f4dd;前言&#xff1a; 上篇文章我们讲了Linux开发工具——apt&#xff0c;这篇文章我们来讲讲Linux开发工具——vim &#x1f3ac;个人简介&#xff1a;努力学习ing &#x1f4cb;个人专栏&#xff1a;Linux &#x1f380;CSDN主页 愚润求学 &#x1f304;其他专栏&a…

在 Langflow 中构建灵活的自定义组件:从基础到高级实践

本文深入探讨了如何在 Langflow 平台中创建功能丰富的自定义组件。通过详细的目录结构解析、分步实现指南和多个实战案例&#xff0c;帮助开发者掌握利用 Python 生态扩展低代码平台的方法&#xff0c;打造高效的数据处理流程。 理解组件架构设计 自定义组件是在 Langflow 中创…

stm32+LTR-390UV使用教程含源码

stm32LTR-390UV使用教程含源码 &#xff08;csdn首发源码&#xff09;&#xff0c;本人大四学生&#xff0c;考研已上岸&#xff0c;毕设做的全向轮小车&#xff0c;这个是环境检测部分LTR-390UV使用教程 文章目录 目录 文章目录 前言 一、硬件准备与连接 二、数据手册 1.…

【嵌入式系统设计师】知识点:第2章 嵌入式系统硬件基础知识

提示:“软考通关秘籍” 专栏围绕软考展开,全面涵盖了如嵌入式系统设计师、数据库系统工程师、信息系统管理工程师等多个软考方向的知识点。从计算机体系结构、存储系统等基础知识,到程序语言概述、算法、数据库技术(包括关系数据库、非关系型数据库、SQL 语言、数据仓库等)…

Java 项目灰度发布的详细实现与实践

前言 灰度发布是一种通过逐步将新功能或更新推向一部分用户来降低上线风险的技术。本文将详细介绍如何在 Java 项目中实现灰度发布&#xff0c;并提供相关的配置参数、代码示例以及 uml 图&#xff0c;帮助您更好地理解和应用这一技术。 一、灰度发布的核心思想 灰度发布的核…

使用 Swift 实现 LRU 缓存淘汰策略

&#x1f4cc; 实现思路 一、核心目标 我们要实现一个缓存类&#xff1a; 支持通过 get(key) 获取缓存的值&#xff1b;支持通过 put(key, value) 写入缓存&#xff1b;缓存容量有限&#xff0c;当超过容量时要淘汰最久未使用的元素。 二、为什么用「哈希表 双向链表」 功…

C#中为自定义控件设置工具箱图标

在C#中为自定义控件设置工具箱图标&#xff0c;可通过以下步骤实现&#xff1a; ### 步骤说明&#xff1a; 1. **准备图标文件** - 创建或选择一个16x16像素的位图&#xff08;.bmp&#xff09;文件&#xff0c;建议使用透明背景以确保清晰显示。 2. **添加位图到项目** -…

Linux数据库:【数据库基础】【库的操作】【表的操作】

目录 一.数据库基础 1.1什么是数据库 1.2基本使用 1.2.1连接服务器 1.2.2服务器&#xff0c;数据库&#xff0c;表关系 1.2.3使用案例 1.2.4数据存储结构 ​编辑 1.3MySQL架构 1.4SQL分类 1.5存储引擎 1.5.1什么是存储引擎 1.5.2查看存储引擎 ​编辑 1.5.3存储引擎…

CKPT文件是什么?

检查点&#xff08;Checkpoint&#xff0c;简称ckpt&#xff09;是一种用于记录系统状态或数据变化的技术&#xff0c;广泛应用于数据库管理、机器学习模型训练、并行计算以及网络安全等领域。以下将详细介绍不同领域中ckpt检查点的定义、功能和应用场景。 数据库中的ckpt检查点…

Redis的公共操作命令

目录 1.Key操作命令1.1 keys *1.2 exists <key]>1.3 type <key>1.4 del <key>1.5 unlink <key>1.6 ttl <key>1.7 expire <key> <秒数>1.8 move <key> <index> 2.库操作命令2.1 select <index>2.2 dbsize2.3 flush…

【LLM】使用MySQL MCP Server让大模型轻松操作本地数据库

随着MCP协议&#xff08;Model Context Protocol&#xff09;的出现&#xff0c;使得 LLM 应用与外部数据源和工具之间的无缝集成成为可能&#xff0c;本章就介绍如何通过MCP Server让LLM能够直接与本地的MySQL数据库进行交互&#xff0c;例如新增、修改、删除数据&#xff0c;…

【C++】从零实现Json-Rpc框架(2)

目录 JsonCpp库 1.1- Json数据格式 1.2 - JsonCpp介绍 • 序列化接口 • 反序列化接口 1.3 - Json序列化实践 JsonCpp使用 Muduo库 2.1 - Muduo库是什么 2.2 - Muduo库常见接口介绍 TcpServer类基础介绍 EventLoop类基础介绍 TcpConnection类基础介绍 TcpClient…

语文常识推翻百年“R完备、封闭”论

​语文常识推翻百年“R完备、封闭”论 黄小宁 李四光&#xff1a;迷信权威等于扼杀智慧。语文常识表明从西方传进来的数学存在重大错误&#xff1a;将无穷多各异数轴误为同一轴。 复平面z各点z的对应点zk的全体是zk平面。z面平移变换为zk&#xff08;k是非1正实常数&#xf…