版权声明:本文为博主原创文章,转载请注明出处: leehao.me https://blog.csdn.net/lihao21/article/details/51620374

一、简述

UNIX系统将所有的外部设备都看作一个文件来看待,所有打开的文件都通过文件描述符来引用。文件描述符是一个非负整数,它指向内核中的一个结构体。当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符。而对于一个socket的读写也会有相应的文件描述符,称为socketfd(socket描述符)。
在UNIX系统中,I/O输入操作(例如标准输入或者套接字的输入)通常包含以下两个不同的阶段:

  • 等待数据准备好
  • 从内核向进程复制数据

例如对于套接字的输入,第一步是等待数据从网络中到达,当所等待的数据到达时,数据被复制到内核中的缓冲区。第二步则是把数据从内核缓冲区复制到应用进程的缓冲区。
根据在这两个不同阶段处理的不同,可以将I/O模型划分为以下五种类型:

  • 阻塞式I/O模型
  • 非阻塞式I/O模型
  • I/O复用
  • 信号驱动式I/O
  • 异步I/O

二、I/O模型

为简单起见,我们以UDP套接字中的recvfrom函数作为系统调用来说明I/O模型。recvfrom函数类似于标准的read函数,它的作用是从指定的套接字中读取数据报。recvfrom会从应用进程空间运行切换到内核空间中运行,一段时间后会再切换回来。有关recvfrom函数的介绍,可以参考本文的参考资料3。

2.1 阻塞式I/O模型

阻塞式I/O模型可以说是最简单的I/O模型。


图1:阻塞式I/O模型

图1是阻塞式I/O模型的示意图,阅读此图须注意箭头的方向,沿着剪头方向顺时针阅读。
图1中,应用进程调用recvfrom,然后切换到内核空间中运行,直到数据报到达且被复制到应用进程缓冲区中才返回。我们说进程从调用recvfrom开始到它返回的整段时间内是被阻塞的。recvfrom成功返回后,应用进程开始处理数据报。

2.2 非阻塞式I/O模型

进程把一个套接字设置为非阻塞是指,在等待I/O数据时,进程并不阻塞,如果数据还没准备好,则直接返回一个错误。


图2:非阻塞式I/O模型

图2是非阻塞I/O模型的示图。
在前两次调用recvfrom时由于数据报没准备好,因此内核马上返回一个系统调用错误。第3次调用recvfrom时,数据报已准备好,数据报被复制到应用进程的缓冲区,接着recvfrom成功返回。
当一个应用进程像这样不断对一个非阻塞描述符循环调用recvfrom时,我们称之为轮询。应用进程会持续轮询内核,以确定某个操作是否就绪。轮询操作会消耗大量的CPU时间。

2.3 I/O复用模型

我们常用的select和poll函数使用了I/O复用模型。我们以select为例说明I/O复用模型的特点。


图3:I/O复用模型

图3是I/O复用模型的示意图。
当我们调用select函数时,将会阻塞于此函数,等待数据报套接字变为可读。当等待的多个套接字中的其中一个或者多个变得可读时,我们调用recvfrom把数据报复制到应用进程缓冲区。
比较图3与图1,I/O复用模型好像没什么优势,而且应用进程为了获取数据报,还得增加了一个额外的select系统调用。不过I/O复用模型的优势在于可以同时等待多个(而不只是一个)套接字描述符就绪。

2.4 信号驱动式I/O模型

信号驱动I/O模型用得比较少,图4是该模型的示意图。


图4:信号驱动式I/O模型

为了使用该I/O模型,需要开启套接字的信号驱动I/O功能,并通过sigaction系统调用安装一个信号处理函数。sigaction函数立即返回,我们的进程继续工作,即进程没有被阻塞。当数据报准备好时,内核会为该进程产生一个SIGIO信号,这样我们可以在信号处理函数中调用recvfrom读取数据报,也可以在主循环中读取数据报。无论如何处理SIGIO信号,这种模型的优势在于等待数据报到达期间不被阻塞。

2.5 异步I/O模型

