睡眠 (系统调用)

电脑程序(或进程、任务或线程等)进入不活跃状态并持续一段时间,称为“睡眠”。当引发程序睡眠的代码所设定的内部计时器归零,或是此程序收到唤醒信号或中断时,程序将恢复执行。

睡眠中的程序也有可能不经唤醒而直接被终止。

用法

睡眠指令通常需要输入一个参数,以说明程序睡眠至少多长时间后才恢复执行。这一时间参数的单位通常是秒[来源请求],有些操作系统可以提供更高精度,例如以毫秒或微秒作为单位。

Windows

Windows 操作系统中,Sleep()函数仅有一个参数,即以毫秒计的睡眠时间。[1]Sleep()函数包含于 kernel32.dll 中,但是原生操作系统的批处理文件却并不支持这一指令。安装诸如Windows 2003 Resource Kit等运行环境后,用户便可使用这一指令。[2]

Unix

类Unix操作系统中,sleep()函数使用以秒做单位的无符号整数作为输入参数。[3]如果需要更精确的控制睡眠时间,可以使用 nanosleep()函数。[4]

C语言样例

Windows 操作系统中:

Sleep(2*1000);  // 睡眠2秒

类Unix操作系统中:

sleep(2);       // 睡眠2秒

底层功能

 
当使用 ps -aux 指令列出当前内存中的所有进程时,列标题为 STAT 的列内容就是进程当前的状态。其中含有标识 S 的进程就在睡眠状态(R为运行状态)
图中没有进程处于不可中断睡眠。

睡眠使得线程或进程让出他们占用的剩余时间片,并在 “不在运行”状态等待一段时间。虽然这段程序在睡眠前被设定了最短睡眠时长,可这并不意味着这段时间结束后此程序能够立即恢复运作——这取决于 调度程序 的分配、等待唤醒的程序的优先级和其他一些因素。在 POSIX 系统中, nanosleep 及其他类似的系统指令会被中断信号中断,相应程序也会停止睡眠。而 sleep 库函数却不尽相同,在一些老一点的操作系统中,它是通过调用 alarm 系统指令实现的,因而必须通过送信号的方式才能运作。

在Windows中,由于除了终止运行信号外没有专门的信号可以用于打断,睡眠函数是不可中断的。不过,Windows提供了 SleepEX 函数来实现通过 APC 调用控制睡眠进程的唤醒。

严格说来,一个线程也可以由于其他线程终止运行或抛出异常被唤醒。

使用场景

一些系统程序在事件轮询过程中从不被终止,只是在每个周期开始前进入睡眠状态,等待着某些事件的发生唤醒它们。当收到事件之后,它们就被唤醒去处理这些事件,然后进入下一个周期的等待。

还有一些程序周期性的从睡眠中恢复运行并收集事件。当运行恢复,程序拉取事件列表和状态变更情况,然后针对睡眠时发生的事件做相应处理,完成后再次进入睡眠,等待下一个时隙。这类事件被称作心跳事件,或“持久连接[5]”信号,可由上述类型的周期性程序发送。

sleep()函数还在程序执行节奏需要放缓的时候被调用。例如长时间高负荷运行程序时应有包含睡眠函数的代码来减轻硬件过热带来的麻烦[6],或是解决遗留代码中不为人知的奇妙程序错误。相比于使用仿真器实现的周期平衡的程序,睡眠-运行周期发生的程序的缺点在于如果切换速度不够快,交互式程序就会有明显的延迟,如果太快,唤醒时间就太短以至于无法完成全部工作。[7]

不可中断睡眠

不可中断睡眠指的是在此状态的程序无法对外界信号做出任何立即的响应,只有在为它提供足够的正在等待的资源或是睡眠时间结束它才会开始运行。这类睡眠常见于硬件驱动程式等待磁碟或网络输入输出结束。当进程处于不可中断睡眠状态,睡眠期间的信号会不断堆积到队列里面去。

在类Unix操作系统中,使用指令ps -l可以列出当前终端的全部进程,其中标识进程状态的列使用代码 D 表示不可中断睡眠[8]。这类进程连SIGKILL这样的无条件终止都没办法结束进程,结束他们最简单的方法就是重新启动系统。[9][10]但Linux操作系统在2.6.25版本[11]以后引入了一种新的睡眠状态TASK_KILLABLE,处于这种状态的进程在ps命令中同样以代码D显示,这样的进程只可以被SIGKILL信号终止,用于在某些场景下替代原来的不可中断睡眠,比如当进程调用NFS接口得不到应答,这时可以通过kill -9命令来终止进程。

另见

参考文献

  1. ^ MSDN Library Reference - Sleep(). [2019-06-25]. (原始内容存档于2018-01-22). 
  2. ^ batch file sleep command. [2019-06-25]. (原始内容存档于2016-03-03). 
  3. ^ UNIX Man Page - SLEEP(3) 互联网档案馆存档,存档日期2011-07-20.
  4. ^ nanosleep.3p - Linux manual page. [2019-06-25]. (原始内容存档于2020-10-20). 
  5. ^ 关于 HTTP 协议中的 keep-alive | Laravel China 社区. learnku.com. [2019-06-25]. 
  6. ^ mion. BES – Battle Encoder Shirase 1.6.3 (stable) & 1.7.4 for Windows 7/XP/2000. mion.faireal.net. 2016-12-06 [2017-02-09]. (原始内容存档于2021-04-15). 
  7. ^ Marletta, Angelo. CPULIMIT. GitHub. 2015-03-12 [2017-02-09]. 
  8. ^ top(1) - Linux manual page. man7.org. 2016-12-12 [2017-02-09]. (原始内容存档于2021-02-18). 
  9. ^ Processes in an Uninterruptible Sleep (D) State. Novell. 2009-02-21 [2017-02-09]. (原始内容存档于2018-02-01). 
  10. ^ Fusco, John. The Linux Programmer's Toolbox. Pearson Education. 2007-03-06. ISBN 9780132703048. 
  11. ^ Change log of linux 2.6.25.. [2024-04-12]. (原始内容存档于2024-05-02).