Provided by: manpages-ru-dev_4.19.0-7_all bug

ИМЯ

       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 ⟨https://www.gnu.org/licenses/gpl-3.0.html⟩ или более позднюю, чтобы
       узнать об условиях авторского права. Мы не несем НИКАКОЙ ОТВЕТСТВЕННОСТИ.

       Если вы обнаружите ошибки в переводе этой страницы руководства, пожалуйста, отправьте
       электронное письмо на ⟨man-pages-ru-talks@lists.sourceforge.net⟩.