Linux timefd函数作用及用法

Linux 中的 timerfd 系列函数(如 timerfd_create, timerfd_settime)提供了一种通过文件描述符管理定时器的机制,使得定时器可以像普通 I/O 事件一样被 selectpollepoll 等多路复用接口监听。这种方式极大简化了定时器与 I/O 事件的协同处理,常用于高性能服务器或事件驱动型程序。


核心函数及作用

  1. timerfd_create
    创建定时器文件描述符:

    1
    2
    3
    #include <sys/timerfd.h>

    int timerfd_create(int clockid, int flags);
    • 参数
      • clockid: 时钟类型(如 CLOCK_REALTIME 系统实时时间,CLOCK_MONOTONIC 系统启动后的单调时间)。
      • flags: 可选标志(如 TFD_NONBLOCK 非阻塞模式,TFD_CLOEXEC 执行时关闭)。
    • 返回值:成功返回文件描述符,失败返回 -1
  2. timerfd_settime
    启动或停止定时器:

    1
    2
    3
    int timerfd_settime(int fd, int flags,
    const struct itimerspec *new_value,
    struct itimerspec *old_value);
    • 参数
      • fd: timerfd_create 返回的文件描述符。
      • flags: 0 表示相对时间,TFD_TIMER_ABSTIME 表示绝对时间。
      • new_value: 设置定时器的初始和间隔时间(struct itimerspec)。
      • old_value: 返回之前的定时器设置(可为 NULL)。
  3. timerfd_gettime
    获取当前定时器设置:

    1
    int timerfd_gettime(int fd, struct itimerspec *curr_value);

关键结构体 itimerspec

定义定时器的触发时间和间隔:

1
2
3
4
5
6
7
8
9
struct itimerspec {
struct timespec it_interval; // 间隔时间(周期性触发)
struct timespec it_value; // 首次触发时间
};

struct timespec {
time_t tv_sec; // 秒
long tv_nsec; // 纳秒
};
  • it_interval 为 0,定时器仅触发一次。
  • it_value 为 0,停止定时器。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <sys/timerfd.h>
#include <time.h>
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>

int main() {
int tfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
if (tfd == -1) {
perror("timerfd_create");
return 1;
}

// 设置首次 2 秒后触发,之后每 1 秒触发一次
struct itimerspec its = {
.it_interval = {.tv_sec = 1, .tv_nsec = 0},
.it_value = {.tv_sec = 2, .tv_nsec = 0}
};

if (timerfd_settime(tfd, 0, &its, NULL) {
perror("timerfd_settime");
close(tfd);
return 1;
}

while (1) {
uint64_t expirations;
ssize_t s = read(tfd, &expirations, sizeof(expirations));
if (s == -1) {
if (errno == EAGAIN) continue; // 非阻塞模式需处理
perror("read");
break;
}
printf("Timer triggered %llu times\n", (unsigned long long)expirations);
}

close(tfd);
return 0;
}

关键注意事项

  1. 读取定时器
    使用 read 读取文件描述符,返回一个 uint64_t 值,表示自上次读取后触发的次数。未读取时值会累积。

  2. 结合 epoll/poll
    可将 tfd 加入 epoll 监听队列,实现定时事件与其他 I/O 事件统一处理:

    1
    2
    3
    4
    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = tfd;
    epoll_ctl(epoll_fd, EPOLL_CTL_ADD, tfd, &ev);
  3. 资源释放
    使用完毕后务必 close(tfd) 关闭文件描述符。


优势

  • 统一事件模型:定时器与套接字、管道等 I/O 事件统一处理,简化代码结构。
  • 高精度:支持纳秒级定时精度。
  • 线程安全:可在多线程环境中安全使用。

timerfd_settime函数首次触发最小延迟时间

在 Linux 中,timerfd_settime 函数的首次触发最小延迟时间理论上可以达到纳秒级,但实际最小延迟受系统调度精度和硬件限制。以下是关键细节:


1. 理论最小延迟(由 API 决定)

timerfd_settime 使用 struct itimerspec 指定时间,其首次触发时间由 it_value 定义:

1
2
3
4
5
6
7
8
9
struct itimerspec {
struct timespec it_interval; // 间隔时间
struct timespec it_value; // 首次触发时间
};

struct timespec {
time_t tv_sec; // 秒
long tv_nsec; // 纳秒(0~999,999,999)
};
  • 理论上,首次触发时间可设置为 1 纳秒tv_sec=0, tv_nsec=1)。
  • 但实际能否生效取决于系统调度能力和硬件精度

