semop(2)

ИМЯ
semop, semtimedop - операции с семафорами System V

LIBRARY
Standard C library (libc, -lc)

СИНТАКСИС
#include <sys/sem.h>

int semop(int semid, struct sembuf *sops, size_t nsops);
int semtimedop(int semid, struct sembuf *sops, size_t nsops,
               const struct timespec *_Nullable timeout);

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

semtimedop():
    _GNU_SOURCE

ОПИСАНИЕ
С каждым семафором в наборе семафоров System V связаны следующие значения:

    unsigned short  semval;   /* semaphore value */
    unsigned short  semzcnt;  /* # waiting for zero */
    unsigned short  semncnt;  /* # waiting for increase */
    pid_t           sempid;   /* PID of process that last
                                 modified the semaphore value */

Вызов semop() производит операции над выбранными семафорами из набора семафоров semid. Каждый из элементов nsops в массиве, указанном в sops является структурой, которой задаётся операция, выполняемая над отдельным семафором. Элементы этой структуры имеют тип struct sembuf, который содержит поля:

    unsigned short sem_num;  /* номер семафора */
    short          sem_op;   /* операция над семафором */
    short          sem_flg;  /* флаги операции */

Флаги в sem_flg могут иметь значения IPC_NOWAIT и SEM_UNDO. Если указан флаг SEM_UNDO, то при завершении процесса будет выполнена откат операции.

Набор операций из sops выполняется в порядке появления в массиве и является атомарным, то есть выполняются или все операции, или ни одной. Поведение системного вызова при обнаружении невозможности немедленного выполнения операций зависит от наличия флага IPC_NOWAIT в полях sem_flg отдельных операций, как это описано далее.

Каждая операция выполняется над sem_num-тым семафором из набора, где первый семафор имеет номер 0. Есть три типа операций, различающихся значением sem_op.

If sem_op is a positive integer, the operation adds this value to the semaphore value (semval). Furthermore, if SEM_UNDO is specified for this operation, the system subtracts the value sem_op from the semaphore adjustment (semadj) value for this semaphore. This operation can always proceed—it never forces a thread to wait. The calling process must have alter permission on the semaphore set.

Если значение sem_op равно нулю, то процесс должен иметь права на чтение набора семафоров. Эта операция «ожидания нуля»: если semval равно нулю, то операция может выполнится сразу. Иначе, если в поле семафора sem_flg указан флаг IPC_NOWAIT, то semop() завершается с ошибкой и errno присваивается значение EAGAIN (и ни одна операция из sops не выполняется). Или же semzcnt (счётчик нитей, ожидающих пока значение семафора не сравнялось с нулём) увеличивается на единицу, а нить переходит в режим ожидания пока не случится одно из:

•   Значение semval станет равным 0, тогда значение semzcnt уменьшается.

•   Набор семафоров удалится: semop() завершается с ошибкой, а errno присваивается значение EIDRM.

•   Вызывающая нить получит сигнал: значение semncnt уменьшается и semop() завершается с ошибкой, а errno присваивается значение EINTR.

Если значение sem_op меньше нуля, то процесс должен иметь права на изменение набора семафоров. Если значение semval больше или равно абсолютному значению sem_op, то операция может выполнятся сразу: абсолютное значение sem_op вычитается из semval, и, если для этой операции установлен флаг SEM_UNDO, система добавляет абсолютное значение sem_op к значению регулировки (semadj) семафора. Если абсолютное значение sem_op больше semval, и в sem_flg указан IPC_NOWAIT, то semop() завершается с ошибкой, а errno присваивается значение EAGAIN (и ни одна операция из sops не выполняется). Иначе semncnt (счётчик нитей, ожидающих увеличения значения семафора) увеличивается на единицу, а нить переходит в режим ожидания пока не случится одно из:

•   semval становится больше или равно абсолютному значению sem_op: операция продолжается как описано выше.

•   Набор семафоров удалится из системы: semop() завершается с ошибкой, а errno присваивается значение EIDRM.

•   Вызывающая нить получит сигнал: значение semncnt уменьшается и semop() завершается с ошибкой, а errno присваивается значение EINTR.

При успешном выполнении значение sempid для каждого семафора, указанного в массиве, на который указывает sops, устанавливается равным идентификатору вызывающего процесса. Также sem_otime присваивается значение текущего времени.

  semtimedop()
Системный вызов semtimedop() ведёт себя идентично semop(), за исключением того, что в случаях, когда вызывающая нить будет спать, длительность этого сна ограничена количеством времени, определяемым структурой timespec, чей адрес передаётся в аргументе timeout. Данное значение интервала будет округлено до точности системных часов, а из-за задержки при планировании в ядре блокирующий интервал будет немного больше. Если достигнут указанный лимит времени, то semtimedop() завершится с ошибкой, а errno устанавливается в EAGAIN (и ни одна из операций в sops не выполняется). Если значение аргумента timeout равно NULL, то semtimedop() ведёт себя аналогично semop().

Заметим, что если semtimedop() прерывается сигналом, то вызов завершается с ошибкой EINTR, а содержимое timeout не изменяется.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
On success, semop() and semtimedop() return 0. On failure, they return -1, and set errno to indicate the error.

ОШИБКИ

E2BIG
    Значение аргумента nsops больше SEMOPM, максимального количества операций, которое может выполнить один системный вызов.

