Unix网络编程5种IO模型

2023-11-14

IO模型

用一幅图表示所支持的I/O模型

I/O模型

纵向维度是“阻塞(Blocking)”、“非阻塞(Non-blocking)”;横向维度是“同步”、“异步”。总结起来是四种模型 同步阻塞、同步非阻塞;异步阻塞、异步非阻塞 。《Unix网络编程》中划分出了“第五种”模型——“信号驱动式IO”其实属于异步阻塞类型,这种模型的通知方式有多种多样后面展开说明。

同步/异步、阻塞/非阻塞

从内核角度看I/O操作分为两步:用户层API调用;内核层完成系统调用(发起I/O请求)。所以“异步/同步”的是指API调用;“阻塞/非阻塞”是指内核完成I/O调用的模式。用一幅图表示更加明显

同步/异步、阻塞/非阻塞

同步是指函数完成之前会一直等待; 阻塞 是指系统调用的时候进程会被设置为Sleep状态直到等待的事件发生(比如有新的数据)。明白这一点之后再看这五种模型相信就会清晰很多,我们挨个分析:

同步阻塞

这种模型最为常见,用户空间调用API( read 、 write )会转化成一个I/O请求,一直等到I/O请求完成API调用才会完成。这意味着: 在API调用期间用户程序是同步的的;这个API调用会导致系统以阻塞的模式执行I/O,如果此时没有数据则一直“等待”(放弃CPU主动挂起——Sleep状态) (注意,对于硬盘来说是不会出现阻塞的,无论是什么时候读它总是有数据。常见的阻塞设备是终端、网卡之类的)。

同步阻塞

以 read 为例子,它由三个参数组成,第一个函数是文件描述符;第二个是 应用缓冲 ;第三个参数是需要读取的字节数。经过系统调用会以阻塞模式执行I/O,I/O模块读取数据后会放入到PageCache中;最后一步是把数据从PageCache复制到 应用缓冲 。如果I/O请求无法得到满足——没有数据,则主动让出CPU直到 有数据 (注意,即便系统调用让出CPU也未必真的就让出。read函数是同步的,所以CPU还是会被用户空间代码占用)。

同步非阻塞

这种模式通过调用 read 、 write 的时候指定 O_NONBLOCK 参数。和“同步阻塞”模式的区别在于系统调用的时候它是以非阻塞的方式执行,无论是否有数据都会立即返回。 以 read 为例,如果成功读取到数据它返回读取到的字节数;如果此时没有数据则返回-1,同时设置errno为EAGAIN(或者EWOULDBLOCK,二者相同)。所以这种模式下我们一般会用一个“循环”不停的尝试读取数据,处理数据。

异步阻塞

同步模型最主要的问题是占用CPU, 阻塞I/O会主动让出CPU但是用户空间的系统调用还是不会返回依然耗费CPU;非阻塞I/O必须不停的“轮询”不断尝试读取数据(会耗费更多CPU更加低效)。如果仔细分析同步模型霸占CPU的原因不难得出结论——都是在等待数据到来。异步模式正是意识到这一点所以把I/O读取细化为 订阅I/O事件,实际I/O读写,在“订阅I/O事件”事件部分会主动让出CPU直到事件发生 。异步模式下的I/O函数和同步模式下的I/O函数是一样的(都是 read 、 write )唯一的区别是异步模式 “读”必有数据 而同步模式则未必。 常见的异步阻塞函数包括 select , poll , epoll ,这些函数的用法需要花费相当大的篇幅介绍而这篇文章我想集中精力介绍“I/O模型”。以 select 为例我们看一下大致原理

异步阻塞

异步模式下我们的API调用分为两步,第一步是通过 select 订阅读写事件 这个函数会主动让出CPU直到事件发生(设置为Sleep状态,等待事件发生) ;select一旦返回就证明可以开始读了所以第二部是通过 read 读取数据( “读”必有数据 )。

异步阻塞模型之信号驱动

“完美主义者”看了上面的 select 之后会有点不爽——我还要“等待”读写事件(即便 select 会主动让出CPU),能不能有读写事件的时候主动通知我啊?。借助“信号”机制我们可以实现这个,但是这并不完美而且有点弄巧成拙的意思。 具体用法:通过 fcntl 函数设置一个 F_GETFL|O_ASYNC ( 曾经信号驱动I/O也叫“异步I/O”所以才有 O_ASYNC 的说法),当有I/O时间的时候操作系统会触发 SIGIO 信号。在程序里只需要绑定 SIGIO 信号的处理函数就可以了。但是这里有个问题—— 信号处理函数由哪个进程执行呢? ,答案是:“属主”进程。操作系统只负责参数信号而实际的信号处理函数必须由用户空间的进程实现。(这就是设置 F_SETOWN 为当前进程PID的原因) 信号驱动性能要比 select 、 poll 高(避免文件描述符的复制)但是缺点是致命的——*Linux中信号队列是有限制的如果操过这个数字问题就完全无法读取数据。