异步I/O模型的工作机制是,启动某个操作,并让内核在整个操作(包括等待数据和将数据从内核复制到用户空间)完成后通知应用进程。


图5:异步I/O模型

图5是异步I/O模型的示意图。我们调用aio_read函数,告诉内核,当整个I/O操作完成后通知我们。该系统调用立即返回,而在等待I/O完成期间,应用进程不会被阻塞。当I/O完成(包括数据从内样复制到用户进程)后,内核会产生一个信号通知应用进程,应用进程对数据报进行处理。
异步I/O模型与信号驱动式I/O的区别在于:信号驱动式I/O在数据报准备好时就通知应用进程,应用进程还需要将数据报从内核复制到用户进程缓冲区;而异步I/O模型则是整个操作完成才通知应用进程,应用进程在整个操作期间都不会被阻塞。

2.6 各种I/O模型的比较


图6:5种I/O模型的比较

从图6可以看到,前四种I/O模型的主要区别在于第一个阶段,它们的第二个阶段是一样的:在数据从内核复制到应用进程的缓冲区期间,进程会被阻塞于recvfrom系统调用。
而异步I/O模型则是整个操作完成内核才通知应用进程。

三、同步I/O和异步I/O

POSIX标准将同步I/O和异步I/O定义为:

  • 同步I/O操作:导致请求进程阻塞,直到I/O操作完成。

  • 异步I/O操作:不导致请求进程阻塞。

根据上述两个定义,本文介绍的前面四种模型,包括阻塞式I/O,非阻塞式I/O,I/O复用和信号驱动式I/O模型都是同步I/O模型,因为其中真正的I/O操作(recvfrom)将阻塞进程。只有异步I/O模型才符合POSIX标准的异步I/O定义。

四、生活中的类比例子

以生活中钓鱼为例子(例子参考了参考资料2),来说明各种I/O模型的不同,例子中的等待鱼上钩对应于上文中的等待数据,拉竿操则作对应于上文的将数据从内核复制到用户空间。

有A,B,C,D,E五个人在钓鱼。
A使用了最古老的鱼竿,所以开始钓鱼后,就一直守着,直接鱼上钩了再拉竿;
B由于着急想知道有没鱼上钩,所以隔一会就看一次鱼竿看有没鱼上钩,直到看到鱼上钩后,再拉竿;
C同时使用了N支鱼竿来钩鱼,然后等着,只要有其中一支鱼竿有鱼上钩,就将对应的鱼竿拉起来;
D的鱼竿比较高级,当有鱼上钩后,会发出警报提示,所以D开始钓鱼后不用一直守着,一旦鱼竿发出警报,D再回来拉竿即可;
E为了更省事,直接雇个佣人给他钓鱼,当佣人钓起鱼后,再通知E去取鱼即可。

五、参考资料

  1. Unix网络编程,卷1:套接字联网API,第三版,W. Richard Stevens著
  2. http://blog.csdn.net/historyasamirror/article/details/5778378
  3. http://pubs.opengroup.org/onlinepubs/009695399/functions/recvfrom.html

