优雅选择用户线程调度策略以及优先级 内核线程 中断 CPU亲和性

360 阅读3分钟

优雅选择调度策略/优先级

  • 本文主要介绍综合考虑内核中断,内核线程以及用户线程的情况下,用户线程如何选择调度策略以及优先级。

问题引入

  • 调试板子时,运行程序,概率性遇到问题 mmc1: Timeout waiting for hardware interrupt。搞得一头雾水。
  • 后来东搞西搞,发现将程序中的线程绑定到 cpu1 上,反复测试后,都不会出现问题。

思考原因

  • 程序默认将线程绑定到 cpu0 上,且使用的是 SCHED_FIFO 策略,会不会是抢占了 cpu0,导致中断或者内核线程(默认策略为 SCHED_NORMAL)没有及时得到运行。但是,转念一想,内核一般都有负载均衡策略,就算 cpu0 被占用,也有其他的 cpu 可以使用。奈何也想不出其他原因,就打算先从中断入手。

  • cat /proc/interrupts 查看中断情况,输出如下:

               CPU0       CPU1       CPU2       CPU3
     19:      26131      12743      12604      20136     GIC-0  27 Level     arch_timer
     21:      32332      18752      16822      20502     GIC-0  56 Level     rk_timer
     27:          2          0          0          0     GIC-0  36 Level     ff3f0000.i2c
     35:          0          0          0          0     GIC-0  71 Level     rockchip_thermal
     36:     199319          0          0          0     GIC-0 145 Level     rockchip_thermal
     37:          0          0          0          0     GIC-0  90 Level     rkcifhw
     38:         77          0          0          0     GIC-0  94 Level     rga
     39:          0          0          0          0     GIC-0  91 Level     ffb00f00.iommu, ffb00000.vop
     59:      34562          0          0          0     GIC-0 127 Level     eth0
     61:      79287          0          0          0     GIC-0 110 Level     dw-mci
    
  • 发现大部分中断都在 cpu0 上,这就让人疑惑了,不是有负载均衡嘛,怎么在其他 cpu 上没有。然后查看中断号的 cpu 亲和性,cat /proc/irq/59/smp_affinity 输出为 fcat /proc/irq/59/smp_affinity_list 输出为 0-3,即中断可以在所有 cpu 上处理。

  • 到这,可以提出猜想:即使有负载均衡策略而且中断号的 cpu 亲和性设置为全部 cpu, 但某些中断号以及内核线程就是只能在 cpu0 上处理,用户程序长时间占用 cpu0,就可能造成内核无法及时相应。

验证猜想

  • 验证方法:运行以下代码,然后将 affinity 改为 1后,再次运行,如果前者引起系统卡顿,而后者没有,则可以验证猜想。

  • 验证代码:

    int main(void)
    {
        int affinity = 0;
        cpu_set_t mask;
        CPU_ZERO(&mask);
        CPU_SET(affinity, &mask);
        pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask);
        sched_param sp{ 0 };
        sp.sched_priority = 1;
        pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp);
        while (1);
    }
    
  • 现象对比:

    内核cpugcc 版本绑定 cpu0绑定 cpu1
    Linux 5.15.0-86-generic Ubuntu x86_64AMD Ryzen 7 3800X11.3系统一切正常系统一切正常
    Linux 5.10.160 SMP aarch64RK358810.3网络卡顿,ping卡住
    通过串口输入 crtl+c 卡顿
    top ps 等命令卡住
    系统一切正常
    Linux 4.19.111 SMP PREEMPT armv7lRV11268.3网络卡顿,ping卡住
    通过串口输入 crtl+c 卡顿
    top ps 等命令卡住
    系统一切正常

结论

  • 该问题看来只会在某些平台上出现,使用存在该问题的平台时,可以参考下面的建议。
  • 用户空间线程最好使用默认的调度策略,如有需要使用类似 SCHED_FIFO 的实时调度策略,则一定要设置其 cpu 亲和性,确保其在 cpu0 以外的 cpu 上运行。
  • 用户空间线程如果需要设置 cpu 亲和性,则应避免在 cpu0 上绑定过多的线程。
  • 对于核心够用的 cpu,完全可以把所有线程绑定在除 cpu0 以外的 cpu 上,把 cpu0 留给系统使用。