异步非阻塞

这种模型是最“省事”的模型,系统调用完成之后就只要坐等数据就可以了。是不是特别爽?其实不然,问题出在实现上。Linux上的AIO两个实现版本,POSIX的实现最烂(蓝色巨人的锅)性能很差而且是基于“事件驱动”还会出现“信号队列不足”的问题(所以它就偷偷的创建线程,导致线程也不可控了);一个是Linux自己实现的(redhat贡献)Native AIO。Native AIO主要涉及到的两个函数io_submit 设置需要I/O动作(读、写,数据大小,应用缓冲区等); io_getevents 等待I/O动作完成。没错,即便你的整个I/O行为是非阻塞的还是需要有一个办法知道数据是否读取/写入成功。

异步非阻塞

注意图中,内核不再为I/O分配PageCache,所有的数据必须有用户自己读取到应用缓冲中维护。所以AIO一定是和“直接I/O”配合使用。 AIO针对网卡设备的意义不大,首先它的实现本质上和epoll差不多;其次它在Linux中的作用更多的是用于磁盘I/O(异步非阻塞可以不用多线程就造成大量的I/O请求便于I/O模块“合并”优化会提高整体I/O的吞吐率——而且对CPU开销比较少)。 在Nginx中用了一个技巧,可以实现AIO和epoll联动,AIO读取到数据后触发epoll发送数据。(这个特性是非常尴尬的,如果是磁盘文件完全可以用sendfile搞定)。

Direct I/O和Buffered I/O

Linux在进行I/O操作的时候会先把数据放到PageCache中然后通过“内存映射”的方式返回给应用程序,这样做的好处是可以预读数据也能在多个进程读取相同数据的时候起到Cache的作用。应用程序不能直接使用PageCache中的数据,通常是复制到一块“用户空间”的内存中再使用。
● Direct I/O是指数据不落在PageCache,直接从设备读取到数据后放到用户空间中
● Buffered I/O是指数据竞购PageCache
同步I/O只能使用Buffered I/O;异步阻塞I/O可以Buffered I/O也可以使用Direct I/O;异步非阻塞I/O只能使用Direct I/O

Zero Copy

考虑从磁盘读取文件经过网卡发送出去,会有 四次内存复制 :1. DMA会复制磁盘数据到内核空间,2. 应用程序复制内核空间的数据到用户空间;3. 应用程序用户空间的数据复制到Socket缓冲(内核空间);4. 协议栈把数据复制到网卡的中发送。 简单来说Zero Copy就是 节省这个过程中的内存复制次数 。有几种做法:
● Direct I/O直接把磁盘数据复制到内核空间; 但是Direct I/O没有办法直接把数据放到网卡中——必须要经过协议栈 。所以可以节省一次内存复制;
● sendfile,磁盘数据通过DMA读取到内核空间后直接交给TCP/IP协议栈;真正的不需要内存复制;
除此之外还可以利用 splice 、 mmap 做一些优化,根据不同的设备需要采用不同的方式此处不再展开。


