菜单

Cobalt/Clocks and timers

详细描述

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() 所规定。


函数文档

clock_getres()

int clock_getres(clockid_t clk_id, struct timespec *res);

获取指定时钟的分辨率。

该服务在 tp 的地址处返回(如果不为 NULL)指定时钟的分辨率 clock_id

对于 CLOCK_REALTIMECLOCK_MONOTONIC,该分辨率是一个系统时钟 tick 的持续时间,不支持其他时钟。

参数:

  • clock_id: 时钟标识符,可以是 CLOCK_REALTIMECLOCK_MONOTONIC
  • tp: 用于存储指定时钟分辨率的地址,成功时填充此地址。

返回值:

  • 成功时返回 0;
  • 如果 clock_id 无效,设置 errno 并返回 -1。

错误:

  • EINVALclock_id 无效。

标签:

  • unrestricted

示例代码

c{filename="app.c"} 复制代码
#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;
}

clock_gettime

int clock_gettime(clockid_t clk_id, struct timespec *tp);

读取指定时钟。

该服务在 tp 的地址处返回时钟 clock_id 的当前值。如果 clock_id 是:

  • CLOCK_REALTIME:时钟值表示自 Epoch 以来的时间量,精度为一个系统时钟 tick;
  • CLOCK_MONOTONICCLOCK_MONOTONIC_RAW:时钟值由架构相关的高分辨率计数器给出,精度独立于系统时钟 tick 的持续时间;
  • CLOCK_HOST_REALTIME:主机(通常是 Linux)看到的时钟值。分辨率和精度取决于主机,但保证主机和 Cobalt 看到相同的信息。

参数:

  • clock_id: 时钟标识符,可以是 CLOCK_REALTIMECLOCK_MONOTONICCLOCK_HOST_REALTIME
  • tp: 用于存储指定时钟值的地址。

返回值:

  • 成功时返回 0
  • 如果 clock_id 无效,设置 errno 并返回 -1
    • EINVALclock_id 无效。

标签:

  • unrestricted

示例代码

c{filename="app.c"} 复制代码
#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;
}

clock_nanosleep

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_REALTIMECLOCK_MONOTONIC
  • flags: 其中之一:
    • 0 表示唤醒时间 rqtp 是一个时间间隔;
    • TIMER_ABSTIME,表示唤醒时间是时钟 clock_id 的绝对值。
  • rqtp: 唤醒时间的地址。
  • rmtp: 如果服务被信号中断,剩余时间将存储在此地址。

返回值:

  • 成功时返回 0
  • 如果出现错误,返回错误号:
    • EPERM,调用者上下文无效;
    • ENOTSUP,指定的时钟不支持;
    • EINVAL,指定的唤醒时间无效;
    • EINTR,该服务被信号中断。

标签:

  • xthread-onlyswitch-primary

示例代码

c{filename="app.c"} 复制代码
#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;
}

clock_settime

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
    • EINVALclock_id 未定义;
    • EINVALtp 指定的日期无效。

注意:

  • 设置 CLOCK_REALTIME 可能会导致调用者切换到次级模式。

标签:

  • unrestrictedswitch-secondary

示例代码

c{filename="app.c"} 复制代码
#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;
}

nanosleep

int nanosleep(const struct timespec *rqtp, struct timespec *rmtp);

休眠一段时间。

该服务挂起调用线程,直到 rqtp 指定的唤醒时间,或信号传递给调用者。唤醒时间被指定为一个时间间隔。

如果该服务被信号中断,并且 rmtp 不为 NULL,则剩余的时间将返回到 rmtp 地址。

该服务的分辨率为一个系统时钟 tick。

参数:

  • rqtp: 唤醒时间的地址。
  • rmtp: 如果服务被信号中断,剩余时间将存储在此地址。

返回值:

  • 成功时返回 0
  • 如果出现错误,返回 -1 并设置 errno
    • EPERM,调用者上下文无效;
    • EINVAL,指定的唤醒时间无效;
    • EINTR,该服务被信号中断。

另见:

标签:

  • xthread-onlyswitch-primary

示例代码

c{filename="app.c"} 复制代码
#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;
}

timer_delete()

int timer_delete(timer_t timerid)

删除定时器对象。

该服务删除定时器 timerid

参数:

  • timerid: 要删除的定时器的标识符。

返回值:

  • 成功时返回 0
  • 如果出现错误,返回 -1 并设置 errno
    • EINVALtimerid 无效;
    • EPERM,定时器 timerid 不属于当前进程。

标签:

  • thread-unrestricted

示例代码

c{filename="app.c"} 复制代码
#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;
}

timer_getoverrun()

int timer_getoverrun(timer_t timerid)

获取自最近一次定时器到期信号传递以来的到期超次数。

该服务返回自最近一次定时器到期信号传递以来 timerid 的到期超次数。如果此计数超过 DELAYTIMER_MAX 次到期,则返回 DELAYTIMER_MAX

参数:

  • timerid: 定时器标识符。

返回值:

  • 成功时返回超次数;
  • 如果出现错误,返回 -1 并设置 errno
    • EINVALtimerid 无效;
    • EPERM,定时器 timerid 不属于当前进程。

标签:

  • unrestricted
    示例代码
c{filename="app.c"} 复制代码
#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);
}
最近修改: 2025-07-24