为什么使用taskset在一组隔离的内核上运行多线程Linux程序会导致所有线程在一个内核上运行?

Edd*_*ett 10 linux multithreading scheduler affinity

期望的行为:在已经隔离的一组核上运行多线程Linux程序isolcpus.

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

#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);
}
Run Code Online (Sandbox Code Playgroud)

如果我在没有隔离CPU的内核上编译并运行它,那么线程将分布在我的4个CPU上.好!

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

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

这篇文章描述了一个类似的问题(虽然给出的例子离pthreads API更远).OP很乐意通过使用不同的调度程序来解决这个问题.我不确定这对我的用例来说是理想的.

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

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

编辑:

如果您使用像fifo调度程序这样的实时调度程序,那么确实会发生正确的事情.查看man schedman chrt了解详情.

Wei*_*hen 5

来自 Linux 内核参数文档:

该选项可用于指定一个或多个 CPU 以与通用 SMP 平衡和调度算法隔离。

因此,此选项将有效防止调度程序将线程从一个核心迁移到另一个竞争较少的核心(SMP 平衡)。由于典型的 isolcpu 与 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 devel 邮件列表中的维护者发送电子邮件进行修复。