2. 实际最小延迟(由系统和硬件决定)

  • Linux 默认调度精度:通常为 1 毫秒(由内核参数 CONFIG_HZ 决定,默认值 250~1000 Hz)。
  • 高精度模式(HPET/TSC):若内核启用 CONFIG_HIGH_RES_TIMERS,精度可达 微秒级(1μs)甚至纳秒级
  • 硬件限制:即使内核支持高精度,硬件计时器(如 CPU 的 TSC 或 HPET)的精度仍可能限制实际最小延迟。

3. 测试最小延迟

可通过以下代码测试实际最小延迟:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <sys/timerfd.h>
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>

int main() {
int tfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
struct itimerspec its = {
.it_value = {.tv_sec = 0, .tv_nsec = 1}, // 首次触发:1 纳秒
.it_interval = {.tv_sec = 0, .tv_nsec = 0} // 仅触发一次
};
timerfd_settime(tfd, 0, &its, NULL);

uint64_t expirations;
read(tfd, &expirations, sizeof(expirations)); // 等待触发
printf("Timer triggered!\n");

close(tfd);
return 0;
}
  • 实际结果:在大多数系统上,首次触发延迟可能在 几十微秒到几毫秒之间,具体取决于系统负载和配置。

4. 如何优化延迟精度

  1. 启用高精度内核配置
    编译内核时启用:

    1
    2
    CONFIG_HIGH_RES_TIMERS=y
    CONFIG_TICKLESS_KERNEL=y
  2. 使用实时调度策略
    通过 sched_setscheduler 设置实时策略(如 SCHED_FIFO),减少调度延迟:

    1
    2
    struct sched_param param = {.sched_priority = 99};
    sched_setscheduler(0, SCHED_FIFO, &param);
  3. CPU 隔离与绑定
    避免其他进程干扰,将定时器线程绑定到专用 CPU 核心:

    1
    2
    3
    4
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    CPU_SET(3, &cpuset); // 绑定到 CPU 3
    pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
  4. 禁用电源管理
    关闭 CPU 频率调节(如 cpufreq 设置为 performance 模式):

    1
    echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor

5. 典型场景的延迟表现

场景 首次触发延迟范围
默认内核配置 1 毫秒 ~ 10 毫秒
启用高精度定时器 1 微秒 ~ 100 微秒
实时内核 + CPU 绑定 < 50 微秒

总结

  • 理论最小延迟:1 纳秒(tv_nsec=1)。
  • 实际最小延迟:通常在 微秒级(1μs ~ 100μs),优化后可达更低。
  • 关键因素:内核配置、硬件支持、系统负载和调度策略。

Linux下高精度定时器有哪些

在 Linux 系统中,实现高精度定时器(微秒或纳秒级)的机制主要有以下几种,适用于不同场景和精度需求:


1. timerfd 系列函数

  • 作用:通过文件描述符管理定时器,与 epoll/poll 等 I/O 多路复用机制无缝集成。
  • API
    • timerfd_create:创建定时器文件描述符。
    • timerfd_settime:设置定时器的首次触发时间和间隔。
    • timerfd_gettime:获取当前定时器状态。
  • 精度
    • 理论精度:纳秒级(由 struct timespec 定义)。
    • 实际精度:依赖内核配置(需启用 CONFIG_HIGH_RES_TIMERS),通常可达 微秒级
  • 适用场景:需要将定时事件与网络 I/O 统一处理的服务器程序。

