title: sched_get_priority_max(2)

summary: возвращают статический

text:
# sched_get_priority_max(2)

## ИМЯ
sched_get_priority_max, sched_get_priority_min - возвращают статический диапазон приоритетов

## ОБЗОР
\#include <sched.h>

int sched_get_priority_max(int policy);

int sched_get_priority_min(int policy);

## ОПИСАНИЕ
Вызов sched_get_priority_max() возвращает максимальное значение приоритета, которое может использоваться с алгоритмом планирования, указанного в policy. Вызов sched_get_priority_min() возвращает минимальное значение приоритета, которое может использоваться с алгоритмом планирования, указанного в policy. Поддерживаемые значения для policy: SCHED_FIFO, SCHED_RR, SCHED_OTHER, SCHED_BATCH, SCHED_IDLE и SCHED_DEADLINE. Подробней об этих политиках смотрите в sched(7).

Процессы с большим числовым значением приоритета планируются к выполнению раньше процессов с меньшим числовым значением приоритета. То есть значение, возвращаемое sched_get_priority_max(), будет больше значения, возвращаемого sched_get_priority_min().

В Linux допускаются статические приоритеты в диапазоне от 1 до 99 для политик SCHED_FIFO и SCHED_RR и приоритет 0 для для остальных политик. Диапазоны планируемых приоритетов для различных политик назначить нельзя.

В разных системах POSIX диапазон планируемых приоритетов может различаться, то есть переносимое приложение должно использовать диапазон виртуальных приоритетов и отображать его в интервал, получаемый с помощью sched_get_priority_max() и sched_get_priority_min(). Согласно POSIX.1 между максимальным и минимальным значениями для SCHED_FIFO и SCHED_RR требуется диапазон из не менее 32 значений.

В системах POSIX, имеющих sched_get_priority_max() и sched_get_priority_min(), определено значение для _POSIX_PRIORITY_SCHEDULING в <unistd.h>.

## ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном выполнении sched_get_priority_max() и sched_get_priority_min() возвращают максимальное/минимальное значение приоритета для указанной политики планирования. В случае ошибки возвращается -1, а errno устанавливается в соответствующее значение.

## ОШИБКИ
EINVAL

Аргумент policy не содержит допустимого значения политики планирования.

## СООТВЕТСТВИЕ СТАНДАРТАМ
POSIX.1-2001, POSIX.1-2008.

## СМОТРИТЕ ТАКЖЕ
sched_getaffinity(2), sched_getparam(2), sched_getscheduler(2), sched_setaffinity(2), sched_setparam(2), sched_setscheduler(2), sched(7)


package: man-pages-ru

title: sched_rr_get_interval(2)

text:
# sched_rr_get_interval(2)

## ИМЯ
sched_rr_get_interval - определяет временной интервал SCHED_RR для заданного процесса

## ОБЗОР
\#include <sched.h>

int sched_rr_get_interval(pid_t pid, struct timespec *tp);

## ОПИСАНИЕ
Вызов sched_rr_get_interval() записывает циклический квант времени процесса pid в структуру timespec, на которую указывает tp. Указанный процесс должен запускаться с политикой выполнения SCHED_RR.

Структура timespec описана следующим образом:

```
struct timespec {

    time_t tv_sec;        /* секунды */

    long   tv_nsec;       /* наносекунды */
};
```

Если значение pid равно нулю, то в *tp записывается квант времени вызывающего процесса.

## ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном выполнении sched_rr_get_interval() возвращается 0. При ошибке возвращается -1, а значение errno устанавливается соответствующим образом.

## ОШИБКИ
EFAULT

Проблема с копированием информации в пространство пользователя.

EINVAL

Неверное значение pid.

ENOSYS

Системный вызов ещё не реализован (только в старых ядрах).

ESRCH

Не удалось найти процесс с идентификатором, указанном в pid.

## СООТВЕТСТВИЕ СТАНДАРТАМ
POSIX.1-2001, POSIX.1-2008.

## ЗАМЕЧАНИЯ
В системах POSIX, в которых доступен вызов sched_rr_get_interval(), определён _POSIX_PRIORITY_SCHEDULING в <unistd.h>.

### Замечания, касающиеся Linux
В POSIX не определён какой-либо механизм для контроля размера циклического кванта времени. В старых ядрах Linux для этого создан (непереносимый) метод. Квант можно контролировать подгоняя значение любезности процесса (см. setpriority(2)). Назначение отрицательного (т.е., высокого) значения любезности приведёт к более длинному кванту; назначение положительного (т.е., низкого) значения любезности приведёт к сокращению кванта. Квант по умолчанию равен 0.1 секунды; степень, с которой значение любезности влияет на квант, различается в разных версиях ядра. Данный метод подгонки кванта был удалён начиная с Linux 2.6.24.

В Linux 3.9 добавлен новый механизм регулировки (и просмотра) кванта SCHED_RR: файл /proc/sys/kernel/sched_rr_timeslice_ms отражает квант как значение в миллисекундах, по умолчанию равное 100. Запись 0 в этот файл сбрасывает квант в значение по умолчанию.

## СМОТРИТЕ ТАКЖЕ
sched(7)


title: sched_setaffinity(2)

summary: устанавливает и получает процессорную

text:
# sched_setaffinity(2)

## ИМЯ
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 (но смотрите «различия в библиотеке C/ядре» ниже, где показано при каких условиях sched_getaffinity() возвращает разные значения). При ошибке возвращается -1, и в errno записывается соответствующее значение.

## ОШИБКИ
EFAULT

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

EINVAL

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

EINVAL

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

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 <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \

                        } while (0)
int
main(int argc, char *argv[])
{

    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)


title: sched_setattr(2)

text:
# sched_setattr(2)

## ИМЯ
sched_setattr, sched_getattr - назначает и возвращает алгоритм планирования и атрибуты

## ОБЗОР

```
#include <sched.h>
```

```
int sched_setattr(pid_t pid, struct sched_attr *attr,
                  unsigned int flags);
```

```
int sched_getattr(pid_t pid, struct sched_attr *attr,
                  unsigned int size, unsigned int flags);
```

## ОПИСАНИЕ

### sched_setattr()
Системный вызов sched_setattr() задаёт алгоритм планирования и соответствующие атрибуты для нити с идентификатором pid. Если pid равно нулю, то будет изменён алгоритм и атрибуты планирования вызывающей нити.

В настоящее время в Linux поддерживаются следующие «обычные» (т. е. не реального времени) алгоритмы планирования, которые можно указать в policy:

SCHED_OTHER

алгоритм циклического обслуживания с разделением времени;

SCHED_BATCH

«пакетный» стиль выполнения процессов;

SCHED_IDLE

для выполнения фоновых заданий с очень низким приоритетом.

Также поддерживаются различные алгоритмы «реального времени», для специальных, критичных ко времени приложений, которым требуется точное управление методом выбора приложением исполняемых нитей. Правила управления, когда процесс может использовать эти алгоритмы, смотрите в sched(7). Алгоритмы реального времени, которые могут быть указаны в policy:

SCHED_FIFO

алгоритм «первым вошёл — первым вышел»;

SCHED_RR

алгоритм циклического обслуживания.

В Linux также предоставляются следующий алгоритм планирования:

SCHED_DEADLINE

алгоритм планирования с предельным сроком; смотрите sched(7).

Аргумент attr является указателем на структуру, в которой определяется новый алгоритм планирования и атрибуты, для заданной нити. Данная структура имеет следующий вид:

```
struct sched_attr {

    u32 size;              /* размер этой структуры */

    u32 sched_policy;      /* алгоритм (SCHED_*) */

    u64 sched_flags;       /* флаги */

    s32 sched_nice;        /* значение уступчивости (SCHED_OTHER,

                              SCHED_BATCH) */

    u32 sched_priority;    /* статический приоритет (SCHED_FIFO,

                              SCHED_RR) */

    /* остальные поля нужны для SCHED_DEADLINE */

    u64 sched_runtime;

    u64 sched_deadline;

    u64 sched_period;
};
```

Поля этой структуры имеют следующее назначение:

size

В этом поле должен быть указан размер структуры в байтах с помощью sizeof(struct sched_attr). Если предоставляемая структура меньше чем структура ядра, то предполагается, что дополнительные поля равны «0». Если представляемая структура больше ядерной, то ядро проверяет, что все дополнительные поля равны 0; если нет, то sched_setattr() завершается с ошибкой E2BIG и заменяет значение size размером структуры ядра.

Возможность иметь разный размер структуры sched_attr пользовательского пространства и структуры ядра позволит расширять интерфейс с будущем. Некорректные приложения, передающие больший размер структуры, ничего не сломают в будущем, если увеличится размер ядерной структуры sched_attr. В будущем, это также может позволить приложениям, которые знают о большей структуре sched_attr пользовательского пространства, определять, что они работают на старом ядре, которое не поддерживает структуру большего размера.

sched_policy

