当前位置:首页>Linux>进阶方向二:实时内核(RT-Linux)- 详细实操指南

进阶方向二:实时内核(RT-Linux)- 详细实操指南

  • 2026-02-05 00:53:59
进阶方向二:实时内核(RT-Linux)- 详细实操指南

预计时间:2-3个月 前置要求:完成基础五阶段学习 目标:掌握实时系统开发


第一部分:实时基础概念

1.1 实时系统分类

类型
特点
应用场景
硬实时
必须在截止时间内完成,否则系统失败
航空、医疗设备、汽车ABS
软实时
尽量满足截止时间,偶尔超时可接受
音视频播放、游戏
准实时
统计意义上满足时间要求
网络服务、数据库

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
│  └─ 线程编号

延迟基准参考

内核类型
典型最大延迟
普通内核
100-1000+ μs
PREEMPT内核
50-200 μs
PREEMPT_RT
10-50 μs

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"0644NULL, &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_periodnowstart;
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, &param);

// 使用显式调度属性(不继承父线程)
    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;
structtimespecnextnow;
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, &param);
    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;
structtimespecstartend;

// 等待低优先级线程先获取锁
    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] = {
        {030500},  // 低优先级,持锁500ms
        {1800},    // 高优先级
        {2500},    // 中优先级
    };

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], &param);
    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], &param);
    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], &param);
    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"0644NULL, &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"0644NULL, &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

实操检查清单

延迟测量

  • [ ] 能使用cyclictest测量延迟
  • [ ] 理解延迟统计指标含义
  • [ ] 能在负载下进行测试

RT内核

  • [ ] 能下载和应用RT补丁
  • [ ] 能配置和编译RT内核
  • [ ] 能验证RT内核运行

用户态编程

  • [ ] 能创建SCHED_FIFO线程
  • [ ] 能设置CPU亲和性
  • [ ] 理解优先级继承机制
  • [ ] 能使用mlockall锁定内存

内核模块

  • [ ] 能使用高精度定时器
  • [ ] 能测量内核定时器抖动
  • [ ] 理解高优先级工作队列

扩展一:实时应用开发(工业控制)

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 = {0000};
staticstructpid_controllerpid = {10.00.52.000-100100};
staticstructcontrol_statsstats = {LONG_MAX, 0000};

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_timecurrent_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, &current_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, &param);
    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, &param);
    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)
{
structtimespecnextnow;
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, &param);
    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"0444NULL, &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, &param);

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"0444NULL, &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, int0644);

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"0444NULL, &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"0444NULL, &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

扩展检查清单

实时应用开发

  • [ ] 能实现PID控制器
  • [ ] 能实现多轴协调控制
  • [ ] 理解实时控制周期和抖动影响

Xenomai

  • [ ] 理解双内核架构(Cobalt + Linux)
  • [ ] 能安装Xenomai环境
  • [ ] 能编写Alchemy API程序
  • [ ] 能使用POSIX skin

调度器源码

  • [ ] 理解调度类层次结构
  • [ ] 能识别CFS/RT/DL任务
  • [ ] 能测量调度延迟

中断线程化

  • [ ] 理解线程化中断原理
  • [ ] 能编写threaded_irq处理
  • [ ] 能测量软中断延迟
  • [ ] 能调整中断线程优先级

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-02-08 07:07:06 HTTP/2.0 GET : https://f.mffb.com.cn/a/472330.html
  2. 运行时间 : 0.147306s [ 吞吐率:6.79req/s ] 内存消耗:4,878.64kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=72b6cfa428947c13268855b91664432a
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000637s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000771s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.003534s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.003427s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000643s ]
  6. SELECT * FROM `set` [ RunTime:0.000279s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000590s ]
  8. SELECT * FROM `article` WHERE `id` = 472330 LIMIT 1 [ RunTime:0.004892s ]
  9. UPDATE `article` SET `lasttime` = 1770505626 WHERE `id` = 472330 [ RunTime:0.002271s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 67 LIMIT 1 [ RunTime:0.002147s ]
  11. SELECT * FROM `article` WHERE `id` < 472330 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000516s ]
  12. SELECT * FROM `article` WHERE `id` > 472330 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.002864s ]
  13. SELECT * FROM `article` WHERE `id` < 472330 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.006837s ]
  14. SELECT * FROM `article` WHERE `id` < 472330 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.013165s ]
  15. SELECT * FROM `article` WHERE `id` < 472330 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.016268s ]
0.148857s