2. POSIX 定时器 (timer_create)

  • 作用:基于 POSIX 标准的高精度定时器,支持多种通知方式(信号、线程回调等)。
  • API
    • timer_create:创建定时器,指定时钟源(如 CLOCK_REALTIME, CLOCK_MONOTONIC)。
    • timer_settime:设置定时器。
    • timer_delete:销毁定时器。
  • 通知方式
    • SIGEV_SIGNAL:通过信号通知(如 SIGALRM)。
    • SIGEV_THREAD:启动新线程执行回调函数。
    • SIGEV_THREAD_ID:向指定线程发送信号。
  • 精度:与 timerfd 类似,依赖内核高精度定时器支持。
  • 适用场景:需要灵活通知机制的实时任务。

3. clock_nanosleep

  • 作用:高精度休眠函数,支持绝对时间和相对时间。
  • API
    1
    2
    3
    int clock_nanosleep(clockid_t clockid, int flags,
    const struct timespec *request,
    struct timespec *remain);
    • flags0 表示相对时间,TIMER_ABSTIME 表示绝对时间。
  • 精度
    • 理论精度:纳秒级
    • 实际精度:依赖系统调度和硬件。
  • 适用场景:需要精确控制线程休眠时间的场景(如音视频同步)。

4. 内核高精度定时器(hrtimers)

  • 机制:Linux 内核内部的高精度定时器框架,用户空间通过以下接口间接使用:
    • timerfd(如前所述)。
    • POSIX 定时器。
    • nanosleep/clock_nanosleep
  • 精度:内核态直接支持 纳秒级 定时。
  • 依赖:需内核启用 CONFIG_HIGH_RES_TIMERS
  • 检查是否启用
    1
    2
    grep CONFIG_HIGH_RES_TIMERS /boot/config-$(uname -r)
    # 输出应为 CONFIG_HIGH_RES_TIMERS=y

5. 实时调度策略优化

  • 作用:通过提高线程调度优先级,减少定时器触发的延迟抖动。
  • API
    1
    2
    3
    #include <sched.h>
    struct sched_param param = {.sched_priority = 99};
    sched_setscheduler(0, SCHED_FIFO, &param);
  • 关键点
    • root 权限或 CAP_SYS_NICE 能力。
    • 实时策略(SCHED_FIFO, SCHED_RR)可显著降低调度延迟。
  • 适用场景:对延迟敏感的实时任务(如工业控制)。

6. 硬件级定时器

  • RTC(实时时钟):提供硬件级时间基准,精度通常为秒级,适用于系统时间同步。
  • TSC(时间戳计数器):CPU 内部计数器,精度高但依赖稳定性(需恒定频率)。
  • HPET(高精度事件定时器):硬件定时器,支持纳秒级精度,由内核自动管理。

7. 其他工具与配置

检查时钟分辨率

1
2
3
4
#include <time.h>
struct timespec res;
clock_getres(CLOCK_MONOTONIC, &res);
printf("Resolution: %ld ns\n", res.tv_nsec);
  • 输出结果表示当前时钟的实际最小时间单位。

优化系统配置

  1. 启用高精度内核
    • 编译内核时启用 CONFIG_HIGH_RES_TIMERSCONFIG_PREEMPT_RT(实时补丁)。
  2. CPU 调频策略
    1
    echo performance > /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
  3. CPU 隔离
    • 使用 isolcpus 内核参数隔离 CPU 核心,供实时任务独占。

典型场景与选择建议

场景 推荐方案 精度
网络服务器定时任务 timerfd + epoll 微秒级
实时信号处理 POSIX 定时器 (SIGEV_SIGNAL) 微秒级
线程精确休眠 clock_nanosleep 纳秒级
超低延迟控制 实时内核 + CPU 绑定 + hrtimers < 50 微秒

总结

Linux 下的高精度定时器核心依赖内核配置(CONFIG_HIGH_RES_TIMERS),通过以下方式实现:

  • 用户态接口timerfd、POSIX 定时器、clock_nanosleep
  • 内核机制:hrtimers。
  • 系统优化:实时调度策略、CPU 隔离、关闭频率调节。

实际精度需结合硬件能力(如 HPET/TSC)和系统负载,在最优配置下可达 数十微秒以内

timer_create功能及用法示例