В этом поле задаётся алгоритм планирования в виде значений SCHED_*, перечисленных выше.

sched_flags

Это поле содержит побитовое ИЛИ нуля и более следующих флагов, для управления поведением планирования:

SCHED_FLAG_RESET_ON_FORK

Потомки, созданные fork(2), не наследуют привилегированные алгоритмы планирования. Подробней смотрите в sched(7).

SCHED_FLAG_RECLAIM (начиная с Linux 4.13)

Этот флаг позволяет нити SCHED_DEADLINE отхватить полосу пропускания, которая не используется другими нитями реального времени.

SCHED_FLAG_DL_OVERRUN (начиная с Linux 4.16)

Этот флаг позволяет приложению получать информацию о времени переработки (run-time overrun) в нитях SCHED_DEADLINE. Такие переработки могут быть вызваны (например) неточным учётом времени выполнения или некорректным назначением параметра. Уведомление поступает в виде сигнала SIGXCPU, который генерируется при каждой переработке.

Сигнал SIGXCPU направляется процессу (смотрите signal(7)), а не нити. Вероятно, это дефект. С одной стороны, sched_setattr() используется для задания атрибутов определённой нити. С другой стороны, если сигнал, направленный процессу, доставляется нити внутри процесса, у которой не было переработки, то приложение не может узнать какая нить переработала.

sched_nice

В этом поле задаётся значение уступчивости, которое будет назначено при указании в sched_policy значения SCHED_OTHER или SCHED_BATCH. Значение уступчивости представляет собой число в диапазоне от -20 (высший приоритет) до +19 (низший приоритет); смотрите sched(7).

sched_priority

В этом поле указывается статический приоритет, который будет установлен при указании в sched_policy значения SCHED_FIFO или SCHED_RR. Допустимый диапазон приоритетов для этих алгоритмов можно определить с помощью sched_get_priority_min(2) и sched_get_priority_max(2). Для других алгоритмов значение этого поля должно быть равно 0.

sched_runtime

В этом поле задаётся параметр «Runtime» для планирования с предельным сроком. Значение выражается в наносекундах. Это и два следующих поля используются только при алгоритме SCHED_DEADLINE; подробности смотрите в sched(7).

sched_deadline

В этом поле задаётся параметр «Deadline» для планирования с предельным сроком. Значение выражается в наносекундах.

sched_period

В этом поле задаётся параметр «Period» для планирования с предельным сроком. Значение выражается в наносекундах.

Аргумент flags предназначен для расширения интерфейса в будущем; в текущей реализации его значение должно быть равно 0.

### sched_getattr()
Системный вызов sched_getattr() возвращает алгоритм планирования и соответствующие атрибуты для нити с идентификатором pid. Если pid равно нулю, то будет возвращён алгоритм и атрибуты планирования вызывающей нити.

В аргументе size должен быть задан размер структуры sched_attr, каким он известен в пользовательском пространстве. Значение должно быть не менее размера изначально показанной структуры sched_attr или вызов завершится с ошибкой EINVAL.

Возвращаемые атрибуты планирования помещаются в поля структуры sched_attr, на которую указывает attr. Ядро присваивает attr.size размер своей структуры sched_attr.

Если буфер attr, предоставленный вызывающим, больше структуры ядра sched_attr, то лишние байты в структуре пользовательского пространства не изменяются. Если структура, предоставленная вызывающим, меньше ядерной структуры sched_attr и ядру необходимо вернуть значение вне предоставляемого пространства, то sched_getattr() завершается с ошибкой E2BIG. Как и для sched_setattr(), такая семантика позволяет расширить интерфейс в будущем.

Аргумент flags предназначен для расширения интерфейса в будущем; в текущей реализации его значение должно быть равно 0.

## ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном выполнении sched_setatt() и sched_getattr() возвращается 0. При ошибке возвращается -1, а в errno содержится причина ошибки.

## ОШИБКИ
Вызовы sched_getattr() и sched_setattr() могут завершиться с ошибкой по следующим причинам:

EINVAL

Значение attr равно NULL; или значение pid отрицательно; или значение flags не равно 0.

ESRCH

Нить с идентификатором pid не найдена.

Также sched_getattr() может завершиться с ошибкой по следующим причинам:

E2BIG

Буфер, задаваемый size и attr, слишком мал.

EINVAL

Некорректное значение size; то есть, оно меньше чем первая версия структуры sched_attr (48 байт) или больше размера системной страницы.

Также sched_setattr() может завершиться с ошибкой по следующим причинам:

E2BIG

Буфер, заданный size и attr, больше структуры ядра и один или более превышающих байт не равны нулю.

EBUSY

Ошибка контроля допуска SCHED_DEADLINE, смотрите sched(7).

EINVAL

Значение attr.sched_policy не является одним из возможных алгоритмов; в attr.sched_flags содержится флаг отличный от SCHED_FLAG_RESET_ON_FORK; некорректное значение attr.sched_priority; значение attr.sched_policy равно SCHED_DEADLINE и есть некорректные параметры алгоритм планирования с предельным сроком в attr.

EPERM

Вызывающий не имеет соответствующих прав.

EPERM

В процессорной маске увязывания нити, заданной в pid, отсутствуют все ЦП системы (смотрите sched_setaffinity(2)).

## ВЕРСИИ
Данные системные вызовы впервые появились в Linux 3.14.

## СООТВЕТСТВИЕ СТАНДАРТАМ
Данные системные вызовы являются нестандартными расширениями Linux.

## ЗАМЕЧАНИЯ
Вызов sched_setattr() предоставляет весь набор возможностей sched_setscheduler(2), sched_setparam(2), nice(2) и (всё кроме способности задавать приоритет всем процессам, принадлежащим указанному пользователю или всем процессам указанной группы) setpriority(2). Аналогично, sched_getattr() предоставляет весь набор возможностей sched_getscheduler(2), sched_getparam(2) и (частично) getpriority(2).

## ДЕФЕКТЫ
В версиях Linux до 3.15, sched_settattr() завершается с ошибкой EFAULT, а не E2BIG в случаях, которые описаны в разделе ОШИБКИ.

## СМОТРИТЕ ТАКЖЕ
chrt(1), nice(2), sched_get_priority_max(2), sched_get_priority_min(2), sched_getaffinity(2), sched_getparam(2), sched_getscheduler(2), sched_rr_get_interval(2), sched_setaffinity(2), sched_setparam(2), sched_setscheduler(2), sched_yield(2), setpriority(2), pthread_getschedparam(3), pthread_setschedparam(3), pthread_setschedprio(3), capabilities(7), cpuset(7), sched(7)


title: sched_setparam(2)

summary: получает и устанавливает параметры

text:
# sched_setparam(2)

## ИМЯ
sched_setparam, sched_getparam - получает и устанавливает параметры планирования

## ОБЗОР

```
#include <sched.h>
```

```
int sched_setparam(pid_t pid, const struct sched_param *param);
```

```
int sched_getparam(pid_t pid, struct sched_param *param);
```

```
struct sched_param {

    ...

    int sched_priority;

    ...
};
```

## ОПИСАНИЕ
Вызов sched_setparam() устанавливает параметры планирования, связанные с алгоритмом планирования нити, идентификатор которой задан в pid. Если pid равен нулю, то устанавливаются параметры вызывающей нити. Тип и значение аргумента param зависит от алгоритма планирования, назначенного нити с идентификатором pid. Описание алгоритмов планирования, поддерживаемых в Linux, смотрите в sched(7).

Вызов sched_getparam() получает текущие параметры планирования нити с идентификатором pid. Если pid равно нулю, то возвращаются параметры вызывающей нити.

Вызов sched_setparam() проверяет соответствие значения аргумента param политике планирования заданной нити. Значение param->sched_priority должно находиться в диапазоне от sched_get_priority_min(2) до sched_get_priority_max(2).

Описание привилегий и ограничений ресурсов, связанных с планирование и алгоритмами, смотрите в sched(7).

В POSIX-системах, в которых существуют вызовы sched_setparam() и sched_getparam(), в <unistd.h> определён макрос _POSIX_PRIORITY_SCHEDULING.

## ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном выполнении sched_setparam() и sched_getparam() возвращают 0. В случае ошибки возвращается -1, а errno устанавливается в соответствующее значение.

## ОШИБКИ
EINVAL

Некорректные аргументы: значение pid отрицательно или param равно NULL

EINVAL

(sched_setparam()) Аргумент param не соответствует текущей политике планирования.

EPERM

(sched_setparam()) Вызывающий не имеет достаточного количества прав (Linux: не имеет мандата CAP_SYS_NICE).

ESRCH

Нить с идентификатором pid не найдена.

## СООТВЕТСТВИЕ СТАНДАРТАМ
POSIX.1-2001, POSIX.1-2008.