博客学习转载,非原创。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Unix网络编程5种IO模型 的相关文章

  • 如何使用 shell 脚本 ftp 多个文件

    我正在尝试使用 shell 脚本将多个文件从一台计算机传输到另一台计算机 下面是我的脚本 ftp nv lt
  • 使用 AWK 或 SED 以及以下标签以逗号分隔

    使用 AWK 或 SED 以及下面使用的标签以逗号分隔 BEGIN AccountID BEGIN CallerID BEGIN Billed Account Attributes 1111111 1111111 1111111 END B
  • 更新写入 java 文本文件的对象

    将 Java 对象或列表写入文本文件是可以的 但我想知道如何更新或重写以前写入的对象而不再次写入对象 例如 假设有一个 java util List 有一组对象 然后将该列表写入文本文件 然后稍后该文件将被再次读取并从列表中获取所有对象 然
  • 在 Unix 中,如何删除当前目录及其下面的所有内容?

    我知道这会删除子目录及其下面的所有内容 rm rf
  • kqueue() 和 O_NONBLOCK

    如果您使用 kqueue 是否应该在文件描述符上设置 O NONBLOCK 换句话说 无论 O NONBLOCK 是否设置 kqueue 是否保证就绪文件描述符上的下一个 I O 操作不会阻塞 如果您使用 kqueue 是否应该在文件描述符
  • 在 nohup 中使用别名

    为什么以下不起作用 alias sayHello bin echo Hello world sayHello Hello world nohup sayHello nohup appending output to nohup out no
  • 在Python中修改大型文本文件最后一行的最有效方法

    我需要更新几个超过 2GB 的文件的最后一行 这些文件由无法读取的文本行组成readlines 目前 它可以通过逐行循环来正常工作 但是 我想知道是否有任何编译库可以更有效地实现这一点 谢谢 目前的方法 myfile open large
  • 常规文件读取可以从非阻塞 IO 中受益吗?

    对我来说似乎不是 我找到了一个支持我的观点的链接 http www remlab net op nonblock shtml 你怎么认为 您发布的链接内容是正确的 以非阻塞模式打开的常规文件套接字将始终 准备好 读取 当您实际尝试读取它时
  • Unix cURL POST 使用文件中的内容到特定变量

    我已经搜索过这个答案 但没有找到任何有效或完全符合我的问题的答案 使用 Unix cURL 我需要将键 值对发布到服务器 密钥将是 MACs 换行符分隔的 MAC 地址文件的内容将是此 POST 的 VALUE 我试过了 curl d fi
  • 模拟用户输入以使用不同参数多次调用脚本

    我必须使用提供的脚本 该脚本在脚本运行时接受用户输入而不是参数 我无法解决这个问题 脚本的一个例子是 bin bash echo param one read one doSomething echo param two read two
  • 将用户添加到组但运行“id”时未反映

    R 创建了一个名为 Staff 的组 我希望能够在不以 sudo 身份启动 R 的情况下更新软件包 所以我使用以下方法将自己添加到员工中 sudo usermod G adm dialout cdrom plugdev lpadmin ad
  • 命令行参数中的“-”(破折号)有什么魔力?

    例子 创建 ISO 映像并将其直接刻录到 CD mkisofs V Photos r home vivek photos cdrecord v dev dev dvdrw 更改到上一个目录 cd 侦听端口 12345 并解压发送到该端口的数
  • 寻找下一个开放端口

    有没有什么办法 使用基本的 Unix 命令 找到下一个未使用的端口号 从端口 4444 开始向上 我通过 ssh 通过 openssh 进入 Windows XP 计算机 运行 Cygwin 工具并使用 bash shell 谢谢 戴夫 尝
  • 操作系统崩溃的常见原因[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我有兴趣了解 操作系统崩溃 不限于Windows崩溃 最常见的技术原因 从操作系统编程的角度 有哪些 我正在寻找一个不像 打开太多应用
  • Windows 中“nice”的等效词

    Windows 中是否有相当于 Unix 命令的命令 nice 我正在专门寻找可以在命令行中使用的东西 并且not任务管理器中的 设置优先级 菜单 我在谷歌上寻找这个的尝试被那些想不出更好形容词的人挫败了 如果您想在启动进程时设置优先级 您
  • 如何用 C 语言练习 Unix 编程?

    经过五年的专业 Java 以及较小程度上的 Python 编程并慢慢感觉到我的计算机科学教育逐渐消失 我决定要拓宽我的视野 对世界的一般用处 并做一些 对我来说 感觉更重要的事情就像我真的对机器有影响一样 我选择学习 C 和 Unix 编程
  • 如何在gcc中打印UINT64_t?

    为什么这段代码不起作用 include
  • 我的 unix 脚本出了什么问题

    bin bash while echo n Player s name read name name ZZZ do searchresult grep name playername if searchresult 0 then echo
  • 无法删除临时文件夹(有时)

    当我启动应用程序时 我创建一个临时文件夹 public static File createTempDir String name throws IOException File tempDir File createTempFile na
  • 对 os.listdir 文件进行排序 Python

    如果已下载数年的数据 这些数据存储在具有以下命名约定的文件中 year day dat 例如 名为 2014 1 dat 的文件包含 2014 年 1 月 1 日的数据 我需要按天排序读取这些数据文件 2014 1 dat 2014 2 d

随机推荐

  • JAVA环境配置及如何解决win10重启后环境配置失效的问题

    一 配置JAVA环境 1 JDK的下载与安装 进入官网下载对应版本 注意 安装路径最好不要包含中文或空格等特殊字符 最好使用纯英文目录 2 配置环境变量的步骤 1 按下win E键 找到此电脑 右键选择属性 点击高级系统设置 在高级中点击右
  • 完美解决python pip安装超时问题

    书接上文 安装python过程中 国外镜像太慢了 使用国内源 1 设置超时时间 pip default timeout 100 install Pillow 2 不使用缓存 pip no cache dir install Pillow 2
  • (pytorch进阶之路)DDPM扩散概率模型

    文章目录 概述 前置知识 diffusion图示 扩散过程 逆扩散过程 后验的扩散条件概率 似然函数 算法 代码实现 概述 扩散概率模型 deep unsupervised learning using nonequilibrium the
  • 快到慢的跨时钟域处理

    如果你写过异步FIFO 格雷码的传输就已经包括慢时钟域到快时钟域以及快时钟域到慢时钟域的处理方法了 自己之前的异步FIFO由于理解没到位 快时钟域到慢时钟域的处理也是打拍 原因是快时钟域的时钟频率不够快 所以debug没问题 步入正题 快到
  • echarts中的data是对象无法使用

    问题描述 前端通常从后端拿到的数据都是数组中有多个对象 而echarts中的data必须是一个数组 形如 Mon Tue Wed Thu Fri Sat Sun 所以需要对后端返回的数据进行处理之后才可以使用 处理办法 this recor
  • UE4、5不让贴花影响Mesh

    如果不想让贴花影响某些在移动中的物体穿过它也会受到影响 例如人 车之类的 可以把mesh里的Receives Decals选项去掉
  • 剑指Offer第十九题:顺时针打印矩阵

    题目描述 输入一个矩阵 按照从外向里以顺时针的顺序依次打印出每一个数字 例如 如果输入如下4 X 4矩阵 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1 2 3 4 8 12 16 15 1
  • 微信JSSDK动态引入

    由于项目中使用到了微信和企业微信的js方法 所以引入了微信jsSDK1 2 0版本的文件 但是后期需要使用微信开放标签 所以微信端的js文件版本需要升级到1 6 0 但是企业微信目前最高支持1 2 0版本 所以添加了如下方法 在vue项目的
  • JS继承

    最近在面试中被问到js的继承 当时回答的不太好 所以今天特别总结一下 我们先来看一个基于原型链的继承例子 父类 function Person 子类 function Student 继承 Student prototype new Per
  • Github上关于iOS的各种开源项目集合(强烈建议大家收藏,查看,总有一款你需要)

    转载地址 http blog csdn net hbblzjy article details 52083919 下拉刷新 EGOTableViewPullRefresh 最早的下拉刷新控件 SVPullToRefresh 下拉刷新控件 M
  • 分数混合运算简便方法_《分数乘法》中的简便运算指导

    六年级上册 分数乘法 是在学生学习了运用乘法运算定律和整数 小数乘法简便计算以及分数加 减 乘法计算的基础上进行教学的 这一节课学生进一步理解整数乘法的运算定律不仅适用于小数 整数乘法 而且也适用于分数乘法 使计算更简便 是让学生在经历探索
  • java.lang.NoClassDefFoundError: org/springframework/context/event/EventListenerFactory

    集成rabbitmq的时候报错 只要看到NoClassDefFoundError基本都是jira包冲突了 1 首先找到相关类 spring tx有2个版本 2 利用IDEAL插件mavenhelper看一下 把高版本排除掉就可以了 java
  • 统计学习系列之参数估计

    参数估计 1 什么是参数估计 简单来说是 参数估计是指使用样本统计量估计总体的参数的 百度百科的解释如下 参数估计 parameter estimation 统计推断的一种 根据从总体中抽取的随机样本来估计总体分布中未知参数的过程 从估计形
  • 使用cmake配置aws-cpp-sdk以及在cmake项目中使用

    目录 环境 配置cmake 编译aws cpp sdk 1 使用git bash下载aws cpp sdk项目到指定目录 2 使用clion打开项目 3 设置cmake编译选项 4 BUILD INSTALL 项目 在cmake项目中使用a
  • 【服务器】ASUS ESC4000-E11 安装系统

    ASUS ESC4000 E11说明书 没找到 ASUS ESC4000 E11的说明书 下面是ESC4000A E11的说明书 https manualzz com doc 65032674 asus esc4000a e11 serve
  • E: 仓库 “http://mirrors.aliyun.com/ubuntu eoan Release” 没有 Release 文件 —— 解决方案

    Ubuntu 20 04 更新的时候 遇到如下问题 可以通过修改源 来进行修复 1 登录如下网址 LUG s repo file generator 2 选择对应的 Ubuntu 版本 这里我是 Ubuntu 20 04 点击 Downlo
  • vue实现excel文件上传并解析数据

    vue实现excel文件上传并解析数据 1 安装xlsx并引入 2 页面使用上传组件 3 补充完善 不使用action实现自定义上传 1 安装xlsx并引入 npm install xlsx 0 17 0 save import XLSX
  • 如何将已加好的脚注或尾注转换成中括号“[]”格式

    下面让流程更加清晰 1 正常插入所有尾注 2 点击word文档上方 编辑 选项按钮 3 点击 查找 4 点击 替换 选项 5 在 查找内容 框中输入 e 这是尾注的象征符 在 替换为 框中输入 6 点击下方的 全部替换
  • 9.调试技巧与调试工具

    9
  • Unix网络编程5种IO模型

    IO模型 用一幅图表示所支持的I O模型 纵向维度是 阻塞 Blocking 非阻塞 Non blocking 横向维度是 同步 异步 总结起来是四种模型 同步阻塞 同步非阻塞 异步阻塞 异步非阻塞 Unix网络编程 中划分出了 第五种 模