1、5中IO模型:

  • 阻塞I/O(blocking IO)
  • 非阻塞I/O(noblocking IO)
  • I/O复用    (IO multiplexing )
  • 信号驱动I/O (signal driven IO)
  • 异步I/O (asynchronous IO)

2、在了解I/O模型前先了解几个概念:

  1. 用户空间和内核空间
  2. 进程切换
  3. 进程的阻塞
  4. 文件描述符
  5. 缓存 I/O

  用户空间与内核空间

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

  进程切换

      为了控制进程的执行,内核必须有能力挂起正在CPU上运行的进程,并恢复以前挂起的某个进程执行,这种行为被称为进程切换。这种切换是由操作系统来完成的。因此可以    说,任何进程都是在操作系统内核的支持下运行的,是与内核紧密相关的    

      从一个进程的运行转到另一个进程上运行,这个过程中经过下面这些变化:

      保存处理机上下文,包括程序计数器和其他寄存器。

      更新PCB信息。

      把进程的PCB移入相应的队列,如就绪、在某事件阻塞等队列。

      选择另一个进程执行,并更新其PCB。

      更新内存管理的数据结构。

      恢复处理机上下文。 
      注:总而言之就是很耗资源的

      进程阻塞

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

     文件描述符fd

   文件描述符(File descriptor)是计算机科学中的一个术语,是一个用于表述指向文件的引用的抽象化概念。 
文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时, 内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。

  缓存 I/O

  缓存 I/O 又被称作标准 I/O,大多数文件系统的默认 I/O 操作都是缓存 I/O。在 Linux 的缓存 I/O 机制中,操作系统会将 I/O 的数据缓存在文件系统的页缓存( page cache )     中,也就是说,数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。用户空间没法直接访问内核空间的,内核态到用户态的数据拷贝 

   缓存 I/O 的缺点: 

数据在传输过程中需要在应用程序地址空间和内核进行多次数据拷贝操作,这些数据拷贝操作所带来的 CPU 以及内存开销是非常大的。

3、IO模型

   这里统一使用Linux下的系统调用recv作为例子,它用于从套接字上接收一个消息,因为是一个系统调用,所以调用时会从用户进程空间切换到内核空间运行一段时间再切换回来。默认情况下recv会等到网络数据到达并且复制到用户进程空间或者发生错误时返回,而第4个参数flags可以让它马上返回。

  • 阻塞模型

  使用recv的默认参数一直等数据直到拷贝到用户空间,这段时间内进程始终阻塞。打个比方,A同学排队买票,他只能排队买上票才可以离开。这一过程就可以看成使用了阻塞       IO模型,因为如果在没买到票之前,他不能离开队伍做别的事情(离开等于白排队,回来又要重新排队)。很显然这种,I/O模型是同步的。

  

  • 非阻塞IO模型

 改变flags,让recv不管有没有获取到数据都返回,如果没有数据那么一段时间后再调用recv看看,如此循环。对比阻塞模型,相当于A同学买票过程中,采用了取号买票,再没有到他前,他可以不断的返回购票大厅看下是不是到了自己的号,中间的过程可以做其他事情。他就不用向之前一样一刻不能离开购票大厅。这就是非阻塞IO模型。但是它只有是检查无数据的时候是非阻塞的,在数据到达的时候依然要等待复制数据到用户空间(到自己的号买上票),因此它还是同步IO。

 

  • IO复用模型

     这里在调用recv前先调用select或者poll,这2个系统调用都可以在内核准备好数据(网络数据到达内核)时告知用户进程,这个时候再调用recv一定是有数据的。因此这一过程中它     是阻塞于select或poll,而没有阻塞于recv,有人将非阻塞IO定义成在读写操作时没有阻塞于系统调用的IO操作(不包括数据从内核复制到用户空间时的阻塞,因为这相对于网络IO       来说确实很短暂),如果按这样理解,这种IO模型也能称之为非阻塞IO模型,但它也是同步IO,那么也和楼上一样称之为同步非阻塞IO吧。

    这种IO模型比较特别,分个段。因为它能同时监听多个文件描述符(fd)。举例A同学来北京到南京的车票,发现有一排售票窗口,售票服务人员告诉他这些窗口目前没有票,等有票告诉他。于是等啊等(select调用中),过了一会售票服务人员告诉他有票了,但不知道是哪个窗口卖北京到南京的车票,自己看吧。于是A同学一个个窗口问,直到找到卖北京到南京车票的窗口买上票(recv)。这里再顺便说说鼎鼎大名的epoll(高性能的代名词啊),epoll也属于IO复用模型,主要区别在于售票服务人员告诉他A同学哪几个窗口卖北京到南京的车票,不需要一个个去问了。

 

  • 信号驱动IO模型

    通过调用sigaction注册信号函数,等内核数据准备好的时候系统中断当前程序,执行信号函数(在这里面调用recv)。A同学让舍售票服务人员等有票的时候通知他(注册信号函数),    没多久A同学得知有票了,跑去买票。是不是很像异步IO?很遗憾,它还是同步IO(省不了买票的时间啊)。

  

  • 异步IO模型

     调用aio_read,让内核等数据准备好,并且复制到用户进程空间后执行事先指定好的函数。A同学让售票服务人员帮他买好票后通知他。整个过程A同学都可以做别的事情(没有           recv),这才是真正的异步IO。

  

 

4、总结

 IO分两阶段:

1.数据准备阶段
2.内核空间复制回用户进程缓冲区阶段

 一般来讲:阻塞IO模型、非阻塞IO模型、IO复用模型(select/poll/epoll)、信号驱动IO模型都属于同步IO,因为阶段2是阻塞的(尽管时间很短)。只有异步IO模型是符合异步IO操作含义的,不管在阶段1还是阶段2都可以干别的事。

   

 

posted on 2019-05-06 21:34  赏孤舟蓑笠  阅读(872)  评论(0编辑  收藏  举报