I/O Models

概念

Input/Output

  • 在硬件层面,I/O是字节在硬盘、网卡、键盘等设备到内存之间流动的过程。
  • 在应用软件的角度上,Input是应用软件经过直接或间接地调用操做系统(kernel)提供的IO接口访问应用进程外部数据的过程。这个过程一般包括两个阶段:第一阶段是在向内核提交需求(调用kernel接口);第二阶段是内核将这部分数据从内核缓冲区复制到应用缓冲区域即成功得到数据(完成任务)。Output是应用调用内核IO接口向应用进程外输出数据的过程。一样地一般涉及两个阶段:第一阶段是直接或间接调用内核IO接口请求输出数据;第二阶段内核将数据从应用缓冲区复制到目的地。从第一阶段进入第二阶段须要知足必定条件。对于Input,条件一般包括应用须要的数据在内核缓冲区准备好并能够复制到应用缓冲区中。对于Output,条件包括目标文件有足够空间存放数据。
  • 对于应用程序来讲,向网络(Network)输出输入数据与读写文件本质上都是I/O:前者是将数据写到网卡,最终发到目的主机,然后者是将数据写到硬盘。在Linux操做系统中,全部外设都被映射成文件,即对外设的操做都被映射成对文件的操做。TCP协议中,通讯连接的两端是Socket(套接字,由IP地址和端口组成)。服务端应用进程与客户端进程的通讯就是经过读写Socket文件:服务端进程想要接收客户端发送的数据,就要从本机上与该客户端对应的Socket文件输入流中读取数据,若是想发送数据,就要向该Socket文件输出流写入数据。因此应用程序间的网络通讯本质上只是I/O的一种。

中断

中断包括程序中断、时间中断、I/O中断和硬件失效中断。当程序向内核发出I/O请求,内核执行I/O程序,I/O设备经过中断的方式通知内核:”已发现须要的数据“,内核执行中断程序回应硬件设备:”已获悉“。网络

中断机制在每一个OS中均可能有不同的实现,下面简述Linux系统中的通常中断机制,具体实现仍取决于开发者。异步

上半部分:内核经过中断处理程序及时回应发出中断请求的硬件(尽快返回被中断的工做中)。当I/O事件完成,I/O设备发送中断信号到中断控制器芯片,若是此时处理器容许中断,中断控制器向处理器发送一个中断请求的电信号,处理器在指令周期中的检测中断阶段检测到I/O中断请求,马上中止当前处理的程序并屏蔽中断,跳到预先由内核设置好的内存地址读取指令并执行,这个地址就是中断处理函数的入口,内核在这个函数内计算出中断号,根据中断号运行对应的处理程序(该中断处理程序来自于外部设备的驱动),完成中断处理程序后即对硬件的中断请求做出了回应。在这个阶段有时还会作另一些一样对时间很敏感的工做,例如从硬件拷贝数据到系统内存缓冲区,如当网卡接收到的数据,以后能够返回到被中断的程序中继续执行指令。函数

下半部分:完成对时间不是很是敏感的工做,这部分工做能够日后推迟。例如处理系统内存中从硬件接收到的数据,若是此时完成了一个I/O请求的条件,能够将阻塞在此I/O请求上的进程设置为就绪态。‘spa

系统调用处理程序

用户空间的程序没法直接执行内核代码,由于内核的函数存放在受保护的系统空间内。应用程序经过软中断的方式,通知系统本身须要执行一个系统调用:经过引起一个异常促使系统从用户态切换到系统态,执行一个异常处理函数,这个函数就是系统调用处理程序。操作系统

阻塞(Blocking)与  非阻塞(Non-Blocking)

阻塞态是进程的一种执行态,此时进程不能被内核调度继续执行。阻塞的缘由有不少种,能够概括为等待的事件仍未发生。在这里咱们讨论的阻塞非阻塞就是进程在执行I/O调用以后的状态。线程

当进程执行一个系统调用以请求I/O资源,但内核检测到I/O资源被占用或者数据未准备好,因此不能马上响应线程为其服务,进程的状态被内核调整为阻塞态,直到该I/O资源准备好并返回系统调用的结果,内核将进程执行态设置为就绪态。相反,若是进程执行一个系统调用,该系统调用不管I/O资源准备好与否都当即返回某个结果,进程就不须要进入阻塞态且能够继续执行后续指令。两种系统调用的区别在于:当操做系统没有准备好程序申请的I/O资源时,进程是否会被内核设置为阻塞态。设计

同步(synchronous )与  异步(Asynchronous)

同步与异步是对请求方与被请求方之间的通讯方式。对象

