为啥使用任务集在一组独立的内核上运行多线程 Linux 程序会导致所有线程在一个内核上运行?

Posted

技术标签:

【中文标题】为啥使用任务集在一组独立的内核上运行多线程 Linux 程序会导致所有线程在一个内核上运行?【英文标题】:Why does using taskset to run a multi-threaded Linux program on a set of isolated cores cause all threads to run on one core?为什么使用任务集在一组独立的内核上运行多线程 Linux 程序会导致所有线程在一个内核上运行? 【发布时间】:2016-04-13 16:27:30 【问题描述】:

期望的行为:在一组已使用isolcpus 隔离的内核上运行多线程 Linux 程序。

这是一个小程序,我们可以将其用作多线程程序的示例:

#include <stdio.h>
#include <pthread.h>
#include <err.h>
#include <unistd.h>
#include <stdlib.h>

#define NTHR    16
#define TIME    60 * 5

void *
do_stuff(void *arg)

    int i = 0;

    (void) arg;
    while (1) 
        i += i;
        usleep(10000); /* dont dominate CPU */
    


int
main(void)

    pthread_t   threads[NTHR];
    int     rv, i;

    for (i = 0; i < NTHR; i++) 
        rv = pthread_create(&threads[i], NULL, do_stuff, NULL);
        if (rv) 
            perror("pthread_create");
            return (EXIT_FAILURE);
        
    
    sleep(TIME);
    exit(EXIT_SUCCESS);

如果我在没有独立 CPU 的内核上编译和运行它,那么线程会分布在我的 4 个 CPU 上。好!

现在如果我将isolcpus=2,3 添加到内核命令行并重新启动:

在没有任务集的情况下运行程序会将线程分配到核心 0 和 1。这是预期的,因为默认的关联掩码现在不包括核心 2 和 3。 使用taskset -c 0,1 运行具有相同的效果。很好。 使用taskset -c 2,3 运行会导致所有线程进入同一个核心(核心2 或核心3)。这是不希望的。线程应该分布在核心 2 和 3 上。对吧?

This post 描述了一个类似的问题(尽管给出的示例离 pthreads API 更远)。 OP 很高兴通过使用不同的调度程序来解决这个问题。不过,我不确定这是否适合我的用例。

有没有办法使用默认调度程序将线程分布在隔离的核心上?

这是我应该报告的内核错误吗?

编辑

如果您使用像 fifo 调度程序这样的实时调度程序,确实会发生正确的事情。详情请参阅man schedman chrt

【问题讨论】:

就我个人而言,我没想到会有这样的行为。向 LKML 发送电子邮件,检查为什么默认调度程序无法跨已通过 taskset 分配的隔离内核迁移。 【参考方案1】:

来自 Linux 内核参数文档:

此选项可用于指定一个或多个 CPU 进行隔离 通用的 SMP 平衡和调度算法。

因此,此选项将有效防止调度程序将线程从一个核心迁移到另一个竞争较少的核心(SMP 平衡)。由于典型的 isolcpus 与 pthread 关联控制一起使用,以通过 CPU 布局知识固定线程以获得可预测的性能。

https://www.kernel.org/doc/Documentation/kernel-parameters.txt

--编辑--

好的,我明白你为什么感到困惑了。是的,我个人会假设这个选项的行为一致。问题在于两个函数,select_task_rq_fair 和 select_task_rq_rt,它们负责选择新的 run_queue(本质上是选择在哪个 next_cpu 上运行)。我对这两个函数进行了快速跟踪(Systemtap),对于 CFS,它总是会在掩码中返回相同的第一个核心;对于 RT,它将返回其他内核。我没有机会研究每个选择算法中的逻辑,但您可以发送电子邮件给 Linux 开发邮件列表中的维护人员进行修复。

【讨论】:

好的,但我不明白为什么线程在强制到独立的内核上时会分散,并且使用实时调度程序(例如 fifo)而不是默认调度程序。 @EddBarrett 跟踪isolcpus 参数在默认和 RT 调度程序中是如何解释的,以在 Linux 内核中获得准确的实现。这将是sched.csched_rt.c(对于旧版本)或sched/core.csched/rt.c 在较新版本的Linux 内核中。 我刚刚为此提出了一个错误:bugzilla.kernel.org/show_bug.cgi?id=116701 @thecodeartist, @wei-shen 我已经努力追踪select_task_rq_fair。结果在这里:bugzilla.kernel.org/show_bug.cgi?id=116701

以上是关于为啥使用任务集在一组独立的内核上运行多线程 Linux 程序会导致所有线程在一个内核上运行?的主要内容,如果未能解决你的问题,请参考以下文章

Linux多线程学习总结

Linux平台服务器多线程开发

Linux平台服务器多线程开发

复习多线程

进程线程对比

多线程编程总结