## СМОТРИТЕ ТАКЖЕ
getpriority(2), gettid(2), nice(2), sched_get_priority_max(2), sched_get_priority_min(2), sched_getaffinity(2), sched_getscheduler(2), sched_setaffinity(2), sched_setattr(2), sched_setscheduler(2), setpriority(2), capabilities(7), sched(7)


title: sched_setscheduler(2)

summary: устанавливает или получает алгоритм

text:
# sched_setscheduler(2)

## ИМЯ
sched_setscheduler, sched_getscheduler - устанавливает или получает алгоритм планирования (и его параметры)

## ОБЗОР

```
#include <sched.h>
```

```
int sched_setscheduler(pid_t pid, int policy,
                       const struct sched_param *param);
```

```
int sched_getscheduler(pid_t pid);
```

## ОПИСАНИЕ
Вызов sched_setscheduler() задаёт алгоритм и параметры планирования выполнения нити с идентификатором pid. Если pid равен нулю, то будет изменён алгоритм и параметры планирования вызывающей нити.

Параметры планирования указываются в аргументе param, который является указателем на следующую структуру:

```
struct sched_param {

    ...

    int sched_priority;

    ...
};
```

В текущей реализации структура содержит только одно поле — sched_priority. Интерпретация param зависит от выбранного алгоритма.

В настоящее время в Linux поддерживаются следующие «обычные» (т. е. не реального времени) алгоритмы планирования, которые можно указать в policy:

SCHED_OTHER

алгоритм циклического обслуживания с разделением времени;

SCHED_BATCH

«пакетный» стиль выполнения процессов;

SCHED_IDLE

для выполнения фоновых заданий с очень низким приоритетом.

Для каждого алгоритма, перечисленного выше, значение param->sched_priority должно быть равно 0.

Также поддерживаются различные алгоритмы «реального времени», для специальных, критичных ко времени приложений, которым требуется точное управление методом выбора приложением исполняемых нитей. Правила управления, когда процесс может использовать эти алгоритмы, смотрите в sched(7). Алгоритмы реального времени, которые могут быть указаны в policy:

SCHED_FIFO

алгоритм «первым вошёл — первым вышел»;

SCHED_RR

алгоритм циклического обслуживания.

Для каждого перечисленного выше алгоритма в param->sched_priority задаётся приоритет планирования для нити. Это число в диапазоне, возвращаемом вызовами sched_get_priority_min(2) и sched_get_priority_max(2) с указанным policy. В Linux эти системные вызовы возвращают 1 и 99, соответственно.

Начиная с Linux 2.6.32, флаг SCHED_RESET_ON_FORK может быть добавлен (OR) в policy при вызове sched_setscheduler(). В результате включения этого флага потомок, созданный fork(2), не наследует алгоритмы привилегированные алгоритмы планирования. Подробней смотрите в sched(7).

Вызов sched_getscheduler() возвращает текущий алгоритм планирования нити с идентификатором pid. Если значение pid равно нулю, то будет выдан алгоритм вызывающей нити.

## ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном выполнении sched_setscheduler() возвращается ноль. При успешном выполнении sched_getscheduler() возвращается алгоритм нити (неотрицательное целое). При ошибках оба вызова возвращают -1, а переменной errno присваивается соответствующее значение.

## ОШИБКИ
EINVAL

Некорректные аргументы: значение pid отрицательно или param равно NULL.

EINVAL

(sched_setscheduler()) Значение policy не равно одному из известных значений алгоритмов.

EINVAL

(sched_setscheduler()) Значение param не имеет смысла для заданного policy.

EPERM

Вызывающая нить не имеет достаточно привилегий.

ESRCH

Нить с идентификатором pid не найдена.

## СООТВЕТСТВИЕ СТАНДАРТАМ
POSIX.1-2001, POSIX.1-2008 (но смотрите ДЕФЕКТЫ далее). Алгоритмы SCHED_BATCH и SCHED_IDLE есть только в Linux.

## ЗАМЕЧАНИЯ
Подробное описание семантики всех алгоритмов планирования («обычных» и «реального времени») можно найти в справочной странице sched(7). В этой странице также описывается дополнительный алгоритм, SCHED_DEADLINE, который доступен только через вызов sched_setattr(2).

В системах POSIX, в которых доступны sched_setscheduler() и sched_getscheduler(), в <unistd.h> определён макрос _POSIX_PRIORITY_SCHEDULING.

В POSIX.1 не описаны права, которые требуются привилегированной нити для вызова sched_setscheduler(), и в разных системах используются разные права. Например, в справочной странице Solaris 7 сказано, что реальный и эффективный пользовательский идентификатор вызывающего должен совпадать с реальным пользовательским идентификатором или сохранённым set-user-ID изменяемой нити.

Алгоритм планирования и параметры, фактически, являются атрибутами нить в Linux. Значение, возвращаемое из вызова gettid(2), можно передавать в аргументе pid. При указании в pid значения 0 операции будут выполняться на атрибутах вызывающей нити, а при передаче значения, полученном из вызова getpid(2), операции будут выполнятся на атрибутах главной нити группы нитей (если вы используете интерфейс нитей POSIX, то используйте pthread_setschedparam(3), pthread_getschedparam(3) и pthread_setschedprio(3) вместо системных вызовов sched_*(2)).

## ДЕФЕКТЫ
В POSIX.1 указано, что при успешном выполнении sched_setscheduler() должно возвращаться предыдущее значение алгоритма планирования. В Linux вызов sched_setscheduler() не выполняет это требование, так как при успешном выполнении всегда возвращает 0.

## СМОТРИТЕ ТАКЖЕ
chrt(1), nice(2), sched_get_priority_max(2), sched_get_priority_min(2), sched_getaffinity(2), sched_getattr(2), sched_getparam(2), sched_rr_get_interval(2), sched_setaffinity(2), sched_setattr(2), sched_setparam(2), sched_yield(2), setpriority(2), capabilities(7), cpuset(7), sched(7)


title: sched_yield(2)

summary: освободить процессор

text:
# sched_yield(2)

## ИМЯ
sched_yield - освободить процессор

## ОБЗОР
\#include <sched.h>

int sched_yield(void);

## ОПИСАНИЕ
Вызов sched_yield() заставляет вызывающую нить освободить процессор. Нить перемещается в конец очереди согласно её статическому приоритету, и управление передаётся другой нити.

## ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном выполнении sched_yield() возвращается 0. При ошибке возвращается -1, а значение errno устанавливается соответствующим образом.

## ОШИБКИ
В Linux-реализации sched_yield() всегда завершается успешно.

## СООТВЕТСТВИЕ СТАНДАРТАМ
POSIX.1-2001, POSIX.1-2008.

## ЗАМЕЧАНИЯ
Если вызывающая нить единственная в списке с самым высоким приоритетом, то она продолжит выполняться после вызова sched_yield().

В системах POSIX, в которых доступен вызов sched_yield(), определён _POSIX_PRIORITY_SCHEDULING в <unistd.h>.

Оперативные вызовы sched_yield() могут улучшить производительность, давая другим нитям или процессам шанс поработать, когда (сильно) оспариваемые ресурсы (например, мьютексы) освобождаются вызывающим. Избегайте частых или нецелесообразных вызовов sched_yield() (например, когда ресурсы, необходимые других планируемым нитям, по-прежнему удерживаются вызывающим), так как это приводит к чрезмерному переключению контекстов, что ухудшит производительность системы.

Функция sched_yield() предназначена для использования с алгоритмами планирования реального времени (т. е., SCHED_FIFO или SCHED_RR). Использование sched_yield() с недерминированными алгоритмами планирования, такими как SCHED_OTHER, не определено и указывает на, вероятно, неправильно разработанное приложение.

## СМОТРИТЕ ТАКЖЕ
sched(7)


seccomp

title: seccomp(2)

text:
# seccomp(2)

## ИМЯ
seccomp - переводит процесс в состояние безопасных вычислений

## ОБЗОР

```
#include <linux/seccomp.h>
#include <linux/filter.h>
#include <linux/audit.h>
#include <linux/signal.h>
#include <sys/ptrace.h>
```

```
int seccomp(unsigned int operation, unsigned int flags, void *args);
```

## ОПИСАНИЕ
Системный вызов seccomp() переводит вызвавший процесс в состояние безопасных вычислений (Secure Computing, seccomp).

В настоящее время в Linux поддерживаются следующие значения operation:

SECCOMP_SET_MODE_STRICT

Вызвавшей нити доступны только системные вызовы read(2), write(2), _exit(2) (но не exit_group(2)) и sigreturn(2). При запуске других системных вызовов генерируется сигнал SIGKILL. Строгий режим безопасных вычислений полезен для вычислительных приложений, которым может потребоваться выполнить недоверительный байт-код, возможно полученный при чтении из канала или сокета.

