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

时间:2016-04-13 16:27:30

标签: linux multithreading scheduler affinity

所需行为:在使用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

1 个答案:

答案 0 :(得分:3)

来自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 devel邮件列表中的维护者进行修复。