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