Заметим, что хотя вызывающая нить больше не вызывает sigprocmask(2), она может использовать sigreturn(2) для блокировки всех сигналов (кроме SIGKILL и SIGSTOP). Это означает, что alarm(2) (например) недостаточно для ограничения времени выполнения процесса. Вместо него для надёжного завершения процесса нужно использовать SIGKILL. Это можно сделать с помощью timer_create(2) с SIGEV_SIGNAL и sigev_signo равным SIGKILL, или используя setrlimit(2) для задания жёсткого ограничения по RLIMIT_CPU.

Эта операция доступна только, если в ядре включён параметр CONFIG_SECCOMP.

Значение flags должно быть равно 0, а args — NULL.

Эта операция функционально идентична вызову:

```
prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT);

```

SECCOMP_SET_MODE_FILTER

Разрешённые системные вызовы определяются указателем на Berkeley Packet Filter (BPF), передаваемый через args. Данный аргумент является указателем на struct sock_fprog; эту структуру можно использовать для отбора произвольных системных вызовов и их аргументов. Если фильтр некорректен, то seccomp() завершается с ошибкой EINVAL в errno.

Если фильтром разрешён fork(2) или clone(2), то все потомки будут ограничены тем же фильтром системных вызовов что и родитель. Если разрешён execve(2), то существующий фильтр сохраняется и после вызова execve(2).

Чтобы использовать операцию SECCOMP_SET_MODE_FILTER вызывающая нить должна иметь мандат CAP_SYS_ADMIN в своём пространстве имён пользователя или у нити уже должен быть установлен бит no_new_privs. Если этот бит не установлен предком этой нити, то в нити нужно сделать следующий вызов:

```
prctl(PR_SET_NO_NEW_PRIVS, 1);

```

В противном случае операция SECCOMP_SET_MODE_FILTER завершается ошибкой и возвращает EACCES в errno. Данное требование гарантирует, что непривилегированный процесс не сможет применить вредоносный фильтр и вызвать программу с set-user-ID или другую привилегированную программу с помощью execve(2), то есть потенциально подвергнуть эту программу опасности (такой вредоносный фильтр может, например, заставить попытаться использовать setuid(2) для установки ID вызывающего пользователя в ненулевые значения вместо возврата 0 без действительного запуска системного вызова. Таким образом, программа может быть обманута и остаться с правами суперпользователя в окружении, где возможно заставить её сделать что-то опасное, так как в действительности она не отказалась от своих прав).

Если prctl(2) или seccomp() разрешены присоединённым фильтром, то могут быть добавлены дополнительные фильтры. Это увеличит время вычисления, но в дальнейшем позволит сократить область атаки при выполнении нити.

Операция SECCOMP_SET_MODE_FILTER доступна только, если в ядре включён параметр CONFIG_SECCOMP_FILTER.

Если значение flags равно 0, то эта операция функционально идентична вызову:

```
prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, args);

```

Возможные значения flags:

SECCOMP_FILTER_FLAG_TSYNC

При добавлении нового фильтра, выполнять синхронизацию с одним деревом фильтров seccomp все нити вызывающего процесса. «Дерево фильтров» — упорядоченный список фильтров, присоединённых к нити (присоединённые одинаковые фильтры отдельными вызовами seccomp() считаются разными фильтрами, с этой точки зрения).

Если в какой-то нити невозможна синхронизация с единым деревом фильтров, то вызов не присоединит новый фильтр seccomp, и завершится с ошибкой, вернув ID первой обнаруженной нити, для которой синхронизация невозможна. Синхронизации не получится, если другая нить того же процесса находится в SECCOMP_MODE_STRICT, или если она присоединила новые фильтры seccomp к самой себе, отличающиеся от дерева фильтров вызывающей нити.

SECCOMP_FILTER_FLAG_LOG (начиная с Linux 4.14)

Все фильтры, возвращающие действия, кроме SECCOMP_RET_ALLOW, должны протоколироваться. Администратор может заменить этот флаг фильтров, предварительно запретив протоколировать определённые действия через файл /proc/sys/kernel/seccomp/actions_logged.

SECCOMP_FILTER_FLAG_SPEC_ALLOW (начиная с Linux 4.17)

Выключить недопущение Speculative Store Bypass.

SECCOMP_GET_ACTION_AVAIL (начиная с Linux 4.14)

Проверить, поддерживается ли действие ядром. Данная операция помогает убедиться, что ядро знает о самых последних добавленных фильтрах, возвращающих действие, так как ядро считает все неизвестные действия как SECCOMP_RET_KILL_PROCESS.

Значение flags должно быть равно 0, а args должно быть указателем на беззнаковый 32-битный фильтр, возвращающих действие.

### Фильтры
При добавлении фильтров посредством SECCOMP_SET_MODE_FILTER, значение args указывает на программу фильтрации:

```
struct sock_fprog {

    unsigned short      len;    /* количество инструкций BPF */

    struct sock_filter *filter; /* указатель на массив

                                   инструкций BPF */
};
```

В каждой программе должно быть не менее одной инструкции BPF:

```
struct sock_filter {            /* блок фильтрации */

    __u16 code;                 /* действительный код фильтра */

    __u8  jt;                   /* переход при совпадении */

    __u8  jf;                   /* переход при несовпадении */

    __u32 k;                    /* общее поле для различных целей */
};
```

При выполнении инструкций информация о системном вызове (когда используется режим адресации BPF_ABS) программе BPF доступна из буфера (только для чтения) в виде:

```
struct seccomp_data {

    int   nr;                   /* номер системного вызова */

    __u32 arch;                 /* значение AUDIT_ARCH_*

                                   (смотрите <linux/audit.h>) */

    __u64 instruction_pointer;  /* указатель на инструкцию ЦП */

    __u64 args[6];              /* до 6 аргументов системного вызова */
};
```

Так как количество системных вызовов различно на разных архитектурах и некоторые архитектуры (например, x86-64) позволяют коду в пользовательском пространстве использовать соглашения о вызовах нескольких архитектур (и используемое соглашение может меняться на протяжении выполнения процесса, если он использует execve(2) для запуска выполняемых файлов, которые задействуют другие соглашения), то, обычно, необходимо проверять значение поля arch.

Настоятельно рекомендуется использовать подход белого списка, когда это возможно, потому что такой подход более устойчив и прост. Черный список нужно будет обновлять каждый раз, когда добавляется потенциально опасный системный вызов (или опасный флаг или параметр, если они помещены в черный список), и это часто возможно изменит представление значения, не изменяя его смысла, что приведёт к обходу черного списка. Также смотрите ЗАМЕЧАНИЯ ниже.

Поле arch не уникально для всех соглашений о вызовах. В x86-64 ABI и x32 ABI в arch используется AUDIT_ARCH_X86_64, и они запускаются на одних и тех же процессорах. Чтобы отличать один ABI от другого используется маска __X32_SYSCALL_BIT с номером системного вызова.

Это означает, что для создания чёрного списка системных вызовов на основе seccomp, выполняемых через x86-64 ABI, необходимо не только проверять что arch равно AUDIT_ARCH_X86_64, но также явно отвергать все системные вызовы, которые содержат __X32_SYSCALL_BIT в nr.

В поле instruction_pointer содержится адрес инструкции машинного языка, который запускает системный вызов. Это может быть полезно вместе с /proc/[pid]/maps для выполнения проверок из какой области (отображение) программы делается системный вызов (вероятно, стоит блокировать системные вызовы mmap(2) и mprotect(2) для запрета программе удалять такие проверки).

При проверке значений из args по чёрному списку имейте в виду, что часто аргументы просто обрезаются до обработки, но после проверки seccomp. Например, это случается, если на ядре x86-64 используется i386 ABI: хотя ядро, обычно, не смотрит дальше 32 младших бит аргументов, в данные seccomp попадут значения полных 64-битных регистров. Менее удивительный пример: если для выполнения системного вызова с аргументом типа int используется x86-64 ABI, то старшая половина регистра аргумента игнорируется системным вызовом, но видима в данных seccomp.

Фильтр seccomp возвращает 32-битное значение, состоящее из двух частей: в старших 16 битах (соответствует маске, определяемой константой SECCOMP_RET_ACTION_FULL) содержится одно из значений «действие», перечисленных далее; в младших 16 битах (определяется константой SECCOMP_RET_DATA) содержатся «данные», связанные с возвращаемым значением.

Если существует несколько фильтров, то все они выполняются в обратном порядке их добавления в дерево фильтров — то есть последние добавленные выполняются первыми (заметим, что все фильтры будут вызваны даже, если ранее выполнявшиеся фильтры вернули SECCOMP_RET_KILL. Это сделано для простоты кода ядра и предоставления крошечного ускорения выполнения набора фильтров, так как не выполняется проверка этого редкого случая). Возвращаемое значение для вычисления данного системного вызова —первое встреченного значение действия наивысшего приоритета (вместе с сопутствующими ему данными), возвращаемое выполнением всех фильтров.

Значения действий, которые могут возвращаться фильтром seccomp (в порядке уменьшения приоритета):

SECCOMP_RET_KILL_PROCESS (начиная с Linux 4.14)

