预计时间:2-3个月 前置要求:完成基础五阶段学习 目标:掌握实时系统开发
第一部分:实时基础概念
1.1 实时系统分类
1.2 延迟类型
用户请求 → [中断延迟] → 中断处理 → [调度延迟] → 任务执行 → [执行时间] → 响应
└─────────────── 总响应延迟 ───────────────┘
1.3 环境准备
# 安装测试工具
sudo apt update
sudo apt install rt-tests stress-ng htop
# 检查当前内核配置
uname -a
cat /boot/config-$(uname -r) | grep PREEMPT
# 可能的输出:
# CONFIG_PREEMPT_NONE=y - 无抢占(服务器)
# CONFIG_PREEMPT_VOLUNTARY=y - 自愿抢占(桌面)
# CONFIG_PREEMPT=y - 完全抢占(低延迟)
# CONFIG_PREEMPT_RT=y - 实时抢占(RT内核)
# 创建工作目录
mkdir -p ~/rt_learning
cd ~/rt_learning
第二部分:延迟测量与分析
2.1 使用cyclictest测量延迟
# 基本测试(需要root权限)
sudo cyclictest -m -p 90 -i 1000 -l 10000
# -m: 锁定内存(mlockall)
# -p: 优先级(1-99,越高越优先)
# -i: 间隔时间(微秒)
# -l: 循环次数
# 详细输出
sudo cyclictest -m -p 90 -i 1000 -l 10000 -v
# 多线程测试(每个CPU一个线程)
sudo cyclictest -m -p 90 -i 1000 -l 10000 -t -a
# 带直方图输出
sudo cyclictest -m -p 90 -i 1000 -l 100000 -h 100 > histogram.txt
# 在负载下测试
# 终端1:产生负载
stress-ng --cpu 4 --io 2 --vm 2 --vm-bytes 256M --timeout 60s
# 终端2:测量延迟
sudo cyclictest -m -p 90 -i 1000 -l 50000 -t -a
2.2 解读cyclictest输出
T: 0 ( 1234) P:90 I:1000 C: 10000 Min: 3 Act: 5 Avg: 6 Max: 42
│ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ └─ 最大延迟(最重要)
│ │ │ │ │ │ │ │ └─ 平均延迟
│ │ │ │ │ │ │ └─ 当前延迟
│ │ │ │ │ │ └─ 最小延迟
│ │ │ │ │ └─ 循环计数
│ │ │ │ └─ 间隔(微秒)
│ │ │ └─ 优先级
│ │ └─ 线程PID
│ └─ 线程编号
延迟基准参考:
2.3 延迟分析模块
创建文件 latency_measure.c:
#include<linux/init.h>
#include<linux/module.h>
#include<linux/hrtimer.h>
#include<linux/ktime.h>
#include<linux/proc_fs.h>
#include<linux/seq_file.h>
#include<linux/sched.h>
#include<linux/sched/rt.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Latency Measurement Module");
#define SAMPLES 1000
#define INTERVAL_NS 1000000 // 1ms
staticstructhrtimertimer;
staticktime_t interval;
staticktime_t expected_time;
static s64 latencies[SAMPLES];
staticint sample_index = 0;
staticbool measurement_done = false;
staticstructproc_dir_entry *proc_entry;
// 统计数据
static s64 min_latency = S64_MAX;
static s64 max_latency = 0;
static s64 total_latency = 0;
staticenum hrtimer_restart timer_callback(struct hrtimer *timer)
{
ktime_t now = ktime_get();
s64 latency;
if (sample_index >= SAMPLES) {
measurement_done = true;
return HRTIMER_NORESTART;
}
// 计算延迟(实际时间 - 预期时间)
latency = ktime_to_ns(ktime_sub(now, expected_time));
latencies[sample_index] = latency;
// 更新统计
if (latency < min_latency) min_latency = latency;
if (latency > max_latency) max_latency = latency;
total_latency += latency;
sample_index++;
// 设置下一次触发时间
expected_time = ktime_add(expected_time, interval);
hrtimer_forward(timer, now, interval);
return HRTIMER_RESTART;
}
staticintlatency_show(struct seq_file *m, void *v)
{
int i;
s64 avg;
int histogram[10] = {0}; // 0-10, 10-20, ..., 90+ μs
if (!measurement_done) {
seq_printf(m, "Measurement in progress... (%d/%d)\n",
sample_index, SAMPLES);
return0;
}
avg = total_latency / SAMPLES;
seq_printf(m, "=== Latency Measurement Results ===\n\n");
seq_printf(m, "Samples: %d\n", SAMPLES);
seq_printf(m, "Interval: %lld ns\n\n", (s64)INTERVAL_NS);
seq_printf(m, "--- Statistics ---\n");
seq_printf(m, "Min: %lld ns (%lld μs)\n", min_latency, min_latency / 1000);
seq_printf(m, "Max: %lld ns (%lld μs)\n", max_latency, max_latency / 1000);
seq_printf(m, "Avg: %lld ns (%lld μs)\n\n", avg, avg / 1000);
// 构建直方图
for (i = 0; i < SAMPLES; i++) {
int bucket = latencies[i] / 10000; // 10μs per bucket
if (bucket >= 9) bucket = 9;
if (bucket >= 0) histogram[bucket]++;
}
seq_printf(m, "--- Histogram (μs) ---\n");
for (i = 0; i < 10; i++) {
int bar_len = histogram[i] * 50 / SAMPLES;
seq_printf(m, "%3d-%3d: %5d |", i*10, (i+1)*10, histogram[i]);
for (int j = 0; j < bar_len; j++) seq_printf(m, "#");
seq_printf(m, "\n");
}
// 显示部分原始数据
seq_printf(m, "\n--- First 20 samples (ns) ---\n");
for (i = 0; i < 20 && i < SAMPLES; i++) {
seq_printf(m, "%4d: %8lld\n", i, latencies[i]);
}
return0;
}
staticintlatency_open(struct inode *inode, struct file *file)
{
return single_open(file, latency_show, NULL);
}
// 写入接口:重新开始测量
staticssize_tlatency_write(struct file *file, constchar __user *buf,
size_t count, loff_t *ppos)
{
// 重置
hrtimer_cancel(&timer);
sample_index = 0;
measurement_done = false;
min_latency = S64_MAX;
max_latency = 0;
total_latency = 0;
// 重新开始
expected_time = ktime_add(ktime_get(), interval);
hrtimer_start(&timer, interval, HRTIMER_MODE_REL);
pr_info("latency_measure: Measurement restarted\n");
return count;
}
staticconststructproc_opslatency_ops = {
.proc_open = latency_open,
.proc_read = seq_read,
.proc_write = latency_write,
.proc_lseek = seq_lseek,
.proc_release = single_release,
};
staticint __init latency_init(void)
{
interval = ktime_set(0, INTERVAL_NS);
hrtimer_init(&timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
timer.function = timer_callback;
expected_time = ktime_add(ktime_get(), interval);
hrtimer_start(&timer, interval, HRTIMER_MODE_REL);
proc_entry = proc_create("latency_measure", 0644, NULL, &latency_ops);
if (!proc_entry) {
hrtimer_cancel(&timer);
return -ENOMEM;
}
pr_info("latency_measure: Started, read /proc/latency_measure for results\n");
pr_info("latency_measure: Write anything to restart measurement\n");
return0;
}
staticvoid __exit latency_exit(void)
{
hrtimer_cancel(&timer);
proc_remove(proc_entry);
pr_info("latency_measure: Removed\n");
}
module_init(latency_init);
module_exit(latency_exit);
测试:
# 编译
make
# 加载模块
sudo insmod latency_measure.ko
# 等待测量完成后查看结果
sleep 2
cat /proc/latency_measure
# 在负载下重新测量
stress-ng --cpu 4 --timeout 10s &
echo 1 | sudo tee /proc/latency_measure
sleep 2
cat /proc/latency_measure
# 卸载
sudo rmmod latency_measure
第三部分:编译PREEMPT_RT内核
3.1 获取源码和补丁
cd ~/rt_learning
# 下载内核源码
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.6.tar.xz
# 下载RT补丁(检查最新版本)
# https://wiki.linuxfoundation.org/realtime/preempt_rt_versions
wget https://cdn.kernel.org/pub/linux/kernel/projects/rt/6.6/patch-6.6.x-rt15.patch.xz
# 解压
tar xf linux-6.6.tar.xz
cd linux-6.6
# 应用补丁
xzcat ../patch-6.6.x-rt15.patch.xz | patch -p1
# 验证补丁应用成功
head Makefile
# 应该看到 EXTRAVERSION = -rt15
3.2 配置内核
# 基于当前配置
cp /boot/config-$(uname -r) .config
make olddefconfig
# 或从头开始
make menuconfig
关键配置选项:
General setup --->
Preemption Model (Fully Preemptible Kernel (Real-Time)) --->
(X) Fully Preemptible Kernel (Real-Time)
Timers subsystem --->
[*] High Resolution Timer Support
Processor type and features --->
[*] Symmetric multi-processing support
Timer frequency (1000 HZ) --->
(X) 1000 HZ
Power management and ACPI options --->
CPU Frequency scaling --->
[ ] CPU Frequency scaling # 建议关闭,避免频率变化影响延迟
Kernel hacking --->
[*] Kernel debugging
[*] Detect Soft Lockups
-*- Detect Hung Tasks
[*] Kernel memory leak detector
使用脚本配置:
# 快速配置脚本
scripts/config --enable PREEMPT_RT
scripts/config --enable HIGH_RES_TIMERS
scripts/config --set-val HZ 1000
scripts/config --disable CPU_FREQ
scripts/config --enable DEBUG_PREEMPT
scripts/config --enable LATENCYTOP
3.3 编译和安装
# 编译(使用所有CPU核心)
make -j$(nproc)
# 安装模块
sudo make modules_install
# 安装内核
sudo make install
# 更新GRUB
sudo update-grub
# 重启选择RT内核
sudo reboot
3.4 验证RT内核
# 检查内核版本
uname -a
# 应该显示 -rt 后缀
# 检查PREEMPT_RT
cat /sys/kernel/realtime
# 应该输出 1
# 检查配置
zcat /proc/config.gz | grep PREEMPT
# CONFIG_PREEMPT_RT=y
# 对比测试
sudo cyclictest -m -p 90 -i 1000 -l 100000
# RT内核应该有更低的最大延迟
第四部分:实时编程实践
4.1 实时线程基础
创建文件 rt_thread_demo.c(用户态程序):
#define _GNU_SOURCE
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include<sched.h>
#include<sys/mman.h>
#include<time.h>
#include<errno.h>
#include<unistd.h>
#define NSEC_PER_SEC 1000000000L
#define ITERATIONS 10000
#define PERIOD_NS 1000000 // 1ms
// 时间辅助函数
staticinlinevoidtimespec_add_ns(struct timespec *ts, long ns)
{
ts->tv_nsec += ns;
while (ts->tv_nsec >= NSEC_PER_SEC) {
ts->tv_nsec -= NSEC_PER_SEC;
ts->tv_sec++;
}
}
staticinlinelongtimespec_diff_ns(struct timespec *a, struct timespec *b)
{
return (a->tv_sec - b->tv_sec) * NSEC_PER_SEC + (a->tv_nsec - b->tv_nsec);
}
// 统计结构
structstats {
long min;
long max;
long sum;
int count;
int histogram[100]; // 0-99μs
};
staticstructstatslatency_stats = {
.min = LONG_MAX,
.max = 0,
.sum = 0,
.count = 0,
};
void *rt_thread_func(void *arg)
{
structtimespecnext_period, now, start;
long latency;
int i;
// 获取初始时间
clock_gettime(CLOCK_MONOTONIC, &next_period);
for (i = 0; i < ITERATIONS; i++) {
// 等待下一个周期
timespec_add_ns(&next_period, PERIOD_NS);
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next_period, NULL);
// 测量唤醒延迟
clock_gettime(CLOCK_MONOTONIC, &now);
latency = timespec_diff_ns(&now, &next_period);
// 更新统计
if (latency < latency_stats.min) latency_stats.min = latency;
if (latency > latency_stats.max) latency_stats.max = latency;
latency_stats.sum += latency;
latency_stats.count++;
// 直方图
int bucket = latency / 1000; // ns -> μs
if (bucket >= 0 && bucket < 100) {
latency_stats.histogram[bucket]++;
}
// 模拟实时任务工作
// 这里可以添加实际的实时处理逻辑
}
returnNULL;
}
voidprint_stats(void)
{
printf("\n=== Latency Statistics ===\n");
printf("Samples: %d\n", latency_stats.count);
printf("Min: %ld ns (%.2f μs)\n", latency_stats.min,
latency_stats.min / 1000.0);
printf("Max: %ld ns (%.2f μs)\n", latency_stats.max,
latency_stats.max / 1000.0);
printf("Avg: %.2f ns (%.2f μs)\n",
(double)latency_stats.sum / latency_stats.count,
(double)latency_stats.sum / latency_stats.count / 1000.0);
printf("\n--- Histogram (μs) ---\n");
for (int i = 0; i < 20; i++) {
if (latency_stats.histogram[i] > 0) {
printf("%3d-%3d: %5d |", i, i+1, latency_stats.histogram[i]);
int bar = latency_stats.histogram[i] * 40 / ITERATIONS;
for (int j = 0; j < bar; j++) printf("#");
printf("\n");
}
}
}
intmain(int argc, char *argv[])
{
pthread_t thread;
pthread_attr_t attr;
structsched_paramparam;
int priority = 80;
int ret;
// 解析参数
if (argc > 1) {
priority = atoi(argv[1]);
if (priority < 1 || priority > 99) {
fprintf(stderr, "Priority must be 1-99\n");
return1;
}
}
printf("Real-Time Thread Demo\n");
printf("Period: %d ns, Iterations: %d, Priority: %d\n\n",
PERIOD_NS, ITERATIONS, priority);
// 锁定内存,防止换页
if (mlockall(MCL_CURRENT | MCL_FUTURE) != 0) {
perror("mlockall failed");
// 继续执行,但可能影响延迟
}
// 初始化线程属性
pthread_attr_init(&attr);
// 设置调度策略为SCHED_FIFO
pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
// 设置优先级
param.sched_priority = priority;
pthread_attr_setschedparam(&attr, ¶m);
// 使用显式调度属性(不继承父线程)
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
// 创建实时线程
ret = pthread_create(&thread, &attr, rt_thread_func, NULL);
if (ret != 0) {
fprintf(stderr, "pthread_create failed: %s\n", strerror(ret));
fprintf(stderr, "Try running with sudo\n");
return1;
}
printf("RT thread started, waiting for completion...\n");
// 等待线程完成
pthread_join(thread, NULL);
// 打印统计
print_stats();
pthread_attr_destroy(&attr);
munlockall();
return0;
}
编译和测试:
gcc -o rt_thread_demo rt_thread_demo.c -lpthread -lrt
# 普通运行(可能失败)
./rt_thread_demo
# 以root运行
sudo ./rt_thread_demo 90
# 不同优先级对比
sudo ./rt_thread_demo 50
sudo ./rt_thread_demo 99
# 在负载下测试
stress-ng --cpu 4 --timeout 20s &
sudo ./rt_thread_demo 90
4.2 CPU亲和性设置
创建文件 rt_affinity.c:
#define _GNU_SOURCE
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<sched.h>
#include<sys/mman.h>
#include<time.h>
#include<unistd.h>
#define ITERATIONS 5000
#define PERIOD_NS 1000000
structthread_data {
int cpu;
int priority;
long min_latency;
long max_latency;
long total_latency;
};
void *rt_thread(void *arg)
{
structthread_data *data = (struct thread_data *)arg;
structtimespecnext, now;
cpu_set_t cpuset;
long latency;
// 设置CPU亲和性
CPU_ZERO(&cpuset);
CPU_SET(data->cpu, &cpuset);
if (pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset) != 0) {
perror("pthread_setaffinity_np");
}
// 验证
if (pthread_getaffinity_np(pthread_self(), sizeof(cpuset), &cpuset) == 0) {
printf("Thread on CPU %d: affinity set to CPU %d\n",
sched_getcpu(), data->cpu);
}
data->min_latency = LONG_MAX;
data->max_latency = 0;
data->total_latency = 0;
clock_gettime(CLOCK_MONOTONIC, &next);
for (int i = 0; i < ITERATIONS; i++) {
next.tv_nsec += PERIOD_NS;
if (next.tv_nsec >= 1000000000L) {
next.tv_nsec -= 1000000000L;
next.tv_sec++;
}
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next, NULL);
clock_gettime(CLOCK_MONOTONIC, &now);
latency = (now.tv_sec - next.tv_sec) * 1000000000L +
(now.tv_nsec - next.tv_nsec);
if (latency < data->min_latency) data->min_latency = latency;
if (latency > data->max_latency) data->max_latency = latency;
data->total_latency += latency;
}
returnNULL;
}
intmain()
{
int num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
pthread_t *threads;
structthread_data *thread_data;
pthread_attr_t attr;
structsched_paramparam;
printf("RT CPU Affinity Demo\n");
printf("CPUs available: %d\n\n", num_cpus);
mlockall(MCL_CURRENT | MCL_FUTURE);
threads = malloc(num_cpus * sizeof(pthread_t));
thread_data = malloc(num_cpus * sizeof(struct thread_data));
pthread_attr_init(&attr);
pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
param.sched_priority = 80;
pthread_attr_setschedparam(&attr, ¶m);
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
// 在每个CPU上创建一个RT线程
for (int i = 0; i < num_cpus; i++) {
thread_data[i].cpu = i;
thread_data[i].priority = 80;
pthread_create(&threads[i], &attr, rt_thread, &thread_data[i]);
}
// 等待所有线程完成
for (int i = 0; i < num_cpus; i++) {
pthread_join(threads[i], NULL);
}
// 打印结果
printf("\n=== Results per CPU ===\n");
printf("%-4s %-12s %-12s %-12s\n", "CPU", "Min(ns)", "Max(ns)", "Avg(ns)");
printf("------------------------------------------\n");
for (int i = 0; i < num_cpus; i++) {
printf("%-4d %-12ld %-12ld %-12ld\n",
i,
thread_data[i].min_latency,
thread_data[i].max_latency,
thread_data[i].total_latency / ITERATIONS);
}
free(threads);
free(thread_data);
pthread_attr_destroy(&attr);
return0;
}
4.3 优先级继承互斥锁
创建文件 priority_inheritance.c:
#define _GNU_SOURCE
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<sched.h>
#include<sys/mman.h>
#include<unistd.h>
#include<time.h>
// 共享资源
staticpthread_mutex_t mutex;
staticvolatileint shared_data = 0;
// 线程信息
structthread_info {
int id;
int priority;
int work_time_ms;
};
voiddo_work(int ms)
{
structtimespects = {0, ms * 1000000L};
nanosleep(&ts, NULL);
}
void *low_priority_thread(void *arg)
{
structthread_info *info = (struct thread_info *)arg;
printf("[T%d] Low priority (%d) starting, acquiring lock...\n",
info->id, info->priority);
pthread_mutex_lock(&mutex);
printf("[T%d] Lock acquired, doing work for %d ms\n",
info->id, info->work_time_ms);
// 模拟长时间持有锁
do_work(info->work_time_ms);
shared_data++;
printf("[T%d] Releasing lock, data=%d\n", info->id, shared_data);
pthread_mutex_unlock(&mutex);
returnNULL;
}
void *high_priority_thread(void *arg)
{
structthread_info *info = (struct thread_info *)arg;
structtimespecstart, end;
// 等待低优先级线程先获取锁
do_work(50);
printf("[T%d] High priority (%d) starting, acquiring lock...\n",
info->id, info->priority);
clock_gettime(CLOCK_MONOTONIC, &start);
pthread_mutex_lock(&mutex);
clock_gettime(CLOCK_MONOTONIC, &end);
long wait_time = (end.tv_sec - start.tv_sec) * 1000 +
(end.tv_nsec - start.tv_nsec) / 1000000;
printf("[T%d] Lock acquired after %ld ms wait\n", info->id, wait_time);
shared_data++;
printf("[T%d] Done, data=%d\n", info->id, shared_data);
pthread_mutex_unlock(&mutex);
returnNULL;
}
void *medium_priority_thread(void *arg)
{
structthread_info *info = (struct thread_info *)arg;
// 等待一段时间后开始
do_work(100);
printf("[T%d] Medium priority (%d) starting CPU-bound work\n",
info->id, info->priority);
// CPU密集型工作,试图抢占低优先级线程
for (volatileint i = 0; i < 100000000; i++);
printf("[T%d] Work done\n", info->id);
returnNULL;
}
intmain()
{
pthread_t threads[3];
pthread_attr_t attr[3];
structsched_paramparam;
pthread_mutexattr_t mutex_attr;
structthread_infoinfo[3] = {
{0, 30, 500}, // 低优先级,持锁500ms
{1, 80, 0}, // 高优先级
{2, 50, 0}, // 中优先级
};
printf("Priority Inheritance Demo\n\n");
printf("Without PI: High priority thread blocked by medium priority\n");
printf("With PI: Low priority inherits high priority while holding lock\n\n");
mlockall(MCL_CURRENT | MCL_FUTURE);
// 初始化优先级继承互斥锁
pthread_mutexattr_init(&mutex_attr);
pthread_mutexattr_setprotocol(&mutex_attr, PTHREAD_PRIO_INHERIT);
pthread_mutex_init(&mutex, &mutex_attr);
printf("Mutex protocol: PTHREAD_PRIO_INHERIT\n\n");
// 创建低优先级线程
pthread_attr_init(&attr[0]);
pthread_attr_setschedpolicy(&attr[0], SCHED_FIFO);
param.sched_priority = info[0].priority;
pthread_attr_setschedparam(&attr[0], ¶m);
pthread_attr_setinheritsched(&attr[0], PTHREAD_EXPLICIT_SCHED);
pthread_create(&threads[0], &attr[0], low_priority_thread, &info[0]);
// 创建高优先级线程
pthread_attr_init(&attr[1]);
pthread_attr_setschedpolicy(&attr[1], SCHED_FIFO);
param.sched_priority = info[1].priority;
pthread_attr_setschedparam(&attr[1], ¶m);
pthread_attr_setinheritsched(&attr[1], PTHREAD_EXPLICIT_SCHED);
pthread_create(&threads[1], &attr[1], high_priority_thread, &info[1]);
// 创建中优先级线程
pthread_attr_init(&attr[2]);
pthread_attr_setschedpolicy(&attr[2], SCHED_FIFO);
param.sched_priority = info[2].priority;
pthread_attr_setschedparam(&attr[2], ¶m);
pthread_attr_setinheritsched(&attr[2], PTHREAD_EXPLICIT_SCHED);
pthread_create(&threads[2], &attr[2], medium_priority_thread, &info[2]);
// 等待所有线程完成
for (int i = 0; i < 3; i++) {
pthread_join(threads[i], NULL);
pthread_attr_destroy(&attr[i]);
}
pthread_mutex_destroy(&mutex);
pthread_mutexattr_destroy(&mutex_attr);
printf("\nDone!\n");
return0;
}
编译和测试:
gcc -o priority_inheritance priority_inheritance.c -lpthread
sudo ./priority_inheritance
第五部分:内核模块实时编程
5.1 高精度定时器模块
创建文件 rt_hrtimer.c:
#include<linux/init.h>
#include<linux/module.h>
#include<linux/hrtimer.h>
#include<linux/ktime.h>
#include<linux/proc_fs.h>
#include<linux/seq_file.h>
#include<linux/slab.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Real-Time High Resolution Timer");
#define TIMER_INTERVAL_NS 100000 // 100μs
#define MAX_SAMPLES 10000
staticstructhrtimerhr_timer;
staticktime_t interval;
staticktime_t last_time;
staticint sample_count = 0;
staticbool running = false;
structsample {
s64 expected_ns;
s64 actual_ns;
s64 jitter_ns;
};
staticstructsample *samples;
static s64 min_jitter = S64_MAX;
static s64 max_jitter = 0;
static s64 total_jitter = 0;
staticstructproc_dir_entry *proc_entry;
staticenum hrtimer_restart timer_callback(struct hrtimer *timer)
{
ktime_t now = ktime_get();
s64 expected, actual, jitter;
if (!running || sample_count >= MAX_SAMPLES) {
running = false;
return HRTIMER_NORESTART;
}
// 计算抖动
if (sample_count > 0) {
expected = ktime_to_ns(ktime_add(last_time, interval));
actual = ktime_to_ns(now);
jitter = actual - expected;
samples[sample_count].expected_ns = expected;
samples[sample_count].actual_ns = actual;
samples[sample_count].jitter_ns = jitter;
if (jitter < min_jitter) min_jitter = jitter;
if (jitter > max_jitter) max_jitter = jitter;
total_jitter += (jitter > 0) ? jitter : -jitter;
}
last_time = now;
sample_count++;
hrtimer_forward_now(timer, interval);
return HRTIMER_RESTART;
}
staticintrt_timer_show(struct seq_file *m, void *v)
{
int i;
s64 avg_jitter;
int histogram[20] = {0}; // -50 to +150 μs, 10μs bins
seq_printf(m, "=== RT High Resolution Timer ===\n\n");
seq_printf(m, "Interval: %lld ns\n", (s64)TIMER_INTERVAL_NS);
seq_printf(m, "Status: %s\n", running ? "Running" : "Stopped");
seq_printf(m, "Samples: %d / %d\n\n", sample_count, MAX_SAMPLES);
if (sample_count < 2) {
seq_printf(m, "Not enough samples yet.\n");
seq_printf(m, "Write 'start' to begin measurement.\n");
return0;
}
avg_jitter = total_jitter / (sample_count - 1);
seq_printf(m, "--- Jitter Statistics ---\n");
seq_printf(m, "Min: %lld ns (%lld μs)\n", min_jitter, min_jitter / 1000);
seq_printf(m, "Max: %lld ns (%lld μs)\n", max_jitter, max_jitter / 1000);
seq_printf(m, "Avg (abs): %lld ns (%lld μs)\n\n", avg_jitter, avg_jitter / 1000);
// 构建直方图
for (i = 1; i < sample_count; i++) {
int bucket = (samples[i].jitter_ns / 1000 + 50) / 10;
if (bucket < 0) bucket = 0;
if (bucket >= 20) bucket = 19;
histogram[bucket]++;
}
seq_printf(m, "--- Jitter Histogram ---\n");
for (i = 0; i < 20; i++) {
int bar_len = histogram[i] * 40 / sample_count;
seq_printf(m, "%4d to %4d μs: %5d |",
(i * 10) - 50, (i + 1) * 10 - 50, histogram[i]);
for (int j = 0; j < bar_len; j++) seq_printf(m, "#");
seq_printf(m, "\n");
}
// 最后10个样本
seq_printf(m, "\n--- Last 10 Samples ---\n");
seq_printf(m, "%-6s %-15s %-15s %-10s\n",
"Index", "Expected(ns)", "Actual(ns)", "Jitter(ns)");
for (i = sample_count - 10; i < sample_count && i > 0; i++) {
seq_printf(m, "%-6d %-15lld %-15lld %-10lld\n",
i, samples[i].expected_ns, samples[i].actual_ns,
samples[i].jitter_ns);
}
return0;
}
staticintrt_timer_open(struct inode *inode, struct file *file)
{
return single_open(file, rt_timer_show, NULL);
}
staticssize_trt_timer_write(struct file *file, constchar __user *buf,
size_t count, loff_t *ppos)
{
char cmd[16];
if (count >= sizeof(cmd)) return -EINVAL;
if (copy_from_user(cmd, buf, count)) return -EFAULT;
cmd[count] = '\0';
if (strncmp(cmd, "start", 5) == 0) {
if (running) {
pr_info("rt_hrtimer: Already running\n");
return count;
}
// 重置
sample_count = 0;
min_jitter = S64_MAX;
max_jitter = 0;
total_jitter = 0;
running = true;
last_time = ktime_get();
hrtimer_start(&hr_timer, interval, HRTIMER_MODE_REL);
pr_info("rt_hrtimer: Started\n");
} elseif (strncmp(cmd, "stop", 4) == 0) {
running = false;
hrtimer_cancel(&hr_timer);
pr_info("rt_hrtimer: Stopped\n");
}
return count;
}
staticconststructproc_opsrt_timer_ops = {
.proc_open = rt_timer_open,
.proc_read = seq_read,
.proc_write = rt_timer_write,
.proc_lseek = seq_lseek,
.proc_release = single_release,
};
staticint __init rt_hrtimer_init(void)
{
samples = kmalloc(MAX_SAMPLES * sizeof(struct sample), GFP_KERNEL);
if (!samples) return -ENOMEM;
interval = ktime_set(0, TIMER_INTERVAL_NS);
hrtimer_init(&hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
hr_timer.function = timer_callback;
proc_entry = proc_create("rt_hrtimer", 0644, NULL, &rt_timer_ops);
if (!proc_entry) {
kfree(samples);
return -ENOMEM;
}
pr_info("rt_hrtimer: Loaded\n");
pr_info("rt_hrtimer: echo 'start' > /proc/rt_hrtimer to begin\n");
return0;
}
staticvoid __exit rt_hrtimer_exit(void)
{
running = false;
hrtimer_cancel(&hr_timer);
proc_remove(proc_entry);
kfree(samples);
pr_info("rt_hrtimer: Unloaded\n");
}
module_init(rt_hrtimer_init);
module_exit(rt_hrtimer_exit);
5.2 实时工作队列
创建文件 rt_workqueue.c:
#include<linux/init.h>
#include<linux/module.h>
#include<linux/workqueue.h>
#include<linux/ktime.h>
#include<linux/proc_fs.h>
#include<linux/seq_file.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Real-Time Workqueue Demo");
staticstructworkqueue_struct *rt_wq;
staticstructdelayed_workrt_work;
staticstructproc_dir_entry *proc_entry;
staticint work_count = 0;
staticktime_t last_work_time;
static s64 work_intervals[100];
staticint interval_index = 0;
staticvoidrt_work_func(struct work_struct *work)
{
ktime_t now = ktime_get();
if (work_count > 0) {
s64 interval = ktime_to_ns(ktime_sub(now, last_work_time));
if (interval_index < 100) {
work_intervals[interval_index++] = interval;
}
}
last_work_time = now;
work_count++;
// 重新调度
if (work_count < 100) {
queue_delayed_work(rt_wq, &rt_work, msecs_to_jiffies(10));
}
}
staticintrt_wq_show(struct seq_file *m, void *v)
{
int i;
s64 min = S64_MAX, max = 0, sum = 0;
seq_printf(m, "=== RT Workqueue Demo ===\n\n");
seq_printf(m, "Work executions: %d\n", work_count);
seq_printf(m, "Recorded intervals: %d\n\n", interval_index);
for (i = 0; i < interval_index; i++) {
if (work_intervals[i] < min) min = work_intervals[i];
if (work_intervals[i] > max) max = work_intervals[i];
sum += work_intervals[i];
}
if (interval_index > 0) {
seq_printf(m, "Interval stats (expected: 10ms):\n");
seq_printf(m, " Min: %lld ns (%lld ms)\n", min, min / 1000000);
seq_printf(m, " Max: %lld ns (%lld ms)\n", max, max / 1000000);
seq_printf(m, " Avg: %lld ns (%lld ms)\n",
sum / interval_index, sum / interval_index / 1000000);
}
return0;
}
staticintrt_wq_open(struct inode *inode, struct file *file)
{
return single_open(file, rt_wq_show, NULL);
}
staticssize_trt_wq_write(struct file *file, constchar __user *buf,
size_t count, loff_t *ppos)
{
// 重置并开始
work_count = 0;
interval_index = 0;
queue_delayed_work(rt_wq, &rt_work, msecs_to_jiffies(10));
pr_info("rt_workqueue: Started\n");
return count;
}
staticconststructproc_opsrt_wq_ops = {
.proc_open = rt_wq_open,
.proc_read = seq_read,
.proc_write = rt_wq_write,
.proc_lseek = seq_lseek,
.proc_release = single_release,
};
staticint __init rt_wq_init(void)
{
// 创建高优先级工作队列
rt_wq = alloc_workqueue("rt_wq", WQ_HIGHPRI | WQ_CPU_INTENSIVE, 0);
if (!rt_wq) return -ENOMEM;
INIT_DELAYED_WORK(&rt_work, rt_work_func);
proc_entry = proc_create("rt_workqueue", 0644, NULL, &rt_wq_ops);
if (!proc_entry) {
destroy_workqueue(rt_wq);
return -ENOMEM;
}
pr_info("rt_workqueue: Write to /proc/rt_workqueue to start\n");
return0;
}
staticvoid __exit rt_wq_exit(void)
{
cancel_delayed_work_sync(&rt_work);
destroy_workqueue(rt_wq);
proc_remove(proc_entry);
}
module_init(rt_wq_init);
module_exit(rt_wq_exit);
第六部分:Makefile和测试
综合Makefile
# 内核模块
obj-m += latency_measure.o
obj-m += rt_hrtimer.o
obj-m += rt_workqueue.o
KDIR := /lib/modules/$(shell uname -r)/build
# 用户态程序
USER_PROGS = rt_thread_demo rt_affinity priority_inheritance
all: modules userspace
modules:
make -C $(KDIR) M=$(PWD) modules
userspace: $(USER_PROGS)
rt_thread_demo: rt_thread_demo.c
gcc -o $@$< -lpthread -lrt -O2
rt_affinity: rt_affinity.c
gcc -o $@$< -lpthread -lrt -O2
priority_inheritance: priority_inheritance.c
gcc -o $@$< -lpthread -O2
clean:
make -C $(KDIR) M=$(PWD) clean
rm -f $(USER_PROGS)
# 测试目标
test-latency:
sudo insmod latency_measure.ko
sleep 2
cat /proc/latency_measure
sudo rmmod latency_measure
test-hrtimer:
sudo insmod rt_hrtimer.ko
echo "start" | sudo tee /proc/rt_hrtimer
sleep 2
cat /proc/rt_hrtimer
sudo rmmod rt_hrtimer
test-userspace:
sudo ./rt_thread_demo 90
sudo ./rt_affinity
sudo ./priority_inheritance
实操检查清单
延迟测量
RT内核
用户态编程
内核模块
扩展一:实时应用开发(工业控制)
1.1 伺服电机控制模拟
创建 servo_control.c:
#define _GNU_SOURCE
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<sched.h>
#include<time.h>
#include<math.h>
#include<signal.h>
#include<sys/mman.h>
#define CONTROL_PERIOD_US 1000 // 1ms控制周期
#define SIMULATION_TIME_S 10 // 模拟时间
// PID控制器参数
structpid_controller {
double kp, ki, kd;
double integral;
double prev_error;
double output_min, output_max;
};
// 电机状态
structmotor_state {
double position; // 当前位置(度)
double velocity; // 当前速度(度/秒)
double target; // 目标位置
double torque; // 输出扭矩
};
// 控制统计
structcontrol_stats {
long min_latency_ns;
long max_latency_ns;
long total_latency_ns;
int cycle_count;
int overrun_count;
};
staticvolatileint running = 1;
staticstructmotor_statemotor = {0, 0, 0, 0};
staticstructpid_controllerpid = {10.0, 0.5, 2.0, 0, 0, -100, 100};
staticstructcontrol_statsstats = {LONG_MAX, 0, 0, 0, 0};
voidsignal_handler(int sig) { running = 0; }
// PID计算
doublepid_compute(struct pid_controller *p, double setpoint, double measured, double dt)
{
double error = setpoint - measured;
p->integral += error * dt;
// 积分限幅
if (p->integral > 50) p->integral = 50;
if (p->integral < -50) p->integral = -50;
double derivative = (error - p->prev_error) / dt;
p->prev_error = error;
double output = p->kp * error + p->ki * p->integral + p->kd * derivative;
// 输出限幅
if (output > p->output_max) output = p->output_max;
if (output < p->output_min) output = p->output_min;
return output;
}
// 电机模型仿真(简化二阶系统)
voidmotor_simulate(struct motor_state *m, double torque, double dt)
{
double inertia = 0.01; // 转动惯量
double damping = 0.1; // 阻尼系数
double acceleration = (torque - damping * m->velocity) / inertia;
m->velocity += acceleration * dt;
m->position += m->velocity * dt;
}
// 实时控制线程
void *control_thread(void *arg)
{
structtimespecnext_time, current_time;
long period_ns = CONTROL_PERIOD_US * 1000;
double dt = CONTROL_PERIOD_US / 1000000.0;
int trajectory_step = 0;
clock_gettime(CLOCK_MONOTONIC, &next_time);
while (running) {
// 等待下一个周期
next_time.tv_nsec += period_ns;
if (next_time.tv_nsec >= 1000000000) {
next_time.tv_nsec -= 1000000000;
next_time.tv_sec++;
}
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next_time, NULL);
// 测量延迟
clock_gettime(CLOCK_MONOTONIC, ¤t_time);
long latency = (current_time.tv_sec - next_time.tv_sec) * 1000000000 +
(current_time.tv_nsec - next_time.tv_nsec);
if (latency < stats.min_latency_ns) stats.min_latency_ns = latency;
if (latency > stats.max_latency_ns) stats.max_latency_ns = latency;
stats.total_latency_ns += latency;
stats.cycle_count++;
if (latency > period_ns) stats.overrun_count++;
// 生成轨迹(正弦波)
trajectory_step++;
motor.target = 45.0 * sin(2 * M_PI * trajectory_step * dt / 2.0);
// PID控制
motor.torque = pid_compute(&pid, motor.target, motor.position, dt);
// 电机仿真
motor_simulate(&motor, motor.torque, dt);
// 每100ms打印一次
if (trajectory_step % 100 == 0) {
printf("Target: %7.2f Pos: %7.2f Err: %6.2f Torque: %6.2f\n",
motor.target, motor.position,
motor.target - motor.position, motor.torque);
}
}
returnNULL;
}
intmain()
{
pthread_t thread;
pthread_attr_t attr;
structsched_paramparam;
cpu_set_t cpuset;
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
// 锁定内存
if (mlockall(MCL_CURRENT | MCL_FUTURE) != 0) {
perror("mlockall");
}
printf("=== Real-Time Servo Control Simulation ===\n");
printf("Control period: %d us\n", CONTROL_PERIOD_US);
printf("Press Ctrl+C to stop\n\n");
// 创建实时线程
pthread_attr_init(&attr);
pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
param.sched_priority = 90;
pthread_attr_setschedparam(&attr, ¶m);
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
// 绑定到CPU 1
CPU_ZERO(&cpuset);
CPU_SET(1, &cpuset);
pthread_attr_setaffinity_np(&attr, sizeof(cpuset), &cpuset);
pthread_create(&thread, &attr, control_thread, NULL);
// 等待指定时间或Ctrl+C
sleep(SIMULATION_TIME_S);
running = 0;
pthread_join(thread, NULL);
// 打印统计
printf("\n=== Control Statistics ===\n");
printf("Total cycles: %d\n", stats.cycle_count);
printf("Latency - Min: %ld ns, Max: %ld ns, Avg: %ld ns\n",
stats.min_latency_ns, stats.max_latency_ns,
stats.total_latency_ns / stats.cycle_count);
printf("Overruns (>%d us): %d (%.2f%%)\n",
CONTROL_PERIOD_US, stats.overrun_count,
100.0 * stats.overrun_count / stats.cycle_count);
return0;
}
1.2 多轴协调控制
创建 multi_axis_control.c:
#define _GNU_SOURCE
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<sched.h>
#include<time.h>
#include<math.h>
#include<sys/mman.h>
#define NUM_AXES 3
#define PERIOD_US 500 // 500us周期
structaxis {
int id;
double position;
double target;
double velocity;
pthread_mutex_t mutex;
};
structsync_data {
pthread_barrier_t barrier;
structaxisaxes[NUM_AXES];
volatileint running;
};
staticstructsync_datasync_data;
void *axis_thread(void *arg)
{
int axis_id = *(int *)arg;
structaxis *ax = &sync_data.axes[axis_id];
structtimespecnext;
long period_ns = PERIOD_US * 1000;
double dt = PERIOD_US / 1000000.0;
int step = 0;
clock_gettime(CLOCK_MONOTONIC, &next);
while (sync_data.running) {
// 同步屏障 - 所有轴同时开始计算
pthread_barrier_wait(&sync_data.barrier);
// 生成协调轨迹
step++;
double phase = 2 * M_PI * step * dt / 3.0;
pthread_mutex_lock(&ax->mutex);
switch (axis_id) {
case0: ax->target = 100 * sin(phase); break; // X轴
case1: ax->target = 100 * cos(phase); break; // Y轴
case2: ax->target = 50 * sin(2 * phase); break; // Z轴
}
// 简单位置控制
double error = ax->target - ax->position;
ax->velocity = 10 * error;
ax->position += ax->velocity * dt;
pthread_mutex_unlock(&ax->mutex);
// 等待下一周期
next.tv_nsec += period_ns;
if (next.tv_nsec >= 1000000000) {
next.tv_nsec -= 1000000000;
next.tv_sec++;
}
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next, NULL);
}
returnNULL;
}
void *monitor_thread(void *arg)
{
while (sync_data.running) {
printf("Axis positions: X=%7.2f Y=%7.2f Z=%7.2f\n",
sync_data.axes[0].position,
sync_data.axes[1].position,
sync_data.axes[2].position);
usleep(100000); // 100ms
}
returnNULL;
}
intmain()
{
pthread_t threads[NUM_AXES], monitor;
pthread_attr_t attr;
structsched_paramparam;
int axis_ids[NUM_AXES];
mlockall(MCL_CURRENT | MCL_FUTURE);
// 初始化同步数据
pthread_barrier_init(&sync_data.barrier, NULL, NUM_AXES);
sync_data.running = 1;
for (int i = 0; i < NUM_AXES; i++) {
sync_data.axes[i].id = i;
sync_data.axes[i].position = 0;
sync_data.axes[i].target = 0;
pthread_mutex_init(&sync_data.axes[i].mutex, NULL);
axis_ids[i] = i;
}
printf("=== Multi-Axis Coordinated Control ===\n");
printf("Period: %d us, Axes: %d\n\n", PERIOD_US, NUM_AXES);
// 创建轴控制线程
pthread_attr_init(&attr);
pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
param.sched_priority = 85;
pthread_attr_setschedparam(&attr, ¶m);
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
for (int i = 0; i < NUM_AXES; i++) {
pthread_create(&threads[i], &attr, axis_thread, &axis_ids[i]);
}
// 监控线程
pthread_create(&monitor, NULL, monitor_thread, NULL);
sleep(5);
sync_data.running = 0;
for (int i = 0; i < NUM_AXES; i++) {
pthread_join(threads[i], NULL);
}
pthread_join(monitor, NULL);
pthread_barrier_destroy(&sync_data.barrier);
printf("\nSimulation complete.\n");
return0;
}
扩展二:Xenomai双内核方案
2.1 Xenomai架构
用户空间
┌─────────────┬─────────────┐
│ 普通Linux │ Xenomai │
│ 应用程序 │ 实时应用 │
└─────┬───────┴──────┬──────┘
│ │
内核空间 │
┌─────┴───────┐ │
│ Linux内核 │ │
│ (非实时) │ │
└─────┬───────┘ │
│ │
┌─────┴──────────────┴──────┐
│ I-Pipe / Dovetail │
│ (中断虚拟化层) │
└─────────────┬─────────────┘
│
┌─────────────┴─────────────┐
│ Cobalt微内核 │
│ (硬实时核心) │
└───────────────────────────┘
2.2 Xenomai安装
# 安装依赖
sudo apt install build-essential libncurses-dev bison flex libssl-dev
# 下载Xenomai
cd ~/rt_learning
git clone https://source.denx.de/Xenomai/xenomai.git
cd xenomai
git checkout v3.2.2
# 下载内核和I-Pipe补丁
cd ..
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.4.tar.xz
tar xf linux-5.4.tar.xz
# 获取I-Pipe补丁
wget https://xenomai.org/downloads/ipipe/v5.x/x86/ipipe-core-5.4-x86-XX.patch
# 应用补丁
cd linux-5.4
../xenomai/scripts/prepare-kernel.sh --arch=x86_64 \
--ipipe=../ipipe-core-5.4-x86-XX.patch
# 配置内核
make menuconfig
# 确保启用:
# - Xenomai/cobalt
# - I-Pipe support
# 编译安装
make -j$(nproc)
sudo make modules_install
sudo make install
# 编译Xenomai用户库
cd ../xenomai
./scripts/bootstrap
./configure --with-core=cobalt --enable-smp
make -j$(nproc)
sudo make install
2.3 Xenomai实时程序
创建 xenomai_rt_task.c:
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
#include<alchemy/task.h>
#include<alchemy/timer.h>
#define TASK_PERIOD_NS 1000000 // 1ms
#define TASK_PRIORITY 80
static RT_TASK rt_task;
staticint running = 1;
voidsignal_handler(int sig) { running = 0; }
voidrt_task_func(void *arg)
{
RTIME period = TASK_PERIOD_NS;
RTIME now, expected;
long min_lat = LONG_MAX, max_lat = 0, total_lat = 0;
int count = 0;
// 设置周期
rt_task_set_periodic(NULL, TM_NOW, period);
printf("Xenomai RT task started (period=%lld ns)\n", period);
while (running && count < 10000) {
expected = rt_timer_read();
rt_task_wait_period(NULL);
now = rt_timer_read();
long latency = (long)(now - expected - period);
if (latency < 0) latency = -latency;
if (latency < min_lat) min_lat = latency;
if (latency > max_lat) max_lat = latency;
total_lat += latency;
count++;
if (count % 1000 == 0) {
printf("Cycle %d: lat min=%ld max=%ld avg=%ld ns\n",
count, min_lat, max_lat, total_lat / count);
}
}
printf("\n=== Final Statistics ===\n");
printf("Cycles: %d\n", count);
printf("Latency: min=%ld max=%ld avg=%ld ns\n",
min_lat, max_lat, total_lat / count);
}
intmain(int argc, char *argv[])
{
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
printf("=== Xenomai Real-Time Task Demo ===\n");
// 创建实时任务
int ret = rt_task_create(&rt_task, "rt_demo", 0, TASK_PRIORITY, T_JOINABLE);
if (ret) {
fprintf(stderr, "rt_task_create failed: %d\n", ret);
return1;
}
// 启动任务
ret = rt_task_start(&rt_task, rt_task_func, NULL);
if (ret) {
fprintf(stderr, "rt_task_start failed: %d\n", ret);
return1;
}
// 等待任务完成
rt_task_join(&rt_task);
rt_task_delete(&rt_task);
printf("Task completed.\n");
return0;
}
2.4 Xenomai编译和运行
# 编译
export XENOMAI_ROOT=/usr/xenomai
$XENOMAI_ROOT/bin/xeno-config --skin=alchemy --cflags
$XENOMAI_ROOT/bin/xeno-config --skin=alchemy --ldflags
gcc -o xenomai_rt_task xenomai_rt_task.c \
$($XENOMAI_ROOT/bin/xeno-config --skin=alchemy --cflags --ldflags)
# 运行(需要Xenomai内核)
sudo ./xenomai_rt_task
# 查看Xenomai状态
cat /proc/xenomai/sched/stat
cat /proc/xenomai/heap
2.5 Xenomai POSIX接口
创建 xenomai_posix.c:
#include<stdio.h>
#include<pthread.h>
#include<time.h>
#include<signal.h>
staticvolatileint running = 1;
voidsig_handler(int sig) { running = 0; }
void *rt_thread(void *arg)
{
structtimespecnext, now;
long period_ns = 1000000; // 1ms
long max_lat = 0, count = 0;
clock_gettime(CLOCK_MONOTONIC, &next);
while (running && count < 10000) {
next.tv_nsec += period_ns;
if (next.tv_nsec >= 1000000000) {
next.tv_nsec -= 1000000000;
next.tv_sec++;
}
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next, NULL);
clock_gettime(CLOCK_MONOTONIC, &now);
long lat = (now.tv_sec - next.tv_sec) * 1000000000 +
(now.tv_nsec - next.tv_nsec);
if (lat > max_lat) max_lat = lat;
count++;
}
printf("Max latency: %ld ns over %ld cycles\n", max_lat, count);
returnNULL;
}
intmain()
{
pthread_t thread;
pthread_attr_t attr;
structsched_paramparam;
signal(SIGINT, sig_handler);
pthread_attr_init(&attr);
pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
param.sched_priority = 80;
pthread_attr_setschedparam(&attr, ¶m);
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
pthread_create(&thread, &attr, rt_thread, NULL);
pthread_join(thread, NULL);
return0;
}
# 使用Xenomai POSIX skin编译
gcc -o xenomai_posix xenomai_posix.c \
$($XENOMAI_ROOT/bin/xeno-config --skin=posix --cflags --ldflags)
扩展三:调度器源码分析
3.1 调度器核心文件
# 调度器源码位置
ls kernel/sched/
# core.c - 调度器核心
# fair.c - CFS(完全公平调度器)
# rt.c - 实时调度器(FIFO/RR)
# deadline.c - Deadline调度器
# idle.c - 空闲调度器
# stop_task.c - 停止任务调度器
3.2 调度器信息模块
创建 sched_info.c:
#include<linux/init.h>
#include<linux/module.h>
#include<linux/proc_fs.h>
#include<linux/seq_file.h>
#include<linux/sched.h>
#include<linux/sched/signal.h>
#include<linux/sched/rt.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Scheduler Information Module");
staticintsched_show(struct seq_file *m, void *v)
{
structtask_struct *task;
int rt_count = 0, fair_count = 0, dl_count = 0;
seq_printf(m, "=== Scheduler Information ===\n\n");
// 统计各调度类任务
rcu_read_lock();
for_each_process(task) {
switch (task->policy) {
case SCHED_NORMAL:
case SCHED_BATCH:
case SCHED_IDLE:
fair_count++;
break;
case SCHED_FIFO:
case SCHED_RR:
rt_count++;
break;
case SCHED_DEADLINE:
dl_count++;
break;
}
}
rcu_read_unlock();
seq_printf(m, "Task Count by Scheduler Class:\n");
seq_printf(m, " CFS (fair): %d\n", fair_count);
seq_printf(m, " RT (FIFO/RR): %d\n", rt_count);
seq_printf(m, " Deadline: %d\n", dl_count);
seq_printf(m, " Total: %d\n\n", fair_count + rt_count + dl_count);
// 显示实时任务
seq_printf(m, "Real-Time Tasks (SCHED_FIFO/RR):\n");
seq_printf(m, "%-8s %-16s %-6s %-8s %-8s\n",
"PID", "COMM", "PRIO", "POLICY", "CPU");
seq_printf(m, "--------------------------------------------\n");
rcu_read_lock();
for_each_process(task) {
if (task->policy == SCHED_FIFO || task->policy == SCHED_RR) {
seq_printf(m, "%-8d %-16s %-6d %-8s %-8d\n",
task->pid,
task->comm,
task->rt_priority,
task->policy == SCHED_FIFO ? "FIFO" : "RR",
task_cpu(task));
}
}
rcu_read_unlock();
// 显示Deadline任务
seq_printf(m, "\nDeadline Tasks (SCHED_DEADLINE):\n");
seq_printf(m, "%-8s %-16s %-12s %-12s %-12s\n",
"PID", "COMM", "Runtime", "Deadline", "Period");
seq_printf(m, "--------------------------------------------------------\n");
rcu_read_lock();
for_each_process(task) {
if (task->policy == SCHED_DEADLINE) {
seq_printf(m, "%-8d %-16s %-12llu %-12llu %-12llu\n",
task->pid,
task->comm,
task->dl.dl_runtime,
task->dl.dl_deadline,
task->dl.dl_period);
}
}
rcu_read_unlock();
return0;
}
staticintsched_open(struct inode *inode, struct file *file)
{
return single_open(file, sched_show, NULL);
}
staticconststructproc_opssched_ops = {
.proc_open = sched_open,
.proc_read = seq_read,
.proc_lseek = seq_lseek,
.proc_release = single_release,
};
staticstructproc_dir_entry *proc_entry;
staticint __init sched_info_init(void)
{
proc_entry = proc_create("sched_info", 0444, NULL, &sched_ops);
return proc_entry ? 0 : -ENOMEM;
}
staticvoid __exit sched_info_exit(void)
{
proc_remove(proc_entry);
}
module_init(sched_info_init);
module_exit(sched_info_exit);
3.3 调度延迟测量模块
创建 sched_latency.c:
#include<linux/init.h>
#include<linux/module.h>
#include<linux/kthread.h>
#include<linux/delay.h>
#include<linux/sched/rt.h>
#include<linux/hrtimer.h>
#include<linux/proc_fs.h>
#include<linux/seq_file.h>
MODULE_LICENSE("GPL");
#define NUM_SAMPLES 1000
staticstructtask_struct *measure_thread;
staticktime_t latencies[NUM_SAMPLES];
staticint sample_count = 0;
staticint measurement_done = 0;
staticintmeasure_func(void *data)
{
structsched_paramparam = { .sched_priority = MAX_RT_PRIO - 1 };
ktime_t expected, actual;
int i;
sched_setscheduler(current, SCHED_FIFO, ¶m);
for (i = 0; i < NUM_SAMPLES && !kthread_should_stop(); i++) {
expected = ktime_add_ns(ktime_get(), 1000000); // 1ms后
set_current_state(TASK_INTERRUPTIBLE);
schedule_hrtimeout(&expected, HRTIMER_MODE_ABS);
actual = ktime_get();
latencies[i] = ktime_sub(actual, expected);
sample_count++;
}
measurement_done = 1;
pr_info("sched_latency: Measurement complete, %d samples\n", sample_count);
return0;
}
staticintlatency_show(struct seq_file *m, void *v)
{
s64 min_ns = S64_MAX, max_ns = 0, total_ns = 0;
int i;
seq_printf(m, "=== Scheduler Latency Measurement ===\n\n");
if (!measurement_done) {
seq_printf(m, "Measurement in progress... (%d/%d)\n",
sample_count, NUM_SAMPLES);
return0;
}
for (i = 0; i < sample_count; i++) {
s64 lat = ktime_to_ns(latencies[i]);
if (lat < min_ns) min_ns = lat;
if (lat > max_ns) max_ns = lat;
total_ns += lat;
}
seq_printf(m, "Samples: %d\n", sample_count);
seq_printf(m, "Min latency: %lld ns\n", min_ns);
seq_printf(m, "Max latency: %lld ns\n", max_ns);
seq_printf(m, "Avg latency: %lld ns\n", total_ns / sample_count);
// 延迟分布
seq_printf(m, "\nLatency Distribution:\n");
int bins[6] = {0}; // <1us, 1-10us, 10-100us, 100us-1ms, 1-10ms, >10ms
for (i = 0; i < sample_count; i++) {
s64 lat = ktime_to_ns(latencies[i]);
if (lat < 1000) bins[0]++;
elseif (lat < 10000) bins[1]++;
elseif (lat < 100000) bins[2]++;
elseif (lat < 1000000) bins[3]++;
elseif (lat < 10000000) bins[4]++;
else bins[5]++;
}
seq_printf(m, " < 1us: %d\n", bins[0]);
seq_printf(m, " 1-10us: %d\n", bins[1]);
seq_printf(m, " 10-100us: %d\n", bins[2]);
seq_printf(m, " 100us-1ms: %d\n", bins[3]);
seq_printf(m, " 1-10ms: %d\n", bins[4]);
seq_printf(m, " > 10ms: %d\n", bins[5]);
return0;
}
staticintlatency_open(struct inode *inode, struct file *file)
{
return single_open(file, latency_show, NULL);
}
staticconststructproc_opslatency_ops = {
.proc_open = latency_open,
.proc_read = seq_read,
.proc_lseek = seq_lseek,
.proc_release = single_release,
};
staticstructproc_dir_entry *proc_entry;
staticint __init sched_latency_init(void)
{
proc_entry = proc_create("sched_latency", 0444, NULL, &latency_ops);
if (!proc_entry) return -ENOMEM;
measure_thread = kthread_run(measure_func, NULL, "sched_lat_measure");
if (IS_ERR(measure_thread)) {
proc_remove(proc_entry);
return PTR_ERR(measure_thread);
}
return0;
}
staticvoid __exit sched_latency_exit(void)
{
if (measure_thread && !measurement_done)
kthread_stop(measure_thread);
proc_remove(proc_entry);
}
module_init(sched_latency_init);
module_exit(sched_latency_exit);
扩展四:中断线程化机制
4.1 中断线程化原理
传统中断处理:
硬件中断 → 禁用中断 → ISR执行 → 重新启用
线程化中断 (PREEMPT_RT):
硬件中断 → 快速确认 → 唤醒中断线程 → 线程处理(可被抢占)
4.2 中断线程化状态查看
# 查看中断线程
ps -eo pid,comm,rtprio,policy | grep irq
# 查看中断线程优先级
cat /proc/irq/*/smp_affinity_list
# 设置中断线程优先级
chrt -f -p 90 <irq_thread_pid>
# 查看中断线程化状态
cat /sys/kernel/debug/irq/<irq>/threads
4.3 线程化中断模块
创建 threaded_irq_demo.c:
#include<linux/init.h>
#include<linux/module.h>
#include<linux/interrupt.h>
#include<linux/gpio.h>
#include<linux/platform_device.h>
#include<linux/proc_fs.h>
#include<linux/seq_file.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Threaded IRQ Demo");
staticint irq_number = -1;
module_param(irq_number, int, 0644);
staticatomic_t hardirq_count = ATOMIC_INIT(0);
staticatomic_t threadirq_count = ATOMIC_INIT(0);
staticktime_t last_hardirq_time;
staticktime_t last_thread_time;
static s64 max_thread_latency_ns = 0;
// 硬中断处理(快速,不可抢占)
staticirqreturn_thardirq_handler(int irq, void *dev_id)
{
atomic_inc(&hardirq_count);
last_hardirq_time = ktime_get();
// 返回IRQ_WAKE_THREAD唤醒线程处理
return IRQ_WAKE_THREAD;
}
// 线程化处理(可被抢占)
staticirqreturn_tthread_handler(int irq, void *dev_id)
{
s64 latency;
atomic_inc(&threadirq_count);
last_thread_time = ktime_get();
latency = ktime_to_ns(ktime_sub(last_thread_time, last_hardirq_time));
if (latency > max_thread_latency_ns)
max_thread_latency_ns = latency;
// 模拟耗时操作
udelay(100);
return IRQ_HANDLED;
}
staticintirq_show(struct seq_file *m, void *v)
{
seq_printf(m, "=== Threaded IRQ Statistics ===\n\n");
seq_printf(m, "IRQ number: %d\n", irq_number);
seq_printf(m, "Hard IRQ count: %d\n", atomic_read(&hardirq_count));
seq_printf(m, "Thread IRQ count: %d\n", atomic_read(&threadirq_count));
seq_printf(m, "Max hard->thread latency: %lld ns\n", max_thread_latency_ns);
return0;
}
staticintirq_open(struct inode *inode, struct file *file)
{
return single_open(file, irq_show, NULL);
}
staticconststructproc_opsirq_ops = {
.proc_open = irq_open,
.proc_read = seq_read,
.proc_lseek = seq_lseek,
.proc_release = single_release,
};
staticstructproc_dir_entry *proc_entry;
staticint __init threaded_irq_init(void)
{
int ret;
if (irq_number < 0) {
pr_err("threaded_irq: Please specify irq_number parameter\n");
return -EINVAL;
}
// 注册线程化中断
ret = request_threaded_irq(irq_number,
hardirq_handler, // 硬中断处理
thread_handler, // 线程处理
IRQF_SHARED,
"threaded_irq_demo",
(void *)&hardirq_count);
if (ret) {
pr_err("threaded_irq: Failed to request IRQ %d: %d\n", irq_number, ret);
return ret;
}
proc_entry = proc_create("threaded_irq", 0444, NULL, &irq_ops);
pr_info("threaded_irq: Registered on IRQ %d\n", irq_number);
return0;
}
staticvoid __exit threaded_irq_exit(void)
{
proc_remove(proc_entry);
free_irq(irq_number, (void *)&hardirq_count);
pr_info("threaded_irq: Unregistered\n");
}
module_init(threaded_irq_init);
module_exit(threaded_irq_exit);
4.4 软中断延迟测量
创建 softirq_latency.c:
#include<linux/init.h>
#include<linux/module.h>
#include<linux/interrupt.h>
#include<linux/hrtimer.h>
#include<linux/proc_fs.h>
#include<linux/seq_file.h>
MODULE_LICENSE("GPL");
staticstructtasklet_structtest_tasklet;
staticktime_t schedule_time;
staticktime_t execute_time;
static s64 min_latency = S64_MAX;
static s64 max_latency = 0;
static s64 total_latency = 0;
staticint sample_count = 0;
staticstructhrtimertrigger_timer;
staticvoidtasklet_func(unsignedlong data)
{
s64 latency;
execute_time = ktime_get();
latency = ktime_to_ns(ktime_sub(execute_time, schedule_time));
if (latency < min_latency) min_latency = latency;
if (latency > max_latency) max_latency = latency;
total_latency += latency;
sample_count++;
}
staticenum hrtimer_restart timer_callback(struct hrtimer *timer)
{
if (sample_count < 1000) {
schedule_time = ktime_get();
tasklet_schedule(&test_tasklet);
hrtimer_forward_now(timer, ns_to_ktime(10000000)); // 10ms
return HRTIMER_RESTART;
}
return HRTIMER_NORESTART;
}
staticintshow(struct seq_file *m, void *v)
{
seq_printf(m, "=== Softirq/Tasklet Latency ===\n\n");
seq_printf(m, "Samples: %d\n", sample_count);
if (sample_count > 0) {
seq_printf(m, "Min: %lld ns\n", min_latency);
seq_printf(m, "Max: %lld ns\n", max_latency);
seq_printf(m, "Avg: %lld ns\n", total_latency / sample_count);
}
return0;
}
staticintopen(struct inode *inode, struct file *file)
{
return single_open(file, show, NULL);
}
staticconststructproc_opsops = {
.proc_open = open, .proc_read = seq_read,
.proc_lseek = seq_lseek, .proc_release = single_release,
};
staticstructproc_dir_entry *proc_entry;
staticint __init softirq_init(void)
{
tasklet_init(&test_tasklet, tasklet_func, 0);
hrtimer_init(&trigger_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
trigger_timer.function = timer_callback;
hrtimer_start(&trigger_timer, ns_to_ktime(10000000), HRTIMER_MODE_REL);
proc_entry = proc_create("softirq_latency", 0444, NULL, &ops);
pr_info("softirq_latency: Started\n");
return0;
}
staticvoid __exit softirq_exit(void)
{
hrtimer_cancel(&trigger_timer);
tasklet_kill(&test_tasklet);
proc_remove(proc_entry);
}
module_init(softirq_init);
module_exit(softirq_exit);
扩展Makefile
# 用户态程序
CC = gcc
CFLAGS = -O2 -Wall -pthread -lm -lrt
all: servo_control multi_axis_control modules
servo_control: servo_control.c
$(CC)$(CFLAGS) -o $@$<
multi_axis_control: multi_axis_control.c
$(CC)$(CFLAGS) -o $@$<
# 内核模块
obj-m += sched_info.o
obj-m += sched_latency.o
obj-m += threaded_irq_demo.o
obj-m += softirq_latency.o
KDIR := /lib/modules/$(shell uname -r)/build
modules:
make -C $(KDIR) M=$(PWD) modules
clean:
make -C $(KDIR) M=$(PWD) clean
rm -f servo_control multi_axis_control
# Xenomai程序(需要Xenomai环境)
xenomai: xenomai_rt_task.c xenomai_posix.c
$(XENOMAI_ROOT)/bin/xeno-config --skin=alchemy --cflags --ldflags | \
xargs $(CC) -o xenomai_rt_task xenomai_rt_task.c
$(XENOMAI_ROOT)/bin/xeno-config --skin=posix --cflags --ldflags | \
xargs $(CC) -o xenomai_posix xenomai_posix.c
扩展检查清单
实时应用开发
Xenomai
- [ ] 理解双内核架构(Cobalt + Linux)
调度器源码
中断线程化