当前位置: 首页 > 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.dtcms.com/a/291841.html

相关文章:

  • java day16
  • Vue开发常用
  • rk平台(rv1126/rk3588)音视频-交叉编译FFmpeg7.1
  • 如何迁移jenkins至另一台服务器
  • 服务器无法访问公网的原因及解决方案
  • 简单了解下npm、yarn 和 pnpm 中 add 与 install(i) 命令的区别(附上两图带你一目明了)
  • nodejs的npm
  • 技术与情感交织的一生 (十)
  • 最新基于R语言结构方程模型分析与实践技术应用
  • Apache Ignite 的 SQL 功能和分布式查询机制
  • Apache Ignite 中事务的使用方式和机制
  • 短剧小程序系统开发:重塑影视内容传播格局
  • LIMO:仅需817样本激活大模型数学推理能力,挑战“数据规模至上”传统范式
  • Axios 二次封装
  • PHP中的异常处理与自定义错误页面
  • EasyGBS算法仓:找算法,变成 “点一下” 的事!
  • 使用 Conda 工具链创建 UV 本地虚拟环境全记录——基于《Python 多版本与开发环境治理架构设计》
  • Docker实战:使用Docker部署TeamMapper思维导图工具
  • 推送git问题_查询索引文件——查导致的文件
  • 按键精灵脚本:自动化利刃的双面性 - 从技术原理到深度实践与反思
  • windows10安装node-v18.18.0-x64安装
  • async/await 函数
  • 【CVPR 2025】低光增强RT-X Net( 红外辅助结构引导)--part1论文精读
  • 开发者的AI认知指南:用大模型重新理解人工智能(下)
  • 公交车客流人数统计管理解决方案:智能化技术与高效运营实践
  • 九鼎X8390 开发板 联发科 MT8390 / MT8370 芯片平台
  • 华为高斯Gauss数据库版本与兼容协议--详解(附带Gorm连接示例代码)
  • 5G工业路由器如何凭借高性价比助力多行业数字化转型?
  • 2025 LCP用2,6酸市场前瞻:全面洞察与投资潜力预测
  • iOS组件化详解