Это значение возвращается при немедленном завершении процесса с образованием дампа. Системный вызов не выполняется. По сравнению с SECCOMP_RET_KILL_THREAD, описанном далее, завершаются все нити в группе нитей (группы нитей представлены в описании CLONE_THREAD в clone(2)).

Процесс завершается думая, что убит сигналом SIGSYS. Даже если обработчик сигнала SIGSYS был зарегистрирован, в этом случае он будет проигнорирован и процесс всегда прекращает выполнение. Родительскому процессу, который ждёт этот процесс (с помощью waitpid(2) или подобного вызова) возвращается wstatus, который будет показывать, что потомок завершился по сигналу SIGSYS.

SECCOMP_RET_KILL_THREAD (или SECCOMP_RET_KILL)

Это значение возвращается при немедленном завершении нити, сделавшей системный вызов. Системный вызов не выполняется. Другие нити в той же группе нитей продолжат выполнение.

Нить завершается думая, что убита сигналом SIGSYS. Смотрите описание SECCOMP_RET_KILL_PROCESS выше.

До Linux 4.11 любой процесс, завершавшийся таким образом, не вызывал образование дампа (несмотря на то, что описание SIGSYS в signal(7) сообщает, что по умолчанию завершение приводит к дампу). Начиная с Linux 4.11 для процесса с единственной нитью будет сделан дамп, если он завершается при таких обстоятельствах.

В дополнении к SECCOMP_RET_KILL_PROCESS в Linux 4.14 как синоним SECCOMP_RET_KILL добавлено значение SECCOMP_RET_KILL_THREAD, для более ясного различения двух этих действий.

SECCOMP_RET_TRAP

Это значение приводит к отправке ядром направленного в нить сигнала SIGSYS возбудившей нити (системный вызов не выполняется). Заполняются некоторые поля структуры siginfo_t (смотрите sigaction(2)), связанные с сигналом:

- В si_signo будет содержаться значение SIGSYS.

- В si_call_addr будет показан адрес инструкции системного вызова.

- В si_syscall и si_arch будет указываться какой системный вызов была попытка запустить.

- В si_code будет содержаться значение SYS_SECCOMP.

- В si_errno будет содержаться часть SECCOMP_RET_DATA из возвращаемого значения фильтра.

Программный счётчик будет таким же как при системном вызове (т. е., программный счётчик не будет указывать на инструкцию системного вызова). В регистре возвращаемого значения будет содержаться значение, зависящее от архитектуры; если выполнение продолжится, оно равно чему-нибудь подходящему для системного вызова (зависимость от архитектуры возникает из-за того, что при замене его на ENOSYS может перезаписаться какая-нибудь полезная информация).

SECCOMP_RET_ERRNO

Это значение приводит к тому, что часть SECCOMP_RET_DATA возвращаемого значения фильтра передаётся в пространство пользователя в виде значения errno без выполнения системного вызова.

SECCOMP_RET_TRACE

При возврате это значение заставит ядро попытаться уведомить трассировщик на основе ptrace(2) до выполнения системного вызова. Если трассировщика нет, то системный вызов не выполняется и возвращается состояние ошибки со значением errno равным ENOSYS.

Трассировщик будет уведомлён, если он запросил PTRACE_O_TRACESECCOMP посредством ptrace(PTRACE_SETOPTIONS). Трассировщик будет уведомлён о PTRACE_EVENT_SECCOMP, а часть SECCOMP_RET_DATA возвращаемого значения фильтра будет доступна через PTRACE_GETEVENTMSG.

Трассировщик может пропустить системный вызов, изменив номер системного вызова на -1. Или же он может изменить запрашиваемый системный вызов на системный вызов с другим номером. Если трассировщик просит пропустить системный вызов, то системный вызов появится в возвращаемом значении, которое трассировщик помещает в регистр возвращаемого значения.

До ядра 4.8 проверка seccomp не будет запущена ещё раз после уведомления трассировщика (для старых ядер это означает, что ограниченные окружения (sandbox) на основе seccomp не должны позволять использовать ptrace(2) — даже другим процессам в окружении — без максимальной предосторожности; ptracer-ы могут использовать этот механизм для выхода из окружения seccomp).

SECCOMP_RET_LOG (начиная с Linux 4.14)

Это значение приводит к выполнению системного вызова после протоколирования фильтра, возвращающего действие. Администратор может заменить протоколирование этого действия в файле /proc/sys/kernel/seccomp/actions_logged.

SECCOMP_RET_ALLOW

Это значение приводит к выполнению системного вызова.

Если значение действия ни одно из указанных выше, то действием фильтра считается или SECCOMP_RET_KILL_PROCESS (начиная с Linux 4.14), или SECCOMP_RET_KILL_THREAD (в Linux 4.13 и старее).

### Интерфейсы /proc
Файлы в каталоге /proc/sys/kernel/seccomp предоставляют дополнительную информацию seccomp и настройку:

actions_avail (начиная с Linux 4.14)

Доступный только для чтения упорядоченный список возвращаемых действий фильтром seccomp в виде строки. Список упорядочен слева направо в порядке уменьшения приоритета. Представляет собой набор возвращаемых фильтром seccomp действий, поддерживаемых ядром.

actions_logged (начиная с Linux 4.14)

Доступный для чтения-записи упорядоченный список возвращаемых действий фильтром seccomp, которые разрешено протоколировать. Записи в файл не нужно упорядочивать, но прочитанные данные будут упорядочены также как в файле actions_avail.

Важно отметить, что значение actions_logged не останавливает от протоколирования определённого фильтра возвращаемых действий, если подсистема аудита настроена на аудит задачи. Если действие не найдено в файле actions_logged, то конечное решение об аудите действия для этой задачи, в конечном итоге, основывается на действие подсистемы аудита для всех фильтров возвращающих действия, кроме SECCOMP_RET_ALLOW.

Строка «allow» недопустима в файле actions_logged, так как невозможно протоколирование действий SECCOMP_RET_ALLOW. Попытка записи «allow» в файле завершится ошибкой EINVAL.

### Ведение журнала контроля действий seccomp
Начиная с Linux 4.14 ядро позволяет протоколировать действия, возвращаемые фильтрами seccomp в журнал контроля (audit log). Ядро принимает решение о протоколировании действие основываясь на типе действия, имеется ли действие в файле actions_logged и включён ли контроль в ядре (например, посредством параметра загрузки ядра audit=1). Правила следующие:

- Если действие — SECCOMP_RET_ALLOW, то оно не протоколируется.

- В противном случае, если действие SECCOMP_RET_KILL_PROCESS или SECCOMP_RET_KILL_THREAD, и это действие есть в файле actions_logged, то действие протоколируется.

- В противном случае, если для фильтра запрошено протоколирование (флаг SECCOMP_FILTER_FLAG_LOG) и действие есть в файле actions_logged, то действие протоколируется.

- В противном случае, если включён контроль в ядре и процесс контролируется (autrace(8)), то действие протоколируется.

- В противном случае действие не протоколируется.

## ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном выполнении seccomp() возвращает 0. При ошибке, если был использован SECCOMP_FILTER_FLAG_TSYNC, то возвращается ID нити, которая была причиной ошибки синхронизации (данный ID — идентификатор нити ядра с типом, возвращаемом clone(2) и gettid(2)). При других ошибках возвращается -1 и в errno записывается причина ошибки.

## ОШИБКИ
Функция seccomp() может завершиться с ошибкой по следующим причинам:

EACCES

У вызывающего нет мандата CAP_SYS_ADMIN в своём пространстве имён пользователя или не установлен no_new_privs до использования SECCOMP_SET_MODE_FILTER.

EFAULT

Аргумент args не содержит допустимого адреса.

EINVAL

Аргумент operation неизвестен или не поддерживается этой версией ядра или из-за настроек.

EINVAL

Указанное значение flags некорректно для заданного значения operation.

EINVAL

Значение operation включает BPF_ABS, но указанное смещение не выровнено по 32-битной границе или превышает sizeof(struct seccomp_data).

EINVAL

Режим безопасных вычислений уже включён, и значение operation отличается от существующей настройки.

EINVAL

В operation указано SECCOMP_SET_MODE_FILTER, но фильтрующая программа, задаваемая в args, некорректна или её длина равна 0 или превышает BPF_MAXINSNS (4096) инструкций.

ENOMEM

Не хватает памяти.

ENOMEM

Общая длина всех фильтрующих программ, присоединённых к вызывающей нити, превысила бы MAX_INSNS_PER_PATH (32768) инструкций. Заметим, что для вычисления этого предела на каждую уже существующую фильтрующую программу прибавляются ещё 4 инструкции.

EOPNOTSUPP

В operation указано SECCOMP_GET_ACTION_AVAIL, но ядро не поддерживает фильтр, возвращающий действие, указанное в args.

ESRCH

Во время синхронизации нити произошла ошибка в другой нити, но её ID невозможно определить.