EACCES
    Вызывающий процесс не имеет прав, требуемых для выполнения указанных операций над семафорами, и не имеет мандата CAP_IPC_OWNER, который управляет его пространством имён IPC.

EAGAIN
    Операция не может быть выполнена немедленно и, либо IPC_NOWAIT был указан в sem_flg, либо истекло время лимита, определённое в timeout.

EFAULT
    Адрес, указанный в sops или timeout, не доступен.

EFBIG
    Для некоторых операций значение sem_num меньше нуля или больше или равно количеству семафоров в наборе.

EIDRM
    Набор семафоров был удалён.

EINTR
    Нить, находясь в режиме ожидания, получила сигнал; смотрите signal(7).

EINVAL
    Набор семафоров не существует, или значение semid меньше нуля, или nsops имеет не положительное значение.

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

ERANGE
    Для некоторых операций sem_op+semval больше чем SEMVMX, максимального значения semval (зависит от реализации).

ВЕРСИИ
semtimedop() first appeared in Linux 2.5.52, and was subsequently backported into Linux 2.4.22. glibc support for semtimedop() first appeared in Linux 2.3.3.

СТАНДАРТЫ
POSIX.1-2001, POSIX.1-2008, SVr4.

ЗАМЕЧАНИЯ
Структуры процесса sem_undo не наследуются потомками, созданными через fork(2), но они наследуются при выполнении системного вызова execve(2).

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

A semaphore adjustment (semadj) value is a per-process, per-semaphore integer that is the negated sum of all operations performed on a semaphore specifying the SEM_UNDO flag. Each process has a list of semadj values—one value for each semaphore on which it has operated using SEM_UNDO. When a process terminates, each of its per-semaphore semadj values is added to the corresponding semaphore, thus undoing the effect of that process's operations on the semaphore (but see BUGS below). When a semaphore's value is directly set using the SETVAL or SETALL request to semctl(2), the corresponding semadj values in all processes are cleared. The clone(2) CLONE_SYSVSEM flag allows more than one process to share a semadj list; see clone(2) for details.

Значения semval, sempid, semzcnt и semnct семафора можно получить с помощью соответствующих вызовов semctl(2).

  Ограничения семафоров
Ниже приведены лимиты ресурсов наборов семафоров, влияющие на вызов semop():

SEMOPM
    Максимальное количество операций, разрешённых для одного вызова semop(). До версии Linux 3.19, значение по умолчанию было 3. Начиная с Linux 3.19, значение по умолчанию равно 500. В Linux это ограничение можно прочитать и изменить через третье поле /proc/sys/kernel/sem. Замечание: это ограничение не должно превышать 1000, так как есть риск, что semop(2) завершится с ошибкой из-за фрагментации памяти ядра при выделении памяти при копировании массива sops.

SEMVMX
    Максимально допустимое значение semval: зависит от реализации (32767).

Реализация не накладывает существенных ограничений на максимальное значение (SEMAEM), на которое можно изменить значение семафора при выходе, максимальное количество системных структур откатываемых операций (SEMMNU) и максимальное количество элементов отката системных параметров на процесс.

ДЕФЕКТЫ
При завершении процесса его набор связанных структур semadj используется для отката выполненных действий над семафорами, для которых был установлен флаг SEM_UNDO. Это повышает сложность: если одно (или более) этих изменений семафоров привело бы в результате к попытке уменьшить значение семафора ниже нуля, что должно быть сделано в реализации? Одним из возможных решений была бы блокировка до тех пор, пока не выполнятся все изменения семафоров. Однако это нежелательно, так как это привело бы к блокированию процесса на неопределённый срок при его завершении. Другим вариантом является игнорирование сразу всех изменений семафоров (в некоторой степени, аналогично завершению с ошибкой, когда для операции с семафором указан IPC_NOWAIT). В Linux используется третий вариант: уменьшение значения семафора до тех пор, пока это возможно ( т.е. до нуля) и разрешение немедленного завершения процесса.

In Linux 2.6.x, x <= 10, there is a bug that in some circumstances prevents a thread that is waiting for a semaphore value to become zero from being woken up when the value does actually become zero. This bug is fixed in Linux 2.6.11.

ПРИМЕРЫ
В следующем фрагменте кода используется semop() для атомарного ожидания момента, когда значение семафора 0 станет равным нулю и последующего увеличения значения семафора на единицу.

    struct sembuf sops[2];
    int semid;
    /* код для установки semid не показан */
    sops[0].sem_num = 0;        /* применяем к семафору 0 */
        sops[0].sem_op = 0;     /* ждём значения, равного 0 */
        sops[0].sem_flg = 0;
    sops[1].sem_num = 0;        /* применяем к семафору 0 */
        sops[1].sem_op = 1;     /* увеличиваем значение на 1 */
        sops[1].sem_flg = 0;
    if (semop(semid, sops, 2) == -1) {
        perror("semop");
        exit(EXIT_FAILURE);
    }

A further example of the use of semop() can be found in shmop(2).

СМ. ТАКЖЕ
clone(2), semctl(2), semget(2), sigaction(2), capabilities(7), sem_overview(7), sysvipc(7), time(7)

ПЕРЕВОД
Русский перевод этой страницы руководства был сделан 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.