原文出处: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
  1. 将要读的文件描述符注册到多路复用器中(如 select(), poll(), epoll() )。
  2. 调用进程阻塞在多路复用器上,其等待已注册的任意一个 I/O 事件发生。
  3. 一旦事件发生(即文件描述符变为可读, 多路复用器返回), 将调用用户提供的事件处理函数( event handler)进行实际的 I/O 复制操作。
  4. 读取完成后,事件处理函数可以对数据进行进一步的处理。
  • Proactor 模式. 基于异步 I/O
  1. 用户进程启动一个异步读文件操作, 同时将该操作注册到多路复用器上。多路复用器并不关心文件是否可读,而只关心该异步操作是否完成。
  2. 整个异步读文件操作由内核完成,用户进程不需要关心。多路复用器阻塞以等待某个完成通知的到达。
  3. 当内核完成了整个读文件操作 – 即数据已经准备好,并已由内核复制到了用户事先提供的缓冲区后 – 通知多路复用器读操作已完成。
  4. 多路复用器再调用相应的事件处理函数( 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.

2. Comparing Two High-Performance I/O Design Pattern

PUBLISHED: June 8, 2013
FILED UNDER: Linux计算机理论

TAGS: blocking : Event-driven : I/O : linux : non-blocking : Unix

浅谈 Unix I/O 模型的更多相关文章

  1. 浅谈Unix I/O模型

    关于I/O模型的文章比较多,参考多篇后理解上仍然不太满意,终需自己整理一次,也是编写高吞吐量高性能网络接口模块的基础.这里所说的主要针对网络I/O,近几年面对越来越大的用户请求量,如何优化这些步骤直接 ...

  2. 浅谈 unix, linux, ios, android 区别和联系

    浅谈 unix, linux, ios, android 区别和联系 网上的答案并不是很好,便从网上整理的相对专业的问答,本人很菜,大佬勿喷 UNIX 和 Linux   UNIX 操作系统(尤尼斯) ...

  3. 浅谈Java的内存模型以及交互

    本文的内存模型只写虚拟机内存模型,物理机的不予描述. Java内存模型 在Java中,虚拟机将运行时区域分成6中,如下图:              程序计数器:用来记录当前线程执行到哪一步操作.在多 ...

  4. 转:浅谈UNIX下Apache的MPM及httpd.conf配置文件中相关参数配置

    为什么要并发处理 以Apache为代表的web服务器中,如果不支持并发,则在一个客户端连接的时候,如果该客户端的任务没有处理完,其他连接的客户端将会一直处于等待状态,这事不可想象的,好像没有为什么要不 ...

  5. 浅谈 css3 box盒子模型以及box-flex的使用

    display:box;box-flex是css3新添加的盒子模型属性,它的出现可以解决我们通过N多结构.css实现的布局方式.经典的一个布局应用就是布局的垂直等高.水平均分.按比例划分.   一.使 ...

  6. 浅谈异步IO各模型优缺点

    本文只讨论OverLapped I/O的三种异步模型及完成端口,像select.SWASelect不作讨论,讨论顺序从劣到优,方便于循序渐进地对比,更容易区分各模型之间的差别. 1. OverLapp ...

  7. 浅谈OSI七层模型及ICP/IP四层模型

    1.OSI七层模型的概念 在网络历史的早期,国际标准化组织(ISO)和国际电报电话咨询委员会(CCITT)共同出版了开放系统互联的七层参考模型. 一台计算机操作系统中的网络过程包括从应用请求(在协议栈 ...

  8. 【转】从Mac/OS和iOS开放源码浅谈UNIX家谱

    阅读数:1245 苹果公司在各类开源项目中长期贡献着自己的力量,但其UNIX系统技术一直都属于闭源阵营(这一点可以从NUX OS阵营和家谱图中得到答案).然而,以封闭闻名的苹果公司,2017年国庆期间 ...

  9. 【转】浅谈Node.js单线程模型

    Node.js采用 事件驱动 和 异步I/O 的方式,实现了一个单线程.高并发的运行时环境,而单线程就意味着同一时间只能做一件事,那么Node.js如何利用单线程来实现高并发和异步I/O?本文将围绕这 ...

随机推荐

  1. 软件工程课堂练习&课下作业

    题目:返回一个整数数组中最大子数组的和.一.设计思路按顺序定义子数组的和,如果为负,则选下一位数为子数组的和,反之则两个相加为子数组的和.二.源代码 package test;import java. ...

  2. 9.DataGrid数据表格

    后台获取数据并将其转换为json数组格式: 前台获取数据并显示在数据表格中:

  3. Scala_控制结构

    控制结构 if条件表达式  val x = 6  if (x>0){    println("This is a positive number") }else if(x== ...

  4. spring boot thymeleaf

    引入支持 <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spr ...

  5. What mind mapping software applications do you recommend.

    选自 https://members.iqmatrix.co/creative-mind-maps I personally use Mindjet MindManager. This is one ...

  6. iOS处理视图上同时添加单击与双击手势的冲突问题

    _bgView.userInteractionEnabled = YES; //在cell上添加 bgView,给bgView添加两个手势检测方法 UITapGestureRecognizer *do ...

  7. linecache

    linecache是专门支持读取大文件,而且支持行式读取的函数库. linecache 预先把文件读入缓存起来,后面如果你访问该文件的话就不再从硬盘读取.对于大文件的读取效率还不错 Help on m ...

  8. C# 移动及限制移动距离

    public class PlaneMove : MonoBehaviour { //h:水平方向的控制,v:前后方向的控制 float h,v; //Speed:飞机的飞行速度 public flo ...

  9. 【CF995F】 Cowmpany Cowmpensation

    CF995F Cowmpany Cowmpensation Solution 这道题目可以看出我的代码能力是有多渣(代码能力严重退化) 我们先考虑dp,很容易写出方程: 设\(f_{i,j}\)表示以 ...

  10. git如何忽略已经加入版本控制的文件

    git移除已经追踪的文件 有时候新增一个文件,会自动追加到git的版本控制当中,但是又不想提交到仓库.可以按照下面的步骤: git status 查看管理状态: ml-py git:(master) ...