## ВЕРСИИ
Системный вызов seccomp() впервые появился в Linux 3.17.

## СООТВЕТСТВИЕ СТАНДАРТАМ
Системный вызов seccomp() является нестандартным расширением Linux.

## ЗАМЕЧАНИЯ
Вместо ручного кодирования фильтров seccomp, как показано в примере ниже, вы можете воспользоваться библиотекой libseccomp, которая предоставляет клиентскую часть для генерации фильтров seccomp.

В поле Seccomp файла /proc/[pid]/status отображается метод просмотра режима seccomp в процессе; смотрите proc(5).

Вызов seccomp() предоставляет больше возможностей по сравнению с операцией PR_SET_SECCOMP prctl(2) (которая не поддерживает flags).

Начиная с Linux 4.4, вызов ptrace(2) с операцией PTRACE_SECCOMP_GET_FILTER можно использовать для получения дампа фильтров seccomp процесса.

### Архитектурная поддержка seccomp BPF
Архитектурная поддержка фильтрации seccomp BPF доступна на следующих архитектурах:

- x86-64, i386, x32 (начиная с Linux 3.5)

- ARM (начиная с Linux 3.8)

- s390 (начиная с Linux 3.8)

- MIPS (начиная с Linux 3.16)

- ARM-64 (начиная с Linux 3.19)

- PowerPC (начиная с Linux 4.3)

- Tile (начиная с Linux 4.3)

- PA-RISC (начиная с Linux 4.6)

### Предостережения
Есть различные тонкости, которые нужно учитывать при применении фильтров seccomp к программе:

- На многих архитектурах некоторые обычные системные вызовы реализованы в пользовательском пространстве в vdso(7). Заметными примерами можно считать clock_gettime(2), gettimeofday(2) и time(2). На таких архитектурах фильтрация seccomp данных системных вызовов не действует (однако, есть случаи где реализации vdso(7) могут вызвать реальный системный вызов и фильтры seccomp filters увидят такое обращение).

- Фильтрация seccomp работает по номерам системных вызовов. Однако, обычно, в приложениях системные вызовы вызываются не напрямую, а через обёрточные функции библиотеки C, которые, в свою очередь, вызывают системные вызовы. Следовательно, нужно учитывать следующее:

- Обёртки glibc некоторых обычных системных вызовов могут использовать системные вызовы ядра с другими именами. Например, обёрточная функция exit(2) использует системный вызов exit_group(2), а обёрточная функция fork(2) в действительности вызывает clone(2).

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

- И наконец, поведение обёрточных функций может различаться в разных версиях glibc. Например, в старых версиях обёрточная функция glibc для open(2) вызывала системный вызов с тем же именем, но начиная с glibc 2.26, в реализации начал вызываться openat(2) на всех архитектурах.

В следствии вышеупомянутого возможно понадобится фильтровать не тот системный вызов, который ожидался. В различных справочных страницах раздела 2 есть абзац Отличия между библиотекой C и ядром, в котором содержится полезная информация о различиях между оберточными функциями и нижележащими системными вызовами.

Кроме того, обратите внимание, что применение фильтров seccomp даже может привести к появлению дефектов приложений, когда фильтры вызывают неожиданные отказы для законных операций, которые приложение, возможно, должно было бы выполнить. Такие дефекты может оказаться нелегко обнаружить при тестировании фильтров seccomp, если дефекты возникают в редко используемых путях кода приложения.

### Особенности seccomp в BPF
Заметим, что следующие особенности BPF относятся только к фильтрам seccomp:

- Модификаторы размера BPF_H и BPF_B не поддерживаются: все операции должны загружать и сохранять слова (4-байта) (BPF_W).

- Для доступа к содержимому буфера seccomp_data используйте модификатор режима адресации BPF_ABS.

- Модификатор режима адресации BPF_LEN выдаёт непосредственный операнд режима, чьё значение равно размеру буфера seccomp_data.

## ПРИМЕР
Программа, показанная далее, обрабатывает четыре и более аргументов. Первые три аргумента — номер системного вызова, числовой идентификатор архитектуры и номер ошибки. Программа использует эти значения для создания фильтра BPF, который используется во время работы для выполнения следующих проверок:

[1]

Если программа не запущена на определённой архитектуре, то фильтр BPF заставляет системные вызовы завершаться с ошибкой ENOSYS.

[2]

Если программа попытается выполнить системный вызов с заданным номером, то фильтр BPF заставит системный вызов завершиться с ошибкой, а в errno будет записан указанный номер ошибки.

В оставшихся аргументах командной строки указываются путь и дополнительные аргументы программы, которую программа из примера должна попытаться выполнить с помощью execv(3) (библиотечной функции, которая использует системный вызов execve(2)). Несколько примеров запуска программы показаны далее.

Сначала мы выведем имя архитектуры, на которой работаем (x86-64), а затем создадим функцию оболочки, которая выдаёт список номеров системных вызовов этой архитектуры:

```
$ uname -m
x86_64
$ syscall_nr() {

    cat /usr/src/linux/arch/x86/syscalls/syscall_64.tbl | \

    awk '$2 != "x32" && $3 == "'$1'" { print $1 }'
}
```

Когда фильтр BPF отклоняет системный вызов (случай [2] выше), системный вызов завершается с номером ошибки, указанной в командной строке. В наших экспериментах используется номер ошибки 99:

```
$ errno 99
EADDRNOTAVAIL 99 Cannot assign requested address
```

В следующем примере мы пытаемся выполнить команду whoami(1), но фильтр BPF отклоняет системный вызов execve(2), и поэтому команда даже не начнёт выполняться:

```
$ syscall_nr execve
59
$ ./a.out
Использование: ./a.out <syscall_nr> <arch> <errno> <prog> [<args>]
Подсказка для <arch>: AUDIT_ARCH_I386: 0x40000003

                 AUDIT_ARCH_X86_64: 0xC000003E
$ ./a.out 59 0xC000003E 99 /bin/whoami
execv: Cannot assign requested address
```

В следующем примере фильтр BPF отклоняет системный вызов write(2), и хотя выполнение началось, команда whoami(1) не может записать в стандартный вывод:

```
$ syscall_nr write
1
$ ./a.out 1 0xC000003E 99 /bin/whoami
```

В последнем примере фильтр BPF отклоняет системный вызов, который не используется в команде whoami(1), и поэтому она выполняется без ошибок и выводит:

```
$ syscall_nr preadv
295
$ ./a.out 295 0xC000003E 99 /bin/whoami
cecilia
```

### Исходный код программы

```
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/audit.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <sys/prctl.h>
#define X32_SYSCALL_BIT 0x40000000
static int
install_filter(int syscall_nr, int t_arch, int f_errno)
{

    unsigned int upper_nr_limit = 0xffffffff;

    /* предполагается, что AUDIT_ARCH_X86_64 означает обычный x86-64 ABI

       (в x32 ABI у всех системных вызовов установлены 30 бит в поле

       «nr», то есть номера >= X32_SYSCALL_BIT) */

    if (t_arch == AUDIT_ARCH_X86_64)

        upper_nr_limit = X32_SYSCALL_BIT - 1;

    struct sock_filter filter[] = {

        /* [0] загружаем архитектуру из буфера «seccomp_data» в

               аккумулятор */

        BPF_STMT(BPF_LD | BPF_W | BPF_ABS,

                 (offsetof(struct seccomp_data, arch))),

        /* [1] прыгаем вперёд на 5 инструкции, если архитектура не совпадает

               с «t_arch» */

        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, t_arch, 0, 4),

        /* [2] загружаем номер системного вызова из буфера «seccomp_data» в

               аккумулятор */

        BPF_STMT(BPF_LD | BPF_W | BPF_ABS,

                 (offsetof(struct seccomp_data, nr))),

        /* [3] проверяем ABI — нужно только для чёрного списка на x86-64.

               Используем BPF_JGT вместо проверки битовой маски,

               чтобы избежать перезагрузки номера syscall. */

        BPF_JUMP(BPF_JMP | BPF_JGT | BPF_K, upper_nr_limit, 3, 0),

        /* [4] прыгаем вперёд на 1 инструкцию, если номер системного вызова

               не совпадает с «syscall_nr» */

        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, syscall_nr, 0, 1),

        /* [5] совпала архитектура и системный вызов: не выполняем
	       системный вызов и возвращаем «f_errno» в «errno» */

        BPF_STMT(BPF_RET | BPF_K,

                 SECCOMP_RET_ERRNO | (f_errno & SECCOMP_RET_DATA)),

        /* [6] не совпал номер системного вызова: разрешаем

               работу других системных вызовов */

        BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),

        /* [7] не совпала архитектура: прерываем задачу */

        BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL),

    };

    struct sock_fprog prog = {

        .len = (unsigned short) (sizeof(filter) / sizeof(filter[0])),

        .filter = filter,

    };

    if (seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog)) {

        perror("seccomp");

        return 1;

    }

    return 0;
}
int
main(int argc, char **argv)
{

    if (argc < 5) {

        fprintf(stderr, "Использование: "

                "%s <syscall_nr> <arch> <errno> <prog> [<args>]\n"

                "Подсказка для <arch>: AUDIT_ARCH_I386: 0x%X\n"

                "                 AUDIT_ARCH_X86_64: 0x%X\n"

                "\n", argv[0], AUDIT_ARCH_I386, AUDIT_ARCH_X86_64);

        exit(EXIT_FAILURE);

    }

    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {

        perror("prctl");

        exit(EXIT_FAILURE);

    }

    if (install_filter(strtol(argv[1], NULL, 0),

                       strtol(argv[2], NULL, 0),

                       strtol(argv[3], NULL, 0)))

        exit(EXIT_FAILURE);

    execv(argv[4], &argv[4]);

    perror("execv");

    exit(EXIT_FAILURE);
}
```

