UNP阅读:Unix下常用IO模型总结

在Unix下,主要包含以下几种IO模型:

  • 阻塞式I/O
  • 非阻塞式I/O
  • I/O多路复用(select、poll、epoll)
  • 信号驱动式I/O
  • 异步I/O(POSIX的aio系列函数)

同步、异步、阻塞、非阻塞#

同步与异步#

所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到了返回值。
而异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在调用发出后,被调用者会通知来通知调用者,或通过回调函数处理这个调用。

具体来说,同步与异步描述的是用户线程与内核的交互方式,这里所说的用户进程/线程和内核是以传输层为分割线的,传输层以上是指用户进程,传输层以下(包括传输层)是指内核(处理所有通信细节,发送数据,等待确认,给无序到达的数据排序等,这四层是操作系统内核的一部分)。同步是指用户线程发起IO请求后需要等待或者轮询内核IO操作,完成后才能继续执行。异步是指用户线程发起IO请求后仍继续执行,当内核IO操作完成后回通知用户线程,或者调用用户线程注册的回调函数。

阻塞与非阻塞#

阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态。

阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。

具体来说,用户线程调用内核IO(read、write函数)操作的方式,阻塞指IO操作需要彻底完成后才能返回用户空间,否则就一直挂起。

而非阻塞时指IO操作被调用后立即返回给用户一个状态值,无需等待IO操作彻底完成。

在客户端输入字符后发生了什么#

在介绍这几种模型之前,先总结一下一个输入操作所包含的两个不同的阶段:

  1. 等待数据准备好
  2. 从内核向进程复制数据

UNP中提到:

对于一个套接字上的输入操作,第一步通常涉及等待数据从网络中到达,当所等待分组到达时,它被复制到内核中的某个缓冲区。

第二步就是把数据从内核缓冲区复制到应用缓冲区。

几种IO模型介绍#

  1. 阻塞IO就是那种recv, read,一直等,等到有了数据才返回;
  2. 非阻塞IO就是立即返回,设置描述符为非阻塞,但是要进程自己一直检查是否可读;
  3. IO复用其实也是阻塞的,不过可以用来等很多描述符,比起阻塞有了进步,可以算有点异步了,但需要阻塞着检查是否可读。对同一个描述符的IO操作也是有序的。
  4. 信号驱动采用信号机制等待,有了更多的进步,不用监视描述符了,而且不用阻塞着等待数据到来,被动等待信号通知,由信号处理程序处理。但对同一个描述符的IO操作还是有序的。
  5. 异步IO,发送IO请求后,不用等了,也不再需要发送IO请求获取结果了。等到通知后,其实是系统帮你把数据读取好了的,你等到的通知也不再是要求你去读写IO了,而是告诉你IO请求过程已经结束了。你要做的就是可以处理数据了。且同一个描述符上可能同时存在很多请求。

其中IO服用和信号驱动,在处理业务逻辑上可以说有异步,但在IO操作层面上来说还是同步的

posix.1严格定义的异步IO是要求没有任何一点阻塞,而上述的前面四个(阻塞IO,非阻塞IO,IO复用,信号驱动)都不同程度阻塞了,而且都有一个共同的阻塞: 内核拷贝数据到进程空间的这段时间需要等待。