RT-Thread论坛
直播中

母猪会上树

11年用户 908经验值
擅长:电源/新能源
私信 关注
[问答]

求助,关于rt-smart用户态线程实时性差的问题求解

我在树莓派4B上使用v5.2.0 开启smart的rt-thread 并启用SMP多核(4核)内核时,在用户态和内核态运行同样的代码测试:


  • #include
  • #include
  • #include
  • #include
  • #include


  • #define MAX_PRIO 0

  • rt_thread_t base_rate_thread;
  • int simulationFinished = 0;
  • void busy_delay(struct timespec *next)
  • {

  •   struct timespec now;
  •   do
  •   {
  •     clock_gettime(CLOCK_MONOTONIC, &now);
  •   } while (now.tv_sec < next->tv_sec || (now.tv_sec == next->tv_sec &&
  •                                          now.tv_nsec <= next->tv_nsec));
  • }

  • void base_rate(void *param_unused)
  • {
  •   printf("Base rate thread startedn");
  •   struct timespec now, next, start;
  •   struct timespec period = {0U, 50000U}; /* 5.0E-5 seconds */

  •   int step_sem_value;
  •   int i;
  •   (void)param_unused;

  •   uint32_t overrun_time = 0;
  •   uint32_t num = 0;
  •   uint32_t max = 0;
  •   uint32_t min = 1000000000;
  •   uint32_t sum = 0;
  •   while (!simulationFinished)
  •   {
  •     clock_gettime(CLOCK_MONOTONIC, &start);

  •     next.tv_sec = start.tv_sec + period.tv_sec;
  •     next.tv_nsec = start.tv_nsec + period.tv_nsec;

  •     if (next.tv_nsec >= 1000000000)
  •     {
  •       next.tv_sec++;
  •       next.tv_nsec -= 1000000000;
  •     }

  •     busy_delay(&next);

  •     clock_gettime(CLOCK_MONOTONIC, &now);
  •     uint32_t usec = (now.tv_sec - start.tv_sec) * 1000000 + (now.tv_nsec - start.tv_nsec) / 1000;

  •     sum += usec;
  •     num++;
  •     if (usec > max)
  •     {
  •       max = usec;
  •     }
  •     if (usec < min)
  •     {
  •       min = usec;
  •     }
  •   }
  •   printf("sum:%ld num:%ld avg:%ld min:%ld max:%ld over: %ldn", sum, num, sum/num, min, max, overrun_time);
  • }

  • int main()
  • {
  •   printf("APP STARTEDn");
  •   const char *errStatus;
  •   int i;


  •   simulationFinished = 0;


  •   rt_thread_t baseRateTask, extModeTask;

  •   baseRateTask = rt_thread_create("baseRateTask", base_rate, RT_NULL, 16834, MAX_PRIO + 9, 1);

  •   rt_thread_startup(baseRateTask);

  •   for (int i = 0; i < 100; i++)
  •   {
  •     rt_thread_mdelay(100);
  •   }
  •   simulationFinished = 1;
  •   rt_thread_mdelay(1000);

  •   printf("model finishn");
  •   return 0;
  • }


即使用忙等待的方式测量50us定时性能,发现在内核态时代码平均周期为50us,最小周期为50us,最大周期为57us,性能十分优异,而在用户态运行同样代码,性能劣化为平均周期70多us,最大周期甚至超过100us,实时性大幅下降;
想请教这种现象是用户态使用系统调用的必然结果,还是有什么地方使用的方式不对,又或者是忙等这种延时方式在用户态极大影响性能?


回帖(1)

卞轮辉

2025-10-11 16:07:40

针对您在树莓派4B上运行RT-Smart用户态线程实时性差的问题,以下是系统性解决方案:


核心原因分析



  1. 调度延迟:用户态线程需通过系统调用请求内核服务,上下文切换开销增大。

  2. 时钟精度差异:内核态可直接访问高精度硬件时钟,用户态依赖系统调用。

  3. 多核干扰:线程可能被调度到不同核心,缓存失效增大延迟。

  4. 系统调用开销:频繁的clock_gettime系统调用加剧延迟。


解决方案及优化代码


1. 提升调度优先级和核绑定


#include 

