Главная » 2017 » Ноябрь » 21 » man 2 sched_setaffinity
01:41
man 2 sched_setaffinity

SEO sprint - Всё для максимальной раскрутки!





ИМЯ


sched_setaffinity, sched_getaffinity - устанавливает и получает процессорную маску
увязывания для нити



ОБЗОР


#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 равно нулю, то
возвращается маска вызывающей нити.



ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ


При успешном выполнении sched_setaffinity() и sched_getaffinity() возвращают 0. В
случае ошибки возвращается -1, а errno устанавливается в соответствующее значение.



ОШИБКИ


EFAULT Указан некорректный адрес памяти.

EINVAL В маске увязывания ЦП mask указаны процессоры, которых физически нет в
системе, и которые разрешены нити согласно любым ограничениям, которые
могут налагаться cpuset cgroups или механизмом «cpuset», описанном в
cpuset(7).

EINVAL (sched_getaffinity() и, в ядрах до 2.6.9, sched_setaffinity()) Значение
cpusetsize меньше размера маски увязывания, используемой в ядре.



ВЕРСИИ


Системные вызовы увязывания ЦП появились в ядре 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() возвращает размер типа данных cpumask_t (в байтах),
который используется в ядре для представления битовой маски процессоров.

Работа систем с масками увязывания ЦП большого размера
Лежащие в основе системные вызовы (которые представляют маски ЦП в виде маски
битов с типом unsigned long *) не накладывают ограничений на размер маски ЦП.
Однако, тип данных cpu_set_t, используемый в glibc, имеет постоянный размер 128
байт, то есть максимальный номер представляемых ЦП равен 1023. Если ядерная маска

При работе в системах с ядерными масками увязывания ЦП большого размера, место под
аргумент mask должно выделяться динамически (смотрите CPU_ALLOC(3)). В настоящее
время единственный способ сделать это — определить размер требуемой маски с
помощью вызовов sched_getaffinity() с увеличиваемым размером маски (пока вызов не
перестанет выдавать ошибку EINVAL).

Учтите, что CPU_ALLOC(3) может выделить несколько больший набор ЦП, чем
запрашивается (так как наборы ЦП реализованы как битовые маски, выделяемые в
объёмах sizeof(long)). Следовательно, sched_getaffinity() может задать биты за
границами запрашиваемого выделяемого размера, так как ядро видит несколько
дополнительных бит. Поэтому вызывающий должен пройтись по всем возвращённым битам,
считая установленные и остановиться при достижении значения, полученного от
CPU_COUNT(3) (а не останавливаться на количестве запрошенных к выделению бит).



ПРИМЕР


Программа, представленная ниже, создаёт дочерний процесс. Затем родитель и потомок
назначают выполнение себя на указанных ЦП и выполняют одинаковые циклы, которые
выполняются на ЦП какое-то время. Перед завершением, родитель ждёт завершения
потомка. Программа имеет три аргумента командной строки: номер ЦП для родителя,
номер ЦП для потомка и количество итераций цикла, который будут выполнять оба
процесса.

В примере работы, показанном ниже, количество реального времени и времени
использованного ЦП при работе программы, будет зависеть он меж ядерного
кэширования и будут ли процессы использовать одинаковый ЦП.

Сначала запустим lscpu(1) для определения, что эта система (x86) имеет по два
потока выполнения в двух ЦП:

$ lscpu | grep -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 <sched.h>
#include <stdio.h>
{
cpu_set_t set;
int parentCPU, childCPU;
int nloops, j;

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: /* Ошибка */
errExit("fork");

case 0: /* потомок */
CPU_SET(childCPU, &set);

if (sched_setaffinity(getpid(), sizeof(set), &set) == -1)
errExit("sched_setaffinity");

for (j = 0; j < nloops; j++)
getppid();

exit(EXIT_SUCCESS);

default: /* родитель */
CPU_SET(parentCPU, &set);

if (sched_setaffinity(getpid(), sizeof(set), &set) == -1)
errExit("sched_setaffinity");

for (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)



Категория: (2) Системные вызовы ядра (функции языка Си) | Просмотров: 594 | Добавил: Администратор | Рейтинг: 0.0/0
Всего комментариев: 0
avatar