当前位置: 首页 > news >正文

linux用户态各定时器抖动测试

在打了实时补丁的linux系统上测试定时器的抖动,测试系统调度抖动最大在30μs左右。

定时器的抖动为相邻两次触发时间之差相对于定时器的周期的差值。

实际使用中建议使用posix ttimer或sleep timer。 

1timefd

timerfd体现了linux中一切皆文件的思想。

空载情况下,定时器抖动最大达到300μs;加压情况下可达到6000μs。

#include <sys/timerfd.h>
#include <pthread.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <inttypes.h>#define NS_PER_US 1000
#define US_PER_SEC 1000000
#define TIMER_INTERVAL_US 10000
#define SAMPLE_COUNT 1000typedef struct {uint64_t max_delta;     // 最大偏差(μs)uint64_t min_delta;     // 最小偏差(μs)uint64_t total_delta;   // 偏差总和(μs)uint32_t count;         // 实际采样计数
} TimerStats;void* timerfd_monitor_thread(void* arg) {int timer_fd = *(int*)arg;TimerStats stats = {0, UINT64_MAX, 0, 0};struct timespec prev_ts = {0, 0};uint64_t expirations;struct sched_param param;param.sched_priority     = 30;pthread_t current_thread = pthread_self();pthread_setschedparam(current_thread, SCHED_FIFO, &param);// 第一次读取(建立基准)if (read(timer_fd, &expirations, sizeof(expirations)) > 0) {clock_gettime(CLOCK_MONOTONIC, &prev_ts);}while (stats.count < SAMPLE_COUNT) {int ret = read(timer_fd, &expirations, sizeof(expirations));if (ret > 0) {struct timespec curr_ts;clock_gettime(CLOCK_MONOTONIC, &curr_ts);uint64_t interval_us = (curr_ts.tv_sec - prev_ts.tv_sec) * US_PER_SEC +(curr_ts.tv_nsec - prev_ts.tv_nsec) / NS_PER_US;if (prev_ts.tv_sec != 0) {int64_t delta = (int64_t)interval_us - TIMER_INTERVAL_US;uint64_t abs_delta = llabs(delta);if (abs_delta > stats.max_delta) stats.max_delta = abs_delta;if (abs_delta < stats.min_delta) stats.min_delta = abs_delta;stats.total_delta += abs_delta;stats.count++;}prev_ts = curr_ts;}}TimerStats* result = (TimerStats *)malloc(sizeof(TimerStats));memcpy(result, &stats, sizeof(TimerStats));return result;
}int main() {int timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);if (timer_fd == -1) {perror("timerfd_create");exit(EXIT_FAILURE);}struct itimerspec timer_spec = {.it_interval = {.tv_sec = 0, .tv_nsec = TIMER_INTERVAL_US * NS_PER_US},.it_value = {.tv_sec = 0, .tv_nsec = 1}};if (timerfd_settime(timer_fd, 0, &timer_spec, NULL) == -1) {perror("timerfd_settime");close(timer_fd);exit(EXIT_FAILURE);}pthread_t monitor_thread;if (pthread_create(&monitor_thread, NULL, timerfd_monitor_thread, &timer_fd)) {perror("pthread_create");close(timer_fd);exit(EXIT_FAILURE);}TimerStats* stats;pthread_join(monitor_thread, (void**)&stats);printf("max: %" PRIu64 "\n", stats->max_delta);printf("min: %" PRIu64 "\n", stats->min_delta);printf("avg: %.2f\n", (double)stats->total_delta / stats->count);free(stats);close(timer_fd);return 0;
}

2posix timer

