IO模型

IO模型

简介

一个IO输入操作通常包括两部分,一部分是等待数据准备好,另一部分是从内核向进程复制数据。Unix的IO模型有五种:

  • 阻塞式IO
  • 非阻塞式IO
  • IO复用(select/poll)
  • 信号驱动式IO(SIGIO)
  • 异步IO(AIO)

阻塞式

阻塞式IO表示对IO函数的调用需要等待整个IO完成才会返回,即仅当数据被复制到应用进程缓冲区后才会返回。例如使用read系统调用读取某个socket,如果不是立即接收到数据,服务器进程会被阻塞,之后一直等待用户数据到达,用户数据到达后会将数据写进内核缓冲区,之后将数据从内核缓冲区复制到用户进程缓冲区,完成了上述工作后才会将执行权限返回给用户(从内核态切换到用户态)

由于阻塞式IO效率较低,如果用户数据一直没有到达则会导致整个服务器被阻塞(单进程/单线程),因此为了不影响服务器接受其他进程的连接,可以考虑为每个连接的用户创建新线程,每次read系统调用仅会阻塞单个线程,不会影响服务器接受新的连接。在多线程模型下,主线程等待用户请求,用户请求到达后会创建新的线程,新线程负责随后于用户的交互,但由于新的线程也会占用较多系统资源,且线程较多后调度开销也会增大,因此并不适合高并发的场景。

非阻塞式IO

非阻塞式IO会在调用后立刻返回,调用者通过对返回值的判断确定该次调用是否完成了IO。应用进程为了获知是否有数据到达需要不断的执行系统调用来确定IO是否完成,这种查询方式称为轮询。由于利用这种方式需要操作系统处理更多的系统调用,因此CPU利用率较低。

IO复用

使用select/poll等待数据,并等待多个套接字中的任何一个变为可读,这一过程会被阻塞,当一个套接字可读后该函数才会返回,之后可以使用read等系统调用从套接字中读取数据。这种方式可以让单个进程拥有处理多个IO时间的能力,因此又被称为事件驱动IO。如果一个web服务器没有使用IO复用而是使用多线程模型,则每个套接字需要一个单独的线程去处理,因此相比之下,IO复用不需要进程创建和切换的开销,因此系统开销更小。

信号驱动式IO

应用进程使用sigaction发出信号,内核立刻返回,应用进程继续执行,而内核在有数据到达时向进程发送SIGIO信号,应用进程在收到信号的信号处理函数中调用read等函数读取数据。

异步IO

应用进程调用aio_read系统调用后立刻返回,应用进程可以继续执行,内核会在操作完成后向应用进程发送信号表示传输已经完成。异步IO与信号驱动式IO的区别就是异步IO的信号是用来通知应用进程IO已经完成的,而信号驱动IO中的信号是用来通知应用进程已有数据到达可以开始IO的。

五种信号模型区别

可以将这五种模型分为同步与异步两类,同步IO指将数据从内核缓冲区复制到应用进程缓冲区的过程会阻塞,同步IO包括阻塞式IO、非阻塞式IO、IO复用、信号驱动式IO;异步IO则完成不会阻塞,该类包括异步IO。同步IO中仅阻塞IO会在等待数据的过程中阻塞,其他三种方式不会阻塞。

参考

https://github.com/CyC2018/CS-Notes/blob/master/notes/Socket.md
https://blog.csdn.net/davidsguo008/article/details/73556811