【Linux网络编程】高效I/O--I/O的五种类型
目录
I/O的概念
网络通信的本质
I/O的本质
高效I/O
五种I/O模型
阻塞I/O
非阻塞I/O
信号驱动I/O
多路转接/多路复用I/O
异步I/O
非阻塞I/O的实现
I/O的概念
网络通信的本质
网络通信的本质其实就是I/O
- I:表示input(输入)
- O:表示output(输出)
- 网络通信时,双方主机中的两个进程本质上就是从套接字中拿取或放入数据,它们的本质是I/O
- 网络中的各种协议,本质上就是规定网络通信时是如何I/O的
I/O的本质
我们所谈的I/O,都是站在内存的角度
- 对于输入来说,本质就是数据从外设放到内存里
- 对于输出来说,本质上就是数据从内存到外设里
- 以读文件为例,本质上不就是把磁盘上的文件数据读取到内存中
I/O的本质:I/O = 等 + 拷贝
- 以recv为例,我们调用recv的时候,若内核发送缓冲区中没有数据,那么执行流会进行阻塞等待。 若内核发送缓冲区中有了数据,那么recv会把数据拷贝到用户层
- 其实不管是哪些函数,printf/scanf/read/write....,它们都会进行等待以及拷贝。所以I/O的本质是等+拷贝
- 等:本质上就是等待I/O的条件就绪
高效I/O
I/O = 等 + 拷贝,所以I/O的时间是由等的时间和拷贝的时间决定的,什么叫做高效I/O呢?
- 对于拷贝的时间,我们是无法从软件方面进行优化的,因为拷贝的速度是由硬件架构所决定的
- 对于等待的时间,我们可以从软件方面优化
- 所以我们所说的I/O效率比较低,大多数情况指的是I/O的等待时间较长
所以对于高效的I/O来说,就是尽可能的减少等待的时间, 即单位时间内,等的比重越低,那么I/O的效率越高!
程序员进行编程时,什么叫做高效的代码?
- 减少I/O的比重
- 若无法减少I/O的比重,那么尽可能把I/O进行等待的时间利用起来
五种I/O模型
从理论回归到日常生活,其实我们日常生活中是有与I/O非常相似的例子,例如钓鱼
- I/O = 等 + 拷贝,钓鱼 = 等 + 钓
当我们去钓鱼的时候,首先把鱼钩丢入湖水中,然后一直等待,直到鱼咬钩就把鱼拉上来,放入到水桶中
- 湖水类比OS内部缓冲区
- 水桶类比用户缓冲区
- 鱼类比数据
- 鱼钩/鱼竿类比一个sockfd
接下来让我们从钓鱼的角度理解五种I/O模型
阻塞I/O
阻塞I/O:
- 你把鱼钩和鱼饵放入水中后,完全专注于等待鱼上钩,整个过程中你什么也不做,无法去干其他事情。如果没有鱼上钩,你只能继续等,直到鱼上钩为止。
- 在阻塞I/O模型中,程序在发起I/O请求后,会被阻塞(即停下来)直到I/O完成。程序不能做其他事情,必须等到数据完全准备好才会继续运行。
- 这是最简单也是最直观的I/O模型,最常见于简单的单线程程序中。它的缺点是等待期间资源不能被其他任务使用,效率较低。
非阻塞I/O
非阻塞I/O:
- 你把鱼钩和鱼饵放入水中,但不光等着鱼上钩,还不时检查鱼线有没有拉动。如果没鱼上钩,你就去做其他事情,比如整理钓具或者享受周围的景色,然后过段时间再回来看看鱼线是否有动静。
- 在非阻塞 I/O 模型中,程序发起 I/O 请求后立即返回,即使数据还没有准备好。程序需要不断地主动检查 I/O 是否已经完成(类似于不断回去查看鱼有没有上钩),然后继续执行其他任务。
- 这种模型不会像阻塞 I/O 一样浪费时间等待,但它需要程序频繁检查 I/O 状态,导致程序需要处理大量轮询操作。
信号驱动I/O
信号驱动I/O
- 你把鱼竿放在水里,然后装上一个铃铛,只有当有鱼上钩时,铃铛会响,提醒你上鱼。这时候你再去处理鱼线,而在铃铛没响之前,你可以做任何其他事情,比如读书或者打电话。
- 在信号驱动I/O模型中,程序发出I/O请求并设置好通知机制(类似于安装铃铛)。当I/O就绪时,操作系统通过信号通知程序进行处理。这时程序无需轮询或阻塞,只需等待信号触发。
- 这种模型减少了频繁检查I/O状态的需要,提升了效率,但对信号处理的实现复杂度要求较高。
多路转接/多路复用I/O
多路复用I/O:
- 你在湖边同时设置了多个鱼竿,你在等的过程中不断轮询检测这多个鱼竿,若发现有一个鱼竿鱼上钩了,那么你就立刻去拉他的鱼线,否则一直轮询等待
- 多路复用I/O的核心思想是通过单一的线程或进程来同时管理多个I/O操作。当多个I/O通道中的任何一个变为就绪状态时(类似轮询时发现鱼上钩了),进程可以立即处理相应的I/O操作(钓),而不需要为每个I/O通道创建独立的线程。(只需要一个人不断轮询的方式就可以管理所有的鱼竿,无需创建执行流)
- 多路复用是我们之后话题的重点!
异步I/O
异步I/O:
- 你把鱼竿放在水里,然后雇了一个助手帮你钓鱼。你自己可以完全不管钓鱼的事情,去做别的任务。当助手钓到鱼时,会通知你鱼已经上钩并帮你把鱼钓上来,整个过程你几乎不需要直接参与。
- 这里的助手就相当于操作系统,当你进行异步I/O时,若数据到来,由操作系统自动放入你的用户缓冲区,你无需参与钓鱼的整个过程
- 异步I/O的主要优势在于提高了CPU的利用率。在等待I/O完成的同时,CPU可以处理其他逻辑,避免了单一线程因I/O阻塞而闲置的情况。
同步I/O vs 异步I/O 同步I/O:
- 只要参与了I/O的等+拷贝,我们就可以把它理解为同步I/O,因为I/O的完整概念就是等+拷贝
- 同步I/O分别有:阻塞I/O,非阻塞I/O,信号驱动I/O,多路复用I/O
注意:信号驱动I/O本质上是同步I/O,虽然等的过程是由铃铛驱动的,但具体到钓鱼的动作是由你自己来钓的。
异步I/O:
- 若完全没参与I/O的等+拷贝,直接可以拿到数据,我们称这种I/O为异步I/O
线程同步 vs I/O同步
线程同步: 主要涉及多线程编程中如何管理线程之间的共享资源和数据访问,以防止线程竞争或数据不一致的情况。它通常应用在多线程或多进程环境下,当多个线程需要同时访问或修改共享资源时,必须通过同步机制来确保数据一致性。
I/O同步:涉及与设备(如硬盘、网络、输入/输出设备等)之间的数据传输。I/O同步主要解决的问题是如何处理慢速的I/O操作和程序的执行之间的协调。
线程同步和I/O同步虽然名字都有同步,但完全是两个不同领域的概念
哪种I/O模型最高效?
首先,我们先明确一个概念,由于I/O的本质是等+拷贝,所以I/O高效指的是单位时间内等的比重比较低,则称这种I/O是最高效的!
最高效的I/O模型是多路转接/多路复用
注意:尽管异步I/O我们看起来比较高效,但由于我们明确了I/O高效的定义是单位时间内等的比重比较低,但异步I/O等的比重其实没有降低,只是它把等的时间利用起来了,所以它不是最高效的I/O
阻塞I/O和非阻塞I/O和信号驱动I/O的单位时间内等的比重也没有降低,只是等的方式不同而已,所以它们不是最高效的I/O
为什么多路转接是最高效的I/O
- 多路转接监管的文件描述符是最多的,回到钓鱼例子,假设鱼咬钩的概率是均等的。若我拿了200个鱼竿,其他4个人每人一个鱼竿,那么谁钓上鱼的可能性最大呢?答案显然易见是我的, 因为我钓上鱼的概率是200/204,其他人是1/204
- 而我钓上鱼的概率越大,也就意味着我钓上一只鱼的等待时间是最短的!也就意味着我钓鱼是最高效的。回到I/O模型,多路复用获取一个I/O数据的等待时间也是最短的,多个文件描述符的等待时间是重叠的,所以它是最高效的
五种I/O模型流程图
阻塞I/O:
非阻塞I/O:
信号驱动I/O:
多路复用I/O:
- 多路复用I/O与阻塞I/O流程图较为相似。
- Linux中提供了select系统调用,用于阻塞监管多个文件描述符,若监管的过程中发现有一个文件描述符准备就绪,那么select就会返回,此时可以调用拷贝函数(如read/write/recv/send...)无需等待,直接拷贝
- 多路复用I/O和阻塞I/O最本质的区别是,多路复用I/O一次阻塞监管了多个文件描述符,而阻塞I/O一次阻塞监管一个文件描述符
异步I/O:
非阻塞I/O的实现
阻塞I/O我们一直都在用,如printf/scanf/read/send....,这里不再过多介绍
我们主要实现的I/O模型分别是:
- 非阻塞I/O
- 多路复用I/O
非阻塞I/O的实现方式有很多,这里我们采用一种最通用的方式
- 把文件描述符设置为非阻塞,此后所有的I/O函数在访问这个文件描述符时都是非阻塞的方式访问
设置文件描述符为非阻塞,我们使用的系统调用是fcntl
功能:fcntl允许程序改变打开文件的属性,包括文件锁定、文件状态标志和其他与文件描述符相关的操作。
int fcntl(int fd, int cmd, ... /* arg */ );
- fd:要操作的文件的文件描述符
- cmd:表示如何对fd进行操作
- 头文件:fcntl.h 和 unistd.h
cmd的操作方法有哪些?
F_GETFL
:获取文件状态标志。F_SETFL
:设置文件状态标志。F_GETLK
:获取锁的信息。F_SETLK
:设置锁定。F_SETLKW
:设置锁定(阻塞方式)。
我们修改文件描述符的阻塞/非阻塞状态,主要是两步
- 由于我们只是想新增文件状态,所以我们需要保存一下旧的文件状态,可以使用F_GETFL获取旧的状态,此时的fcntl若获取成功的话返回值就是旧的状态,获取失败返回值是-1
- 设置文件状态,若设置为非阻塞,则第三个参数(cmd之后)填写旧的状态按位或上O_NONBLOCK即可
设置文件描述符为非阻塞的实现:
#include <unistd.h>
#include <fcntl.h>void SetNonBlock(int fd)
{int fl = fcntl(fd,F_GETFL);//保存旧的文件状态if(fl < 0){//获取错误return;}//获取成功fcntl(fd,F_SETFL,fl | O_NONBLOCK);//设置非阻塞
}
非阻塞测试代码:以0号文件描述符(标准输入为例)
- read在读取非阻塞文件描述符时,读取错误和底层I/O条件不就绪的返回值都是-1
- 要区分读取错误和底层I/O条件不就绪,我们只能用错误码的方式进行区分。
- 若底层I/O条件不就绪,错误码被设置为11,宏表示为EAGAIN 或 EWOULDBLOCK
- 若read读取时被信号中断,那么错误码会被设置为EINTR
- 除了上述两种情况以外,就是发生了读取错误
#include <iostream>
#include <unistd.h>
#include <fcntl.h>void SetNonBlock(int fd)
{int fl = fcntl(fd, F_GETFL); // 保存旧的文件状态if (fl < 0){// 获取错误return;}// 获取成功fcntl(fd, F_SETFL, fl | O_NONBLOCK); // 设置非阻塞
}int main()
{while (true){char buffer[1024];SetNonBlock(0); // 设置0号文件描述符为非阻塞ssize_t n = read(0, buffer, sizeof(buffer));if (n > 0){// 读取成功buffer[n] = 0;std::cout << "Echo# " << buffer << std::endl;}else{if (errno == EWOULDBLOCK || errno == EAGAIN){// 缓冲区中无数据std::cout << "数据未就绪" << std::endl;}else if(errno == EINTR){std::cout << "读取被信号中断" << std::endl;}else{// 读取错误std::cerr << "读取错误" << std::endl;break;}}sleep(1);}return 0;
}
运行结果:
相关文章:

【Linux网络编程】高效I/O--I/O的五种类型
目录 I/O的概念 网络通信的本质 I/O的本质 高效I/O 五种I/O模型 阻塞I/O 非阻塞I/O 信号驱动I/O 多路转接/多路复用I/O 异步I/O 非阻塞I/O的实现 I/O的概念 网络通信的本质 网络通信的本质其实就是I/O I:表示input(输入)O:表示ou…...

企业级NoSQL数据库Redis
1.浏览器缓存过期机制 1.1 最后修改时间 last-modified 浏览器缓存机制是优化网页加载速度和减少服务器负载的重要手段。以下是关于浏览器缓存过期机制、Last-Modified 和 ETag 的详细讲解: 一、Last-Modified 头部 定义:Last-Modified 表示服务器上资源…...

Vscode:问题解决办法 及 Tips 总结
Visual Studio Code(简称VSCode)是一个功能强大的开源代码编辑器,广泛用于各种编程语言和开发场景,本博客主要记录在使用 VSCode 进行verilog开发时遇到的问题及解决办法,使用过程中的技巧 文章目录 扩展安装失败调试配…...
二十三种设计模式-装饰器模式
一、定义与核心思想 装饰器模式是一种结构型设计模式,其核心思想是动态地给一个对象添加一些额外的职责。通过这种方式,可以在不改变原有对象结构的基础上,灵活地增加新的功能,使得对象的行为可以得到扩展,同时又保持…...

架构思考与实践:从通用到场景的转变
在当今复杂多变的商业环境中,企业架构的设计与优化成为了一个关键议题。本文通过一系列随笔,探讨了业务架构的价值、从通用架构到场景架构的转变、恰如其分的架构设计以及如何避免盲目低效等问题。通过对多个实际案例的分析,笔者揭示了架构设…...

Spring MVC(一)
RestController RestController 是由 Controller 和 ResponseBody 两个注解构成的。 Spring 启动的时候会扫描所有包含 Controller 或者 RestController 注解的类,创建出对外的接口,这样外界就可以从这里与服务器实现交互,如果没有这个注解…...
vue3使用tsx渲染复杂逻辑的表单
前置 目前的应用场景是:检查项目是树结构,有的项目还需要动态显示对应的子集 项目用的是uniappvue3tsvite生成的app tsx模版 统一渲染入口 <script lang"ts">import uniForms from dcloudio/uni-ui/lib/uni-forms/uni-forms.vueimport…...
python助力WRF自动化运行
对大部分人而言,特别是新用户,WRF模式的安装繁琐且不必要,可以作为后续进阶掌握的技能,本学习跳过繁琐的安装步骤,直接聚焦模式的运行部分,通过短平快的教学,快速掌握模式运行。进一步将python语…...
Windows 下 Postgres 安装 TimescaleDB 插件
Windows 下 Postgres 安装 TimescaleDB 插件 一、准备工作 安装 PostgreSQL:首先确保你已经在 Windows 系统中成功安装了 PostgreSQL 数据库。可以从 PostgreSQL 官方网站下载适合你系统的安装包,并按照安装向导进行安装。安装过程中,记住设…...

【Vim Masterclass 笔记21】S09L39:Vim 设置与 vimrc 文件的用法示例(二)
文章目录 S09L39 Vim Settings and the Vimrc File - Part 21 Vim 的配色方案与 color 命令2 map 命令3 示例:用 map 命令快速生成 HTML 代码片段4 Vim 中的 Leader 键5 用 mkvimrc 命令自动生成配置文件 写在前面 本篇为 Vim 自定义配置的第二部分。当中的每个知识…...
【Docker】Supervisor 实现单容器运行多服务进程
本文内容均来自个人笔记并重新梳理,如有错误欢迎指正! 如果对您有帮助,烦请点赞、关注、转发、订阅专栏! 专栏订阅入口 | 精选文章 | Kubernetes | Docker | Linux | 羊毛资源 | 工具推荐 | 往期精彩文章 【Docker】(全…...
【网络协议】【http】【https】ECDHE-TLS1.2
【网络协议】【http】【https】ECDHE-TLS1.2 ECDHE算法 1.客户端和服务器端事先确定好使用哪种椭圆曲线,和曲线上的基点G,这两个参数都是公开的, 双方各自随机生成一个随机数作为私钥d,并与基点 G相乘得到公钥Q(QdG),…...
(十五)WebGL中gl.texImage2D函数使用详解
在 WebGL 中,gl.texImage2D 是一个非常关键的函数,用于将图像数据上传到 WebGL 上下文中并作为纹理对象的一部分。它允许你将图像、视频、画布等作为纹理源。理解如何使用 gl.texImage2D 是在 WebGL 中处理纹理的核心之一。 文章目录 gl.texImage2D 的基…...
CSS 颜色
所有浏览器都支持的颜色名 所有现代浏览器均支持以下 140 种颜色名称(单击颜色名称或十六进制值,可查看将以该颜色为背景颜色以及不同的文本颜色): 颜色名十六进制颜色值颜色AliceBlue#F0F8FFAntiqueWhite#FAEBD7Aqua#00FFFFAqu…...

C#,入门教程(03)——Visual Studio 2022编写彩色Hello World与动画效果
C#,入门教程(01)—— Visual Studio 2022 免费安装的详细图文与动画教程https://blog.csdn.net/beijinghorn/article/details/123350910 C#,入门教程(02)—— Visual Studio 2022开发环境搭建图文教程https://blog.csdn.net/beijinghorn/article/detail…...

杀死安装 CentOS-7-x86_64-DVD-1908
使用 VMware 安装 CentOS-7-x86_64-DVD-1908 CentOS是 reahat 的 免费版本,有了ubutun ,为什么还要使用 CentOS呢? 在linux 服务器实际开发中,大家都用的CentOS,因为两个原因,一个是免费,第二是…...

55.【5】BUUCTF WEB NCTF2019 sqli
进入靶场 输入admin 123 过滤的这么严格??? 过滤很严格,此时要么爆破,要么扫描 直接扫描,得到robots.txt 访问后又得到hint.txt 继续访问 图片内容如下 $black_list "/limit|by|substr|mid|,|admi…...

LeetCode 题目 2545. 根据第 K 场考试的分数排序
在本篇文章中,我们将探讨如何根据第 K 场考试的分数对学生进行排序。这个问题是 LeetCode 上的一个中等难度问题,涉及到排序算法和自定义比较函数的使用。 问题描述 解题思路 理解问题 首先,我们需要理解问题的核心:根据第 K 场…...
算法随笔_12:最短无序子数组
上一篇: 算法随笔_11: 字符串的排列-CSDN博客 题目描述如下: 给你一个整数数组 nums ,你需要找出一个 连续子数组 ,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。请你找出符合题意的最短子数组,并输出它的长度。…...

计算机毕业设计PySpark+Hadoop+Hive机票预测 飞机票航班数据分析可视化大屏 航班预测系统 机票爬虫 飞机票推荐系统 大数据毕业设计
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...

TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...

家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...

React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...
Mobile ALOHA全身模仿学习
一、题目 Mobile ALOHA:通过低成本全身远程操作学习双手移动操作 传统模仿学习(Imitation Learning)缺点:聚焦与桌面操作,缺乏通用任务所需的移动性和灵活性 本论文优点:(1)在ALOHA…...