使用SIGEV_THREAD_ID触发方式,用户创建线程,定时器超时之后,回调函数在用户线程中执行。使用实时信号34,即SIGRTMIN。在空载和加压情况下,最大抖动比较稳定,稳定在60μs左右。实际测试时,使用普通信号,抖动情况和实时信号是相近的。

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/syscall.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
#include <math.h>#define _GNU_SOURCE
#include <sys/types.h>// 全局统计数据结构
volatile struct {long long max_jitter;    // 最大抖动(纳秒)long long min_jitter;    // 最小抖动(纳秒)long long total_jitter;  // 抖动累计值(纳秒)long long count;         // 触发次数统计struct timespec prev_ts; // 上一次触发时间戳
} stats = { .min_jitter = 100000 };// 信号处理函数
void sig_handler(int a, siginfo_t *b, void *c) {struct timespec curr_ts;clock_gettime(CLOCK_MONOTONIC, &curr_ts);// 第一次触发时只记录时间戳不计算抖动if (stats.count > 0) {// 计算实际时间差(纳秒)long long actual_interval = (curr_ts.tv_sec - stats.prev_ts.tv_sec) * 1000000000LL+ (curr_ts.tv_nsec - stats.prev_ts.tv_nsec);// 计算抖动(实际间隔 - 设定周期10ms)const long long expected_interval = 10 * 1000000LL; // 10ms in nslong long jitter = actual_interval - expected_interval;// 更新统计值stats.total_jitter += llabs(jitter);if (llabs(jitter) > stats.max_jitter) stats.max_jitter = llabs(jitter);if (llabs(jitter) < stats.min_jitter) stats.min_jitter = llabs(jitter);}// 更新状态stats.prev_ts = curr_ts;stats.count++;
}void create_timer(int signum, int period_in_ms, void (*cb)(int, siginfo_t*, void*), timer_t* timerid) {struct sigaction sa;sa.sa_flags = SA_SIGINFO;sa.sa_sigaction = cb;sigemptyset(&sa.sa_mask);sigaction(signum, &sa, NULL);sigevent_t event;event.sigev_notify = SIGEV_THREAD_ID;event.sigev_signo = signum;event._sigev_un._tid = syscall(SYS_gettid);event.sigev_value.sival_ptr = NULL;timer_create(CLOCK_MONOTONIC, &event, timerid);struct itimerspec its;its.it_interval.tv_sec = period_in_ms / 1000;its.it_interval.tv_nsec = (period_in_ms % 1000) * 1000000;its.it_value.tv_sec = 0;its.it_value.tv_nsec = 1; // 立即启动timer_settime(*timerid, 0, &its, NULL);
}void* thread_func(void* arg) {struct sched_param param;param.sched_priority     = 30;pthread_t current_thread = pthread_self();pthread_setschedparam(current_thread, SCHED_FIFO, &param);timer_t timerid;create_timer(34, 10, sig_handler, &timerid);while(stats.count < 1000) {usleep(20000);}// 取消定时器timer_delete(timerid);// 打印统计结果printf("\n--- Timer Jitter Statistics ---\n");printf("Samples collected: %lld\n", stats.count - 1); // 有效样本数printf("Max jitter: %lld ns (%.3f ms)\n", stats.max_jitter, stats.max_jitter / 1000000.0);printf("Min jitter: %lld ns\n", stats.min_jitter);printf("Avg jitter: %.2f ns (%.3f ms)\n",(double)stats.total_jitter / (stats.count - 1),(double)stats.total_jitter / (stats.count - 1) / 1000000.0);return NULL;
}int main() {pthread_t thread;pthread_create(&thread, NULL, thread_func, NULL);pthread_join(thread, NULL);return 0;
}

 3rtc定时器

#include <stdio.h>
#include <fcntl.h>
#include <linux/rtc.h>
#include <sys/ioctl.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <stdint.h>
#include <pthread.h>#define RTC_DEVICE "/dev/rtc"
#define RTC_FREQ 1024
#define EXPECTED_INTERVAL_NS (1000000000/RTC_FREQ)// 全局统计结构
volatile struct {long long max_jitter;   // 最大抖动(纳秒)long long min_jitter;   // 最小抖动(纳秒)long long total_jitter; // 抖动累计值int count;              // 有效样本数struct timespec prev_ts;// 上一次中断时间戳
} stats = { .min_jitter = 1000000};int main() {int fd = open(RTC_DEVICE, O_RDONLY);if (fd < 0) {perror("open");exit(EXIT_FAILURE);}// 设置RTC中断频率if (ioctl(fd, RTC_IRQP_SET, RTC_FREQ) < 0) {perror("ioctl(RTC_IRQP_SET)");close(fd);exit(EXIT_FAILURE);}// 启用周期性中断if (ioctl(fd, RTC_PIE_ON, 0) < 0) {perror("ioctl(RTC_PIE_ON)");close(fd);exit(EXIT_FAILURE);}// 初始化首次时间戳clock_gettime(CLOCK_MONOTONIC, &stats.prev_ts);unsigned long data;const int SAMPLE_COUNT = 10000;struct sched_param param;param.sched_priority     = 30;pthread_t current_thread = pthread_self();pthread_setschedparam(current_thread, SCHED_FIFO, &param);while (stats.count < SAMPLE_COUNT) {if (read(fd, &data, sizeof(data)) < 0) {perror("read");break;}struct timespec curr_ts;clock_gettime(CLOCK_MONOTONIC, &curr_ts);if (stats.count > 0) {  // 首次读取不计算抖动// 计算实际间隔(纳秒)long long actual_interval = (curr_ts.tv_sec - stats.prev_ts.tv_sec) * 1000000000LL +(curr_ts.tv_nsec - stats.prev_ts.tv_nsec);// 计算抖动 = |实际间隔 - 预期间隔|long long jitter = llabs(actual_interval - EXPECTED_INTERVAL_NS);// 更新统计stats.total_jitter += jitter;if (jitter > stats.max_jitter) stats.max_jitter = jitter;if (jitter < stats.min_jitter) stats.min_jitter = jitter;}stats.prev_ts = curr_ts;stats.count++;}// 关闭中断并清理ioctl(fd, RTC_PIE_OFF, 0);close(fd);// 输出统计结果printf("\n--- RTC Timer Jitter Statistics (Frequency: %d Hz) ---\n", RTC_FREQ);printf("Samples: %d\n", stats.count - 1);printf("Max Jitter: %lld ns (%.3f ms)\n", stats.max_jitter, stats.max_jitter / 1000000.0);printf("Min Jitter: %lld ns\n", stats.min_jitter);printf("Avg Jitter: %.2f ns (%.3f ms)\n",(double)stats.total_jitter / (stats.count - 1),(double)stats.total_jitter / (stats.count - 1) / 1000000.0);return 0;
}

