I/O模型,


I/O介绍

  1)I/O(Input/Output),即输入/输出,分为IO设备和IO接口两个部分
  2)IO分类:
    • 网络IO:网络IO的本质是socket的读取,socket在linux系统被抽象为流,IO可以理解为对流的操作
    • 磁盘IO:文件的一次读写操作就会发生IO操作
  3)IO操作类型:
    • 缓存IO:
      - 基本介绍:又称为标准IO、高级磁盘IO,遵循 ANSI C 相关标准,C标准库中提供了标准IO库,即stdio,其实现了跨平台的用户缓存解决方案,大多数文件系统的默认IO操作
      - 工作机制:第一阶段:等待数据准备就绪(内核将数据复制到内核内存);第二阶段:真正IO操作的过程(进程将数据复制到进程内存)
        ◇ 对于磁盘IO来说:先将数据从磁盘复制到内核内存(也称为文件系统的页缓存),再从内核内存复制到进程内存
        ◇ 对于网络IO来说:等待网络上的数据分组到达,然后被复制到内核内存,再把数据从内核内存复制到进程内存
      - 缺点:数据在传输过程中需要在进程内存和内核内存进行多次数据拷贝操作,这些数据拷贝操作所带来的CPU以及内存开销是非常大的
    • 直接IO:
      - 基本介绍:又称为文件IO、低级磁盘IO,遵循 POSIX 相关标准,任何兼容POSIX标准的操作系统上都支持直接IO
      - 工作机制:IO操作不经过内核内存,数据在磁盘/网卡和进程内存之间直接进行传输,所以其效率更高
    • 其他:Linux中使用的是GLIBC,它是标准C库的超集,不仅包含 ANSI C 中定义的函数,还包括POSIX标准中定义的函数,因此,Linux下既可以使用直接IO,也可以使用缓存IO
  4)进程是基于串行模式处理多路IO(即多个IO)的,WEB进程通常要处理双路IO:
    • 网络IO
    • 磁盘IO

同步与异步 & 阻塞与非阻塞

  1)同步与异步关注的是消息通信机制,即【被调用者】【通过何种消息通知机制】【通知】【调用者】
    • 同步(sync):系统调用发出后,被调用者不会立即返回消息,但一旦返回则返回的是最终调用结果;同步会导致请求进程阻塞,直到IO操作完成
    • 异步(async):系统调用发出后,被调用者会立即返回消息,但返回的并非调用结果,而是告知调用者调用已收到、回去等待;被调用者通过状态、通知机制等告知调用者或通过回调函数来处理结果;异步不会导致请求进程阻塞
  2)阻塞与非阻塞关注的是等待调用结果时的状态,即【调用者】【等待被调用者返回调用结果】【时的状态】
    • 阻塞(block):在调用结果返回前,调用者会被挂起(转为不可中断式睡眠状态),调用者只有在得到调用结果之后才能继续工作,在被阻塞期间无法响应任何信号
    • 非阻塞(unblock):在调用结果返回前,调用者不会被挂起,其可以进行其他操作,即调用不会阻塞调用者

I/O模型