在 Linux 中,timer_createPOSIX 定时器 的核心函数,允许用户创建高精度定时器,并通过信号线程回调等方式接收定时事件通知。其功能灵活,支持多种触发模式,适用于需要精确时间控制的场景。


核心功能

  1. 创建定时器:绑定到特定的时钟源(如 CLOCK_REALTIMECLOCK_MONOTONIC)。
  2. 设置触发方式:支持通过信号(如 SIGALRM)、线程启动或自定义方式通知定时事件。
  3. 高精度控制:理论精度可达 纳秒级(依赖内核配置 CONFIG_HIGH_RES_TIMERS)。
  4. 周期性或单次触发:通过 timer_settime 设置首次触发时间和间隔。

函数原型

1
2
3
4
5
6
7
8
#include <signal.h>
#include <time.h>

int timer_create(clockid_t clockid, struct sigevent *evp, timer_t *timerid);
int timer_settime(timer_t timerid, int flags,
const struct itimerspec *new_value,
struct itimerspec *old_value);
int timer_delete(timer_t timerid);

参数说明

  1. **clockid**:时钟源类型:
    • CLOCK_REALTIME:系统实时时间(受时间跳变影响)。
    • CLOCK_MONOTONIC:系统启动后的单调递增时间(不受时间调整影响)。
  2. **evp**:指定定时事件通知方式(struct sigevent)。
  3. **timerid**:成功创建后返回的定时器标识符。

关键结构体

1. struct sigevent(事件通知配置)

1
2
3
4
5
6
7
struct sigevent {
int sigev_notify; // 通知方式:SIGEV_NONE, SIGEV_SIGNAL, SIGEV_THREAD 等
int sigev_signo; // 信号编号(当 sigev_notify=SIGEV_SIGNAL 时有效)
union sigval sigev_value; // 传递给信号处理函数或线程的数据
void (*sigev_notify_function)(union sigval); // 线程回调函数
pthread_attr_t *sigev_notify_attributes; // 线程属性(可为 NULL)
};

2. struct itimerspec(定时器时间设置)

1
2
3
4
5
6
7
8
struct itimerspec {
struct timespec it_interval; // 间隔时间(周期性触发)
struct timespec it_value; // 首次触发时间
};
struct timespec {
time_t tv_sec; // 秒
long tv_nsec; // 纳秒(0~999,999,999)
};

用法示例

示例 1:通过信号(SIGEV_SIGNAL)接收定时事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>

// 信号处理函数
void timer_handler(int sig, siginfo_t *si, void *uc) {
printf("Timer triggered! Signal: %d\n", sig);
}