图解UNIX的I/O模型的更多相关文章

  1. Netty源码分析一<序一Unix网络I/O模型简介>

    Unix网络 I/O 模型   我们都知道,为了操作系统的安全性考虑,进程是无法直接操作I/O设备的,其必须通过系统调用请求内核来协助完成I/O动作,而内核会为每个I/O设备维护一个buffer.以下 ...

  2. Unix/Linux 网络 IO 模型简介

    概述 Linux内核将所有外部设备都看做一个文件来操作.对该文件的读写操作会调用内核提供的系统命令, 返回一个fd(file descriptor)文件描述符.而对一个socket的读写也有相应的描述 ...

  3. Unix的I/O模型

    对于一次I/O操作(以read为例),数据首先被拷贝到内核的某个缓冲区,然后再从内核缓冲区拷贝到应用进程缓冲区. 因此,一次I/O操作通常包含两个阶段: (1) 等待数据准备好 (2) 从内核向进程复 ...

  4. UNIX网络编程-Select模型学习

    1.相关接口介绍 1.1 select ---------------------------------------------------------------------- #include ...

  5. UNIX网络编程-Poll模型学习

    1.相关接口介绍 1.1 poll ---------------------------------------------------------------------- #include &l ...

  6. 浅谈Unix I/O模型

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

  7. Unix下5种I/O模型

    Unix下I/O模型主要分为5种: (1)阻塞式I/O (2)非阻塞式I/O (3)I/O复用(select和poll) (4)信号驱动式I/O (5)异步I/O 1.阻塞式I/O模型 unix基本的 ...

  8. UNIX 5种I/O模型

    Unix 5 I/O模型 I/O操作分为两步: (1)先将数据从 存储介质 (磁盘或者网络等)拷贝到 内核缓冲区,此时称为数据准备好,可以被用户读取. (2)由用户应用程序拷贝内核缓冲区数据 到用户缓 ...

  9. Java I/O 模型的演进

    什么是同步?什么是异步?阻塞和非阻塞又有什么区别?本文先从 Unix 的 I/O 模型讲起,介绍了5种常见的 I/O 模型.而后再引出 Java 的 I/O 模型的演进过程,并用实例说明如何选择合适的 ...

随机推荐

  1. Docker+Jenkins持续集成

    Docker+Jenkins持续集成 使用etcd+confd实现容器服务注册与发现   前面我们已经通过jenkins+docker搭建了基本的持续集成环境,实现了服务的自动构建和部署,但是,我们遇 ...

  2. C/C++(数据结构栈的实现)

    栈的实现 特点FILO(先进后出) 假设栈的空间为8 top == 0 不能出栈,已到栈底 top == 8 不能入栈,已到栈顶 top始终指向一个待插入的位置 push操作,1.写入数据,2.top ...

  3. WebMethod Description

    http://www.webxml.com.cn/WebServices/WeatherWebService.asmx https://www.cnblogs.com/wanganyi/p/72202 ...

  4. scrollwidth ,clientwidth ,offsetwidth 三者的区别

    clientwidth:内容可视区域的宽度 offsetwidth:元素整体宽度 scrollwidth:实际内容的宽度

  5. SDNU 1206.蚂蚁感冒 【代码如此简单,思维练习】【7月29】

    蚂蚁感冒 Description 长100厘米的细长直杆子上有n仅仅蚂蚁. 它们的头有的朝左,有的朝右. 每仅仅蚂蚁都仅仅能沿着杆子向前爬,速度是1厘米/秒. 当两仅仅蚂蚁碰面时.它们会同一时候掉头往 ...

  6. 13.AxisUtil

    1. package com.glodon.gspm.adapter.plugin.common; import lombok.SneakyThrows; import org.apache.axis ...

  7. ajax嵌套ajax 可能出现问题 的解决办法

    ajax由于他的异步特性 在第一次请求中的循环中嵌套第二个ajax会数据会读不出来 第一种 描述:如果条件许可,把两次请求都放在服务端处理掉一起发回来,这些就在客户端只有一次ajax了 优点:代码放在 ...

  8. 关于大数据项目创建时所需setting.xml(博主推荐)

    我目前,收录经常用的是,这两个版本,这个根据博主我本人的经验之谈,最为稳定和合理的. 注意:我的本地路径是在D:/SoftWare/maven/repository,大家自己改为你们自己的即可.   ...

  9. sql之group by的用法

    1.概述 “Group By”从字面意义上理解就是根据“By”指定的规则对数据进行分组,所谓的分组就是将一个“数据集”划分成若干个“小区域”,然后针对若干个“小区域”进行数据处理. 2.原始表 3.简 ...

  10. 用Navicat连接MySQL数据库出现1251错误:密码方式错误

    原因:因为MySQL8.0是最新版密码保存方式,而图形化数据库管理工具还是原先的密码保存方式. 解决方式: 用CMD命令号方式进入MySQL use mysql: ALTER USER 'root'@ ...