gpt4 book ai didi

C、clock_gettime,返回不正确的纳秒值?

转载 作者:太空宇宙 更新时间:2023-11-04 01:49:51 27 4
gpt4 key购买 nike

我正在编写一个简单的程序,用于检查耗时是否超过 1 秒。我使用 clock_gettime() 获取开始时间,然后调用 sleep(5),获取新时间并检查差异是否大于 1;我睡了 5 秒,那么它应该大于 5,但是我的程序打印了一个奇怪的结果。

这是代码:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

int main()
{
struct timespec tv1,tv3,expected;
struct timespec nano1;

tv1.tv_nsec = 0;
tv3.tv_nsec = 0;

expected.tv_nsec = 400000;

if(clock_gettime(CLOCK_MONOTONIC,&tv3) == -1){
perror(NULL);
}

sleep(5);

if(clock_gettime(CLOCK_MONOTONIC,&tv1) == -1){
perror(NULL);
}


long nsec = tv1.tv_nsec - tv3.tv_nsec;

if(nsec>expected.tv_nsec){
printf("nsec runned: %ld nsec timeout: %ld\n",nsec,expected.tv_nsec);
}


printf("elapsed nanoseconds: %ld\n",nsec);


if((nsec>expected.tv_nsec))
printf("expired timer\n");

else
printf("not expired timer\n");

exit(EXIT_SUCCESS);
}

我的程序的输出是:

“经过的纳秒:145130”和“未过期超时”

问题出在哪里?

最佳答案

struct timespec 中表示的时间有两个组成部分:

  • tv_sec —一个time_t整数秒的值。
  • tv_nsec — 纳秒数的 32 位整数,0..999,999,999

您的计算没有考虑 tv_sec 之间的差异值。令人惊讶的是,纳秒值之间的差异与您所说的一样大,但并非不可能。要获得整体差异,您需要同时考虑 tv_sectv_nsec组件。

sub_timespec()

您可以使用如下函数减去两个值(以获得差异):

enum { NS_PER_SECOND = 1000000000 };

void sub_timespec(struct timespec t1, struct timespec t2, struct timespec *td)
{
td->tv_nsec = t2.tv_nsec - t1.tv_nsec;
td->tv_sec = t2.tv_sec - t1.tv_sec;
if (td->tv_sec > 0 && td->tv_nsec < 0)
{
td->tv_nsec += NS_PER_SECOND;
td->tv_sec--;
}
else if (td->tv_sec < 0 && td->tv_nsec > 0)
{
td->tv_nsec -= NS_PER_SECOND;
td->tv_sec++;
}
}

fmt_timespec

您可以使用如下函数将其格式化为具有指定小数位数的浮点值:

int fmt_timespec(const struct timespec *value, int dp, char *buffer, size_t buflen)
{
assert(value != 0 && buffer != 0 && buflen != 0);
if (value == 0 || buffer == 0 || buflen == 0)
{
errno = EINVAL;
return -1;
}
assert(dp >= 0 && dp <= 9);
if (dp < 0 || dp > 9)
{
errno = EINVAL;
return -1;
}
if ((value->tv_sec > 0 && value->tv_nsec < 0) ||
(value->tv_sec < 0 && value->tv_nsec > 0))
{
/* Non-zero components of struct timespec must have same sign */
errno = EINVAL;
return -1;
}

int len;
if (dp == 0)
len = snprintf(buffer, buflen, "%ld", value->tv_sec);
else
{
long nsec = value->tv_nsec;
long secs = value->tv_sec;
const char *sign = (secs < 0 || (secs == 0 && nsec < 0)) ? "-" : "";
if (secs < 0)
secs = -secs;
if (nsec < 0)
nsec = -nsec;
for (int i = 0; i < 9 - dp; i++)
nsec /= 10;
len = snprintf(buffer, buflen, "%s%ld.%.*ld", sign, secs, dp, nsec);
}
if (len > 0 && (size_t)len < buflen)
return len;
errno = EINVAL;
return -1;
}

问题代码的修订版本

