42

是否有一种编程方法可以为 Linux 操作系统的 c/c++ 中的进程设置 CPU 亲和性?

4

6 回答 6

54

你需要使用sched_setaffinity(2).

例如,仅在 CPU 0 和 2 上运行:

#define _GNU_SOURCE
#include <sched.h>

cpu_set_t  mask;
CPU_ZERO(&mask);
CPU_SET(0, &mask);
CPU_SET(2, &mask);
int result = sched_setaffinity(0, sizeof(mask), &mask);

0对于第一个参数表示当前进程,如果它是您想要控制的其他进程,请提供 PID)。

另请参阅sched_getcpu(3)

于 2008-11-11T13:52:48.417 回答
10

在进程级别使用 sched_setaffinity,或对单个线程使用pthread_attr_setaffinity_np

于 2008-11-11T13:57:05.867 回答
5

我做了很多努力来意识到正在发生的事情,所以我添加了这个答案来帮助像我这样的人(我gcc在 linux mint 中使用编译器)

#include <sched.h> 
cpu_set_t  mask;

inline void assignToThisCore(int core_id)
{
    CPU_ZERO(&mask);
    CPU_SET(core_id, &mask);
    sched_setaffinity(0, sizeof(mask), &mask);
}
int main(){
    //cal this:
    assignToThisCore(2);//assign to core 0,1,2,...

    return 0;
}

但不要忘记将此选项添加到编译器命令:-D _GNU_SOURCE 因为操作系统可能会为特定内核分配一个进程,您可以将其添加GRUB_CMDLINE_LINUX_DEFAULT="quiet splash isolcpus=2,3"到位于的 grub 文件/etc/default和运行 sudo update-grub终端中以保留您想要的内核

更新: 如果你想分配更多的核心,你可以按照这段代码:

inline void assignToThisCores(int core_id1, int core_id2)
{
    CPU_ZERO(&mask1);
    CPU_SET(core_id1, &mask1);
    CPU_SET(core_id2, &mask1);
    sched_setaffinity(0, sizeof(mask1), &mask1);
    //__asm__ __volatile__ ( "vzeroupper" : : : ); // It is hear because of that bug which dirtied the AVX registers, so, if you rely on AVX uncomment it.
}
于 2016-12-23T10:36:57.590 回答
4

sched_setaffinity+sched_getaffinity最小的 C 可运行示例

这个例子摘自我的回答:How to use sched_getaffinity and sched_setaffinity in Linux from C? 我相信这些问题不是重复的,因为它是这个问题的一个子集,因为它只询问sched_getaffinity,并且没有提到 C++。

在这个例子中,我们获取了affinity,修改它,并检查它是否已经生效sched_getcpu()

主程序

#define _GNU_SOURCE
#include <assert.h>
#include <sched.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void print_affinity() {
    cpu_set_t mask;
    long nproc, i;

    if (sched_getaffinity(0, sizeof(cpu_set_t), &mask) == -1) {
        perror("sched_getaffinity");
        assert(false);
    }
    nproc = sysconf(_SC_NPROCESSORS_ONLN);
    printf("sched_getaffinity = ");
    for (i = 0; i < nproc; i++) {
        printf("%d ", CPU_ISSET(i, &mask));
    }
    printf("\n");
}

int main(void) {
    cpu_set_t mask;

    print_affinity();
    printf("sched_getcpu = %d\n", sched_getcpu());
    CPU_ZERO(&mask);
    CPU_SET(0, &mask);
    if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) == -1) {
        perror("sched_setaffinity");
        assert(false);
    }
    print_affinity();
    /* TODO is it guaranteed to have taken effect already? Always worked on my tests. */
    printf("sched_getcpu = %d\n", sched_getcpu());
    return EXIT_SUCCESS;
}

GitHub 上游.

编译并运行:

gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out

样本输出:

sched_getaffinity = 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 
sched_getcpu = 9
sched_getaffinity = 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
sched_getcpu = 0

意思就是:

  • 最初,我的所有 16 个内核都已启用,并且该进程在内核 9(第 10 个)上随机运行
  • 在我们将亲和性设置为仅第一个核心后,进程必然移动到核心 0(第一个)

通过以下方式运行此程序也很有趣taskset

taskset -c 1,3 ./a.out

这给出了形式的输出:

sched_getaffinity = 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 
sched_getcpu = 2
sched_getaffinity = 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
sched_getcpu = 0

所以我们看到它从一开始就限制了亲和力。

这是因为亲和性是由子进程继承的,即taskset分叉:如何防止子分叉进程继承 CPU 亲和性?

蟒蛇:os.sched_getaffinityos.sched_setaffinity

请参阅:如何使用 python 找出 CPU 的数量

在 Ubuntu 16.04 中测试。

于 2019-02-01T11:09:31.433 回答
2

简而言之

unsigned long mask = 7; /* processors 0, 1, and 2 */
unsigned int len = sizeof(mask);
if (sched_setaffinity(0, len, &mask) < 0) {
    perror("sched_setaffinity");
}

查看CPU Affinity以获取更多详细信息

