Как установить привязку к процессору для процесса из C или C++ в Linux?

Существует ли программный метод для установки соответствия процессору процесса в c/ C++ для операционной системы Linux.

5 ответов

Решение

Вам нужно использовать sched_setaffinity(2),

Например, для запуска только на процессорах 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),

Используйте sched_setaffinity на уровне процесса или pthread_attr_setaffinity_np для отдельных потоков.

Я приложил много усилий, чтобы понять, что происходит, поэтому я добавляю этот ответ, чтобы помочь таким людям, как я (я использую 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 в терминале зарезервировать нужные вам ядра

sched_setaffinity+ sched_getaffinityминимальный C работоспособный пример

Этот пример был извлечен из моего ответа по адресу: Как использовать sched_getaffinity и sched_setaffinity в Linux из 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 Разветвление: Как предотвратить наследование процессора процессами дочернего разветвленного процесса?

Протестировано в Ubuntu 16.04, GitHub upstream.

Короче

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 для более подробной информации

Также возможно сделать это через оболочку без каких-либо изменений в программах с cgroups и подсистемой cpuset. Cgroups (по крайней мере v1) обычно монтируются в /sys/fs/cgroup, в которой находится подсистема cpuset. Например:

       $ 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 определяет диапазон процессоров, на которых процессы, принадлежащие к этой контрольной группе разрешено работать. Здесь, на верхнем уровне, все процессоры настроены для всех процессов системы. Здесь в системе 8 процессоров:

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

Список процессов, принадлежащих этой контрольной группе, указан в файле cgroup.procs:

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

Можно создать дочернюю контрольную группу, в которую разрешено использование подмножества процессоров. Например, давайте определим подгруппу с ядрами ЦП 1 и 3:

       $ 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

В cpuset.mems файлы должны быть заполнены перед перемещением любого процесса в эту контрольную группу. Здесь мы перемещаем текущую оболочку в эту новую 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

Последнее показывает, что текущая оболочка (pid#4753) теперь находится во вновь созданной cgroup (второй pid 12569 - это командная оболочка cat, являющаяся потомком текущей оболочки, она наследует ее cgroups). С помощью отформатированной команды ps можно проверить, на каком процессоре выполняются процессы (столбец PSR):

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

Мы видим, что текущая оболочка работает на ЦП №3, а ее дочерний элемент (команда ps), который наследует его контрольные группы, работает на ЦП №1.

В заключении, вместо использования sched_setaffinity() или любой PTHREAD услуги, можно создать cpuset иерархию в дереве контрольных групп и перемещать процессы в них, написав их ИДП в соответствующем cgroup.procs файлов.

Другие вопросы по тегам