关于:5种I/O模型

来自Wikioe
跳到导航 跳到搜索


关于

讨论的背景是 Unix/Linux 环境下的 network IO

I/O(Input/Output,输入/输出)即数据的读取(接收)或写入(发送)操作。

  • I/O有:内存I/O、网络I/O 和 磁盘I/O 三种。(通常说的I/O指的是后两者)


LINUX 中进程无法直接操作 I/O 设备,其必须通过系统调用请求 kernel 来协助完成 I/O 动作;内核会为每个 I/O 设备维护一个缓冲区

对于一个输入操作来说,进程I/O系统调用后,内核会先看缓冲区中有没有相应的缓存数据,没有的话再到设备中读取,因为设备I/O一般速度较慢,需要等待;内核缓冲区有数据则直接复制到进程空间。


所以,对于一个 network I/O,它会涉及到两个系统对象:调用I/O的process(or thread),系统内核(kernel)。

当一个 I/O 操作发生时,kernel 中会经历两个阶段:

  1. 等待数据准备(Waiting for the data to be ready);
  2. 将数据从内核拷贝到进程中(Copying the data from the kernel to the process);

记住这两点很重要,因为这些 IO Model 的区别就是在两个阶段上各有不同的情况。

5种I/O模型

《UNIX网络编程》:5 种I/O模型分别是阻塞I/O模型非阻塞I/O模型I/O复用模型信号驱动的I/O模型异步I/O模型

  • 其中,前 4 种为“同步I/O操作”,只有 异步I/O模型 是“异步I/O操作”。

阻塞I/O模型(blocking IO)


进程阻塞于 recvfrom 调用,直到返回成功指示。(进程阻塞直到 kernel 中两个阶段结束)

5种IO模型:阻塞IO模型.png


典型应用:

  • 阻塞socket;【默认情况下所有的 socket 都是 blocking】
  • Java BI/O

特点:

  • 进程阻塞挂起不消耗CPU资源,及时响应每个操作
  • 实现难度低、开发应用较容易;
  • 适用并发量小的网络应用开发;
  • 不适用并发量大的应用:因为一个请求I/O会阻塞进程,所以,得为每请求分配一个处理进程(线程)以及时响应,系统开销大。

非阻塞I/O模型(nonblocking IO)


进程反复调用 recvfrom,并等待返回成功指示。(进程反复调用 recvfrom 而不阻塞)

5种IO模型:非阻塞IO模型.png

进程发起 I/O 系统调用后:

  1. 如果内核缓冲区没有数据,需要到 I/O 设备中读取,进程返回一个错误而不会被阻塞
  2. 如果内核缓冲区有数据,内核就会把数据返回进程。


典型应用:

  • socket(linux下,可以通过设置socket使其变为non-blocking)

特点:

  • 进程轮询(重复)调用,消耗CPU的资源
  • 实现难度低、开发应用相对阻塞I/O模式较难;
  • 适用并发量较小、且不需要及时响应的网络应用开发;

I/O复用模型(IO multiplexing)


I/O复用模型(也称:“event driven IO”,事件驱动I/O),其好处就在于单个process就可以同时处理多个网络连接的 IO

5种IO模型:IO复用模型.png

基本原理:【select/epoll 这个 function 会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程】

等待数据准备:
用户进程调用 select,整个进程被block。
同时,kernel 会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。

将数据从内核拷贝到进程中:
用户进程再调用 recvfrom(read 操作),将数据从kernel拷贝到用户进程。
  • 使用到两个系统调用:(select 和 recvfrom)
    1. select:轮询socket,等待数据;
    2. recvfrom:将数据从内核拷贝到进程中;
  • 用户进程阻塞两次
    1. 进程阻塞于 select 调用,等待某个socket可读;(阻塞于 select 调用
    2. 进程阻塞于 数据拷贝到应用缓冲区;(阻塞于 recvfrom 调用


注意:

  • 其优势,并不在于性能(连接数不高时,并不一定比“阻塞I/O”更好),而是在于同时处理多个connection;


典型应用:

  • select、poll、epoll三种多路I/O复用方案;(nginx都可以选择使用这三个方案)
  • Java NIO;

特点:

  • 专一进程解决多个进程I/O的阻塞问题,性能好;Reactor模式;
  • 实现、开发应用难度较大;
  • 适用高并发服务应用开发:一个进程(线程)响应多个请求;

select、poll、epoll

Linux 中I/O复用的实现方式主要有:

  1. select:注册I/O、阻塞扫描。
    • 监听的 I/O 最大连接数不能多于 FD_SIZE
  2. poll:原理和 select 相似,没有数量限制,但I/O数量大扫描线性性能下降;
  3. epoll :事件驱动不阻塞,mmap 实现内核与用户空间的消息传递,数量很大。
    • Linux 2.6 后内核支持;
    • 【Redis 的单线程模型中,“文件事件处理器”就采用 epoll 实现 多路I/O复用】

信号驱动的I/O模型(signal driven IO)


当进程发起一个 I/O 操作,会向内核注册一个信号处理函数,然后进程返回不阻塞;当内核数据就绪时会发送一个信号给进程,进程便在信号处理函数中调用 I/O 读取数据。

  • 进程阻塞在第二个阶段(数据拷贝到应用缓冲区期间);
5种IO模型:信号驱动IO模型.png


典型应用:

  • 【好像应用比较少】

特点:

  • 回调机制,实现、开发应用难度大;

异步I/O模型(asynchronous IO)


用户线程与内核异步执行:

  • 用户线程:发起一个 aio_read 后(立刻得到内核返回),继续执行其他逻辑;
  • 内核:
    1. 收到一个 aio_read 后立刻返回。
    2. 然后,两个阶段(等待数据准备完成,将数据拷贝到用户内存)完成之后,给用户进程发送一个 signal。
5种IO模型:异步IO模型.png


典型应用:

  • JAVA7 AIO;【???】
  • 高性能服务器应用;

特点:

  • 不阻塞,数据一步到位;Proactor模式
  • 需要操作系统的底层支持,LINUX 2.5 版本内核首现,2.6 版本产品的内核标准特性;
  • 实现、开发应用难度大;
  • 非常适合高性能高并发应用

I/O模型比较

5种IO模型比较.png

I/O:blocking vs non-blocking、synchronous vs asynchronous

blocking vs non-blocking

blocking:用户进程会阻塞直到整个 I/O 结束;(阻塞I/O模型) non-blocking:在kernel还准备数据的情况下会立刻返回;(非阻塞I/O模型)

  • 但,阻塞I/O模型、I/O复用模型、信号驱动的I/O模型,都存在用户线程阻塞的情况。

synchronous vs asynchronous

对于 synchronous IO 和 asynchronous IO,Stevens给出的定义(其实是POSIX的定义)如下:

A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes;

An asynchronous I/O operation does not cause the requesting process to be blocked;
  • 此处,“I/O operation”是指真实的IO操作,就是例子中的recvfrom这个system call。


对于“非阻塞I/O模型”(non-blocking IO):在执行 recvfrom 这个system call的时候,如果 kernel 的数据没有准备好,这时候不会block进程。但是,当kernel中数据准备好的时候,recvfrom 会将数据从kernel拷贝到用户内存中,这个时候进程是被block了,在这段时间内,进程是被block的。【见上一节:“I/O模型比较”图】


抛却以上说法,其实,二者重点关注在“用户线程发送I/O之后,是否关注内核操作”,以此作为区分:

只有“异步I/O模型”是异步I/O,其余“阻塞I/O模型”、“非阻塞I/O模型”、“I/O复用模型”、“信号驱动的I/O模型”都属于同步I/O