如何睡几微秒

时间:2011-02-13 20:55:13

标签: c linux real-time glibc libc

请考虑以下代码:

#include <stdio.h>
#include <time.h>
#include <math.h>

// Compile with gcc -lrt -lm -o test_clock test_clock.c

#define CLOCK CLOCK_MONOTONIC

int main(int argc, char** argv) {
    double temp, elapsed;
    int j;
    struct timespec requestStart, requestEnd, req;

    // Pseudo-sleep
    clock_gettime(CLOCK, &requestStart);
    temp = 0;
    for(j=0; j < 40; j++)
        temp += sin(j);
    clock_gettime(CLOCK, &requestEnd);
    elapsed = ( requestEnd.tv_sec - requestStart.tv_sec ) / 1e-6
                 + ( requestEnd.tv_nsec - requestStart.tv_nsec ) / 1e3;
    printf("Elapsed: %lf us\n", elapsed);

    // Nanosleep
    clock_gettime(CLOCK, &requestStart);
    req.tv_nsec = 5000;
    req.tv_sec = 0;
    clock_nanosleep(CLOCK, 0, &req, NULL);
    clock_gettime(CLOCK, &requestEnd);
    elapsed = ( requestEnd.tv_sec - requestStart.tv_sec ) / 1e-6
                 + ( requestEnd.tv_nsec - requestStart.tv_nsec ) / 1e3;

    printf("Elapsed: %lf us\n", elapsed);

}

在我的2.6.32系统上,结果是

Elapsed: 5.308000 us
Elapsed: 69.142000 us

我同意这很可能是因为nanosleep()要求内核重新安排进程。我怎么能避免这个?我希望保留CPU的所有权,并且在一段精确的时间内闲置。

7 个答案:

答案 0 :(得分:8)

如果您希望应用程序能够尽可能精确地“睡眠”,请首先将您的应用程序置于实时状态

  • 为您的程序/线程使用实时调度程序类:SCHED_FIFO或SCHED_RR
  • 提升您的程序/线程优先级
  • 如果你要“睡眠”的时间少于内核要处理的最小数量,请手动忙碌

查看http://www.drdobbs.com/184402031

另外一个问题:nanosleep high cpu usage?

答案 1 :(得分:7)

操作系统调度程序不会执行任何操作,例如“哦,将此线程从处理器中取出正好86个时钟周期然后重新启用”。

你放弃了处理器,你放弃了处理器。操作系统会让你重新回到它的感觉。您可能需要等到运行的其他任何东西放弃处理器才能重新开始。

答案 2 :(得分:2)

好吧,你必须学会​​使用它,因为手册页说明了部分:the actual time slept may be longer, due to system latencies and possible limitations in the timer resolution of the hardware: - )

现在关于你的问题的回答,我最好的猜测是因为你的第一个循环是在进程中运行的。换句话说,没有涉及上下文切换,因为您正在运行CPU,并且您将在调度程序为您提供的100ms量程内完成所有工作。

然而,由于您明确要求进入睡眠状态,nanosleep很可能会将您转出。在持续时间结束之前将流程置于紧密的while循环中,效率不会太低: - )

这意味着您受到调度程序的所有变幻莫测的影响,包括另一个进程可能完全耗尽其量子的事实,因此您的进程可能至少在100ms之外。在一个负载很重的系统上,它可能已经存在了很长一段时间。

答案 3 :(得分:2)

// busy wait for 10 microseconds
struct timespec ttime,curtime;

// get the time
clock_gettime(CLOCK_REALTIME,&ttime);

// clear the nanoseconds and keep the seconds in order not to overflow the nanoseconds
ttime.tv_nsec = 0;

// set it back
clock_settime(CLOCK_REALTIME,&ttime);

// get the time again 
clock_gettime(CLOCK_REALTIME,&ttime);

// increase the nano seconds by 10*1000
ttime.tv_nsec += 10000;

// loop
while(true){
  clock_gettime(CLOCK_REALTIME,&curtime);
  if (curtime.tv_nsec > ttime.tv_nsec)
    break;
}

//它比usleep要好得多。

答案 4 :(得分:1)

您可以使用usleep方法以微秒为单位获得睡眠。

答案 5 :(得分:0)

效率 - 允许以几个时钟周期的精度切换和切换任务的Os将会做很少的事情。

有专门的操作系统可以执行此操作 - 但在常规硬件上,您需要为虚拟机管理程序支付大量开销

答案 6 :(得分:0)

这是一个持久的答案 - 我不知道相关的linux内部,希望专家可以来并清理它。

一种可能性是69us只是解决问题的原始开销,然后重新安排线程。即使睡眠很短,内核可能会做很多工作来执行上下文切换(或半个上下文切换,如果没有什么可以安排),然后几乎立即撤消它。我不知道在典型的PC上“应该”花多长时间。

如果这不能解释它,调度程序通常具有“时间片”的概念,即在调度程序考虑切换它之前调度线程将保持运行多长时间,除非它自行计划或其他具有较高优先级的东西变得可调度。内核将在时间片结束时触发中断的低级定时器(除了为某些其他事件(例如可以解除阻塞线程的I / O)触发的中断)。当时间片结束时,调度程序可以决定是继续使用相同的线程,还是切换到另一个线程。

所以看起来好像在你睡觉时,(a)调度程序实际上并没有设置一个定时器,它会使你的线程在请求​​的时间内可调度,它只是在等待一个时间片,所以CPU正在空闲超过必要的;或者(b)它使你的线程在所要求的时间内可以调度,但当你通过睡眠放弃执行时,其他一些具有相同优先级的线程进入,并且调度程序没有理由更喜欢你,直到它“轮流” “再次根据调度程序通常用来决定要调度的线程的任何规则。

但是,69us很难成为一个时空的人工制品。

你似乎有一个基本的解决方案 - 你可以通过坐在循环中检查时间来延迟很短的时间,就像一个螺旋锁。然而,正如其他人所说的那样,在非实时系统中,或多或少根据定义,您无法要求调度程序在任何特定时间运行您的线程。即使在实时系统中,如果您正在与同等优先级的线程竞争,您可能会失败,如果您与优先级较高的线程竞争,输掉。