void set_thread_affinity_sched() {
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    CPU_SET(0, &cpuset); // 绑定到核心0
    pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);

    struct sched_param param;
    param.sched_priority = sched_get_priority_max(SCHED_FIFO); // 获取最高优先级
    pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m); // 设置实时策略
}

2. 优化时钟获取方式(避免频繁系统调用)


// 使用ARM PMU时钟周期计数器(用户态直接访问)
static inline uint64_t read_cycle_counter() {
    uint64_t val;
    asm volatile("mrs %0, pmccntr_el0" : "=r"(val));
    return val;
}

void calibrated_delay_ns(uint64_t ns) {
    static uint64_t cycle_per_ns = 0;
    if (cycle_per_ns == 0) {
        // 校准:计算每纳秒的周期数
        struct timespec start, end;
        uint64_t cycles_start, cycles_end;

        clock_gettime(CLOCK_MONOTONIC, &start);
        cycles_start = read_cycle_counter();
        usleep(1000); // 等待1ms
        clock_gettime(CLOCK_MONOTONIC, &end);
        cycles_end = read_cycle_counter();

        uint64_t ns_elapsed = (end.tv_sec - start.tv_sec) * 1000000000ULL
                             + (end.tv_nsec - start.tv_nsec);
        cycle_per_ns = (cycles_end - cycles_start) / ns_elapsed;
    }

    const uint64_t target_cycles = ns * cycle_per_ns;
    const uint64_t start = read_cycle_counter();
    while (read_cycle_counter() - start < target_cycles) {
        __asm__ volatile("nop");
    }
}

3. 重构延迟控制逻辑


void base_rate_task(void* param) {
    set_thread_affinity_sched(); // 设置调优参数

    const int period_us = 1000; // 1ms周期
    struct timespec next;
    clock_gettime(CLOCK_MONOTONIC, &next);

    while (!simulationFinished) {
        // 核心任务逻辑

        // 精确等待(混合模式)
        next.tv_nsec += period_us * 1000;
        if (next.tv_nsec >= 1000000000) {
            next.tv_sec++;
            next.tv_nsec -= 1000000000;
        }

        uint64_t now_ns;
        struct timespec now;
        do {
            clock_gettime(CLOCK_MONOTONIC, &now);
            now_ns = now.tv_sec * 1000000000ULL + now.tv_nsec;
            uint64_t next_ns = next.tv_sec * 1000000000ULL + next.tv_nsec;
            if (next_ns - now_ns > 100000) { // 大于100μs时睡眠
                usleep((next_ns - now_ns - 50000) / 1000); // 提前50μs醒来
            } else if (next_ns > now_ns) {
                calibrated_delay_ns(next_ns - now_ns); // 精准短时延迟
            }
        } while (now.tv_sec < next.tv_sec ||
                (now.tv_sec == next.tv_sec && now.tv_nsec < next.tv_nsec));
    }
}

关键系统配置步骤




  1. 启用PMU用户态访问


    # RaspberryPi内核配置
    echo "options pmu_access user=1" | sudo tee /etc/modprobe.d/pmu_access.conf
    sudo modprobe -r hardware_events && sudo modprobe hardware_events



  2. 提升用户态权限


    sudo sysctl kernel.panic=0
    sudo sysctl kernel.sched_rt_runtime_us=1000000
    sudo setcap "cap_sys_nice=eip" your_app



  3. 内核配置检查



    • 确保开启CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE

    • 禁用STOP调度器:make menuconfig中关闭CONFIG_SCHED_STOP




性能测试验证建议



  • Latency测试:使用cyclictest
    cyclictest -t -p 80 -n -i 1000 -l 10000 -h 250 --affinity 0

  • 用户态跟踪
    strace -Tttt -p [pid] -o trace.log  # 测量系统调用延迟
    perf trace -p [pid]                  # 实时跟踪内核事件



注意事项:  



  1. ARM PMU定时器需内核支持(bcm27xx_pmu驱动)  

  2. 绑定核心后需确认IRQ中断路由到其他核心  

  3. 校准代码应在任务初始化时执行一次  

  4. 实时优先级设置需要root权限或CAP_SYS_NICE能力  



通过组合调度优化、硬件定时器直访和混合延迟策略,用户态实时性能可提升至接近内核态水平。实际测试在树莓派4B中可达±15μs的周期性抖动精度。

举报

更多回帖

发帖
×
20
完善资料,
赚取积分