int main() {
timer_t timerid;
struct sigevent sev;
struct itimerspec its;

// 配置信号处理
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = timer_handler;
sigemptyset(&sa.sa_mask);
sigaction(SIGRTMIN, &sa, NULL); // 绑定到实时信号

// 创建定时器(使用 CLOCK_MONOTONIC,通过信号 SIGRTMIN 通知)
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIGRTMIN;
sev.sigev_value.sival_ptr = &timerid;
if (timer_create(CLOCK_MONOTONIC, &sev, &timerid) {
perror("timer_create");
return 1;
}

// 设置首次触发 1 秒后,之后每 500 毫秒触发一次
its.it_value.tv_sec = 1;
its.it_value.tv_nsec = 0;
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 500000000; // 500ms
if (timer_settime(timerid, 0, &its, NULL)) {
perror("timer_settime");
return 1;
}

// 主线程等待信号
while (1) pause();

timer_delete(timerid);
return 0;
}

示例 2:通过线程回调(SIGEV_THREAD)处理定时事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <stdio.h>
#include <time.h>
#include <unistd.h>

// 线程回调函数
void timer_thread(union sigval val) {
printf("Timer triggered! Thread ID: %ld\n", (long)pthread_self());
}

int main() {
timer_t timerid;
struct sigevent sev;
struct itimerspec its;

// 创建定时器(使用 CLOCK_REALTIME,启动新线程执行回调)
sev.sigev_notify = SIGEV_THREAD;
sev.sigev_notify_function = timer_thread;
sev.sigev_notify_attributes = NULL; // 默认线程属性
sev.sigev_value.sival_ptr = NULL; // 传递给回调的参数
if (timer_create(CLOCK_REALTIME, &sev, &timerid)) {
perror("timer_create");
return 1;
}

// 设置首次触发 2 秒后,仅触发一次(it_interval=0)
its.it_value.tv_sec = 2;
its.it_value.tv_nsec = 0;
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 0;
if (timer_settime(timerid, 0, &its, NULL)) {
perror("timer_settime");
return 1;
}

// 等待定时器触发(主线程休眠)
sleep(3);

timer_delete(timerid);
return 0;
}

关键注意事项

  1. 信号选择

    • 使用实时信号(如 SIGRTMINSIGRTMAX)避免与标准信号冲突。
    • 标准信号(如 SIGALRM)可能因多次触发被合并,导致事件丢失。
  2. 线程回调开销

    • SIGEV_THREAD 每次触发会创建新线程,频繁触发时可能产生性能问题。
    • 建议在回调函数中避免阻塞操作。
  3. 资源释放

    • 定时器使用完毕后需调用 timer_delete 释放资源。
  4. 精度保障

    • 启用高精度内核(CONFIG_HIGH_RES_TIMERS=y)以获得微秒级精度。
    • 结合实时调度策略(SCHED_FIFO)减少延迟抖动。

适用场景

  • 信号通知:适合与现有信号处理逻辑集成的场景(如定时任务中断)。
  • 线程回调:需要异步执行复杂逻辑时(需注意线程管理开销)。
  • 高精度需求:工业控制、音视频同步等对时间敏感的领域。

通过合理选择通知机制和时钟源,timer_create 能够满足多样化的定时需求。

Linux下高精度定时器有哪些

在 Linux 系统中,实现高精度定时器(微秒或纳秒级精度)的核心机制和接口如下,可根据场景需求选择合适的方案:


1. 用户态高精度定时接口

(1) timerfd 系列函数

  • 作用:通过文件描述符管理定时器,与 epoll/poll 等 I/O 多路复用机制无缝集成。
  • 关键函数
    1
    2
    3
    4
    5
    #include <sys/timerfd.h>
    int timerfd_create(int clockid, int flags); // 创建定时器 fd
    int timerfd_settime(int fd, int flags, // 设置定时器
    const struct itimerspec *new_value,
    struct itimerspec *old_value);
  • 特点
    • 精度:理论纳秒级,实际依赖内核配置(需 CONFIG_HIGH_RES_TIMERS)。
    • 适用场景:需将定时事件与网络 I/O 统一处理的服务器程序。
  • 示例
    1
    2
    3
    4
    5
    6
    7
    int tfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
    struct itimerspec its = {
    .it_value = {.tv_sec = 1, .tv_nsec = 0}, // 首次触发 1 秒后
    .it_interval = {.tv_sec = 0, .tv_nsec = 5e8} // 间隔 500ms
    };
    timerfd_settime(tfd, 0, &its, NULL);
    // 通过 read(tfd, ...) 或 epoll 监听事件

(2) POSIX 定时器 (timer_create)

  • 作用:通过信号或线程回调接收定时事件通知,功能灵活。
  • 关键函数
    1
    2
    3
    4
    #include <signal.h>
    #include <time.h>
    int timer_create(clockid_t clockid, struct sigevent *evp, timer_t *timerid);
    int timer_settime(timer_t timerid, int flags, const struct itimerspec *new_value, ...);
  • 特点
    • 支持通知方式:信号(SIGEV_SIGNAL)、线程回调(SIGEV_THREAD)。
    • 精度:与 timerfd 相同,需内核支持高精度模式。
  • 示例(信号通知)
    1
    2
    3
    4
    5
    struct sigevent sev = {
    .sigev_notify = SIGEV_SIGNAL,
    .sigev_signo = SIGRTMIN // 使用实时信号
    };
    timer_create(CLOCK_MONOTONIC, &sev, &timerid);

(3) clock_nanosleep

  • 作用:高精度休眠函数,支持绝对时间和相对时间。
  • 原型
    1
    2
    3
    4
    #include <time.h>
    int clock_nanosleep(clockid_t clockid, int flags,
    const struct timespec *request,
    struct timespec *remain);
  • 特点
    • 精度:理论纳秒级,实际受调度延迟影响。
    • 适用场景:需要精确控制线程休眠时间的任务(如音视频同步)。

2. 内核态高精度定时机制

(1) 高分辨率定时器(hrtimers)

  • 机制:Linux 内核内部的高精度定时框架,用户态通过以下接口间接使用:
    • timerfd、POSIX 定时器、clock_nanosleep 等。
  • 依赖
    • 内核需启用 CONFIG_HIGH_RES_TIMERS(检查 /boot/config-$(uname -r))。
    • 若启用,系统时钟源切换为高精度模式(如 TSC、HPET)。

3. 硬件级定时器

(1) TSC(时间戳计数器)

  • 原理:CPU 内部计数器,通过 rdtsc 指令读取。
  • 特点
    • 精度:纳秒级(依赖 CPU 主频稳定性)。
    • 缺点:可能因 CPU 频率变化(如节能模式)导致误差。

(2) HPET(高精度事件定时器)

  • 原理:硬件定时器芯片,提供高精度时钟中断。
  • 启用:内核启动参数添加 hpet=enable
  • 精度:通常为 100ns 级

4. 实时性优化手段

(1) 实时调度策略

  • 方法:设置线程调度策略为 SCHED_FIFO/SCHED_RR
    1
    2
    3
    #include <sched.h>
    struct sched_param param = {.sched_priority = 99};
    sched_setscheduler(0, SCHED_FIFO, &param);
  • 效果:减少调度延迟,显著降低定时抖动。

(2) CPU 隔离与绑定

  • 隔离 CPU:通过内核参数 isolcpus=3 隔离 CPU 核心。
  • 绑定线程
    1
    2
    3
    4
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    CPU_SET(3, &cpuset); // 绑定到 CPU 3
    pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);

