为了更加方便地理解这四个概念,首先提出一个进程“时间片”的概念:现代的操作系统会 分配一个时间片给进程(例如 100ms),进程每次占用处理器运行的时间不超过这个时间。

阻塞、非阻塞

阻塞和非阻塞是系统中进程的状态。用户进程如果想使用内核提供的功能,那么就需要使用 内核制定的接口:“系统调用”。内核提供的这些系统调用,可以分为两大类:阻塞和非阻 塞。一旦进程调用了阻塞系统调用,并且确实阻塞了,那么它就会“被动”地放弃处理器。

非阻塞的系统调用,无论成功与否,它都不会阻塞。

同步、异步

同步和异步是进程请求资源时,访问资源的模型。而在进程中实现这两个处理模型,需要用 到内核提供的接口,也就是系统调用。

同步的处理模型是这样的:

  • 请求
  • 等待,如果资源可用,则可能不用等待
  • 处理

这个模型比较容易理解。在发出请求动作时,可以使用会阻塞的系统调用,这时候进程会被 换出处理器,它剩下的时间片都不可用了。如果使用非阻塞的系统调用,那么它需要自己去 检查资源什么时候可用。在用户程序中,同步的接口常用的有 spinlock 和 mutex。对于 spinlock 来说,它没有被换出处理器,而是一直占用着处理器,但什么都不做;对于mutex, 它是“主动”地让出处理器,但它有一个条件:如果资源就绪了,那么它要马上得到处理器, 这里看来,mutex 可以使用阻塞的接口实现。

对于异步的模型,处理的控制流是这样的:

  • 请求,如果资源可用,则处理
  • 如果资源不可用,希望能做其他工作
  • 得到一个请求完成的通知,开始处理

因为异步的模型需要在请求没有完成之前,不要浪费剩下的时间片去等待,它想利用剩下的 时间片去做其他的事情。也就是说,在发起请求到等到完成请求通知的这一段时间之内,它 希望能够继续占用处理器。

如果使用阻塞的系统调用,那么它将被换出处理器,剩下的时间片就白白地丢失了。它不能 接受剩下的时间片都没有的这个事情,因为它有其他工作要做。如果它没有工作要做呢?能 不能使用阻塞的系统调用?对于系统整体来说,系统肯定是希望进程能够让其他进程运行的, 因为不做工作的进程就是在浪费资源;但对于这个进程来说,如果它换出了处理器,那么就 可能会错过在剩下的时间片处理请求的机会了。如果系统内核能够提供一个阻塞接口,能够 在进程接到它需要的通知就唤醒它,那么就是一个双赢的选择。所以说,阻塞的系统调用可 能是可以实现一些有限制的异步模型的。

对于请求没有完成之前,有其他工作要做的进程来说,使用折中的可唤醒的阻塞系统调用是 不能接受的。因此,它必须使用非阻塞的系统调用,并且这个系统调用能够在请求完成时, 通知这个进程。