header time_io.h声明 struct timespec 的格式和扫描函数; time_math.h header 声明了加减函数struct timespec值。拥有那么多 header 可能会过度划分代码。

#include "time_io.h"
#include "time_math.h"
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

enum { NS_PER_SECOND = 1000000000 };

int main(void)
{
struct timespec tv3;
if (clock_gettime(CLOCK_MONOTONIC, &tv3) == -1)
perror("clock_gettime()");

sleep(5);

struct timespec tv1;
if (clock_gettime(CLOCK_MONOTONIC, &tv1) == -1)
perror("clock_gettime()");

struct timespec td;
sub_timespec(tv3, tv1, &td);

int64_t ts_in_ns = td.tv_sec * NS_PER_SECOND + td.tv_nsec;

char buffer[32];
fmt_timespec(&td, 9, buffer, sizeof(buffer));

printf("Elapsed time: %s (%" PRId64 " nanoseconds)\n", buffer, ts_in_ns);

return 0;
}

运行示例:

Elapsed time: 5.005192000 (5005192000 nanoseconds)

在运行 macOS Sierra 10.12.6(终于有了 clock_gettime()——早期版本的 Mac OS X 不支持它)的 Mac 上,clock_gettime() 的分辨率是 1000 纳秒,实际上是微秒。因此,在 Mac 上,小数点后 3 位始终为零。

add_timespec()

为了完整性,您可以添加两个 struct timespec值:

void add_timespec(struct timespec t1, struct timespec t2, struct timespec *td)
{
td->tv_nsec = t2.tv_nsec + t1.tv_nsec;
td->tv_sec = t2.tv_sec + t1.tv_sec;
if (td->tv_nsec >= NS_PER_SECOND)
{
td->tv_nsec -= NS_PER_SECOND;
td->tv_sec++;
}
else if (td->tv_nsec <= -NS_PER_SECOND)
{
td->tv_nsec += NS_PER_SECOND;
td->tv_sec--;
}
}

scn_timespec()

而且“扫描”过程更困惑(输入通常比输出更困惑):

int scn_timespec(const char *str, struct timespec *value)
{
assert(str != 0 && value != 0);
if (str == 0 || value == 0)
{
errno = EINVAL;
return -1;
}
long sec;
long nsec = 0;
int sign = +1;
char *end;
/* No library routine sets errno to 0 - but this one needs to */
int old_errno = errno;

errno = 0;

/* Skip leading white space */
while (isspace((unsigned char)*str))
str++;

/* Detect optional sign */
if (*str == '+')
str++;
else if (*str == '-')
{
sign = -1;
str++;
}

/* Next character must be a digit */
if (!isdigit((unsigned char)*str))
{
errno = EINVAL;
return -1;
}

/* Convert seconds part of string */
sec = strtol(str, &end, 10);
if (end == str || ((sec == LONG_MAX || sec == LONG_MIN) && errno == ERANGE))
{
errno = EINVAL;
return -1;
}

if (*end != '\0' && !isspace((unsigned char)*end))
{
if (*end++ != '.')
{
errno = EINVAL;
return -1;
}
if (*end == '\0')
nsec = 0;
else if (isdigit((unsigned char)*end))
{
char *frac = end;
nsec = strtol(frac, &end, 10);
if (end == str ||
((nsec == LONG_MAX || nsec == LONG_MIN) && errno == ERANGE) ||
(nsec < 0 || nsec >= NS_PER_SECOND) || (end - frac > 9))
{
errno = EINVAL;
return -1;
}
for (int i = 0; i < 9 - (end - frac); i++)
nsec *= 10;
}
}

/* Allow trailing white space - only */
unsigned char uc;
while ((uc = (unsigned char)*end++) != '\0')
{
if (!isspace(uc))
{
errno = EINVAL;
return -1;
}
}

/* Success! */
value->tv_sec = sec * sign;
value->tv_nsec = nsec * sign;
errno = old_errno;
return 0;
}

关于C、clock_gettime,返回不正确的纳秒值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45883052/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com