4sleep timer

完全通过sleep来实现定时器,最大抖动20μs。比上边三种定时器的抖动都要小。

#include <sys/timerfd.h>
#include <pthread.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <inttypes.h>#define NS_PER_US 1000
#define US_PER_SEC 1000000
#define TIMER_INTERVAL_US 10000
#define SAMPLE_COUNT 1000typedef struct {uint64_t max_delta;     // 最大偏差(μs)uint64_t min_delta;     // 最小偏差(μs)uint64_t total_delta;   // 偏差总和(μs)uint32_t count;         // 实际采样计数
} TimerStats;void* timerfd_monitor_thread() {TimerStats stats = {0, UINT64_MAX, 0, 0};struct timespec prev_ts = {0, 0};struct sched_param param;param.sched_priority     = 20;pthread_t current_thread = pthread_self();pthread_setschedparam(current_thread, SCHED_FIFO, &param);struct timespec req = {.tv_sec  = 0,.tv_nsec = 10000000};clock_gettime(CLOCK_MONOTONIC,&prev_ts);while (stats.count < SAMPLE_COUNT) {int ret = clock_nanosleep(CLOCK_MONOTONIC, 0, &req, NULL);if (ret) {if (ret == EINTR) {printf("休眠被信号中断\n");} else {perror("clock_nanosleep 错误");}} else {struct timespec curr_ts;clock_gettime(CLOCK_MONOTONIC, &curr_ts);uint64_t interval_us = (curr_ts.tv_sec - prev_ts.tv_sec) * US_PER_SEC +(curr_ts.tv_nsec - prev_ts.tv_nsec) / NS_PER_US;if (prev_ts.tv_sec != 0) {int64_t delta = (int64_t)interval_us - TIMER_INTERVAL_US;uint64_t abs_delta = llabs(delta);if (abs_delta > stats.max_delta) stats.max_delta = abs_delta;if (abs_delta < stats.min_delta) stats.min_delta = abs_delta;stats.total_delta += abs_delta;stats.count++;}prev_ts = curr_ts;}}TimerStats* result = (TimerStats *)malloc(sizeof(TimerStats));memcpy(result, &stats, sizeof(TimerStats));return result;
}int main() {pthread_t monitor_thread;if (pthread_create(&monitor_thread, NULL, timerfd_monitor_thread, NULL)) {perror("pthread_create");exit(EXIT_FAILURE);}TimerStats* stats;pthread_join(monitor_thread, (void**)&stats);printf("max: %" PRIu64 "\n", stats->max_delta);printf("min: %" PRIu64 "\n", stats->min_delta);printf("avg: %.2f\n", (double)stats->total_delta / stats->count);free(stats);return 0;
}
http://www.lryc.cn/news/595964.html

相关文章:

  • 「Linux命令基础」用户组管理
  • MongoDB频繁掉线频繁断开服务的核心原因以及解决方案-卓伊凡|贝贝|莉莉|糖果
  • stream流入门
  • 企业知识库软件选型与实践指南
  • LINUX 722 逻辑卷快照
  • useState
  • 3.4 安全-分布式-数据库-挖掘
  • Java并发编程:JUC核心组件全解析
  • IMU(LSM6DSMTR+LIS2MDLTR)
  • 隧道代理与普通代理:一场网络隐身术的“智能革命”
  • 开发者的AI认知指南:用大模型重新理解人工智能(上)
  • 基于AutoJawSegment项目的CBCT图像分割实践指南
  • Qt开发环境搭建全攻略(Windows+Linux+macOS)
  • Navicat 远程连接SQLlite数据库
  • 【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts) 视频教程 - 主页-微博基本信息实现
  • DearMom以“新生儿安全系统”重塑婴儿车价值,揽获CBME双项大奖
  • 数据库隔离级别
  • 在vscode 使用 remote-ssh
  • Vue3 面试题及详细答案120道(16-30 )
  • Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型实现持械检测(C#代码,UI界面版)
  • 【Altium Designer2025】电子设计自动化(EDA)软件——Altium Designer25版保姆级下载安装详细图文教程(附安装包)
  • ob导出租户所有表记录
  • SpringBoot--Mapper XML 和 Mapper 接口在不同包
  • C++中的list(2)简单复现list中的关键逻辑
  • 文本分类与情感分析Python实战
  • liunx运维进阶脚本
  • 2025.7.25论文阅读
  • VUE2 项目学习笔记 ? 语法 v-if/v-show
  • 为何在 Vue 的 v-model 指令中不能使用可选链(Optional Chaining)?
  • 【Spring Boot】Spring Boot循环依赖破解:@Lazy与Setter注入的取舍指南(流程图修复版)