睡眠/纳米睡眠是否通过忙碌的等待计划工作?

Kam*_*Kam 6 c c++ posix sleep

我想知道睡眠/纳米睡眠是如何实现的?考虑以下代码:

{ // on a thread other than main() thread
  while(1)
  {
    //do something
    sleep(1);
  }
}
Run Code Online (Sandbox Code Playgroud)

CPU是否会进行恒定的上下文切换以检查是否完成了1秒的睡眠(即内部忙等待).

我怀疑这种方式是否有效,效率太低.但那又如何运作?

同样的问题适用于nanosleep.

注意:如果这是特定于实现/操作系统的,那么我怎样才能实现一个不会导致上下文切换的更有效的方案呢?

Bre*_*dan 6

实现的典型方法sleep()nanosleep()是成任何缩放OS的调度器使用(而四舍五入)参数转换和当前时间添加到它形成一个“绝对苏醒时间”; 然后告诉调度程序在达到“绝对唤醒时间”之前不要给线程 CPU 时间。不涉及忙等待。

请注意,操作系统的调度程序使用的任何规模通常取决于可用的硬件和/或用于计时的硬件。它可以小于纳秒(例如,在“TSC 截止时间模式”中使用的 80x86 上的本地 APIC)或大至 100 毫秒。

另请注意,操作系统保证延迟不会少于您的要求;但通常不能保证它不会更长,并且在某些情况下(例如重负载系统上的低优先级线程),延迟可能比请求的大得多。例如,如果您要求休眠 123 纳秒,那么您可能会在调度程序决定它可以为您提供 CPU 时间之前休眠 2 毫秒,然后在调度程序实际为您提供 CPU 时间之前可能需要另外 500 毫秒(例如,因为其他线程正在使用 CPU)。

某些操作系统可能会尝试减少这种“睡眠时间比请求的时间长得多”的问题,而某些操作系统(例如,专为硬实时设计)可能会为延迟之间的最短时间提供某种保证(有限制 - 例如受线程优先级的约束)到期并取回 CPU。为此,操作系统/内核会将参数转换为操作系统调度程序使用的任何比例(向下舍入而不是向上舍入),并且可能会“以防万一”减去一小部分;以便调度程序在请求的延迟到期之前(而不是之后)唤醒线程;然后当线程获得 CPU 时间时(在上下文切换到线程的成本之后,并且可能在预取线程保证使用的各种缓存行之后),内核将忙于短暂等待,直到延迟实际到期。

例如,如果您要求休眠 123 纳秒,那么调度程序可能不会为您提供 100 纳秒的 CPU 时间,然后它可能会花费 10 纳秒切换到您的线程,然后它可能会忙于等待剩余的 13 纳秒。即使在这种情况下(忙等待完成),它通常不会忙等待延迟的整个持续时间。然而,如果延迟极短,内核将只做最后的忙等待。

最后,还有一个特殊情况可能值得一提。在 POSIX 系统sleep(0);上通常被滥用为yield(). 我不太确定这种做法的合法性 - 调度程序不可能支持类似的事情,yield()除非调度程序愿意在更重要的工作等待时浪费 CPU 时间做不重要的工作。


Rom*_*nko 2

这里不能保证精确的实现,但您可以期待一些属性。

通常sleep(3) 是相当不准确的,Linux 的“man sleep 3”状态甚至可以使用SIGALM(signals) 来实现。所以这绝对不是性能的问题。它绝对与自旋锁无关,因此不会是 CPU 密集型的。

nanosleep是完全不同的动物,甚至可以使用自旋锁来实现。哪个更重要,至少在 Linux 中,nanosleepman 在第 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);
   }
Run Code Online (Sandbox Code Playgroud)

如果您需要在线程中休眠某些事件并且该事件可以链接到文件描述符,则这是经过验证的机制。

  • 注意:此问题当前标记为 [tag:posix],而不是 [tag:linux]。在 POSIX 中,“nanosleep”不能用自旋锁实现。线程需要被挂起。Linux 中是否存在这种情况是另一个问题,因为 Linux 不兼容 POSIX。 (3认同)