1

更新:修复代码中的增量计算,问题仍然存在

伙计们,您能否解释一下为什么我会使用以下代码不时得到非常奇怪的结果:

#include <unistd.h>
#include <sys/time.h>
#include <stdio.h>

int main()
{
  struct timeval start, end;
  long mtime1, mtime2, diff;

  while(1)
  {
    gettimeofday(&start, NULL);

    usleep(2000);

    gettimeofday(&end, NULL);

    mtime1 = (start.tv_sec * 1000 + start.tv_usec/1000.0);
    mtime2 = (end.tv_sec * 1000 + end.tv_usec/1000.0);

    diff = mtime2 - mtime1;
    if(diff > 10) 
        printf("WTF: %ld\n", diff);
  }

  return 0;
}

(您可以使用以下命令编译和运行它:gcc test.c -o out -lrt && ./out

我正在经历的是几乎每秒甚至更频繁的diff变量的零星大值,例如:

$ gcc test.c -o out -lrt && ./out 
WTF: 14
WTF: 11
WTF: 11
WTF: 11
WTF: 14
WTF: 13
WTF: 13
WTF: 11
WTF: 16

这怎么可能?是操作系统的错吗?它是否做了太多的上下文切换?但是我的盒子是空闲的(平均负载:0.02、0.02、0.3)。

这是我的 Linux 内核版本:

$ uname -a
Linux kurluka 2.6.31-21-generic #59-Ubuntu SMP Wed Mar 24 07:28:56 UTC 2010 i686 GNU/Linux
4

5 回答 5

4

睡眠功能只能确保您至少睡一定的时间。由于 Linux 不是实时操作系统,因此您无法确定它只会休眠您想要的时间。这是一个问题,因为您不能指望该值。正如您所指出的,睡眠时间确实很大。

Linux 调度程序不能保证这一点。使用实时操作系统,您可以做到这一点。

你的公式在某种程度上是错误的,但我认为这不可能是你睡眠时间如此之长的原因。我用这个片段检查了两个公式,我得到了相同的结果:

#include <unistd.h>
#include <sys/time.h>
#include <time.h>
#include <stdio.h>

int main()
{
  struct timeval start, end;
  long mtime, mtime2, start_time, end_time, seconds, useconds;

  while(1)
  {
    gettimeofday(&start, NULL);

    usleep(2000);

    gettimeofday(&end, NULL);

    seconds  = end.tv_sec  - start.tv_sec;
    useconds = end.tv_usec - start.tv_usec;

    mtime = ((seconds) * 1000 + useconds/1000.0) + 0.5;

    start_time = ((start.tv_sec) * 1000 + start.tv_usec/1000.0) + 0.5;
    end_time = ((end.tv_sec) * 1000 + end.tv_usec/1000.0) + 0.5;

    mtime2 = end_time - start_time;

    if(mtime > 10 || mtime2 > 10)
    {
      printf("WTF: %ld\n", mtime);
      printf("WTF2: %ld\n", mtime2);
    }
  }

  return 0;
}

结果 :

$ gcc test.c -o out -lrt && ./out
WTF: 11
WTF2: 12
WTF: 21
WTF2: 21

我认为这是错误的,因为 useconds 部分是循环的,可能会导致很大的负面差异。但这不会像您使用有符号长整数那样导致如此大的时间......

my2cents

编辑:来自 man nanosleep :

nanosleep() 的当前实现基于正常的内核计时器机制,其分辨率为 1/HZ 秒(参见 time(7))。因此,nanosleep() 至少会暂停指定的时间,但它可能需要比指定的时间长 10 毫秒,直到进程再次可运行。出于同样的原因,在 *rem 中传递信号的情况下返回的值通常四舍五入到 1/HZ s 的下一个较大倍数。

于 2010-05-21T13:28:57.400 回答
2

My guess is that it's related to the OS. Try running the process at realtime priority (see the chrt program) and see if that helps.

On another note, you're calculating mtime incorrectly. Here' a routine that I use, although it's for struct timespec rather than struct timeval (nanoseconds instead of microseconds) the principle should be clear:

timespec diff(timespec start, timespec end)
{
    timespec temp;
    if ((end.tv_nsec - start.tv_nsec) < 0) {
        temp.tv_sec = end.tv_sec - start.tv_sec - 1;
        temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec;
    } else {
        temp.tv_sec = end.tv_sec - start.tv_sec;
        temp.tv_nsec = end.tv_nsec - start.tv_nsec;
    }
    return temp;
}
于 2010-05-21T13:14:51.800 回答
1

找到了,看man page

http://linux.die.net/man/3/usleep

通过系统计时器的粒度。

这是10毫秒afaik。因此,usleep可以在重新安排流程之前很久就过期。

它也与您获得的值一致,这些值处于“正常”时间片的数量级。

于 2010-05-21T13:56:03.513 回答
0

你的公式是错误的。您必须以相同的比例转换两次。在您的示例中,女士。

double mtime1 = (start.tv_sec * 1000 + start.tv_usec/1000.0) ;
double mtime2 = (end.tv_sec * 1000 + end.tv_usec/1000.0) ;

double diff = mtime2 - mtime1;
if(diff > 10) 
  printf("WTF: %ld\n", diff);

您必须减去更正的值

示例: t1 = 1.999999 t2 = 2.000001所以间隔为 2 µs

使用您的公式计算:

2 - 1 == 11 - 9999999给出 (1 * 1000 - 999998 / 1000) + 0.5== 0.502 的结果,这显然是错误的。

我的方法给出:

mtime1 = (1 * 1000 + 999999 / 1000) = 1999.999
mtime2 = (2 * 1000 +      1 / 1000) = 2000.001

2000.001 - 1999.999 = 0.002 ms
于 2010-05-21T13:12:22.470 回答
0

我已经做过这样的措施,我的结论是完全一样的。在 Windows 和 Linux 上同样发生。

在 10^-n 秒内构建直方图的程序给出以下结果。

0.1 0
0.01 0
0.001 0
0.0001 2
1e-05 24
1e-06 69726023
Total: 69726049
Duration: 6.47403 seconds.
Average: 0.0928495 microseconds.

但请注意,这是在一个全新的系统上。我记得一年前在 2004 年的系统上使用过它,并且每秒在 0.01 范围内(超过 10 毫秒)有几次点击。

于 2010-05-21T13:13:27.327 回答