OS中阻塞与挂起的区别&sleep()的实现原理

阻塞 VS 挂起

阻塞与挂起都是进程的状态,但他们有一些相似之处,也有一些区别,下面先对他们进行概述,再进行比较

阻塞:正在执行的进程由于发生某时间(如I/O请求、申请缓冲区失败等)暂时无法继续执行。此时引起进程调度,OS把处理机分配给另一个就绪进程,而让受阻进程处于暂停状态,一般将这种状态称为阻塞状态。

挂起:由于系统和用户的需要引入了挂起的操作,进程被挂起意味着该进程处于静止状态。如果进程正在执行,它将暂停执行,若原本处于就绪状态,则该进程此时暂不接受调度。

共同点

  1. 进程都暂停执行
  2. 进程都释放CPU,即两个过程都会涉及上下文切换

不同点

  1. 对系统资源占用不同:虽然都释放了CPU,但阻塞的进程仍处于内存中,而挂起的进程通过“对换”技术被换出到外存(磁盘)中。
  2. 发生时机不同:阻塞一般在进程等待资源(IO资源、信号量等)时发生;而挂起是由于用户和系统的需要,例如,终端用户需要暂停程序研究其执行情况或对其进行修改、OS为了提高内存利用率需要将暂时不能运行的进程(处于就绪或阻塞队列的进程)调出到磁盘
  3. 恢复时机不同:阻塞要在等待的资源得到满足(例如获得了锁)后,才会进入就绪状态,等待被调度而执行;被挂起的进程由将其挂起的对象(如用户、系统)在时机符合时(调试结束、被调度进程选中需要重新执行)将其主动激活

sleep()

之所以将sleep一起讨论,是因为sleep是一个很常见的系统调用,在很多编程语言中,也有对应的函数,所以一起讨论可以明白sleep的过程与阻塞和挂起在底层的效果又有哪些区别。

sleep():进程、线程或任务(Linux中不区分进程与线程,都称为task)可以sleep,这会导致它们暂停执行一段时间,直到等待的时间结束才恢复执行或在这段时间内被中断。

OS中sleep()的实现

sleep()在OS中的实现的大概流程:

  • 挂起进程(或线程)并修改其运行状态
  • 用sleep()提供的参数来设置一个定时器。
  • 当时间结束,定时器会触发,内核收到中断后修改进程(或线程)的运行状态。例如线程会被标志为就绪而进入就绪队列等待调度。

PS:关于第二点在这里要介绍一些背景知识:可变定时器(variable timer)一般在硬件层面是通过一个固定的时钟和计数器来实现的,每经过一个时钟周期将计数器递减,当计数器的值为0时产生中断。内核注册一个定时器后可以在一段时间后收到中断。

在Linux下,sleep()的实现流程大概如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
///时钟编程 alarm()
void wakeUp()
{
printf("please wakeup!!/n");
}
int main(void)
{
printf("you have 4 s sleep!/n");
signal(SIGALRM,wakeUp);
alarm(4);
//将进程挂起
pause();
printf("good morning!/n");
return EXIT_SUCCESS;
}

综上所述,内核的sleep()函数是在挂起原语的基础上利用定时器实现的。

编程语言中sleep()的实现

首先希望大家都了解用户线程和内核线程的概念。编程语言中的线程(用户线程)与内核线程是有着一定的映射关系的。下面以Java为例,解释一下用户线程与内核线程的映射关系,不过多涉及实现细节。想要了解用户线程和内核线程的关系模型,见 线程的三种实现模型

Java线程API通常使用宿主系统的线程库来实现,也就是说,在Windows中,Java线程使用Win32 API来实现,而在Linux和Unix系统中使用Pthread。而且,JVM规范并没有指明Java线程如何被映射到底层的OS,而是让特定的JVM实现来决定。例如,在Window XP中采用一对一模型,而对于Solaris系统,刚开始采用多对一模型,从Solaris 9开始采用多对多模型。

看到这里大家应该明白了,用户线程的sleep()正是利用内核提供的sleep()来实现的,没有内核的支持,用户线程也只能忙等直到规定的时间结束。

参考资料:

  1. 《计算机操作系统(第四版)》,汤子瀛等著
  2. 《操作系统概念(第七版)》,Silberschatz等著,郑扣根译
  3. Quora 问答
  4. http://blog.csdn.net/freezgw1985/article/details/5631922
  5. https://en.wikipedia.org/wiki/Sleep_(system_call)
  6. http://man7.org/linux/man-pages/man3/sleep.3.html
  7. http://man7.org/linux/man-pages/man2/nanosleep.2.html
  8. http://man7.org/linux/man-pages/man2/alarm.2.html