于 2008-11-11T14:20:56.817 回答
0

也可以通过 shell 实现,而无需对带有cgroupscpuset子系统的程序进行任何修改。Cgroups(至少 v1)通常安装在cpuset子系统所在的/sys/fs/cgroup上。例如:

$ ls -l /sys/fs/cgroup/
total 0
drwxr-xr-x 15 root root 380 nov.   22 20:00 ./
drwxr-xr-x  8 root root   0 nov.   22 20:00 ../
dr-xr-xr-x  2 root root   0 nov.   22 20:00 blkio/
[...]
lrwxrwxrwx  1 root root  11 nov.   22 20:00 cpuacct -> cpu,cpuacct/
dr-xr-xr-x  2 root root   0 nov.   22 20:00 cpuset/
dr-xr-xr-x  5 root root   0 nov.   22 20:00 devices/
dr-xr-xr-x  3 root root   0 nov.   22 20:00 freezer/
[...]

cpuset下,cpuset.cpus定义了允许属于该 cgroup 的进程运行的 CPU 范围。在这里,在顶层,为系统的所有进程配置了所有 CPU。在这里,系统有 8 个 CPU:

$ cd /sys/fs/cgroup/cpuset
$ cat cpuset.cpus
0-7

属于该 cgroup 的进程列表在cgroup.procs文件中列出:

$ cat cgroup.procs
1
2
3
[...]
12364
12423
12424
12425
[...]

可以创建允许一部分 CPU 进入的子 cgroup。例如,让我们定义一个具有 CPU 核心 1 和 3 的子 cgroup:

$ pwd
/sys/fs/cgroup/cpuset
$ sudo mkdir subset1
$ cd subset1
$ pwd
/sys/fs/cgroup/cpuset/subset1
$ ls -l 
total 0
-rw-r--r-- 1 root root 0 nov.   22 23:28 cgroup.clone_children
-rw-r--r-- 1 root root 0 nov.   22 23:28 cgroup.procs
-rw-r--r-- 1 root root 0 nov.   22 23:28 cpuset.cpu_exclusive
-rw-r--r-- 1 root root 0 nov.   22 23:28 cpuset.cpus
-r--r--r-- 1 root root 0 nov.   22 23:28 cpuset.effective_cpus
-r--r--r-- 1 root root 0 nov.   22 23:28 cpuset.effective_mems
-rw-r--r-- 1 root root 0 nov.   22 23:28 cpuset.mem_exclusive
-rw-r--r-- 1 root root 0 nov.   22 23:28 cpuset.mem_hardwall
-rw-r--r-- 1 root root 0 nov.   22 23:28 cpuset.memory_migrate
-r--r--r-- 1 root root 0 nov.   22 23:28 cpuset.memory_pressure
-rw-r--r-- 1 root root 0 nov.   22 23:28 cpuset.memory_spread_page
-rw-r--r-- 1 root root 0 nov.   22 23:28 cpuset.memory_spread_slab
-rw-r--r-- 1 root root 0 nov.   22 23:28 cpuset.mems
-rw-r--r-- 1 root root 0 nov.   22 23:28 cpuset.sched_load_balance
-rw-r--r-- 1 root root 0 nov.   22 23:28 cpuset.sched_relax_domain_level
-rw-r--r-- 1 root root 0 nov.   22 23:28 notify_on_release
-rw-r--r-- 1 root root 0 nov.   22 23:28 tasks
$ cat cpuset.cpus

$ sudo sh -c "echo 1,3 > cpuset.cpus"
$ cat cpuset.cpus 
1,3

在将任何进程移入此 cgroup 之前,必须填充cpuset.mems文件。在这里,我们将当前的 shell 移动到这个新的 cgroup 中(我们只是将进程的 pid 写入到cgroup.procs文件中):

$ cat cgroup.procs

$ echo $$
4753
$ sudo sh -c "echo 4753 > cgroup.procs"
sh: 1: echo: echo: I/O error
$ cat cpuset.mems

$ sudo sh -c "echo 0 > cpuset.mems"
$ cat cpuset.mems
0
$ sudo sh -c "echo 4753 > cgroup.procs"
$ cat cgroup.procs
4753
12569

后者显示当前 shell (pid#4753) 现在位于新创建的 cgroup 中(第二个 pid 12569 是cat的命令之一,作为当前 shell 的子级,它继承了它的 cgroups)。使用格式化的ps命令,可以验证进程在哪个 CPU 上运行(PSR列):

$ ps -o pid,ppid,psr,command
    PID    PPID PSR COMMAND
   4753    2372   3 bash
  12672    4753   1 ps -o pid,ppid,psr,command

我们可以看到当前 shell 正在 CPU#3 上运行,其继承其 cgroups的子 ( ps命令) 正在 CPU#1 上运行。

作为结论,可以不使用sched_setaffinity()或任何pthread服务,而是在 cgroups 树中创建一个cpuset层次结构,并通过将进程的 pid 写入相应的cgroup.procs文件中来将进程移动到其中。

于 2020-11-22T22:52:14.187 回答