5. 系统配置与验证

(1) 检查内核高精度支持

1
2
3
4
5
6
7
# 查看内核是否启用高精度定时器
grep CONFIG_HIGH_RES_TIMERS /boot/config-$(uname -r)
# 输出应为 CONFIG_HIGH_RES_TIMERS=y

# 查看当前时钟源
cat /sys/devices/system/clocksource/clocksource0/current_clocksource
# 理想输出:tsc 或 hpet

(2) 调整 CPU 频率策略

1
2
# 设置为性能模式(禁用动态调频)
echo performance | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor

方案选择建议

场景 推荐方案 精度 复杂度
网络服务器定时任务 timerfd + epoll 微秒级
实时信号处理 POSIX 定时器 (SIGEV_SIGNAL) 微秒级
线程级精确休眠 clock_nanosleep 纳秒级
超低延迟控制 实时内核 + CPU 绑定 + hrtimers <50 微秒

注意事项

  1. 信号竞争:使用标准信号(如 SIGALRM)可能导致事件合并,优先选择实时信号(SIGRTMIN~SIGRTMAX)。
  2. 线程安全:POSIX 定时器的线程回调需避免资源竞争。
  3. 资源释放:定时器使用后需调用 timer_deleteclose(fd) 释放内核资源。

通过合理选择定时器类型和系统优化,可在 Linux 下实现微秒甚至纳秒级精度的定时控制。

clock_nanosleep函数用法及示例


clock_nanosleep 是 Linux 下提供高精度休眠的函数,支持纳秒级时间控制,并允许使用绝对时间(Absolute Time)或相对时间(Relative Time)模式。相比传统的 sleepnanosleep,它更加灵活且精度更高,适用于需要严格时间控制的场景(如音视频同步、实时任务)。


函数原型

1
2
3
4
5
#include <time.h>

int clock_nanosleep(clockid_t clockid, int flags,
const struct timespec *request,
struct timespec *remain);

参数说明

