Cobalt/POSIX 时钟和定时器服务。
Cobalt 支持三种内置时钟:
CLOCK_REALTIME 映射到核心系统时钟,以自 Epoch 以来的时间量计时,分辨率为一纳秒。
CLOCK_MONOTONIC 映射到与架构相关的高分辨率计数器,因此适合测量短时间间隔。然而,当用于休眠(使用 clock_nanosleep())时,CLOCK_MONOTONIC 时钟的分辨率与 CLOCK_REALTIME 时钟一样,都是一纳秒。
CLOCK_MONOTONIC_RAW 是 Linux 特有的,它提供来自硬件定时器的单调时间值,不受 NTP 调整。这与 Cobalt 的 CLOCK_MONOTONIC 严格等效,后者也不受 NTP 调整。
此外,可以使用 cobalt_clock_register() 服务动态注册外部时钟。这些时钟完全由 Cobalt 扩展代码管理,应该在中断上下文中通过调用 xnclock_tick() 为相关时钟通告每个传入的滴答。
可以使用 timer_create() 服务创建使用任何内置或外部时钟的定时器对象。这些定时器的分辨率是特定于时钟的。然而,内置时钟的分辨率都是纳秒,如 clock_nanosleep() 所规定。
int clock_getres(clockid_t clk_id, struct timespec *res);
获取指定时钟的分辨率。
该服务在 tp
的地址处返回(如果不为 NULL)指定时钟的分辨率 clock_id
。
对于 CLOCK_REALTIME
和 CLOCK_MONOTONIC
,该分辨率是一个系统时钟 tick 的持续时间,不支持其他时钟。
参数:
clock_id
: 时钟标识符,可以是 CLOCK_REALTIME
或 CLOCK_MONOTONIC
。tp
: 用于存储指定时钟分辨率的地址,成功时填充此地址。返回值:
clock_id
无效,设置 errno
并返回 -1。错误:
EINVAL
:clock_id
无效。标签:
unrestricted
示例代码
#include <stdio.h>
#include <time.h>
int main() {
// 定义一个 timespec 结构体变量,用于存储时钟的分辨率
struct timespec res;
// 定义一个时钟ID,例如 CLOCK_REALTIME
clockid_t clock_id = CLOCK_REALTIME;
// 调用 clock_getres() 函数获取时钟的分辨率
// 如果成功,返回 0;如果失败,返回 -1
if (clock_getres(clock_id, &res) == -1) {
perror("clock_getres");
return 1;
}
// 打印时钟的分辨率,转换为秒和纳秒
printf("Resolution of clock %d: %ld seconds and %ld nanoseconds\n", clock_id, res.tv_sec, res.tv_nsec);
return 0;
}
int clock_gettime(clockid_t clk_id, struct timespec *tp);
读取指定时钟。
该服务在 tp
的地址处返回时钟 clock_id
的当前值。如果 clock_id
是:
CLOCK_REALTIME
:时钟值表示自 Epoch 以来的时间量,精度为一个系统时钟 tick;CLOCK_MONOTONIC
或 CLOCK_MONOTONIC_RAW
:时钟值由架构相关的高分辨率计数器给出,精度独立于系统时钟 tick 的持续时间;CLOCK_HOST_REALTIME
:主机(通常是 Linux)看到的时钟值。分辨率和精度取决于主机,但保证主机和 Cobalt 看到相同的信息。参数:
clock_id
: 时钟标识符,可以是 CLOCK_REALTIME
、CLOCK_MONOTONIC
或 CLOCK_HOST_REALTIME
;tp
: 用于存储指定时钟值的地址。返回值:
0
;clock_id
无效,设置 errno
并返回 -1
:
EINVAL
,clock_id
无效。标签:
unrestricted
示例代码
#include <stdio.h>
#include <time.h>
int main() {
// 定义一个 timespec 结构体变量,用于存储时钟的当前值
struct timespec tp;
// 定义一个时钟ID,例如 CLOCK_REALTIME
clockid_t clock_id = CLOCK_REALTIME;
// 调用 clock_gettime() 函数获取时钟的当前值
// 如果成功,返回 0;如果失败,返回 -1
if (clock_gettime(clock_id, &tp) == -1) {
perror("clock_gettime");
return 1;
}
// 打印时钟的当前值,转换为秒和纳秒
printf("Current time for clock %d: %ld seconds and %ld nanoseconds since the Epoch\n", clock_id, (long)tp.tv_sec, tp.tv_nsec);
return 0;
}
int clock_nanosleep(clockid_t clock_id, int flags,
const struct timespec *rqtp,
struct timespec *rmtp);
休眠一段时间。
该服务挂起调用线程,直到 rqtp
指定的唤醒时间,或信号传递给调用者。如果在 flags
参数中设置了 TIMER_ABSTIME
标志,则唤醒时间被指定为时钟 clock_id
的绝对值。如果未设置 TIMER_ABSTIME
标志,则唤醒时间被指定为一个时间间隔。
如果该服务被信号中断,未设置 TIMER_ABSTIME
标志,并且 rmtp
不为 NULL
,则剩余的时间将返回到 rmtp
地址。
该服务的分辨率为一个系统时钟 tick。
参数:
clock_id
: 时钟标识符,可以是 CLOCK_REALTIME
或 CLOCK_MONOTONIC
。flags
: 其中之一:
0
表示唤醒时间 rqtp
是一个时间间隔;TIMER_ABSTIME
,表示唤醒时间是时钟 clock_id
的绝对值。rqtp
: 唤醒时间的地址。rmtp
: 如果服务被信号中断,剩余时间将存储在此地址。返回值:
0
;EPERM
,调用者上下文无效;ENOTSUP
,指定的时钟不支持;EINVAL
,指定的唤醒时间无效;EINTR
,该服务被信号中断。标签:
xthread-only
,switch-primary
示例代码
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <string.h>
int main() {
// 定义一个 timespec 结构体,用于存储睡眠时间
struct timespec req, rem;
// 设置需要休眠的时间(秒和纳秒)
req.tv_sec = 2; // 休眠2秒
req.tv_nsec = 0; // 0纳秒
// 打印开始休眠的时间
printf("Going to sleep for %ld seconds and %ld nanoseconds\n", req.tv_sec, req.tv_nsec);
int res = clock_nanosleep(CLOCK_REALTIME, 0, &req, NULL);
// 检查函数调用是否成功
if (res == 0) {
printf("Woke up after sleeping\n");
} else {
fprintf(stderr, "clock_nanosleep failed: %s\n", strerror(errno));
}
return 0;
}
int clock_settime(clockid_t clock_id, const struct timespec *tp);
设置指定时钟。
设置 CLOCK_REALTIME
或 Cobalt 特定的时钟。
参数:
clock_id
: 要设置的时钟的 ID。支持 CLOCK_REALTIME
和 Cobalt 特定的时钟。tp
: 指定新日期的 struct timespec
的地址。返回值:
0
;-1
并设置 errno
:
EINVAL
,clock_id
未定义;EINVAL
,tp
指定的日期无效。注意:
CLOCK_REALTIME
可能会导致调用者切换到次级模式。标签:
unrestricted
,switch-secondary
示例代码
#include <stdio.h>
#include <time.h>
#include <errno.h>
int main() {
// 定义一个 timespec 结构体变量,用于存储要设置的时钟值
struct timespec ts;
// 获取当前时间作为示例
if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
perror("clock_gettime");
return 1;
}
// 将当前时间增加 5 秒作为示例
ts.tv_sec += 5;
// 定义一个时钟ID,例如 CLOCK_REALTIME
clockid_t clock_id = CLOCK_REALTIME;
// 调用 clock_settime() 函数设置时钟的当前值
// 如果成功,返回 0;如果失败,返回 -1
if (clock_settime(clock_id, &ts) == -1) {
perror("clock_settime");
if (errno == EPERM) {
printf("This program needs superuser privileges to set the system clock.\n");
}
return 1;
}
printf("System clock has been set to: %ld seconds since the Epoch\n", (long)ts.tv_sec);
return 0;
}
int nanosleep(const struct timespec *rqtp, struct timespec *rmtp);
休眠一段时间。
该服务挂起调用线程,直到 rqtp
指定的唤醒时间,或信号传递给调用者。唤醒时间被指定为一个时间间隔。
如果该服务被信号中断,并且 rmtp
不为 NULL
,则剩余的时间将返回到 rmtp
地址。
该服务的分辨率为一个系统时钟 tick。
参数:
rqtp
: 唤醒时间的地址。rmtp
: 如果服务被信号中断,剩余时间将存储在此地址。返回值:
0
;-1
并设置 errno
:
EPERM
,调用者上下文无效;EINVAL
,指定的唤醒时间无效;EINTR
,该服务被信号中断。另见:
标签:
xthread-only
,switch-primary
示例代码
#include <stdio.h>
#include <time.h>
int main() {
// 定义一个 timespec 结构体变量,用于存储暂停的时间长度
struct timespec req, rem;
// 设置请求的暂停时间为 2 秒和 500 毫秒(500000 纳秒)
req.tv_sec = 2; // 2 秒
req.tv_nsec = 500000000; // 500 毫秒转换为纳秒
// 打印暂停前的时间
printf("Thread is going to sleep for %ld seconds and %ld nanoseconds.\n", req.tv_sec, req.tv_nsec);
// 调用 nanosleep() 函数使线程暂停
// 如果成功,返回 0;如果被信号打断,返回 -1 并将剩余时间存储在 rem 中
if (nanosleep(&req, &rem) == -1) {
perror("nanosleep");
printf("Remaining time: %ld seconds and %ld nanoseconds.\n", rem.tv_sec, rem.tv_nsec);
}
// 打印暂停后的时间
printf("Thread woke up.\n");
return 0;
}
int timer_delete(timer_t timerid)
删除定时器对象。
该服务删除定时器 timerid
。
参数:
timerid
: 要删除的定时器的标识符。返回值:
0
;-1
并设置 errno
:
EINVAL
,timerid
无效;EPERM
,定时器 timerid
不属于当前进程。标签:
thread-unrestricted
示例代码
#include <stdio.h>
#include <time.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
// 定时器到期时调用的信号处理函数
void timer_handler(int sv) {
printf("Timer expired\n");
}
int main() {
// 定义定时器 ID
timer_t timerid;
// 定义定时器的超时时间
struct itimerspec ts;
ts.it_interval.tv_sec = 5; // 5 秒
ts.it_interval.tv_nsec = 0; // 0 纳秒
ts.it_value.tv_sec = 2;
ts.it_value.tv_nsec = 0;
// 定义定时器的信号处理函数参数
struct sigevent evp;
evp.sigev_value.sival_ptr = &timerid;
evp.sigev_notify = SIGEV_SIGNAL;
evp.sigev_signo = SIGUSR1;
signal(SIGUSR1, timer_handler);
// 创建定时器
if (timer_create(CLOCK_REALTIME, &evp, &timerid) == -1) {
perror("timer_create");
return 1;
}
// 设置定时器到期时间为 5 秒后
if (timer_settime(timerid, 0, &ts, NULL) == -1) {
perror("timer_settime");
timer_delete(timerid); // 删除定时器以释放资源
return 1;
}
printf("Timer created, waiting for it to expire...\n");
// 等待定时器到期
pause(); // 暂停进程,直到捕获信号
// 删除定时器
if (timer_delete(timerid) == -1) {
perror("timer_delete");
return 1;
}
printf("Timer deleted\n");
return 0;
}
int timer_getoverrun(timer_t timerid)
获取自最近一次定时器到期信号传递以来的到期超次数。
该服务返回自最近一次定时器到期信号传递以来 timerid
的到期超次数。如果此计数超过 DELAYTIMER_MAX
次到期,则返回 DELAYTIMER_MAX
。
参数:
timerid
: 定时器标识符。返回值:
-1
并设置 errno
:
EINVAL
,timerid
无效;EPERM
,定时器 timerid
不属于当前进程。标签:
unrestricted
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#define CLOCKID CLOCK_REALTIME
#define SIG SIGRTMIN
#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \
} while (0)
static void
print_siginfo(siginfo_t *si)
{
int or;
timer_t *tidp;
tidp = si->si_value.sival_ptr;
printf(" sival_ptr = %p; ", si->si_value.sival_ptr);
printf(" *sival_ptr = %#jx\n", (uintmax_t) *tidp);
or = timer_getoverrun(*tidp);
if (or == -1)
errExit("timer_getoverrun");
else
printf(" overrun count = %d\n", or);
}
static void
handler(int sig, siginfo_t *si, void *uc)
{
printf("Caught signal %d\n", sig);
print_siginfo(si);
signal(sig, SIG_IGN);
}
int
main()
{
timer_t timerid;
sigset_t mask;
long long freq_nanosecs;
struct sigevent sev;
struct sigaction sa;
struct itimerspec its;
// 创建定时器处理程序
printf("Establishing handler for signal %d\n", SIG);
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = handler;
sigemptyset(&sa.sa_mask);
if (sigaction(SIG, &sa, NULL) == -1)
errExit("sigaction");
/* 暂时阻断定时器信号,用于触发overrun. */
printf("Blocking signal %d\n", SIG);
sigemptyset(&mask);
sigaddset(&mask, SIG);
if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1)
errExit("sigprocmask");
// 创建定时器
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIG;
sev.sigev_value.sival_ptr = &timerid;
if (timer_create(CLOCKID, &sev, &timerid) == -1)
errExit("timer_create");
printf("timer ID is %#jx\n", (uintmax_t) timerid);
// 启动定时器,100ms触发一次
freq_nanosecs = 100*1000*1000;
its.it_value.tv_sec = freq_nanosecs / 1000000000;
its.it_value.tv_nsec = freq_nanosecs % 1000000000;
its.it_interval.tv_sec = its.it_value.tv_sec;
its.it_interval.tv_nsec = its.it_value.tv_nsec;
if (timer_settime(timerid, 0, &its, NULL) == -1)
errExit("timer_settime");
// 停止1s,用于触发overrun
printf("Sleeping for 1 seconds\n");
sleep(1);
// 释放定时器信号,触发定时器函数
printf("Unblocking signal %d\n", SIG);
if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
errExit("sigprocmask");
exit(EXIT_SUCCESS);
}