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