linux 的五种I/O模式

本文主要转自http://www.cnblogs.com/chy2055/p/5220793.html

 一、关于I/O模型的引出

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


整个请求过程为: 用户进程发起请求,内核接受到请求后,从I/O设备中获取数据到buffer中,再将buffer中的数据copy到用户进程的地址空间,该用户进程获取到数据后再响应客户端。

用户空间和内核空间:现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方)。操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。为了保证用户进程不能直接操作内核(kernel),保证内核的安全,操心系统将虚拟空间划分为两部分,一部分为内核空间,一部分为用户空间。针对linux操作系统而言,将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为内核空间,而将较低的3G字节(从虚拟地址0x00000000到0xBFFFFFFF),供各个进程使用,称为用户空间。

在整个请求过程中,数据输入至buffer需要时间,而从buffer复制数据至进程也需要时间。因此根据在这两段时间内等待方式的不同,I/O动作可以分为以下五种模式:

  (1) 阻塞I/O (Blocking I/O)

  (2) 非阻塞I/O (Non-Blocking I/O)

  (3) I/O复用(I/O Multiplexing)

  (4) 信号驱动的I/O (Signal Driven I/O)

  (5) 异步I/O (Asynchrnous I/O)

关于进程切换:

从一个进程的运行转到另一个进程上运行,这个过程中经过下面这些变化:
          保存处理机上下文,包括程序计数器和其他寄存器。
          更新PCB信息。
          把进程的PCB移入相应的队列,如就绪、在某事件阻塞等队列。
          选择另一个进程执行,并更新其PCB。
          更新内存管理的数据结构。
          恢复处理机上下文。

关于进程的阻塞:

正在执行的进程,由于期待的某些事件未发生,如请求系统资源失败、等待某种操作的完成、新数据尚未到达或无新工作做等,则由系统自动执行阻塞原语(Block),使自己由运行状态变为阻塞状态。可见,进程的阻塞是进程自身的一种主动行为,也因此只有处于运行态的进程(获得CPU),才可能将其转为阻塞状态。当进程进入阻塞状态,是不占用CPU资源的。

关于文件描述符:

文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在网络中,一个socket对象就是1个文件描述符,在文件中,1个文件句柄(即file对象)就是1个文件描述符。其实可以理解为就是一个“指针”或“句柄”,指向1个socket或file对象,当file或socket发生改变时,这个对象对应的文件描述符,也会发生相应改变。

二、关于I/O模型的划分

  阻塞:调用的进程一直处于等待状态,直到操作完成。

  非阻塞:在内核的数据还未准备好时,会立即返回,进程可以去干其他事情。

从同步异步,以及阻塞、非阻塞两个维度来划分来看:

    

同步/异步主要针对Client端: 

同步:所谓同步,就是在Client端发出一个功能调用时,在没有得到结果之前,该调用就不返回。也就是必须一件一件事做,等前一件做完了才能做下一件事,因此这里同步指的是功能调用者本身没有得到结果

异步的概念和同步相对。当Client端一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。

阻塞/非阻塞主要针对S端:

阻塞调用是指调用结果返回之前,当前线程会被挂起(线程进入非可执行状态,在这个状态下,cpu不会给线程分配时间片,即线程暂停运行)。函数只有在得到结果之后才会返回。

非阻塞:
      非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。

同步与非同步

阻塞和非阻塞是指当server端的进程访问的数据如果尚未就绪,进程是否需要等待,简单说这相当于函数内部的实现区别,也就是未就绪时是直接返回还是等待就绪;
  而同步和异步是指client端访问数据的机制,同步一般指主动请求并等待I/O操作完毕的方式,当数据就绪后在读写的时候必须阻塞(区别就绪与读写二个阶段,同步的读写必须阻塞),异步则指主动请求数据后便可以继续处理其它任务,随后等待I/O,操作完毕的通知,这可以使进程在数据读写时也不阻塞。(等待"通知")

  总之,同步IO和异步IO的区别就在于:数据访问的时候进程是否阻塞!
     阻塞IO和非阻塞IO的区别就在于:应用程序的调用是否立即返回!


三、I/O模型分述

1、阻塞I/O

     

  从上图可以看到在整个过程中,当用户进程进行系统调用时,内核就开始了I/O的第一个阶段,准备数据到缓冲区中,当数据都准备完成后,则将数据从内核缓冲区中拷贝到用户进程的内存中,这时用户进程才解除block的状态重新运行。

  所以,Blocking I/O的特点就是在I/O执行的两个阶段都被block了。

2、非阻塞I/O

     

  从上图可以看到在I/O执行的两个阶段中,用户进程只有在第二个阶段被阻塞了,而第一个阶段没有阻塞,但是在第一个阶段中,用户进程需要盲等,不停的去轮询内核,看数据是否准备好了,因此该模型是比较消耗CPU的。

3、I/O复用

    

   从上图可以看到在I/O复用模型中,I/O执行的两个阶段都是用户进程都是阻塞的,但是两个阶段是独立的,在一次完整的I/O操作中,该用户进程是发起了两次系统调用。

4、信号驱动的I/O

     

  该模型也叫作基于事件驱动的I/O模型,可以看到该模型中,只有在I/O执行的第二阶段阻塞了用户进程,而在第一阶段是没有阻塞的。

  乍看起来感觉和非阻塞模型很相似,其实不同之处就在于,该模型在I/O执行的第一阶段,当数据准备完成之后,会主动的通知用户进程数据已经准备完成,即对用户进程做一个回调。该通知分为两种,一为水平触发,即如果用户进程不响应则会一直发送通知,二为边缘触发,即只通知一次。

 5、异步I/O

     

  在该模型中,当用户进程发起系统调用后,立刻就可以开始去做其它的事情,然后直到I/O执行的两个阶段都完成之后,内核会给用户进程发送通知,告诉用户进程操作已经完成了。

对unix来讲:阻塞式I/O(默认),非阻塞式I/O(nonblock),I/O复用(select/poll/epoll),信号驱动IO都属于同步I/O,因为它们在数据由内核空间复制回进程缓冲区时都是阻塞的(不能干别的事)。只有异步I/O模型(AIO)是符合异步I/O操作的含义的,即在1数据准备完成、2由内核空间拷贝回缓冲区后 通知进程,在等待通知的这段时间里可以干别的事。异步I/O跟信号驱动I/O的不同之处在于,它不用调用recv进行数据的复制,如果将后者比做”拉pull“,则前者可以认为是”push推“,push的效率会高点,

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值