如何在Linux中为C或C ++的进程设置CPU关联?

时间:2008-11-11 13:47:00

标签: c linux smp affinity

是否有一种编程方法可以为Linux操作系统在c / c ++中为进程设置CPU关联。

6 个答案:

答案 0 :(得分:47)

您需要使用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);
result = sched_setaffinity(0, sizeof(mask), &mask);

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

另见sched_getcpu(3)

答案 1 :(得分:8)

在流程级别使用sched_setaffinity,或在单个线程中使用pthread_attr_setaffinity_np

答案 2 :(得分: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了解更多详情

答案 3 :(得分:1)

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

#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"添加到/etc/default中的grub文件和终端中的运行sudo update-grub以保留所需的核心

答案 4 :(得分:1)

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

此示例摘自我在How to use sched_getaffinity and sched_setaffinity in Linux from C?处的回答,我认为这些问题不是重复的,因为该问题只是该问题的子集,因为它仅询问sched_getaffinity,并且没有提及C ++

在此示例中,我们获得了亲和力,对其进行了修改,并检查它是否已与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);
    } else {
        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;
}

编译并运行:

gcc -std=c99 main.c
./a.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正在分叉的子进程继承的:How to prevent inheriting CPU affinity by child forked process?

在Ubuntu 16.04 GitHub upstream中进行了测试。

答案 5 :(得分:0)

还可以通过cgroups cpuset 子系统在程序中进行任何修改而无需修改即可通过外壳。 Cgroup(至少为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中(我们只写了要移到 cgroup.procs 文件中的进程的pid):

$ 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

后者显示当前外壳程序(pid#4753)现在位于新创建的cgroup中(第二个pid 12569是 cat 的命令,它是当前外壳程序的子代) ,它将继承其cgroup)。使用格式化的 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上运行,并且继承其cgroup的子级( ps 命令)正在CPU#1上运行。

作为结论,可以使用cgroup中的 cpuset 层次结构来代替使用 sched_setaffinity()或任何 pthread 服务通过将它们的pid写入相应的 cgroup.procs 文件中,将这些树移入它们。