我想知道睡眠/纳米睡眠是如何在内部实施的?请考虑以下代码:
<div id="circuit"><div class='inner'></div></div>
CPU是否会进行恒定的上下文切换以检查1秒的睡眠是否完成(即内部忙等待)。
我怀疑这种方式是否有效,效率太低。但那它是如何运作的?
同样的问题适用于nanosleep。
注意:如果这是特定于实现/操作系统的,那么我怎样才能实现一个不会导致上下文切换的更有效的方案呢?
答案 0 :(得分:4)
实现sleep()
和nanosleep()
的典型方法是将参数转换为OS调度程序使用的任何比例(在向上舍入时)并将当前时间添加到其中以形成&#34;绝对叫醒时间&#34 ;;然后告诉调度程序不要给线程CPU时间,直到那之后&#34;绝对唤醒时间&#34;已达到。没有忙碌的等待。
请注意,OS调度程序使用的任何比例通常取决于可用的硬件和/或用于计时的硬件。它可以小于纳秒(例如,在#x; TSC截止日期模式&#34;中使用80x86上的本地APIC)或大到100毫秒。
另请注意,操作系统保证延迟不会低于您的要求;但是通常不保证它不会更长并且在某些情况下(例如,在高负载系统上的低优先级线程),延迟可能比请求的要大得多。例如,如果您要求休眠123纳秒,那么您可能会在调度程序确定它可以为您提供CPU时间之前休眠2毫秒,然后在调度程序实际为您提供CPU时间之前可能是另一个500毫秒(例如,因为其他线程正在使用CPU。)
有些操作系统可能会尝试减少这种情况,并且睡眠时间比请求的时间长得多。问题,并且一些OS(例如,设计用于硬实时)可以提供某种保证(具有限制 - 例如受线程优先级限制)在延迟期满和获得CPU之间的最小时间。为此,OS /内核会将参数转换为OS调度程序使用的任何比例(在向下舍入而不向上舍入的情况下)并且可以减去一小部分&#34;以防万一&#34 ;;这样调度程序就会在请求的延迟到期之前唤醒线程(而不是之后);然后当线程被给予CPU时间时(在上下文切换到线程的成本之后,并且可能在预先获取线程保证使用的各种高速缓存行之后),内核将忙于等待直到延迟实际到期。这允许内核将控制权传递回非常接近延迟到期的线程。
例如,如果你要求休眠123纳秒,那么调度程序可能不会给你100纳秒的CPU时间,那么它可能花费10纳秒切换到你的线程,然后它可能忙等待剩余的13纳秒。即使在这种情况下(忙碌等待完成),它通常也不会等待延迟的整个持续时间。但是,如果延迟非常短,内核只会进行最后的繁忙等待。
最后,有一个特殊情况值得一提。在POSIX系统上sleep(0);
通常被滥用为yield()
。我不太确定这种做法有多合理 - 调度程序不可能支持yield()
之类的东西,除非调度程序愿意浪费CPU时间做更重要的工作而不重要的工作。
答案 1 :(得分:2)
这里不保证确切的实施,但你可以期待一些属性。
通常sleep
(3)是非常不准确的,因为Linux'人睡3'状态甚至可以使用SIGALM
(信号)实现。所以绝对不是性能。它绝对不是关于自旋锁,所以不能用于CPU。
nanosleep
是完全不同的动物,即使使用自旋锁也可以实现。更重要的是,至少在Linux nanosleep
中,man在第2节中表示它是系统调用,所以至少它应该包括切换到内核模式。你真的需要它的高分辨率吗?
<强>更新强>
在我看到您的评论时,我建议将select()
用作man select 3
州:
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int
main(void)
{
fd_set rfds;
struct timeval tv;
int retval;
/* Watch stdin (fd 0) to see when it has input. */
FD_ZERO(&rfds);
FD_SET(0, &rfds);
/* Wait up to five seconds. */
tv.tv_sec = 5;
tv.tv_usec = 0;
retval = select(1, &rfds, NULL, NULL, &tv);
/* Don't rely on the value of tv now! */
if (retval == -1)
perror("select()");
else if (retval)
printf("Data is available now.\n");
/* FD_ISSET(0, &rfds) will be true. */
else
printf("No data within five seconds.\n");
exit(EXIT_SUCCESS);
}
如果您需要在某个事件的线程中睡眠并且此事件可以链接到文件描述符,那么它已被证明是一种机制。
答案 2 :(得分:1)
sleep
和nanosleep
的POSIX规范说(强调我的)
sleep()函数将导致调用线程暂停执行,直到参数seconds指定的实时秒数已经过去或者信号被传递给调用线程及其动作是调用信号捕获功能或终止进程。由于系统安排了其他活动,暂停时间可能比请求的时间长。
(来源:http://pubs.opengroup.org/onlinepubs/9699919799/functions/sleep.html。)
和
nanosleep()函数将导致当前线程暂停执行,直到rqtp参数指定的时间间隔已经过去或者信号被传递给调用线程,并且其动作是调用信号捕获功能或终止进程。暂停时间可能比请求的时间长,因为参数值被舍入到睡眠分辨率的整数倍或者由于系统调度其他活动。但是,除了被信号中断的情况外,暂停时间不应小于rqtp规定的时间,由系统时钟CLOCK_REALTIME测量。
(来源:http://pubs.opengroup.org/onlinepubs/9699919799/functions/nanosleep.html。)
我读到这一点,说POSIX兼容系统无法使用sleep
或nanosleep
的繁忙循环。调用线程需要暂停执行。
答案 3 :(得分:0)
&#34;我想知道内部如何实现睡眠/纳米睡眠?&#34;
它没有 一个 实现,但sleep()
和nanosleep()
的每个OS和POSIX兼容实现都是免费的他们如何实际实现此功能。
因此,在没有特定OS / POSIX库实现的更多上下文的情况下,询问它实际上是如何完成的。