1、基本介绍
  1)阻塞式IO(Blocking IO):同步
    • 基本介绍:
      - 最常用的一个模型,也是最简单的模型,在Linux中,默认情况下所有的socket都是blocking的
    • 工作机制:
      - 第一阶段(等待数据准备就绪):阻塞,等待数据准备完成
      - 第二阶段(真正IO操作过程):阻塞,等待数据从内核内存复制到进程内存
    • 优点:
      - 能够及时返回数据,无延迟
      - 开发实现简单容易
    • 缺点:
      - 效率低
  2)非阻塞式IO(Unblocking IO):同步
    • 工作机制:
      - 第一阶段(等待数据准备就绪):非阻塞,忙等待状态,即进程发起IO操作后,在此阶段进程不会被阻塞,其可以进行其他操作,但会隔指定时间来查看调用结果
      - 第二阶段(真正IO操作过程):阻塞,等待数据从内核内存复制到进程内存
    • 优点:
      - 在等待数据准备阶段,可以进行其他操作
    • 缺点:
      - 忙等待会导致整体数据吞吐量的降低,效率不一定比同步阻塞式IO高
  3)IO复用(IO Multiplexing):同步
    • 基本介绍:
      - 通常情况下,进程在同一时间内仅能处理一个IO,IO复用就是通过一种特殊的IO调用来实现同时处理多个IO
    • 工作机制:
      - 第一阶段(等待数据准备就绪):阻塞,进程将IO调用请求发给代理(select、poll等)处理,代理去发起真正的IO调用。在IO处理工程中,代理处于空闲状态,进程如果没有其他IO操作请求,则会阻塞在代理处;如果有其他IO请求,则可以继续接收,并将其交由代理处理。其本质还是阻塞的,只是阻塞在了代理上,而非阻塞在了真正的系统调用上
      - 第二阶段(真正IO操作过程):阻塞,等待数据从内核内存复制到进程内存
    • 实现:
      - select(BSD风格):
        ◇ 最多不能超过1024个IO请求,httpd-prefork模式仅能支持1024的最大并发就是因为其使用的是select模型,httpd-worker模式是基于线程来调用select()
        ◇ 内核每准备好一个IO,select()就会扫描一遍所管理的数据结构,然后将其输出至用户空间,进程看到IO完成,则会响应给用户
      - poll(SysV风格)
    • 因为还是阻塞,所以性能上没有提升,但处理并发的能力有所提升
  4)事件驱动式IO(Signal Driven I/O):异步
    • 工作机制:
      - 第一阶段(等待数据准备就绪):非阻塞,进程发起IO操作,内核收到请求后会立即通知进程数据准备中,进程无需等待数据准备完成,也无需忙等待,可以去进行其他操作,准备完成后,内核会通知进程(事件指的就是数据准备完成)
      - 第二阶段(真正IO操作过程):阻塞,等待数据从内核内存复制到进程内存。但是,如果其它已完成第一阶段的IO操作,在通知此处于阻塞状态的进程时,其是无法响应的,然后只能通过回调函数将数据从内核内存复制到进程内存
    • 事件驱动的消息通知机制:
      - 水平触发:多次通知,直至响应;可靠、但浪费资源
      - 边缘触发:通知一次,如在指定时间内未取走资源,则稍后进程会通过回调函数来取走资源
    • 实现:
      - epoll(Linux):httpd-event模式(据说2.4版本支持异步IO)就是通过此模型来处理高并发,NGINX设计之初就是基于事件驱动模型+边缘触发机制
      - kqueue(FreeBSD)
      - /dev/poll(Solaris)
  5)异步IO(Asynchronous IO):异步
    • 工作机制:
      - 第一阶段(等待数据准备就绪):非阻塞,进程发起IO操作,内核通知进程数据准备中,进程无需等待数据准备完成,也无需忙等待,可以去进行其他操作,数据在此期间被复制到内核内存
      - 第二阶段(真正IO操作过程):非阻塞,数据从内核内存复制到进程内存,然后通知进程来取,进程直接将数据封装为响应报文,返回用户
    • NGINX也支持异步IO和内存映射机制

2、相关图示

 

 

工作模型

  1)单进程
    • 阻塞、串行
  2)多进程
    • 需要大量的进程切换,资源浪费在切换上
    • 每个进程的内存地址空间是独立的,当多个用户访问同一资源时,此资源会加载至多个进程内存地址空间,造成内存空间的数据重复,降低了内存的利用率
  3)线程(Linux不支持原生线程,而是轻量级进程,其依赖于线程库)
    • 分为单进程多线程和多进程多线程
    • 线程共享所在进程的内存地址空间,对内存的需求较之进程略有下降
    • 快速切换时会带来线程抖动
    • 注意:单颗CPU下的线程模式,几乎没有优势
      - 指令区:存放指令
      - 堆区:存放文件
      - 栈区:存放变量
    • 每个线程响应一个请求,线程依然需要切换,但属于轻量级切换
    • 每个线程响应多个请求,如果其中一个请求进行了IO操作,必然引起阻塞,那么其他请求就会被中断,所以在此情况下, 不能让IO阻塞

相关内容

    暂无相关文章