同步(synchronous ):当请求方发送请求后,只能作获取被请求方的响应的事情,能够是什么都不作,等待被请求方回应结果(阻塞),也能够是不断地询问被请求方是否有结果(非阻塞、轮询-polling),但就是不会作与此次请求无关的其余任务。当进程执行I/O请求后,被阻塞等待内核通知I/O事件完成,或者非阻塞地经过轮询的方式询问内核I/O事件是否完成,而不会去执行其余不相干的代码。blog

异步(Asynchronous):当请求方发送请求后,不关心结果,直接执行其余任务的操做(非阻塞),等到被请求方处理完请求后再通知请求方,请求方在合适的时候再处理这个结果,或者在请求的时候就告诉被请求方当处理完请求后帮其干点什么。当程序执行I/O系统调用,该系统调用是非阻塞的,即会当即返回一个结果,程序能够继续执行后面的指令,这些指令不会涉及到这个结果,当内核通知其I/O事件完成后才会考虑处理结果。或者设置回调函数,当I/O事件完成时执行回调函数。接口

I/O Models

阻塞I/O(Blocking I/O:BIO)Model

BIO模型是同步阻塞的。

阻塞:进程执行系统调用请求获取数据,被阻塞直到数据复制到系统缓冲区,并从系统缓冲区复制到应用缓冲区。

同步:因为在内核准备数据这段时间,程序没有作其余工做,直到获取到内核的结果。

graphics/06fig01.gif

非阻塞I/O(Non-Blocking I/O:NIO)Model

NIO模型是同步非阻塞的。

非阻塞于:进程执行系统调用请求数据,当系统调用检测系统缓冲区没有准备好数据时,并无让程序等待直到数据准备好,而是直接返回一个负数表明数据没有准备好,程序能够继续执行后面的指令。

同步于:虽然程序能够继续执行后面的指令,可是想先收到数据再作其余工做,经过轮询的方式不断询问内核数据是否准备好。

graphics/06fig02.gif

I/O 多路复用(I/O Multiplexing)Model

在多路复用模型中,复用的是线程,即用一个线程监听多个I/O流上可读可写的事件。

在以前的BIO模型与NIO模型中,服务端在一个线程里只等待一个客户端Socket文件I/O流上可读或可写的事件发生,这意味着一个线程只服务一个客户端,当客户端Socket文件的输入流不可读,线程同步地等待直到该输入流可读,而后读取数据并处理。若是服务端进程要同时处理多个客户端即监听多个Socket文件上的I/O流可读写事件,须要增长线程数目。值得注意的是,除了根据客户端数目1:1地增长线程数目来服务多个客户端,也能够经过设置线程池的方式,用若干个线程服务多个客户端。但本质上,这两种I/O模型中每一个线程都只能同时监听一个Socket文件上的I/O流事件。

在I/O多路复用模型中,服务端在一个线程里能够同时监听多个客户端Socket文件上的I/O流。线程等待多个I/O流上的可读可写事件,当一个或以上的I/O流可读/可写事件发生,内核返回被包装好的I/O流对象,线程串行地根据事件的类型对这些I/O流进行读或者写。

I/O多路复用是同步阻塞的。

阻塞于:当没有一个I/O上有可读或可写事件发生,线程是被阻塞的,select()函数不能立刻返回(select函数选择一个或以上的可读写I/O流)。值得注意的是,有的文章会根据:即便有部分I/O流不知足读/写条件也不会阻塞整个线程妨碍其余I/O流读写这一观点,认为I/O复用是非阻塞的,这是对的,只是你们角度不一样。

同步于:由于在发送I/O请求(select函数)后若是没有可读或可写事件发生,线程是阻塞的,线程没办法执行其余工做,因此是同步的。

graphics/06fig03.gif

异步I/O模型(Asynchronous I/O Model)

异步I/O模型是异步非阻塞的。在该模型中,线程为I/O请求执行的系统调用不会阻塞线程而是立刻返回,这次请求仅仅是通知内核将需求数据复制到应用缓冲区,以后线程能够执行后面的指令,与NIO不一样的是,AIO不会以轮询地方式等待I/O操做完成,而是去执行其余工做,直到内核将数据复制到应用缓冲区再通知线程,所以AIO是非阻塞的,异步的。

 graphics/06fig05.gif

参考

  • 《操做系统——精髓与设计原理》
  • 《 UNIX Network Programming, Volume 1 》 
  • 《Linux内核设计与实现》
  • 《Computer Systems - A Programmer's Perspective》
相关文章
相关标签/搜索