# 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_REALTIME` 和 `CLOCK_MONOTONIC`，该分辨率是一个系统时钟 tick 的持续时间，不支持其他时钟。

参数:

- `clock_id`: 时钟标识符，可以是 `CLOCK_REALTIME` 或 `CLOCK_MONOTONIC`。
- `tp`: 用于存储指定时钟分辨率的地址，成功时填充此地址。

返回值:

- 成功时返回 0；
- 如果 `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;
    }

#### clock_gettime

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

### 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_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;
    }

### 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`：
  - `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;
    }

### nanosleep

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

### timer_delete()

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

### timer_getoverrun()

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