## СМОТРИТЕ ТАКЖЕ
bpfc(1), strace(1), bpf(2), prctl(2), ptrace(2), sigaction(2), proc(5), signal(7), socket(7)

Various pages from the libseccomp library, including: scmp_sys_resolver(1), seccomp_init(3), seccomp_load(3), seccomp_rule_add(3) и seccomp_export_bpf(3).

Файлы исходного кода ядра Documentation/networking/filter.txt и Documentation/userspace-api/seccomp_filter.rst (до Linux 4.13 файл Documentation/prctl/seccomp_filter.txt).

McCanne, S. and Jacobson, V. (1992) The BSD Packet Filter: A New Architecture for User-level Packet Capture, Proceedings of the USENIX Winter 1993 Conference http://www.tcpdump.org/papers/bpf-usenix93.pdf


select

title: select(2)

summary: многопоточный

text:
# select(2)

## ИМЯ
select, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO - многопоточный синхронный ввод-вывод

## ОБЗОР

```
/* в соответствии с POSIX.1-2001, POSIX.1-2008 */
#include <sys/select.h>
```

```
/* в соответствии с ранними стандартами */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
```

```
int select(int nfds, fd_set *readfds, fd_set *writefds,
           fd_set *exceptfds, struct timeval *timeout);
```

```
void FD_CLR(int fd, fd_set *set);
int  FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);
#include <sys/select.h>
```

```
int pselect(int nfds, fd_set *readfds, fd_set *writefds,
            fd_set *exceptfds, const struct timespec *timeout,
            const sigset_t *sigmask);
```

Требования макроса тестирования свойств для glibc (см. feature_test_macros(7)):

pselect(): _POSIX_C_SOURCE >= 200112L

## ОПИСАНИЕ
Вызовы select() и pselect() позволяют программам отслеживать изменения нескольких файловых дескрипторов ожидая, когда один или более файловых дескрипторов станут «готовы» для операции ввода-вывода определённого типа (например, ввода). Файловый дескриптор считается готовым, если к нему возможно применить соответствующую операцию ввода-вывода (например, read(2) или очень маленький write(2)) без блокировки.

Вызов select() может следить только за номерами файловых дескрипторов, которые меньше FD_SETSIZE; вызов poll(2) не имеет этого ограничения. Смотрите ДЕФЕКТЫ.

Работа select() и pselect() идентична за исключением трёх моментов:

(i)

В вызове select() время ожидания задаётся в структуре struct timeval (с секундами и микросекундами), а в pselect() используется структура struct timespec (с секундами и наносекундами).

(ii)

Вызов select() может обновить аргумент timeout, который показывает сколько времени прошло. Вызов pselect() не изменяет этот аргумент.

(iii)

Вызов select() не имеет аргумента sigmask, и ведёт себя также как pselect(), если при вызове было указано значение sigmask равное NULL.

Отслеживаются 3 независимых набора файловых дескрипторов. В тех, что перечислены в readfds, будет отслеживаться появление символов, доступных для чтения (проверяется доступность чтения без блокировки; в частности, файловый дескриптор готов для чтения, если он указывает на конец файла). Файловые дескрипторы, указанные в writefds, будут отслеживаться для возможности записи без блокировки, если доступно пространство для записи (хотя при большом количестве данных для записи будет по-прежнему выполнена блокировка). Файловые дескрипторы, указанные в exceptfds, будут отслеживаться для обнаружения исключительных условий (примеры некоторых исключительных условий смотрите в описании POLLPRI из poll(2)).

При возврате из вызова наборы файловых дескрипторов изменяются, показывая какие файловые дескрипторы фактически изменили состояние (то есть, если используется select() в цикле, то наборы должны переинициализироваться перед каждым вызовом).

Значение каждого из трёх наборов файловых дескрипторов может быть задано как NULL, если слежение за определённым классом событий над файловыми дескрипторами не требуется.

Для манипуляций наборами существуют четыре макроса: FD_ZERO() очищает набор; FD_SET() добавляет заданный файловый дескриптор к набору; FD_CLR() удаляет файловый дескриптор из набора; FD_ISSET() проверяет, является ли файловый дескриптор частью набора. Эти макросы полезны после возврата из вызова select().

Значение nfds должно быть на единицу больше самого большого номера файлового дескриптора из всех трёх наборов плюс 1. Указанные файловые дескрипторы в каждом наборе проверяются до этого порога (но смотрите ДЕФЕКТЫ).

В аргументе timeout указывается интервал, на который должен заблокироваться select() в ожидании готовности файлового дескриптора. Вызов будет блокирован пока:

- файловый дескриптор не станет готов;

- вызов не прервётся обработчиком сигнала;

- не истечёт время ожидания.

Заметим, что интервал timeout будет округлён с точностью системных часов, а из-за задержки при планировании в ядре блокирующий интервал будет немного больше. Если оба поля структуры timeval равны нулю, то select() завершится немедленно (полезно при опросе (polling)). Если значение timeout равно NULL (время ожидания не задано), то select() может блокировать работу неопределённо долго.

Значение sigmask является указателем на маску сигналов (смотрите sigprocmask(2)); если оно не равно NULL, то сначала pselect() заменяет текущую маску сигналов на заданную sigmask, затем выполняет функцию «select», после чего восстанавливает первоначальную сигнальную маску.

Кроме различия в точности аргумента timeout вызов pselect()

```
ready = pselect(nfds, &readfds, &writefds, &exceptfds,

                timeout, &sigmask);
```

эквивалентен атомарному выполнению следующих вызовов:

```
sigset_t origmask;
pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
ready = select(nfds, &readfds, &writefds, &exceptfds, timeout);
pthread_sigmask(SIG_SETMASK, &origmask, NULL);
```

Причина необходимости pselect() в том, что если нужно ждать какого-то сигнала или готовности файлового дескриптора, то необходимо атомарное тестирование для избежания состязательности. (Предположим, что обработчик сигнала устанавливает глобальный флаг и завершается. В этом случае тест этого глобального флага после вызова select() мог бы длиться бесконечно, если сигнал прибыл бы сразу после тестирования, но до вызова. В отличие от этого, pselect() позволяет сначала заблокировать сигналы, обработать уже поступившие и затем вызвать pselect() с желаемым значением sigmask, избегая состязательности.)

### Время ожидания
Используемые структуры времени определены в <sys/time.h> и выглядят следующим образом:

```
struct timeval {

    long    tv_sec;         /* секунды */

    long    tv_usec;        /* микросекунды */
};
```

и

```
struct timespec {

    long    tv_sec;         /* секунды */

    long    tv_nsec;        /* наносекунды */
};
```

(Однако, смотрите ниже про версию POSIX.1.)

Иногда select() вызывается с пустыми наборами (всеми тремя), nfds равным нулю и непустым timeout для переносимой реализации перехода в режим ожидания (sleep) на периоды с точностью менее секунды.

В Linux вызов select() изменяет timeout для отражения времени, проведённого не в режиме ожидания; большая часть других реализаций этого не делает (согласно POSIX.1 допускается любой из этих вариантов). Это вызывает проблемы как при переносе кода Linux, читающего timeout, на другие операционные системы, так и при переносе на Linux кода, использующего struct timeval для многократного вызова select() в цикле без его переинициализации. Во избежание этого следует считать, что значение timeout не определено после возврата из select().

## ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном выполнении select() и pselect() возвращают количество файловых дескрипторов, находящихся в трёх возвращаемых наборах (то есть, общее количество бит, установленных в readfds, writefds, exceptfds), при чём это количество может быть нулевым, если время ожидания истекло, а интересующие события так и не произошли. При ошибке возвращается значение -1, а переменной errno присваивается соответствующий номер ошибки; наборы файловых дескрипторов не изменяются и значение timeout становится неопределённым.

## ОШИБКИ
EBADF

В одном из наборов находится неверный файловый дескриптор (возможно файловый дескриптор уже закрыт, или при работе с ним произошла ошибка). Однако смотрите ДЕФЕКТЫ.

EINTR

При выполнении поступил сигнал; см. signal(7).

