C,clock_gettime,返回了不正确的纳秒值? [英] C, clock_gettime, returned incorrect nanosecond value?

查看:143
本文介绍了C,clock_gettime,返回了不正确的纳秒值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个简单的程序,该程序检查经过的时间是否超过1秒.我使用clock_gettime()作为开始时间,然后调用sleep(5),采用新时间,然后检查差值是否大于1;否则,取值为0.我睡了5秒钟,然后应该大于5秒钟,但是我的程序打印出一个奇怪的结果.

I'm writing a simple program, which checks if elapsed time is more than 1 seconds. I take start time with a clock_gettime(), then I call sleep(5), take new time and I check if the difference is bigger than 1; I sleep 5 seconds, then it should be greater than 5, but my program prints a strange result.

这是代码:

#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"和未过期的超时"

"elapsed nanoseconds: 145130" and "not expired timeout"

问题出在哪里?

推荐答案

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

The time represented in a struct timespec has two components:

  • tv_sec —整数秒数的time_t值.
  • tv_nsec — 32位整数,表示纳秒数,0..999,999,999
  • tv_sec — a time_t value for the integer number of seconds.
  • tv_nsec — a 32-bit integer for the number of nanoseconds, 0..999,999,999

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

Your calculation didn't take into account the difference between the tv_sec values. It is somewhat surprising that the difference between the nanosecond values was as large as you say, but far from impossible. To get the whole difference, you need to take into account both the tv_sec and the tv_nsec components.

您可以使用以下函数将两个值相减(得到差值):

You can subtract two values (to get the difference) using a function like:

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

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

fmt_timespec

You can format it as a floating-point value with the specified number of decimal places using a function like this:

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;
}


相关代码的修订版

头文件time_io.h声明struct timespec的格式和扫描功能; time_math.h标头声明用于添加和减去struct timespec值的函数.具有太多标头的代码可能过于精简.


Revised version of code in question

The header time_io.h declares the format and scanning functions for struct timespec; the time_math.h header declares the functions for adding and subtracting struct timespec values. It is probably over-compartmenting the code to have that many headers.

#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个小数位始终为零.

On a Mac running macOS Sierra 10.12.6 (which finally has clock_gettime() — earlier versions of Mac OS X did not support it), the resolution of clock_gettime() is 1000 nanoseconds, effectively microseconds. So, the last 3 decimal places are always zeros on a Mac.

为完整起见,您可以添加两个struct timespec值:

For completeness, you can add two struct timespec values with:

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()

扫描"过程更混乱(输入通常比输出更混乱):

scn_timespec()

And the 'scanning' process is messier (input often is messier than output):

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,返回了不正确的纳秒值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