参数 说明
clockid 时钟源类型:
- CLOCK_REALTIME:系统实时时间(受时间调整影响)
- CLOCK_MONOTONIC:系统启动后的单调时间(不受时间调整影响)
flags 时间模式:
- 0:相对时间(从当前时间开始休眠)
- TIMER_ABSTIME:绝对时间(休眠到指定的时间点)
request 请求的休眠时间(struct timespec,秒 + 纳秒)
remain 返回未完成的休眠时间(若休眠被信号中断,需重新休眠剩余时间;可为 NULL

返回值

  • 0:休眠成功完成。
  • 错误码:失败时返回错误编号(如 EINTR 表示被信号中断)。

核心功能

  1. 高精度休眠:支持纳秒级休眠时间(理论精度,实际受内核和硬件限制)。
  2. 绝对时间模式:避免因函数调用延迟导致的累积误差。
  3. 多时钟源支持:灵活选择是否受系统时间调整影响。

用法示例

示例 1:相对时间模式(等待 1.5 秒)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <time.h>
#include <stdio.h>
#include <errno.h>

int main() {
struct timespec req = {
.tv_sec = 1, // 1 秒
.tv_nsec = 5e8 // 500 毫秒(500,000,000 纳秒)
};

// 使用 CLOCK_MONOTONIC 时钟,相对时间模式
int ret = clock_nanosleep(CLOCK_MONOTONIC, 0, &req, NULL);
if (ret != 0) {
if (ret == EINTR) {
printf("休眠被信号中断!\n");
} else {
perror("clock_nanosleep");
}
return 1;
}

printf("休眠完成!\n");
return 0;
}

示例 2:绝对时间模式(精确间隔循环)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <time.h>
#include <stdio.h>

int main() {
struct timespec start, next;
clock_gettime(CLOCK_MONOTONIC, &start); // 获取当前时间

for (int i = 0; i < 5; i++) {
// 计算下一次触发时间点(间隔 200ms)
next.tv_sec = start.tv_sec + i * 0.2;
next.tv_nsec = start.tv_nsec + (i * 200000000) % 1000000000;

// 使用绝对时间模式(TIMER_ABSTIME)
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next, NULL);

printf("第 %d 次触发,时间戳: %ld.%09ld\n",
i+1, next.tv_sec, next.tv_nsec);
}

return 0;
}

关键注意事项

  1. 精度与内核配置

    • 实际精度依赖内核是否启用高精度定时器(CONFIG_HIGH_RES_TIMERS)。
    • 检查内核配置:
      1
      2
      grep CONFIG_HIGH_RES_TIMERS /boot/config-$(uname -r)
      # 输出应为 CONFIG_HIGH_RES_TIMERS=y
  2. 信号中断处理

    • 若休眠中被信号中断,函数返回 EINTR,此时需通过 remain 参数重新计算剩余时间:
      1
      2
      3
      4
      5
      struct timespec req = {.tv_sec = 1, .tv_nsec = 0};
      struct timespec rem;
      while (clock_nanosleep(CLOCK_REALTIME, 0, &req, &rem) == EINTR) {
      req = rem; // 重新设置剩余时间
      }
  3. 绝对时间模式的优势

    • 避免因函数执行延迟导致的误差累积,适合周期性任务。
    • 需预先计算目标时间点(通常结合 clock_gettime)。
  4. 时钟源选择

    • CLOCK_REALTIME:适合需要与系统时间同步的场景(但受时间跳变影响)。
    • CLOCK_MONOTONIC:适合需要稳定时间基准的场景(如性能测量)。

nanosleep 的对比

特性 clock_nanosleep nanosleep
时间模式 支持相对时间和绝对时间 仅支持相对时间
时钟源 可指定 CLOCK_REALTIME/CLOCK_MONOTONIC 固定为 CLOCK_REALTIME
精度 纳秒级(依赖内核配置) 纳秒级(同上)
中断处理 通过 remain 返回剩余时间 类似,但无绝对时间模式

典型应用场景

  1. 音视频同步:精确控制帧率或采样间隔。
  2. 实时任务调度:周期性执行任务(如传感器数据采集)。
  3. 基准测试:高精度延迟测量。

通过合理选择时间模式和时钟源,clock_nanosleep 能够满足对时间精度和稳定性要求较高的场景需求。