浅谈 Unix I/O 模型
原文出处:http://miaoo.in/talk-about-unix-io-model.html
在实际应用中,数据操作通常分为输入和输出,那么以输入为例,在操作系统中,一个数据的输入通常分为以下两个过程:
a. 等待数据准备好.
b. 将准备好的数据从内核拷贝到用户空间.
下面我们将会分别讨论 I/O 模型中的两个大类,即 同步 I/O 与 异步 I/O。
1. 同步 I/O
同步与异步 I/O 的最大不同,就是在在进行数据复制时(即过程 b ),所有的同步 I/O 模型均会发生阻塞。进一步来看,又可以根据在等待数据准备好时( 过程 a )是否发生阻塞,将同步 I/O 分为:
- 阻塞 I/O ( blocking I/O ). 其在过程 a 与 过程 b 中均会阻塞。
- 非阻塞 I/O ( non-blocking I/O ). 过程 a 不阻塞, 过程 b 阻塞。在过程 a 阶段若无数据准备好,则内核立即返回 EWOULDBLOCK 错误 (通过设置 errno),用户进程立即返回因而不会发生阻塞。此时,用户进程可以不断的通过轮询( polling )的方式查询数据是否准备好。一旦数据准备就绪, 进程便会进入阻塞模式(即阻塞于过程 b ),进行数据的拷贝直至完成。
- I/O 复用( I/O multiplexing, event-driven ). I/O 复用有时又被称为 事件驱动 I/O, 它的最大优势在于,我们可以将感兴趣的多个I/O事件(更精确的说,应该是 I/O 所对应的文件描述符)注册到 select/poll/epoll/kqueue 之中某一个系统调用上(很多时候,这些系统调用又被称为多路复用器。假设此时我们选择了 select() )。此后,调用进程会阻塞在 select() 系统调用之上(而不是阻塞在真正的 I/O 系统调用(如 read(), write() 等)上)。select() 会负责监视所有已注册的 I/O 事件,一旦有任意一个事件的数据准备好,那么 select() 会立即返回,此时我们的用户进程便能够进行数据的复制操作(过程 b )。总而言之,I/O 复用的优点就在于可以同时等待多个I/O事件;而缺点是会进行两次系统调用(一次 select(), 一次 read() )。
通过上面的讨论可以清楚的看到,同步 I/O 总会有阻塞的过程,这就是“同步”最本质的特征。
2.异步 I/O
如前文所说,异步 I/O 的最大特点在于在过程 a 和过程 b 中, 用户进程均不阻塞。 用户进程告知内核启动某一 I/O 操作, 并让内核全权代为执行(包括等待数据及拷贝数据至用户空间),此后用户进程可以立即执行其它的任何操作。等到所有 I/O 过程执行完成后, 内核会通知用户程。由此可见,在整个过程中,用户进程均不阻塞。
以上谈到了 Unix 系统当中 I/O 操作的具体执行方式。在此基础之上, 我们介绍两种常见的 I/O 事件处理模型。事件处理模型的意义在于我们从更宏观的角度来看待实际应用中如何来处理 I/O 事件。我们还是以一个读操作( read() )为例, 根据 I/O 操作是基于同步或异步,分别介绍如下两种模型:
- Reactor 模式( event loop ) . 基于同步 I/O
- 将要读的文件描述符注册到多路复用器中(如 select(), poll(), epoll() )。
- 调用进程阻塞在多路复用器上,其等待已注册的任意一个 I/O 事件发生。
- 一旦事件发生(即文件描述符变为可读, 多路复用器返回), 将调用用户提供的事件处理函数( event handler)进行实际的 I/O 复制操作。
- 读取完成后,事件处理函数可以对数据进行进一步的处理。
- Proactor 模式. 基于异步 I/O
- 用户进程启动一个异步读文件操作, 同时将该操作注册到多路复用器上。多路复用器并不关心文件是否可读,而只关心该异步操作是否完成。
- 整个异步读文件操作由内核完成,用户进程不需要关心。多路复用器阻塞以等待某个完成通知的到达。
- 当内核完成了整个读文件操作 – 即数据已经准备好,并已由内核复制到了用户事先提供的缓冲区后 – 通知多路复用器读操作已完成。
- 多路复用器再调用相应的事件处理函数( event handler )处理位于用户缓冲区的数据。
由此我们可以看到 Reactor 与 Proactor 最大的区别在于数据准备好后,是由谁来将这些数据复制到用户空间中的缓存区之中。Reactor 由于调用的是同步 I/O , 所以当多路复用器由于数据准备好而返回之后,将会由用户的事件处理函数自行将数据复制到用户缓冲区中, 而 Proactor 由于调用的是异步 I/O , 因此等待及复制数据均由内核完成,用户进程只需要等待内核的完成通知,此后由事件处理函数对已在缓冲区中的数据进行进一步的处理。
由于 Proactor 模型需要操作系统提供异步 I/O 的支持,要求较高,故基于上面所描述的思想, 我们可以用 Reactor 模型来模拟 Proactor (只需要用户额外提供用户缓冲区来存放读取出的数据)。简单来说,就是由多路复用器来代替用户进程来完成实际的 I/O 复制操作。
在 Reactor 模型的第 3 步中,当某个文件描述符可读使得多路复用器返回之后,多路复用器执行一个非阻塞读操作, 将数据从内核读至用户提供的缓冲区中,此操作完成后通知(调用)对应的事件处理函数,告知其 I/O 操作已完成。 这样一来,我们就可以在各种系统中(无论支持异步 I/O 与否)均对外提供统一的 Proactor 模型接口,而对用户隐藏其后的具体实现细节。
参考资料
1. < Unix network programming >, Chapter 6.
TAGS: blocking : Event-driven : I/O : linux : non-blocking : Unix
浅谈 Unix I/O 模型的更多相关文章
- 浅谈Unix I/O模型
关于I/O模型的文章比较多,参考多篇后理解上仍然不太满意,终需自己整理一次,也是编写高吞吐量高性能网络接口模块的基础.这里所说的主要针对网络I/O,近几年面对越来越大的用户请求量,如何优化这些步骤直接 ...
- 浅谈 unix, linux, ios, android 区别和联系
浅谈 unix, linux, ios, android 区别和联系 网上的答案并不是很好,便从网上整理的相对专业的问答,本人很菜,大佬勿喷 UNIX 和 Linux UNIX 操作系统(尤尼斯) ...
- 浅谈Java的内存模型以及交互
本文的内存模型只写虚拟机内存模型,物理机的不予描述. Java内存模型 在Java中,虚拟机将运行时区域分成6中,如下图: 程序计数器:用来记录当前线程执行到哪一步操作.在多 ...
- 转:浅谈UNIX下Apache的MPM及httpd.conf配置文件中相关参数配置
为什么要并发处理 以Apache为代表的web服务器中,如果不支持并发,则在一个客户端连接的时候,如果该客户端的任务没有处理完,其他连接的客户端将会一直处于等待状态,这事不可想象的,好像没有为什么要不 ...
- 浅谈 css3 box盒子模型以及box-flex的使用
display:box;box-flex是css3新添加的盒子模型属性,它的出现可以解决我们通过N多结构.css实现的布局方式.经典的一个布局应用就是布局的垂直等高.水平均分.按比例划分. 一.使 ...
- 浅谈异步IO各模型优缺点
本文只讨论OverLapped I/O的三种异步模型及完成端口,像select.SWASelect不作讨论,讨论顺序从劣到优,方便于循序渐进地对比,更容易区分各模型之间的差别. 1. OverLapp ...
- 浅谈OSI七层模型及ICP/IP四层模型
1.OSI七层模型的概念 在网络历史的早期,国际标准化组织(ISO)和国际电报电话咨询委员会(CCITT)共同出版了开放系统互联的七层参考模型. 一台计算机操作系统中的网络过程包括从应用请求(在协议栈 ...
- 【转】从Mac/OS和iOS开放源码浅谈UNIX家谱
阅读数:1245 苹果公司在各类开源项目中长期贡献着自己的力量,但其UNIX系统技术一直都属于闭源阵营(这一点可以从NUX OS阵营和家谱图中得到答案).然而,以封闭闻名的苹果公司,2017年国庆期间 ...
- 【转】浅谈Node.js单线程模型
Node.js采用 事件驱动 和 异步I/O 的方式,实现了一个单线程.高并发的运行时环境,而单线程就意味着同一时间只能做一件事,那么Node.js如何利用单线程来实现高并发和异步I/O?本文将围绕这 ...
随机推荐
- DataStage 的优化原则
DataStage Job优化指导原则之一:算法的优化. 任何程序的优化,第一点首先都是算法的优化.当然这一点并不仅仅局限于计算机程序的优化,实际生活中也处处可以体现这一点.条条大路通罗 ...
- NameNode工作机制
NameNode工作机制
- JVM虚拟机-类加载器子系统
转自博客:http://www.cnblogs.com/muffe/p/3541189.html 还有一些自己补充的知识点 一.类加载器基本概念 顾名思义,类加载器(class loader)用来 ...
- Maven的插件管理
<pluginManagement> 这个元素和<dependencyManagement>相类似,它是用来进行插件管理的. 在我们项目开发的过程中,也会频繁的引入插件,所以解 ...
- iOS笔记之UIKit_UITextField
- (void)viewDidLoad { [super viewDidLoad]; //建立在你已经遵守了<协议UITextFieldDelegate> self.numTF.deleg ...
- Cerebro_变量名搜索插件
Cerebro 安装 兼容环境:Windows, MacOS, Linux 插件依赖于 Cerebro,下载地址: https://github.com/KELiON/cerebro/releases ...
- JSON知识介绍
JSON资料整理 目录 1.什么是json 2.json语法规则 3.json基础结构 4.json基础示例 5.JSON和XML比较 6. .NET操作JSON 原始方式 通用方式 内置方式 契 ...
- C# 一些代码小结--UI操作
C# 一些代码小结--UI操作 使用控件名调用控件 object obj = this.GetType().GetField("控件名", System.Reflection.Bi ...
- 数据导出之winfrom导出word(一)
我们常会用winfrom程序开发小工具,使用dataGridView控件展示数据.同时,我们也会有将这些数据导出的需求. 本篇文章介绍了开发过程中遇到的问题. 一.引用组件 首先,需要在窗体程序中引用 ...
- python中硬要写抽象类和抽象方法
由于python没有抽象类.接口的概念,所以要实现这种功能得abc.py这个类库,具体方式如下: # coding: utf-8import abc #抽象类class StudentBase(obj ...