EINVAL

Значение nfds отрицательно или превышает ограничение ресурса RLIMIT_NOFILE (смотрите getrlimit(2)).

EINVAL

Значение, содержащееся внутри timeout, некорректно.

ENOMEM

Не удалось выделить память для внутренних таблиц.

## ВЕРСИИ
Вызов pselect() был добавлен в ядро Linux версии 2.6.16. До этого pselect() эмулировался в glibc (но, см. ДЕФЕКТЫ).

## СООТВЕТСТВИЕ СТАНДАРТАМ
Вызов select() соответствует POSIX.1-2001, POSIX.1-2008 и 4.4BSD (впервые select() появился в 4.2BSD). Обычно перенос выполняется с не-BSD систем и на них, если они поддерживают уровень BSD-сокетов (включая варианты System V). Однако, заметим, что вариант System V, обычно, устанавливает значение переменной timeout перед выходом, а вариант BSD - нет.

Вызов pselect() определён в стандартах POSIX.1g, в POSIX 1004.1-2001 и POSIX.1-2008.

## ЗАМЕЧАНИЯ
fd_set представляет собой буфер фиксированного размера. Выполнение FD_CLR() или FD_SET() с отрицательным значением fd, равным или большим чем FD_SETSIZE, приводит к неопределённому поведению. Более того, согласно POSIX fd должен быть корректным файловым дескриптором.

На операции select() и pselect() флаг O_NONBLOCK не влияет.

В некоторых системах UNIX вызов select() может завершаться с ошибкой EAGAIN, если системе не удаётся выделить внутренние ресурсы ядра, вместо ошибки ENOMEM, как это происходит в Linux. В POSIX эта ошибка указана для poll(2), но не для select(). В переносимых программах лучше ожидать EAGAIN в цикле, как для EINTR.

В системах без pselect(), надёжного (и более переносимого) перехвата сигнала можно достичь с помощью трюка с каналом в самого себя. В этом методе обработчик сигнала пишет байт в канал, чей второй конец отслеживается select() в основной программе (чтобы избежать возможной блокировки при записи в канал, который может быть заполнен, или при чтении из канала, который может быть пуст, нужно использовать неблокирующий ввод/вывод).

Что касается задействованных типов, классическим вариантом является структура timeval с двумя полями типа long (как показано ниже), которая определена в <sys/time.h>. В POSIX.1:

```
struct timeval {

    time_t         tv_sec;     /* секунды */

    suseconds_t    tv_usec;    /* микросекунды */
};
```

где структура определена в <sys/select.h>, а типы данных time_t и suseconds_t определены в <sys/types.h>.

Что касается прототипов, классическим вариантом является объявление select() в <time.h>. Согласно POSIX.1 объявления select() и pselect() должны включаться в <sys/select.h>.

В <sys/select.h> из glibc 2.0 указан ошибочный прототип pselect(). В glibc 2.1 до версии 2.2.1 pselect() доступен при определённом _GNU_SOURCE. Требования, которые необходимы для работы с glibc начиная с версии 2.2.2, показаны в разделе ОБЗОР.

### Соотношение между уведомлениями select() и poll()
Внутри исходного кода ядра Linux имеются определения, которые показывают соотношение между уведомлениями чтения, записи и исключительного условия select() и уведомляющими событиями, предоставляемыми poll(2) (и epoll(7)):

```
#define POLLIN_SET (POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP |

                    POLLERR)

                   /* готов для чтения */
#define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR)

                   /* готов для записи */
#define POLLEX_SET (POLLPRI)

                   /* исключительное условие */
```

### Многонитевые приложения
Если файловой дескриптор, отслеживаемый select(), закрывается в другой нити, то результат непредсказуем. В некоторых системах UNIX select() разблокируется и возвращает выполнение с указанием того, что файловый дескриптор готов (последующая операция ввода-вывода, вероятно, завершится с ошибкой, если другой процесс не переоткроет файловый дескриптор между возвратом select() и выполнением операции ввода-вывода). В Linux (и некоторых других системах), закрытие файлового дескриптора в другой нити не влияет на select(). Резюмируя, любое приложение, которое полагается на определённое поведение в таком сценарии, должно считаться ошибочным.

### Отличия между библиотекой C и ядром
Ядро Linux разрешает наборы файловых дескрипторов любого размера, определяя длину набора по значению nfds. Однако в реализации glibc тип fd_set имеет фиксированный размер. Смотрите также ДЕФЕКТЫ.

Интерфейс pselect(), описанный в этой странице, реализован в glibc. Для этого используется системный вызов pselect6(). Поведение данного системного вызова несколько отличается от обёрточной функции glibc.

В Linux системный вызов pselect6() изменяет содержимое своего аргумента timeout. Однако, обёрточная функция glibc скрывает это поведение используя локальную переменную для аргумента timeout при передаче в системный вызов. Таким образом, функция pselect() в glibc не изменяет свой аргумент timeout; это поведение требуется в POSIX.1-2001.

Последний аргумент системного вызова pselect6() не является указателем sigset_t *, он представляет собой структуру в виде:

```
struct {

    const kernel_sigset_t *ss;   /* указатель на набор сигналов */

    size_t ss_len;               /* размер (в байтах) объекта,

                                    на который указывает «ss» */
};
```

Это позволяет системному вызову получить указатель на набор сигналов и его размер, так как в большинстве архитектур системным вызовам можно передать максимум 6 аргументов. В sigprocmask(2) смотрите описание различий между обозначением набора сигналов в ядре и libc.

## ДЕФЕКТЫ
В POSIX разрешено в реализации определять верхнее ограничение, объявляемое через константу FD_SETSIZE, для диапазона файловых дескрипторов, который можно задать в наборе файловых дескрипторов. Ядро Linux не имеет фиксированного ограничения, но реализация glibc использует тип fd_set постоянного размера, определяет FD_SETSIZE равным 1024, а также предоставляет набор макросов операций FD_*(), учитывающих это ограничение. Для слежения за файловыми дескрипторами большими чем 1023 используйте вызов poll(2).

Реализация аргументов fd_set в виде аргументов значение-результат означает, что они должен инициализироваться перед каждым вызовом select(). Такой ошибки проектирования избежали в poll(2), где используется отдельные поля структуры для входных и выходных параметров вызова.

Согласно POSIX, select() должен проверять все файловые дескрипторы, указанные в трёх наборах файловых дескрипторов до верхнего порога nfds-1. Однако текущая реализация игнорирует любой файловый дескриптор в этих наборах, значение которого больше максимального номера файлового дескриптора, который в настоящий момент открыт процессом. Согласно POSIX, такой файловый дескриптор, указанный в любом наборе, должен приводить к ошибке EBADF.

Glibc 2.0 предоставляет версию pselect(), которая не принимает аргумент sigmask.

Начиная с версии 2.1, glibc предоставляет эмуляцию pselect(), которая реализована с помощью sigprocmask(2) и select(). Эта реализация остаётся уязвимой к той самой состязательности, для устранения которой и был разработан pselect(). В современных версии glibc используется (бессостязательный) системный вызов pselect(), если он предоставляется ядром.

В Linux, вызов select() может сообщать о файловом дескрипторе сокета как о «готовом для чтения», хотя при последующем чтении произойдёт блокировка. Это может случиться, например, когда данные прибыли, но при анализе их контрольная сумма не совпала и они были отброшены. Также могут быть другие обстоятельства, при которых файловый дескриптор ошибочно считается готовым. Поэтому, возможно безопасней будет использовать для сокетов O_NONBLOCK, которые не должны блокироваться.

В Linux, вызов select() также изменяет timeout, если он прерван обработчиком сигнала (т. е., возвращается ошибка EINTR). Согласно POSIX.1 это не разрешено. В Linux системный вызов pselect() действует также, но обёртка glibc скрывает это поведение копируя перед вызовом timeout в локальную переменную и передавая её в системный вызов.

## ПРИМЕР

```
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int
main(void)
{

    fd_set rfds;

    struct timeval tv;

    int retval;

    /* Следить, когда в stdin (fd 0) что-нибудь появится. */

    FD_ZERO(&rfds);

    FD_SET(0, &rfds);

    /* Ждать не больше пяти секунд. */

    tv.tv_sec = 5;

    tv.tv_usec = 0;

    retval = select(1, &rfds, NULL, NULL, &tv);

    /* Больше не полагаться на значение tv! */

    if (retval == -1)

        perror("select()");

    else if (retval)

        printf("Есть данные.\n");

        /* FD_ISSET(0, &rfds) will be true. */

    else

        printf("Данные не появились в течение пяти секунд.\n");

    exit(EXIT_SUCCESS);
}
```

## СМОТРИТЕ ТАКЖЕ
accept(2), connect(2), poll(2), read(2), recv(2), restart_syscall(2), send(2), sigprocmask(2), write(2), epoll(7), time(7)

Обсуждение и примеры смотрите в select_tut(2).