Cla*_*oro 2 c posix gettime timer
我正在编写一个简单的程序,它检查经过的时间是否超过 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);
}
Run Code Online (Sandbox Code Playgroud)
我的程序的输出是:
“经过的纳秒:145130”和“未过期超时”
哪里有问题?
a 中表示的时间struct timespec有两个组成部分:
tv_sec\xe2\x80\x94time_t整数秒值。tv_nsec\xe2\x80\x94 纳秒数的 32 位整数,0..999,999,999您的计算没有考虑值之间的差异tv_sec。纳秒值之间的差异如您所说的那么大,这有点令人惊讶,但绝非不可能。为了获得整体差异,您需要同时考虑tv_sec和tv_nsec组件。
sub_timespec()您可以使用如下函数减去两个值(以获得差值):
\n\nenum { NS_PER_SECOND = 1000000000 };\n\nvoid sub_timespec(struct timespec t1, struct timespec t2, struct timespec *td)\n{\n td->tv_nsec = t2.tv_nsec - t1.tv_nsec;\n td->tv_sec = t2.tv_sec - t1.tv_sec;\n if (td->tv_sec > 0 && td->tv_nsec < 0)\n {\n td->tv_nsec += NS_PER_SECOND;\n td->tv_sec--;\n }\n else if (td->tv_sec < 0 && td->tv_nsec > 0)\n {\n td->tv_nsec -= NS_PER_SECOND;\n td->tv_sec++;\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\nfmt_timespec您可以使用如下函数将其格式化为具有指定小数位数的浮点值:
\n\nint fmt_timespec(const struct timespec *value, int dp, char *buffer, size_t buflen)\n{\n assert(value != 0 && buffer != 0 && buflen != 0);\n if (value == 0 || buffer == 0 || buflen == 0)\n {\n errno = EINVAL;\n return -1;\n }\n assert(dp >= 0 && dp <= 9);\n if (dp < 0 || dp > 9)\n {\n errno = EINVAL;\n return -1;\n }\n if ((value->tv_sec > 0 && value->tv_nsec < 0) ||\n (value->tv_sec < 0 && value->tv_nsec > 0))\n {\n /* Non-zero components of struct timespec must have same sign */\n errno = EINVAL;\n return -1;\n }\n\n int len;\n if (dp == 0)\n len = snprintf(buffer, buflen, "%ld", value->tv_sec);\n else\n {\n long nsec = value->tv_nsec;\n long secs = value->tv_sec;\n const char *sign = (secs < 0 || (secs == 0 && nsec < 0)) ? "-" : "";\n if (secs < 0)\n secs = -secs;\n if (nsec < 0)\n nsec = -nsec;\n for (int i = 0; i < 9 - dp; i++)\n nsec /= 10;\n len = snprintf(buffer, buflen, "%s%ld.%.*ld", sign, secs, dp, nsec);\n }\n if (len > 0 && (size_t)len < buflen)\n return len;\n errno = EINVAL;\n return -1;\n}\nRun Code Online (Sandbox Code Playgroud)\n\n标头time_io.h声明了格式和扫描功能struct timespec;标time_math.h头声明了用于添加和减去值的函数struct timespec。拥有这么多标头可能会过度划分代码。
#include "time_io.h"\n#include "time_math.h"\n#include <inttypes.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <time.h>\n#include <unistd.h>\n\nenum { NS_PER_SECOND = 1000000000 };\n\nint main(void)\n{\n struct timespec tv3;\n if (clock_gettime(CLOCK_MONOTONIC, &tv3) == -1)\n perror("clock_gettime()");\n\n sleep(5);\n\n struct timespec tv1;\n if (clock_gettime(CLOCK_MONOTONIC, &tv1) == -1)\n perror("clock_gettime()");\n\n struct timespec td;\n sub_timespec(tv3, tv1, &td);\n\n int64_t ts_in_ns = td.tv_sec * NS_PER_SECOND + td.tv_nsec;\n\n char buffer[32];\n fmt_timespec(&td, 9, buffer, sizeof(buffer));\n\n printf("Elapsed time: %s (%" PRId64 " nanoseconds)\\n", buffer, ts_in_ns);\n\n return 0;\n}\nRun Code Online (Sandbox Code Playgroud)\n\n运行示例:
\n\nElapsed time: 5.005192000 (5005192000 nanoseconds)\nRun Code Online (Sandbox Code Playgroud)\n\n在运行 macOS Sierra 10.12.6 的 Mac 上(最终有clock_gettime()\xe2\x80\x94 早期版本的 Mac OS X 不支持它),分辨率为clock_gettime()1000 纳秒,实际上是微秒。因此,在 Mac 上,最后 3 位小数始终为零。
add_timespec()为了完整起见,您可以添加两个struct timespec值:
void add_timespec(struct timespec t1, struct timespec t2, struct timespec *td)\n{\n td->tv_nsec = t2.tv_nsec + t1.tv_nsec;\n td->tv_sec = t2.tv_sec + t1.tv_sec;\n if (td->tv_nsec >= NS_PER_SECOND)\n {\n td->tv_nsec -= NS_PER_SECOND;\n td->tv_sec++;\n }\n else if (td->tv_nsec <= -NS_PER_SECOND)\n {\n td->tv_nsec += NS_PER_SECOND;\n td->tv_sec--;\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\nscn_timespec()并且“扫描”过程更加混乱(输入通常比输出更混乱):
\n\nint scn_timespec(const char *str, struct timespec *value)\n{\n assert(str != 0 && value != 0);\n if (str == 0 || value == 0)\n {\n errno = EINVAL;\n return -1;\n }\n long sec;\n long nsec = 0;\n int sign = +1;\n char *end;\n /* No library routine sets errno to 0 - but this one needs to */\n int old_errno = errno;\n\n errno = 0;\n\n /* Skip leading white space */\n while (isspace((unsigned char)*str))\n str++;\n\n /* Detect optional sign */\n if (*str == \'+\')\n str++;\n else if (*str == \'-\')\n {\n sign = -1;\n str++;\n }\n\n /* Next character must be a digit */\n if (!isdigit((unsigned char)*str))\n {\n errno = EINVAL;\n return -1;\n }\n\n /* Convert seconds part of string */\n sec = strtol(str, &end, 10);\n if (end == str || ((sec == LONG_MAX || sec == LONG_MIN) && errno == ERANGE))\n {\n errno = EINVAL;\n return -1;\n }\n\n if (*end != \'\\0\' && !isspace((unsigned char)*end))\n {\n if (*end++ != \'.\')\n {\n errno = EINVAL;\n return -1;\n }\n if (*end == \'\\0\')\n nsec = 0;\n else if (isdigit((unsigned char)*end))\n {\n char *frac = end;\n nsec = strtol(frac, &end, 10);\n if (end == str ||\n ((nsec == LONG_MAX || nsec == LONG_MIN) && errno == ERANGE) ||\n (nsec < 0 || nsec >= NS_PER_SECOND) || (end - frac > 9))\n {\n errno = EINVAL;\n return -1;\n }\n for (int i = 0; i < 9 - (end - frac); i++)\n nsec *= 10;\n }\n }\n\n /* Allow trailing white space - only */\n unsigned char uc;\n while ((uc = (unsigned char)*end++) != \'\\0\')\n {\n if (!isspace(uc))\n {\n errno = EINVAL;\n return -1;\n }\n }\n\n /* Success! */\n value->tv_sec = sec * sign;\n value->tv_nsec = nsec * sign;\n errno = old_errno;\n return 0;\n}\nRun Code Online (Sandbox Code Playgroud)\n