9

考虑以下代码:

#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 的所有权,并且只是闲置一段精确的时间。

4

7 回答 7

10

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

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

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

还有另一个问题:nanosleep 高 cpu 使用率?

于 2011-02-13T22:31:43.970 回答
8

操作系统调度程序不会做任何类似“哦,将这个线程从处理器中取出正好 86 个时钟周期然后重新打开”之类的事情。

你放弃了处理器,你已经放弃了处理器。操作系统会在您愿意时让您重新开机。您可能必须等到其他正在运行的东西放弃处理器,然后才能偷偷溜回来。

于 2011-02-13T20:59:31.303 回答
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好多了。

于 2014-07-08T20:55:06.280 回答
2

好吧,您必须学会忍受它,因为手册页指出,部分原因是:the actual time slept may be longer, due to system latencies and possible limitations in the timer resolution of the hardware:-)

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

nanosleep但是,由于您明确要求让您入睡,因此很有可能将您转出。while在持续时间结束之前将您的流程置于紧密循环中不会效率低下:-)

这意味着您会受到调度程序的所有变幻莫测,包括另一个进程可能完全用完其量子的事实,因此您的进程可能至少在 100 毫秒内离开那里。在负载足够重的系统上,它可能会出现很长一段时间。

于 2011-02-13T21:04:46.627 回答
1

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

于 2012-10-09T05:47:05.603 回答
0

这是一个持有答案 - 我不知道相关的 linux 内部结构,希望专家能来解决它​​。

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

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

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

不过,69us 很短,不能成为时间切片的产物。

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

于 2011-02-13T22:22:41.480 回答
0

效率——一个允许以几个时钟周期的精度切换任务的操作系统将几乎没有其他作用。

有专门的操作系统可以做到这一点 - 但在常规硬件上,您需要为管理程序支付大量开销

于 2011-02-13T21:07:21.577 回答