Provided by: manpages-ru-dev_4.21.0-2_all 

ИМЯ
sched_setaffinity, sched_getaffinity - устанавливает и получает процессорную маску увязывания для нити
LIBRARY
Standard C library (libc, -lc)
СИНТАКСИС
#define _GNU_SOURCE /* Смотрите feature_test_macros(7) */
#include <sched.h>
int sched_setaffinity(pid_t pid, size_t cpusetsize,
const cpu_set_t *mask);
int sched_getaffinity(pid_t pid, size_t cpusetsize,
cpu_set_t *mask);
ОПИСАНИЕ
Процессорной маской увязывания нити задаётся набор процессоров, на которых разрешено выполняться нити. В
многопроцессорных системах задание процессорной маски увязывания можно использовать для получения большей
производительности. Например, выделение специального процессора определённой нити (т.е., задание в
процессорной маске увязывания для нити одного ЦП и исключение этого ЦП из процессорных масок увязывания
для остальных нитей) обеспечивает максимальную скорость выполнения этой нити. Ограничение для нити одним
ЦП также исключает сокращение производительности в следствие недостоверности данных кэша, которая
возникает, когда нить прекращает выполнение на одном ЦП и затем продолжает выполнение на другом.
Маска увязывания ЦП представляется в виде структуры cpu_set_t, «набором процессоров», на которую
указывает mask. В CPU_SET(3) описаны макросы для изменения набора ЦП.
Вызов sched_setaffinity() устанавливает маску увязывания ЦП mask для нити, чей ID указан в pid. Если
значение pid равно нулю, то используется вызывающая нить. В аргументе cpusetsize задаётся количество
данных (в байтах), на которые указывает mask. Обычно его значение указывается как sizeof(cpu_set_t).
Если нить, указанная в pid, в данный момент не выполняется на одном из ЦП, заданном в mask, то эта нить
переносится на один из процессоров, назначаемых mask.
Вызов sched_getaffinity() записывает в структуру cpu_set_t, на которую указывает mask, значение маски
увязывания ЦП для нити, чей ID указан в pid. В аргументе cpusetsize задаётся размер mask (в байтах). Если
значение pid равно нулю, то возвращается маска вызывающей нити.
ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
On success, sched_setaffinity() and sched_getaffinity() return 0 (but see "C library/kernel
differences" below, which notes that the underlying sched_getaffinity() differs in its return value).
On failure, -1 is returned, and errno is set to indicate the error.
ОШИБКИ
EFAULT Указан некорректный адрес памяти.
EINVAL В маске увязывания ЦП mask указаны процессоры, которых физически нет в системе, и которые
разрешены нити согласно любым ограничениям, которые могут налагаться cpuset cgroups или механизмом
«cpuset», описанном в cpuset(7).
EINVAL (sched_getaffinity() and, before Linux 2.6.9, sched_setaffinity()) cpusetsize is smaller than the
size of the affinity mask used by the kernel.
EPERM (sched_setaffinity()) Вызывающая нить не имеет достаточно прав. Вызывающему требуется иметь
эффективный пользовательский ID равный реальному пользовательскому ID или эффективному
пользовательскому ID нити, указанной в pid, или он должен обладать мандатом CAP_SYS_NICE в
пространстве имён пользователя нити pid.
ESRCH Нить с идентификатором pid не найдена.
ВЕРСИИ
Системные вызовы увязывания ЦП появились в ядре Linux версии 2.5.8. Обёрточные функции появились в glibc
2.3. Первоначально, в интерфейсе glibc присутствовал аргумент cpusetsize, имевший тип unsigned int. В
glibc 2.3.3 аргумент cpusetsize был удалён, но появился вновь в glibc 2.3.4 с типом size_t.
СТАНДАРТЫ
Данные системные вызовы есть только в Linux.
ЗАМЕЧАНИЯ
После вызова sched_setaffinity() набор процессоров, на которых действительно будет выполняться нить,
вычисляется пересечением набора из аргумента mask и набором процессоров, присутствующих в системе. В
дальнейшем, система может ограничить набор процессоров нити, если задействован механизм «cpuset»,
описанный в cpuset(7). Эти ограничения на действительный набор процессоров, используемых для нити, без
уведомления налагаются ядром.
Есть несколько способов определения количества процессоров в системе: по содержимому /proc/cpuinfo; с
помощью sysconf(3) получить значение параметров _SC_NPROCESSORS_CONF и _SC_NPROCESSORS_ONLN; посчитать
количество подкаталогов cpu в /sys/devices/system/cpu/.
В sched(7) приведено описание схемы планирования Linux.
Маска увязывания является атрибутом нити, которая может изменяться независимо для каждой нити в группе
нитей. В аргументе pid можно передавать значение, возвращаемое вызовом gettid(2). При значении pid равным
0 будет установлен атрибут вызывающей нити, а при передаче значения, возвращаемого вызовом getpid(2),
устанавливается атрибут главной нити группы нитей (при работе с программным интерфейсом POSIX используйте
функцию pthread_setaffinity_np(3) вместо sched_setaffinity()).
Параметр начальной загрузки isolcpus можно использовать для изоляции одного и более ЦП во время загрузки,
и ни один процесс не будет запланирован к выполнению на этих ЦП. После использования этого параметра
единственный способ запланировать процессы на изолированных ЦП — использовать sched_setaffinity() или
механизм cpuset(7). Подробности смотрите в файле исходного кода ядра
Documentation/admin-guide/kernel-parameters.txt. Согласно тексту файла, isolcpus является
предпочтительным механизмом изоляции ЦП (по сравнению с ручным увязыванием ЦП всех процессов в системе).
Потомок, создаваемый с помощью fork(2), наследует маску увязывания ЦП. Маска увязывания сохраняется при
вызове execve(2).
Отличия между библиотекой C и ядром
В данной справочной странице описан интерфейс glibc для вызовов увязывания ЦП. Интерфейс реальных
системных вызов чуть отличается: аргумент mask имеет тип unsigned long *, отражая факт того, что
используемая реализация наборов ЦП представляет собой просто битовую маску.
При успешном выполнении ядерный системный вызов sched_getaffinity() возвращает количество скопированных в
буфер mask байт; минимальным значением будет cpusetsize и размер (в байтах) типа данных cpumask_t,
который используется в ядре для представления процессорной битовой маски.
Работа систем с масками увязывания ЦП большого размера
Лежащие в основе системные вызовы (которые представляют маски ЦП в виде маски битов с типом unsigned
long *) не накладывают ограничений на размер маски ЦП. Однако, тип данных cpu_set_t, используемый в
glibc, имеет постоянный размер 128 байт, то есть максимальный номер представляемых ЦП равен 1023. Если
ядерная маска увязывания ЦП больше 1024, то вызовы вида:
sched_getaffinity(pid, sizeof(cpu_set_t), &mask);
завершается ошибкой EINVAL; ошибка выдаётся подлежащим системным вызовом в случае, когда размер mask,
указанный в cpusetsize, меньше чем размер маски увязывания используемой ядром (в зависимости от топологии
ЦП системы, ядерная маска увязывания может быть значительно больше, чем количество активных ЦП в
системе).
При работе в системах с ядерными масками увязывания ЦП большого размера, место под аргумент mask должно
выделяться динамически (смотрите CPU_ALLOC(3)). В настоящее время единственный способ сделать это —
определить размер требуемой маски с помощью вызовов sched_getaffinity() с увеличиваемым размером маски
(пока вызов не перестанет выдавать ошибку EINVAL).
Учтите, что CPU_ALLOC(3) может выделить несколько больший набор ЦП, чем запрашивается (так как наборы ЦП
реализованы как битовые маски, выделяемые в объёмах sizeof(long)). Следовательно, sched_getaffinity()
может задать биты за границами запрашиваемого выделяемого размера, так как ядро видит несколько
дополнительных бит. Поэтому вызывающий должен пройтись по всем возвращённым битам, считая установленные и
остановиться при достижении значения, полученного от CPU_COUNT(3) (а не останавливаться на количестве
запрошенных к выделению бит).
ПРИМЕРЫ
Программа, представленная ниже, создаёт дочерний процесс. Затем родитель и потомок назначают выполнение
себя на указанных ЦП и выполняют одинаковые циклы, которые выполняются на ЦП какое-то время. Перед
завершением, родитель ждёт завершения потомка. Программа имеет три аргумента командной строки: номер ЦП
для родителя, номер ЦП для потомка и количество итераций цикла, который будут выполнять оба процесса.
В примере работы, показанном ниже, количество реального времени и времени использованного ЦП при работе
программы, будет зависеть он меж ядерного кэширования и будут ли процессы использовать одинаковый ЦП.
Сначала запустим lscpu(1) для определения, что эта система (x86) имеет по два потока выполнения в двух
ЦП:
$ lscpu | egrep -i 'core.*:|socket'
Thread(s) per core: 2
Core(s) per socket: 2
Socket(s): 1
Затем запустим подсчёт времени выполнения программы для трёх случаев: оба процесс выполняются на одном
ЦП; оба процесса выполняются на разных ЦП одного ядра; оба процесса выполняются на разных ЦП разных ядер.
$ time -p ./a.out 0 0 100000000
real 14.75
user 3.02
sys 11.73
$ time -p ./a.out 0 1 100000000
real 11.52
user 3.98
sys 19.06
$ time -p ./a.out 0 3 100000000
real 7.89
user 3.29
sys 12.07
Исходный код программы
#define _GNU_SOURCE
#include <err.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int
main(int argc, char *argv[])
{
int parentCPU, childCPU;
cpu_set_t set;
unsigned int nloops;
if (argc != 4) {
fprintf(stderr, "Использование: %s parent-cpu child-cpu num-loops\n",
argv[0]);
exit(EXIT_FAILURE);
}
parentCPU = atoi(argv[1]);
childCPU = atoi(argv[2]);
nloops = atoi(argv[3]);
CPU_ZERO(&set);
switch (fork()) {
case -1: /* Ошибка */
err(EXIT_FAILURE, "fork");
case 0: /* потомок */
CPU_SET(childCPU, &set);
if (sched_setaffinity(getpid(), sizeof(set), &set) == -1)
err(EXIT_FAILURE, "sched_setaffinity");
for (unsigned int j = 0; j < nloops; j++)
getppid();
exit(EXIT_SUCCESS);
default: /* родитель */
CPU_SET(parentCPU, &set);
if (sched_setaffinity(getpid(), sizeof(set), &set) == -1)
err(EXIT_FAILURE, "sched_setaffinity");
for (unsigned int j = 0; j < nloops; j++)
getppid();
wait(NULL); /* ждём завершения потомка */
exit(EXIT_SUCCESS);
}
}
СМ. ТАКЖЕ
lscpu(1), nproc(1), taskset(1), clone(2), getcpu(2), getpriority(2), gettid(2), nice(2),
sched_get_priority_max(2), sched_get_priority_min(2), sched_getscheduler(2), sched_setscheduler(2),
setpriority(2), CPU_SET(3), get_nprocs(3), pthread_setaffinity_np(3), sched_getcpu(3), capabilities(7),
cpuset(7), sched(7), numactl(8)
ПЕРЕВОД
Русский перевод этой страницы руководства был сделан Alexander Golubev <fatzer2@gmail.com>, Azamat
Hackimov <azamat.hackimov@gmail.com>, Hotellook, Nikita <zxcvbnm3230@mail.ru>, Spiros Georgaras
<sng@hellug.gr>, Vladislav <ivladislavefimov@gmail.com>, Yuri Kozlov <yuray@komyakino.ru> и Иван Павлов
<pavia00@gmail.com>
Этот перевод является бесплатной документацией; прочитайте Стандартную общественную лицензию GNU версии 3
или более позднюю, чтобы узнать об условиях авторского права. Мы не несем НИКАКОЙ ОТВЕТСТВЕННОСТИ.
Если вы обнаружите ошибки в переводе этой страницы руководства, пожалуйста, отправьте электронное письмо
на man-pages-ru-talks@lists.sourceforge.net.
Linux man-pages 6.03 5 февраля 2023 г. sched_setaffinity(2)