title: select_tut(2)

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

text:
# select_tut(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 *utimeout);
```

```
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 *ntimeout,
            const sigset_t *sigmask);
```

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

pselect(): _POSIX_C_SOURCE >= 200112L

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

Основные параметры задаются в виде «набора» файловых дескрипторов: readfds, writefds и exceptfds. Каждый набор имеет тип fd_set и его содержимое можно изменять с помощью макросов FD_CLR(), FD_ISSET(), FD_SET() и FD_ZERO(). При создании нового набора сначала его нужно очистить с помощью FD_ZERO(). Вызов select() изменяет содержимое наборов в соответствии с правилами, описанными далее; после вызова select() вы можете проверить существует ли ещё файловый дескриптор в наборе с помощью макроса FD_ISSET(). Макрос FD_ISSET() возвращает ненулевое значение, если указанный файловый дескриптор присутствует в наборе и ноль, если отсутствует. Макрос FD_CLR() удаляет файловый дескриптор из набора.

### Аргументы
readfds

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

writefds

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

exceptfds

Этот набор служит для слежения за «исключительными ситуациями». На самом деле, отслеживается только одна распространённая исключительная ситуация: доступность внепоточных (out-of-band — OOB) данных для чтения из сокета TCP. Более подробно о данных OOB смотрите в recv(2), send(2) и tcp(7). Другая менее распространённая ситуация: select(2) указывает на исключительную ситуацию с псевдотерминалом в пакетном режиме; смотрите ioctl_tty(2). После возврата из select() в exceptfds остаются только те файловые дескрипторы, в которых произошла исключительная ситуация.

nfds

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

utimeout

Этот аргумент задаёт наибольшее время, которое вызов select() будет ожидать событий, по прошествии которого завершит работу, даже если ничего не произойдёт. Если значение этого аргумента равно NULL, то select() будет ожидать бесконечно. Значение utimeout может быть установлено в ноль секунд; в этом случае select() возвратит управление немедленно, с информацией о готовности файловых дескрипторов на момент вызова. Структура struct timeval определена следующим образом:

```
struct timeval {

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

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

```

ntimeout

Этот аргумент pselect() имеет то же значение, что и utimeout, но структура struct timespec позволяет указывать время с точностью до наносекунд:

```
struct timespec {

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

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

```

sigmask

Этот аргумент содержит набор сигналов, которые ядро должно разблокировать (то есть удалить из маски сигналов вызывающей нити) на время, пока вызывающий заблокирован в вызове pselect() (см. sigaddset(3) и sigprocmask(2)). В качестве аргумента может быть передано значение NULL — вызов не изменяет маску сигналов при входе и выходе из функции. То есть pselect() ведёт себя как select().

### Комбинирование событий сигналов и данных
Вызов pselect() полезен как для ожидания сигнала, так и для ожидания готовности файлового дескриптора для ввода-вывода. Программы, принимающие сигналы, как правило, лишь выставляют в обработчике сигнала глобальный флаг, который означает, что требуется обработка события в главном цикле программы. Появление сигнала заставит вызов select() (или pselect()) вернуть управление вызвавшей программе; при этом errno будет присвоено EINTR. Это поведение продиктовано необходимостью обработки сигналов в главном цикле программы во избежание бесконечной блокировки select(). В главном цикле программы должно быть условие, проверяющее глобальный флаг. Возникает вопрос: а что если сигнал придёт после проверки этого условия, но до вызова select()? В этом случае программа навсегда останется в select(), хотя и есть ожидающее событие. Для разрешения этой проблемы существует вызов pselect(). Этот вызов можно использовать для установки в сигнальной маске сигналов, которые принимаются только внутри вызова pselect(). Например, предположим что интересующее нас событие — это завершение дочернего процесса. Перед запуском главного цикла заблокируем SIGCHLD с помощью sigprocmask(2). Наш вызов pselect() разблокирует SIGCHLD, указав пустую маску сигналов. Программа будет выглядеть так:

```
static volatile sig_atomic_t got_SIGCHLD = 0;
static void
child_sig_handler(int sig)
{

    got_SIGCHLD = 1;
}
int
main(int argc, char *argv[])
{

    sigset_t sigmask, empty_mask;

    struct sigaction sa;

    fd_set readfds, writefds, exceptfds;

    int r;

    sigemptyset(&sigmask);

    sigaddset(&sigmask, SIGCHLD);

    if (sigprocmask(SIG_BLOCK, &sigmask, NULL) == -1) {

        perror("sigprocmask");

        exit(EXIT_FAILURE);

    }

    sa.sa_flags = 0;

    sa.sa_handler = child_sig_handler;

    sigemptyset(&sa.sa_mask);

    if (sigaction(SIGCHLD, &sa, NULL) == -1) {

        perror("sigaction");

        exit(EXIT_FAILURE);

    }

    sigemptyset(&empty_mask);

    for (;;) {          /* главный цикл */

        /* Инициализация readfds, writefds и exceptfds

           до вызова pselect() (код не показан). */

        r = pselect(nfds, &readfds, &writefds, &exceptfds,

                    NULL, &empty_mask);

        if (r == -1 && errno != EINTR) {

            /* обработка ошибки */

        }

        if (got_SIGCHLD) {

            got_SIGCHLD = 0;

            /* Здесь обработка сигнального события; например с

               помощью wait() для завершения потомком (код не показан). */

        }

        /* код основной программы */

    }
}
```

### Практика
Итак, какой прок от использования select()? Разве нельзя просто считывать и записывать данные в файловые дескрипторы когда этого захочется? Смысл использования select() в том, что он позволяет следит за несколькими дескрипторами одновременно и корректно переводить процесс в режим ожидания, когда активности не наблюдается. Программисты UNIX часто попадают в ситуацию, когда необходимо обработать ввод-вывод из более чем одного файлового дескриптора в то время как поток данных может быть неравномерным. Если вы просто создадите последовательность вызовов read(2) и write(2), то можете попасть в ситуацию, когда один из вызовов будет ожидать данные из/в файлового дескриптора, в то время как другой будет простаивать, хотя данные для него уже появились. Вызов select() позволяет эффективно справиться с такой ситуацией.

### Правила использования
Многие из тех, кто пытался использовать select(), сталкивались с поведением, которое трудно понять, и которое приводило к непереносимым или просто плохим результатам. Например, вышеприведенная программа тщательно спланирована так, чтобы ни в каком случае не блокироваться, хотя для её файловых дескрипторов не установлен неблокирующий режим. Несложно перечислить не очевидные ошибки, которые лишат всех преимуществ использования select(), поэтому вот список основных моментов, на которые нужно обращать внимание при использовании select().

1.

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

2.

Для повышения эффективности значение nfds должно правильно вычисляться, как это объяснялось выше.

3.

Файловые дескрипторы не должны добавляться в наборы, если вы не планируете после вызова select() проверять результат и соответствующим образом реагировать. Смотрите следующее правило.

4.

После возврата из select() должны быть проверены все файловые дескрипторы во всех наборах.

5.

Вызовы read(2), recv(2), write(2) и send(2) не обязательно считывают/записывают данные в полном объёме. Такое, конечно, возможно при низком трафике или быстром потоке, однако происходит далеко не всегда. Вы должны рассчитывать, что ваши функции получают/отправляют только один байт за раз.

6.

Никогда не считывайте/записывайте побайтно, если только вы не абсолютно уверены в том, что нужно обработать небольшой объём данных. Крайне неэффективно считывать/записывать меньшее количество байт, чем вы можете поместить в буфер за один раз. Буферы в вышеприведённом примере имеют размер 1024 байта, однако могут быть легко увеличены до максимального размера пакета в вашей локальной сети.

7.

Вызовы read(2), recv(2), write(2), send(2) и select() могут завершиться ошибкой EINTR, а вызовы read(2), recv(2) write(2) и send(2) могут завершиться присвоением errno значения EAGAIN (EWOULDBLOCK). Эти варианты должны быть правильно обработаны (в вышеприведенной программе этого не сделано). Если ваша программа не собирается принимать сигналы, то маловероятно, что вы получите EINTR. Если ваша программа не использует неблокирующий ввод-вывод, то вы не получите EAGAIN.

8.

Никогда не вызывайте read(2), recv(2), write(2) или send(2) с буфером нулевой длины.

9.

Если вызовы read(2), recv(2), write(2) и send(2) завершаются с ошибками, отличными от перечисленных в пункте 7. или один из вызовов ввода вернул 0, что указывает на конец файла, то вы не должны передавать этот файловый дескриптор в select() снова. В примере выше я немедленно закрываю файловый дескриптор и устанавливаю его в -1 для предотвращения его включения в набор.

10.

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

11.

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

### Эмуляция usleep
В системах, не имеющих функции usleep(3), вы можете использовать select() с конечной задержкой и без файловых дескрипторов следующим образом:

```
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 200000;  /* 0.2 секунды */
select(0, NULL, NULL, NULL, &tv);
```

Однако работа гарантируется только в системах UNIX.

## ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном выполнении select() возвращает общее число файловых дескрипторов, которые остались в наборах.

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

Значение -1 указывает на ошибку, при этом errno устанавливается соответствующим образом. В случае ошибки содержимое наборов и структуры struct timeout не определено и не должно быть использовано. Однако вызов pselect() никогда не изменяет ntimeout.

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

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

Программный интерфейс Linux epoll(7) предоставляет более эффективный метод для слежения за большим количеством файловых дескрипторов чем select(2) и poll(2).

## ПРИМЕР
Вот пример, который лучше демонстрирует возможности select(). Программа осуществляет перенаправление одного порта TCP в другой.

```
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <string.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
static int forward_port;
#undef max
#define max(x,y) ((x) > (y) ? (x) : (y))
static int
listen_socket(int listen_port)
{

    struct sockaddr_in addr;

    int lfd;

    int yes;

    lfd = socket(AF_INET, SOCK_STREAM, 0);

    if (lfd == -1) {

        perror("socket");

        return -1;

    }

    yes = 1;

    if (setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR,

            &yes, sizeof(yes)) == -1) {

        perror("setsockopt");

        close(lfd);

        return -1;

    }

    memset(&addr, 0, sizeof(addr));

    addr.sin_port = htons(listen_port);

    addr.sin_family = AF_INET;

    if (bind(lfd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {

        perror("bind");

        close(lfd);

        return -1;

    }

    printf("приём соединений на порту %d\n", listen_port);

    listen(lfd, 10);

    return lfd;
}
static int
connect_socket(int connect_port, char *address)
{

    struct sockaddr_in addr;

    int cfd;

    cfd = socket(AF_INET, SOCK_STREAM, 0);

    if (cfd == -1) {

        perror("socket");

        return -1;

    }

    memset(&addr, 0, sizeof(addr));

    addr.sin_port = htons(connect_port);

    addr.sin_family = AF_INET;

    if (!inet_aton(address, (struct in_addr *) &addr.sin_addr.s_addr)) {

        fprintf(stderr, "inet_aton(): неправильный формат IP-адреса\n");

        close(cfd);

        return -1;

    }

    if (connect(cfd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {

        perror("connect()");

        shutdown(cfd, SHUT_RDWR);

        close(cfd);

        return -1;

    }

    return cfd;
}
#define SHUT_FD1 do {                                \

                     if (fd1 >= 0) {                 \

                         shutdown(fd1, SHUT_RDWR);   \

                         close(fd1);                 \

                         fd1 = -1;                   \

                     }                               \

                 } while (0)
#define SHUT_FD2 do {                                \

                     if (fd2 >= 0) {                 \

                         shutdown(fd2, SHUT_RDWR);   \

                         close(fd2);                 \

                         fd2 = -1;                   \

                     }                               \

                 } while (0)
#define BUF_SIZE 1024
int
main(int argc, char *argv[])
{

    int h;

    int fd1 = -1, fd2 = -1;

    char buf1[BUF_SIZE], buf2[BUF_SIZE];

    int buf1_avail = 0, buf1_written = 0;

    int buf2_avail = 0, buf2_written = 0;

    if (argc != 4) {

        fprintf(stderr, "Использование\n\tfwd <прослушиваемый-порт> "

                 "<порт-перенаправления> <ip-адрес-перенаправления>\n");

        exit(EXIT_FAILURE);

    }

    signal(SIGPIPE, SIG_IGN);

    forward_port = atoi(argv[2]);

    h = listen_socket(atoi(argv[1]));

    if (h == -1)

        exit(EXIT_FAILURE);

    for (;;) {

        int ready, nfds = 0;

        ssize_t nbytes;

        fd_set readfds, writefds, exceptfds;

        FD_ZERO(&readfds);

        FD_ZERO(&writefds);

        FD_ZERO(&exceptfds);

        FD_SET(h, &readfds);

        nfds = max(nfds, h);

        if (fd1 > 0 && buf1_avail < BUF_SIZE)

            FD_SET(fd1, &readfds);

            /* Замечание: nfds обновляется ниже, когда добавляется fd1

               в exceptfds. */

        if (fd2 > 0 && buf2_avail < BUF_SIZE)

            FD_SET(fd2, &readfds);

        if (fd1 > 0 && buf2_avail - buf2_written > 0)

            FD_SET(fd1, &writefds);

        if (fd2 > 0 && buf1_avail - buf1_written > 0)

            FD_SET(fd2, &writefds);

        if (fd1 > 0) {

            FD_SET(fd1, &exceptfds);

            nfds = max(nfds, fd1);

        }

        if (fd2 > 0) {

            FD_SET(fd2, &exceptfds);

            nfds = max(nfds, fd2);

        }

        ready = select(nfds + 1, &readfds, &writefds, &exceptfds, NULL);

        if (ready == -1 && errno == EINTR)

            continue;

        if (ready == -1) {

            perror("select()");

            exit(EXIT_FAILURE);

        }

        if (FD_ISSET(h, &readfds)) {

            socklen_t addrlen;

            struct sockaddr_in client_addr;

            int fd;

            addrlen = sizeof(client_addr);

            memset(&client_addr, 0, addrlen);

            fd = accept(h, (struct sockaddr *) &client_addr, &addrlen);

            if (fd == -1) {

                perror("accept()");

            } else {

                SHUT_FD1;

                SHUT_FD2;

                buf1_avail = buf1_written = 0;

                buf2_avail = buf2_written = 0;

                fd1 = fd;

                fd2 = connect_socket(forward_port, argv[3]);

                if (fd2 == -1)

                    SHUT_FD1;

                else

                    printf("connect from %s\n",

                            inet_ntoa(client_addr.sin_addr));

                /* пропускаем все события для старых, закрытых

                   файловых дескрипторов */

                continue;

            }

        }

        /* Замечание: чтение данных OOB до обычных */

        if (fd1 > 0 && FD_ISSET(fd1, &exceptfds)) {

            char c;

            nbytes = recv(fd1, &c, 1, MSG_OOB);

            if (nbytes < 1)

                SHUT_FD1;

            else

                send(fd2, &c, 1, MSG_OOB);

        }

        if (fd2 > 0 && FD_ISSET(fd2, &exceptfds)) {

            char c;

            nbytes = recv(fd2, &c, 1, MSG_OOB);

            if (nbytes < 1)

                SHUT_FD2;

            else

                send(fd1, &c, 1, MSG_OOB);

        }

        if (fd1 > 0 && FD_ISSET(fd1, &readfds)) {

            nbytes = read(fd1, buf1 + buf1_avail,

                      BUF_SIZE - buf1_avail);

            if (nbytes < 1)

                SHUT_FD1;

            else

                buf1_avail += nbytes;

        }

        if (fd2 > 0 && FD_ISSET(fd2, &readfds)) {

            nbytes = read(fd2, buf2 + buf2_avail,

                      BUF_SIZE - buf2_avail);

            if (nbytes < 1)

                SHUT_FD2;

            else

                buf2_avail += nbytes;

        }

        if (fd1 > 0 && FD_ISSET(fd1, &writefds) && buf2_avail > 0) {

            nbytes = write(fd1, buf2 + buf2_written,

                       buf2_avail - buf2_written);

            if (nbytes < 1)

                SHUT_FD1;

            else

                buf2_written += nbytes;

        }

        if (fd2 > 0 && FD_ISSET(fd2, &writefds) && buf1_avail > 0) {

            nbytes = write(fd2, buf1 + buf1_written,

                       buf1_avail - buf1_written);

            if (nbytes < 1)

                SHUT_FD2;

            else

                buf1_written += nbytes;

        }

        /* Проверить, что записанные данные были прочитаны */

        if (buf1_written == buf1_avail)

            buf1_written = buf1_avail = 0;

        if (buf2_written == buf2_avail)

            buf2_written = buf2_avail = 0;

        /* Одна из сторон закрыла соединение, продолжать

            записывать, пока другая сторона не закончит */

        if (fd1 < 0 && buf1_avail - buf1_written == 0)

            SHUT_FD2;

        if (fd2 < 0 && buf2_avail - buf2_written == 0)

            SHUT_FD1;

    }

    exit(EXIT_SUCCESS);
}
```

Вышеприведенная программа правильно перенаправляет большинство данных задач, использующих соединения TCP, включая внепоточные (OOB) данные, передаваемые серверами telnet. Она справляется со сложной проблемой поддержания одновременного двустороннего обмена данными. Возможно, вы решите, что эффективнее использовать fork(2) и выделить отдельную нить для каждого потока. На самом деле это сложнее, чем кажется. Другой идеей может быть использование неблокирующего ввода-вывода с помощью fcntl(2). Это также может вызвать проблемы из-за того, что придётся использовать неэффективные таймауты.

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

## СМОТРИТЕ ТАКЖЕ
accept(2), connect(2), ioctl(2), poll(2), read(2), recv(2), select(2), send(2), sigprocmask(2), write(2), sigaddset(3), sigdelset(3), sigemptyset(3), sigfillset(3), sigismember(3), epoll(7)


semctl

package: man-pages-ru

title: semctl(2)

summary: операции управления семафорами System V

text:
# semctl(2)

## ИМЯ
semctl - операции управления семафорами System V

## ОБЗОР

```
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
```

```
int semctl(int semid, int semnum, int cmd, ...);
```

## ОПИСАНИЕ
Вызов semctl выполняет операцию, определённую в cmd, над набором семафоров System V, указанном в semid, или над семафором с номером semnum из этого набора (семафоры нумеруются, начиная с 0).

Данный вызов имеет три или четыре аргумента, в зависимости от значения cmd. Если аргументов четыре, то четвертый аргумент arg имеет тип union semun. В вызывающей программе это объединение должно быть определено следующим образом:

```
union semun {

    int              val;    /* значение для SETVAL */

    struct semid_ds *buf;    /* буфер для IPC_STAT, IPC_SET */

    unsigned short  *array;  /* массив для GETALL, SETALL */

    struct seminfo  *__buf;  /* буфер для IPC_INFO

                                (есть только в Linux) */
};
```

Структура данных semid_ds определена в <sys/sem.h> следующим образом:

```
struct semid_ds {

    struct ipc_perm sem_perm;  /* владелец и права */

    time_t          sem_otime; /* время последней операции semop */

    time_t          sem_ctime; /* время последнего изменения */

    unsigned long  sem_nsems; /* кол-во семафоров в наборе */
};
```

Структура ipc_perm определена следующим образом (значения полей устанавливаются с помощью IPC_SET):

```
struct ipc_perm {

    key_t          __key; /* ключ, передаваемый в semget(2) */

    uid_t          uid;   /* эффективный UID владельца */

    gid_t          gid;   /* эффективный GID владельца */

    uid_t          cuid;  /* эффективный UID создателя */

    gid_t          cgid;  /* эффективный GID создателя */

    unsigned short mode;  /* права */

    unsigned short __seq; /* порядковый номер */
};
```

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

IPC_STAT

Копирует информацию из структуры данных ядра, связанной с semid, в структуру semid_ds, расположенную по адресу arg.buf. Аргумент semnum игнорируется. Вызывающий процесс должен иметь права на чтение набора семафоров.

IPC_SET

Записывает значения некоторых полей структуры semid_ds, на которую указывает arg.buf, в структуру данных ядра, связанную с этим набором семафоров, также обновляя при этом поле sem_ctime. Обновляются следующие поля структуры: sem_perm.uid, sem_perm.gid и (младшие 9 значащих битов) sem_perm.mode. Эффективный UID вызывающего процесса должен совпадать с идентификатором владельца (sem_perm.uid) или создателя (sem_perm.cuid) набора семафоров, или вызывающий должен иметь расширенные права. Аргумент semnum игнорируется.

IPC_RMID

Немедленно удаляет набор семафоров, пробуждая все процессы, заблокированные в вызове semop(2) (при этом возвращается сообщение об ошибке, а errno присваивается значение EIDRM). Эффективный идентификатор пользователя вызывающего процесса должен совпадать с идентификатором создателя или владельца набора семафоров, или вызывающий должен иметь расширенные права. Аргумент semnum игнорируется.

IPC_INFO (есть только в Linux)

Возвращает параметры и информацию о системных ограничениях семафоров в структуре, указанной в arg.__buf. Данная структура имеет тип seminfo, который определён в <sys/sem.h>, если определён макрос тестирования свойств _GNU_SOURCE:

```
struct  seminfo {

    int semmap;  /* количество записей в карте

                    семафоров; не используется в ядре */

    int semmni;  /* максимальное количество наборов

                    семафоров */

    int semmns;  /* максимальное количество семафоров во

                     всех наборах семафоров */

    int semmnu;  /* максимальное количество структур undo

                    в системе; не используется в ядре */

    int semmsl;  /* максимальное количество семафоров в

                    наборе */

    int semopm;  /* максимальное количество операция для

                    semop(2) */

    int semume;  /* максимальное количество записей undo на

                    процесс; не используется в ядре */

    int semusz;  /* размер struct sem_undo */

    int semvmx;  /* максимальное значение семафора */

    int semaem;  /* максимальное значение, которое может

                    быть записано для регулирования

                    семафора (SEM_UNDO) */
};

```

Значения semmsl, semmns, semopm и semmni можно изменить через /proc/sys/kernel/sem; подробности в proc(5).

SEM_INFO (есть только в Linux)

Возвращает структуру seminfo, содержащую такую же информацию что и для IPC_INFO, за исключением того, что следующие поля содержат информацию о системных ресурсах, потребляемых семафорами: в поле semusz возвращается количество наборов семафоров, существующих в системе; в поле semaem возвращается общее количество семафоров во всех наборах семафоров в системе.

SEM_STAT (есть только в Linux)

Возвращает структуру semid_ds как для IPC_STAT. Однако аргумент semid содержит не идентификатор семафора, а индекс во внутреннем массиве ядра, который хранит информацию о всех наборах семафоров в системе.

SEM_STAT_ANY (есть только в Linux-specific, начиная с Linux 4.17)

Возвращает структуру seminfo, содержащую информацию как у SEM_STAT. Однако sem_perm.mode не проверяется на доступность чтения для semid, что означает, что эту операцию может выполнять пользователь (как и любой пользователь, который может прочитать эту же информацию из /proc/sysvipc/sem).

GETALL

Возвращает значение semval (т.е. текущее значение) всех семафоров в наборе в arg.array. Аргумент semnum игнорируется. Вызывающему процессу нужны права на чтение набора семафоров.

GETNCNT

Возвращает значение semncnt для semnum-того семафора (т.е., число процессов, ожидающих увеличения значения semval в semnum-ом семафоре набора). Вызывающему процессу нужны права на чтение набора семафоров.

GETPID

Возвращает значение sempid для semnum-того семафора набора. Это PID процесса, который последним выполнял операцию с этим семафором (но смотрите ЗАМЕЧАНИЯ). Вызывающему процессу нужны права на чтение набора семафоров.

GETVAL

Возвращает значение semval для semnum-того семафора набора. Вызывающему процессу нужны права на чтение набора семафоров.

GETZCNT

Возвращает значение semzcnt для semnum-того семафора (т.е., количество процессов, ожидающих, когда значение semval semnum-того семафора набора станет равным 0). Вызывающему процессу нужны права на чтение набора семафоров.

SETALL

Устанавливает значение semval всех семафоров набора, используя arg.array и изменяя также поле sem_ctime структуры semid_ds, связанной с набором. Записи undo (см. semop(2)) очищаются для изменённых семафоров во всех процессах. Если изменения значений семафоров приводят к отмене блокировки в вызове semop(2) других процессов, то эти процессы пробуждаются. Аргумент semnum игнорируется. Вызывающему процессу нужны права на запись в набор семафоров.

SETVAL

Устанавливает значение semval равным arg.val для semnum-го семафора набора, изменяя также поле sem_ctime в структуре semid_ds, связанной с этим набором. Записи undo очищаются для изменённых семафоров во всех процессах. Если изменения значений семафоров приводят к отмене блокировки в вызове semop(2) других процессов, то эти процессы пробуждаются. Аргумент semnum игнорируется. Вызывающему процессу нужны права на запись в набор семафоров.

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

При успешном выполнении системный вызов возвращает положительное значение, зависящее от cmd:

GETNCNT

значение semncnt.

GETPID

значение sempid.

GETVAL

значение semval.

GETZCNT

значение semzcnt.

IPC_INFO

самое большое значение индекса, использованного в записи внутреннего массива ядра, содержащего информацию о всех наборах семафоров (эта информация может использоваться в повторяющихся операциях SEM_STAT или SEM_STAT_ANY для получения информации о всех наборах семафоров в системе).

SEM_INFO

как для IPC_INFO.

SEM_STAT

идентификатор набора семафоров, индекс которого указан в semid.

SEM_STAT_ANY

как у SEM_STAT.

Для всех остальных значений cmd возвращается 0.

## ОШИБКИ
При ошибке errno присваиваются следующие значения:

EACCES

Аргумент cmd равен GETALL, GETPID, GETVAL, GETNCNT, GETZCNT, IPC_STAT, SEM_STAT, SEM_STAT_ANY, SETALL или SETVAL и вызывающий процесс не имеет требуемых прав на набор семафоров и не имеет мандата CAP_IPC_OWNER в пользовательском пространстве имён, которое управляет своим пространством имён IPC.

EFAULT

Адрес, указанный в arg.buf или arg.array, недоступен.

EIDRM

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

EINVAL

Неверное значение cmd или semid. Или: при операции SEM_STAT значение индекса, указанное в semid, ссылается на место в массиве, которое в данные момент не используется.

EPERM

Аргумент cmd имеет значение IPC_SET или IPC_RMID, но эффективный идентификатор пользователя вызывающего процесса не совпадает с ID создателя (указанного в sem_perm.cuid) или с ID владельца (указанного в sem_perm.uid) набора семафоров, и процесс не имеет мандата CAP_SYS_ADMIN.

ERANGE

Аргумент cmd имеет значение SETALL или SETVAL и значение, присваиваемое semval (для какого-то семафора в наборе), меньше нуля или больше, чем ограничение реализации SEMVMX.

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

В POSIX.1 указано, что поле sem_nsems структуры semid_ds имеет тип unsigned short, и это так на в большинстве других систем. Это было и в Linux 2.2 и более ранних версиях, но начиная с Linux 2.4 это поле имеет тип unsigned long.

## ЗАМЕЧАНИЯ
Включение файлов <sys/types.h> и <sys/ipc.h> не требуется в Linux или любых версий POSIX. Однако, некоторые старые реализации требуют включения данных заголовочных файлов, и это также требуется по SVID. В приложениях, которые нужно перенести на такие старые системы, может потребоваться включить данных заголовочные файлы.

Операции IPC_INFO, SEM_STAT и SEM_INFO используются программой ipcs(1) для получения информации о выделенных ресурсах. В будущем для этого может быть задействован интерфейс файловой системы /proc.

В Linux 2.2 различные поля struct semid_ds имели тип short. В Linux 2.4 тип был изменён на long. Для задействования преимуществ этого изменения необходима перекомпиляция программы с glibc-2.1.91 или более поздней версией (ядро различает старые и новые вызовы по флагу IPC_64 в аргументе cmd).

В некоторых ранних версиях glibc объединение semun определялось в <sys/sem.h>, но в POSIX.1 требовалось, чтобы это объединение определял вызывающий. В версиях glibc, в которых это объединение не определено, в <sys/sem.h> определён макрос _SEM_SEMUN_UNDEFINED.

На работу наборов семафоров и вызова semctl() влияет системное ограничение:

SEMVMX

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

Для лучшей переносимости программ желательно всегда вызывать semctl() c четырьмя аргументам.

### Значение sempid
В POSIX.1 значение sempid определено как «ID процесса последней операции» над семафором, и явно отмечено, что это значение устанавливается успешным вызовом semop(2), подразумевая, что больше никто не изменяет sempid.

Одни реализации следуют требованиям, указанным в POSIX.1, а другие нет (вина здесь, вероятно, лежит на POSIX.1, так как ему не удастся охватить поведение всех существующих реализаций). Также, разные реализации обновляют sempid другими операциями, обновляющими значение семафора: SETVAL и SETALL, а также изменение семафора, выполняемое при завершении процесса как следствие использования флага SEM_UNDO (смотрите semop(2)).

Linux также обновляет sempid при операциях SETVAL и регулировки семафора. Однако, непоследовательно, ранние версии Linux по 4.5 включительно не обновляли sempid при операциях SETALL. Это было исправлено в Linux 4.6.

## СМОТРИТЕ ТАКЖЕ
ipc(2), semget(2), semop(2), capabilities(7), sem_overview(7), sysvipc(7)


semget

title: semget(2)

text:
# semget(2)

## ИМЯ
semget - считывает идентификатор набора семафоров System V

## ОБЗОР

```
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
```

int semget(key_t key, int nsems, int semflg);

## ОПИСАНИЕ
Системный вызов semget() возвращает идентификатор набора семафоров System V, связанный со значением аргумента key. Его можно использовать для получения идентификатора уже созданного набора семафоров (если semflg равно нулю и значение key не равно IPC_PRIVATE) или для создания нового.

Новый набор семафоров nsems создаётся, если значение key равно IPC_PRIVATE или с ключом key не связано ни одного существующего набора семафоров, а в semflg задано IPC_CREAT.

Если в semflg одновременно указаны IPC_CREAT и IPC_EXCL и набор семафоров для key уже существует, то semget() завершается с ошибкой и errno присваивается значение EEXIST (такой же результат как с O_CREAT | O_EXCL у open(2)).

При создании в 9 начальных битах аргумента semflg указываются права (владелец, группа и др.) на набор семафоров. Формат значения битов совпадает с аргументом mode вызова open(2) (но права на выполнение для семафоров ничего не означают, а права на запись означают право изменять значения семафоров).

При создании нового набора семафоров semget() инициализирует связанную с семафором структуру данных semid_ds (см. semctl(2)) следующим образом:

Полям sem_perm.cuid и sem_perm.uid присваиваются значения эффективного идентификатора пользователя вызывающего процесса.

Полям sem_perm.cgid и sem_perm.gid присваиваются значения эффективного идентификатора группы вызывающего процесса.

Младшим 9 битам sem_perm.mode присваивается значение младших 9 бит semflg.

Полю sem_nsems присваивается значение nsems.

Полю sem_otime присваивается значение 0.

Полю sem_ctime присваивается значение текущего времени.

Если набор семафоров не создаётся, то аргумент nsems может быть равен 0 (не учитывать). Иначе аргумент nsems должен быть больше 0 и меньше или равен максимальному количеству семафоров в наборе (SEMMSL).

Если набор семафоров уже существует, то проверяются права доступа.

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

## ОШИБКИ
При ошибке errno присваиваются следующие значения:

EACCES

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

EEXIST

В semflg указаны IPC_CREAT и IPC_EXCL, но набор семафоров уже существует для key.

EINVAL

Значение nsems меньше 0 или больше максимально возможного для набора количества семафоров (SEMMSL).

EINVAL

Набор семафоров, соответствующий key, уже существует, но nsems больше, чем количество семафоров в этом наборе.

ENOENT

Для ключа key не существует набора семафоров и в semflg нет флага IPC_CREAT.

ENOMEM

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

ENOSPC

Набор семафоров должен быть создан, но при этом будет превышен системный лимит на количество наборов семафоров (SEMMNI) или системный лимит на количество семафоров (SEMMNS).

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

## ЗАМЕЧАНИЯ
Включение файлов <sys/types.h> и <sys/ipc.h> не требуется в Linux или любых версий POSIX. Однако, некоторые старые реализации требуют включения данных заголовочных файлов, и это также требуется по SVID. В приложениях, которые нужно перенести на такие старые системы, может потребоваться включить данных заголовочные файлы.

Значение IPC_PRIVATE не поле флага, а тип key_t. Если key равно этому специальному значению, то системный вызов игнорирует всё кроме 9-ти младших битов semflg и создаёт новый набор семафоров (при успешном выполнении).

### Инициализация семафоров
Значения только что созданных семафоров не определены (в POSIX.1-2001 и POSIX.1-2008 это указано явно, хотя в POSIX.1-2008 сказано, что в будущих версиях стандарта от реализации может потребоваться инициализировать семафоры 0). Хотя Linux, подобно многим другим реализациям, инициализирует семафоры значением 0, переносимое приложение не может полагаться на это: оно должно явно инициализировать семафоры нужным значением.

Инициализацию можно выполнять с помощью semctl(2) с операцией SETVAL или SETALL. При наличии нескольких абонентов неизвестно, кто первый выполнит инициализацию, и чтобы избежать состязательности нужно проверять sem_otime на ненулевое значение в связанной структуре данных, получаемой с помощью semctl(2) и операции IPC_STAT.

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

SEMMNI

Ограничение на максимальное количество наборов семафоров. В системах Linux до версии 3.19, значением по умолчанию этого ограничения было 128. Начиная с Linux 3.19, значение по умолчанию равно 32000. В Linux это ограничение можно получать и изменять через четвёртое поле /proc/sys/kernel/sem).

SEMMSL

Максимальное количество семафоров на один ID семафора. В системах Linux до версии 3.19, значением по умолчанию этого ограничения было 250. Начиная с Linux 3.19, значение по умолчанию равно 32000. В Linux это ограничение можно получать и изменять через первое поле /proc/sys/kernel/sem).

SEMMNS

Ограничение на максимальное количество семафоров в системе: зависит от политики (в Linux это ограничение можно получать и изменять через второе поле /proc/sys/kernel/sem). Заметим, что количество семафоров в системе также ограничено произведением SEMMSL и SEMMNI.

## ДЕФЕКТЫ
Имя IPC_PRIVATE, возможно, было выбрано неудачно, IPC_NEW отражает смысл действия более ясно.

## СМОТРИТЕ ТАКЖЕ
semctl(2), semop(2), ftok(3), capabilities(7), sem_overview(7), sysvipc(7)


semop

title: semop(2)

summary: операции с семафорами System V

text:
# semop(2)

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

## ОБЗОР

```
#include <sys/types.h>
#include <sys/ipc.h>
#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 *timeout);
```

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

semtimedop(): _GNU_SOURCE

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

```
unsigned short  semval;   /* значение семафора */
unsigned short  semzcnt;  /* # ожидает ноль */
unsigned short  semncnt;  /* # ожидает увеличения */
pid_t           sempid;   /* PID процесса, выполнявшегося последним
```

Вызов 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.

Если значение sem_op — положительное целое число, то оно добавляется к значению семафора (semval). Если для операции стоит флаг SEM_UNDO, то система вычитает значение sem_op из значения регулировки (semadj) семафора. Эта операция выполняется всегда и не переводит нить в режим ожидания. Вызывающий процесс должен иметь права на изменение набора семафоров.

Если значение 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 не изменяется.

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

## ОШИБКИ
В случае возникновения ошибки errno может принимать следующие значения:

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() впервые появился в Linux 2.5.52, а затем был перенесён в ядро версии 2.4.22. Поддержка в glibc для semtimedop() впервые появилась в версии 2.3.3.

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

## ЗАМЕЧАНИЯ
Включение файлов <sys/types.h> и <sys/ipc.h> не требуется в Linux или любых версий POSIX. Однако, некоторые старые реализации требуют включения данных заголовочных файлов, и это также требуется по SVID. В приложениях, которые нужно перенести на такие старые системы, может потребоваться включить данных заголовочные файлы.

Структуры процесса sem_undo не наследуются потомками, созданными через fork(2), но они наследуются при выполнении системного вызова execve(2).

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

Значение регулировки семафора (semadj) есть в каждом процессе. Это целое число — простой (отрицательный) счётчик всех операций над семафорами, для которых установлен флаг SEM_UNDO. В каждом процессе есть список значений semadj — по одному значению на каждый семафор, у которых установлен флаг SEM_UNDO. При завершении процесса. каждое из значений semadj семафора добавляется к соответствующему семафору, достигая таким образом эффекта выполнения операций процесса над семафорами (но смотрите раздел ДЕФЕКТЫ). Когда значение семафора явно устанавливается с помощью запроса SETVAL или SETALL вызовом semctl(2), то соответствующие значения semadj во всех процессах очищаются. Флаг CLONE_SYSVSEM clone(2) позволяет нескольким процессам совместно использовать список semadj; подробности смотрите в clone(2).

Значения 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 используется третий вариант: уменьшение значения семафора до тех пор, пока это возможно ( т.е. до нуля) и разрешение немедленного завершения процесса.

В ядрах 2.6.x, где x <= 10, есть дефект, из-за которого при определённых обстоятельствах нить, ожидающая установления значения семафора равного нулю, не будет разбужен когда значение станет равным нулю. Этот дефект исправлен в ядре 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);
}
```

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


send

title: send(2)

summary: отправляет сообщения в сокет

text:
# send(2)

## ИМЯ
send, sendto, sendmsg - отправляет сообщения в сокет

## ОБЗОР

```
#include <sys/types.h>
#include <sys/socket.h>
```

```
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
```

```
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
               const struct sockaddr *dest_addr, socklen_t addrlen);
```

```
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
```

## ОПИСАНИЕ
Системные вызовы send(), sendto() и sendmsg() используются для пересылки сообщений в другой сокет.

Вызов send() можно использовать, только если сокет находится в состоянии соединения (то есть известен получатель). Вызов send() отличается от write(2) только наличием аргумента flags. Если значение flags равно нулю, то вызов send() эквивалентен write(2). Также, вызов

send(sockfd, buf, len, flags);

эквивалентен

sendto(sockfd, buf, len, flags, NULL, 0);

Аргумент sockfd представляет файловый дескриптор сокета отправления.

Если sendto() используется с сокетом в режиме с установлением соединения (SOCK_STREAM, SOCK_SEQPACKET), то аргументы dest_addr и addrlen игнорируются (и может быть возвращена ошибка EISCONN, если их значения не равны NULL и 0) и возвращается ошибка ENOTCONN, если соединение через сокет не установлено. Иначе в dest_addr задаётся адрес назначения и его размер в addrlen. Для sendmsg() адрес назначения указывается в msg.msg_name, а его размер в msg.msg_namelen.

У send() и sendto() сообщение находится в buf, а его длина в len. У sendmsg() сообщение указывается в элементах массива msg.msg_iov. Вызов sendmsg() также позволяет отправлять вспомогательные данные (так называемую управляющую информацию).

Если сообщение слишком длинно для передачи за раз через используемый нижележащий протокол, то возвращается ошибка EMSGSIZE и сообщение не передаётся.

Неудачная отправка с помощью send() никак не отмечается. При обнаружении локальных ошибок возвращается значение -1.

Когда сообщение не помещается в буфер отправки сокета, выполнение блокируется в send(), если сокет не находится в неблокирующем режиме. Если сокет находится в неблокирующем режиме, то возвращается ошибка EAGAIN или EWOULDBLOCK. Для выяснения, возможна ли отправка данных, можно использовать вызов select(2).

### Аргумент флагов
Аргумент flags является битовой маской и может содержать следующие флаги:

MSG_CONFIRM (начиная с Linux 2.3.15)

Сообщить уровню связи, что процесс пересылки произошел: вы получили успешный ответ с другой стороны. Если уровень связи не получит его, то он будет регулярно перепроверять наличие ответной стороны (например посредством однонаправленной передачи ARP). Это работает только с сокетами SOCK_DGRAM и SOCK_RAW и в настоящее время реализовано только для IPv4 и IPv6. В arp(7) представлена более подробная информация.

MSG_DONTROUTE

Не использовать маршрутизацию для отправки пакета, а посылать его только на узлы локальной сети. Обычно это используется в диагностических программах и программах маршрутизации. Этот флаг определён только для маршрутизируемых семейств протоколов; пакетные сокеты не используют маршрутизацию.

MSG_DONTWAIT (начиная с Linux 2.2)

Включить неблокирующий режим. Если операция могла бы привести к блокировке, возвращается EAGAIN или EWOULDBLOCK. Такое поведение подобно заданию флага O_NONBLOCK (в fcntl(2) операцией F_SETFL), но отличие в том, что MSG_DONTWAIT указывается в вызове, а O_NONBLOCK задаётся в описании открытого файла (смотрите open(2)), что влияет на все нити вызывающего процесса, а также на другие процессы, у которых есть файловые дескрипторы, ссылающиеся на это описание открытого файла.

MSG_EOR (начиная с Linux 2.2)

Завершить запись (record) (если поддерживается, например в сокетах типа SOCK_SEQPACKET).

MSG_MORE (начиная с Linux 2.4.4)

Вызывающий имеет дополнительные данные для отправки. Этот флаг используется с сокетами TCP для получения такого же эффекта как с параметром сокета TCP_CORK (см. tcp(7)), с той разницей, что этот флаг можно устанавливать при каждом вызове.

Начиная с Linux 2.6 этот флаг также поддерживается для сокетов UDP и информирует ядро, о том что нужно упаковать все отправляемые данные вызовов с этим флагом в одну дейтаграмму, которая передаётся только когда выполняется вызов без указания этого флага (смотрите также описание параметра сокета UDP_CORK в udp(7)).

MSG_NOSIGNAL (начиная с Linux 2.2)

Не генерировать сигнал SIGPIPE, если сторона потокоориентированного сокета закрыла соединение. Ошибка EPIPE по прежнему возвращается. Это создаёт поведение как при использовании sigaction(2) для игнорирования SIGPIPE, но MSG_NOSIGNAL является свойством вызова, а установка SIGPIPE в атрибутах процесса влияет на все нити процесса.

MSG_OOB

Послать внепоточные данные, если сокет это поддерживает (как, например, сокеты типа SOCK_STREAM); протокол более низкого уровня также должен поддерживать внепоточные данные.

### sendmsg()
Определение структуры msghdr, используемой sendmsg():

```
struct msghdr {

    void         *msg_name;       /* необязательный адрес */

    socklen_t     msg_namelen;    /* размер адреса */

    struct iovec *msg_iov;        /* массив приёма/передачи */

    size_t        msg_iovlen;     /* # количество элементов в msg_iov */

    void         *msg_control;    /* вспомогательные данные,

                                     смотрите ниже */

    size_t        msg_controllen; /* размер буфера вспомогательных

                                     данных */

    int           msg_flags;      /* флаги (не используется) */
};
```

Поле msg_name используется на неподключённом сокете для указания адреса назначения дейтаграммы. Оно указывает на буфер с адресом; в поле msg_namelen должен быть указан размер адреса. Для подключённого сокета значения этих полей должны быть равны NULL и 0, соответственно.

В полях msg_iov и msg_iovlen задаются места приёма/передачи, как для writev(2).

Управляющую информацию можно посылать через поля msg_control и msg_controllen. Максимальная длина управляющего буфера, которую поддерживает ядро, ограничена значением /proc/sys/net/core/optmem_max; см. socket(7).

Поле msg_flags игнорируется.

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

## ОШИБКИ
Здесь представлено несколько стандартных ошибок, возвращаемых с уровня сокетов. Могут также появиться другие ошибки, возвращаемые из соответствующих модулей протоколов; их описание находится в соответствующих справочных страницах.

EACCES

(для доменных сокетов UNIX, которые идентифицируются по имени пути) Нет прав на запись в файл сокета назначения или в одном из каталогов пути запрещён поиск (см. также path_resolution(7)).

(для сокетов UDP) Попытка отправки по сетевому/широковещательному адресу, как будто это был однозначный (unicast) адрес.

EAGAIN или EWOULDBLOCK

Сокет помечен как неблокирующий, но запрошенная операция привела бы к блокировке. POSIX.1-2001 допускает в этих случаях возврат ошибки и не требует, чтобы эти константы имели одинаковое значение, поэтому переносимое приложение должно проверять обе возможности.

EAGAIN

(доменные датаграммные сокеты Интернета) Сокет, указанный sockfd, ранее не был привязан к адресу и при попытке привязать его к эфемеридному порту, было определено, что все номера в диапазоне эфемеридных портов уже используются. Смотрите обсуждение /proc/sys/net/ipv4/ip_local_port_range в ip(7).

EALREADY

Сейчас выполняется другая операция Fast Open.

EBADF

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

ECONNRESET

Соединение сброшено другой стороной.

EDESTADDRREQ

Сокет в режиме без установления соединения и адрес второй стороны не задан.

EFAULT

В аргументе указано неверное значение адреса пользовательского пространства.

EINTR

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

EINVAL

Передан неверный аргумент.

EISCONN

Сокет в режиме с установлением соединения уже выполнил подключение, но указан получатель (теперь или возвращается эта ошибка, или игнорируется указание получателя).

EMSGSIZE

Для типа сокета требуется, чтобы сообщение было отослано за время одной операции (атомарно), а размер сообщения не позволяет этого.

ENOBUFS

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

ENOMEM

Больше нет доступной памяти.

ENOTCONN

Сокет не подключён и назначение не задано.

ENOTSOCK

Файловый дескриптор sockfd указывает не на каталог.

EOPNOTSUPP

Один из битов в аргументе flags не может устанавливаться для этого типа сокета.

EPIPE

Локальный сокет, ориентированный на соединение, был закрыт. В этом случае процесс также получит сигнал SIGPIPE, если не установлен флаг MSG_NOSIGNAL.

## СООТВЕТСТВИЕ СТАНДАРТАМ
4.4BSD, SVr4, POSIX.1-2001. Эти интерфейсы впервые появились в 4.2BSD.

В POSIX.1-2001 описаны только флаги MSG_OOB и MSG_EOR. В POSIX.1-2008 добавлено описание MSG_NOSIGNAL. Флаг MSG_CONFIRM является нестандартным расширением Linux.

## ЗАМЕЧАНИЯ
В соответствие с POSIX.1-2001 поле msg_controllen структуры msghdr должно иметь тип socklen_t, но в настоящее время в glibc оно имеет тип size_t.

В sendmmsg(2) можно найти информацию о специальном системном вызове Linux, который можно использовать для передачи нескольких дейтаграмм за один вызов.

## ДЕФЕКТЫ
Linux может вернуть EPIPE вместо ENOTCONN.

## ПРИМЕР
Пример использования sendto() показан в getaddrinfo(3).

## СМОТРИТЕ ТАКЖЕ
fcntl(2), getsockopt(2), recv(2), select(2), sendfile(2), sendmmsg(2), shutdown(2), socket(2), write(2), cmsg(3), ip(7), ipv6(7), socket(7), tcp(7), udp(7), unix(7)


sendfile

title: sendfile(2)

text:
# sendfile(2)

## ИМЯ
sendfile - производит обмен данными между дескрипторами файлов

## ОБЗОР
\#include <sys/sendfile.h>

ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

## ОПИСАНИЕ
Вызов sendfile() копирует данные из одного файлового дескриптора в другой. Так как копирование производится в ядре, использование sendfile() более эффективно чем комбинация read(2) и write(2), в которой требуется скопировать данные в и из пользовательского пространства.

В in_fd должен указываться файловый дескриптор, открытый для чтения, а в out_fd должен указываться файловый дескриптор, открытый для записи.

Если значение offset не равно NULL, то оно указывает на переменную, содержащую файловое смещение с которого sendfile() начнёт чтение данных из in_fd. При завершении sendfile() значение этой переменной будет содержать смещение следующего байта после последнего прочитанного. Если значение offset не равно NULL, то sendfile() не изменяет файловое смещение in_fd; иначе файловое смещение изменяется для отражения количества прочитанных из in_fd байт.

Если значение offset равно NULL, то данные будут прочитаны из in_fd начиная с файлового смещения, и по окончании работы вызова файловое смещение будет обновлено.

В count содержится количество байт, копируемых между файловыми дескрипторами.

Значение in_fd должно описывать файл, который поддерживает операции типа mmap(2) (т.е., не сокет).

В ядрах Linux до версии 2.6.33, значение out_fd должно указывать на сокет. Начиная с Linux 2.6.33 можно указывать любой файл. Если это обычный файл, то sendfile() изменит файловое смещение соответствующим образом.

## ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
Если пересылка прошла успешно, то возвращается количество записанных в out_fd байт. Заметим, что при успешном выполнении вызов sendfile() может записать меньше байт, чем запрашивалось; вызывающий должен быть готов повторить вызов, если останутся не отправленные байты. Также смотрите ЗАМЕЧАНИЯ.

В случае ошибки возвращается -1 и значение errno устанавливается соответствующим образом.

## ОШИБКИ
EAGAIN

Был выбран неблокирующий ввод-вывод с помощью O_NONBLOCK, но запись привела бы блокировке.

EBADF

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

EFAULT

Неправильный адрес.

EINVAL

Неправильный или заблокированный дескриптор, или для in_fd недоступна операция типа mmap(2) или значение count отрицательно.

EINVAL

У out_fd установлен флаг O_APPEND. Пока это не поддерживается в sendfile().

EIO

Неизвестная ошибка при чтении in_fd.

ENOMEM

Не хватает памяти для чтения in_fd.

EOVERFLOW

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

ESPIPE

Значение offset не равно NULL, но для входного файла не работает seek(2).

## ВЕРСИИ
Вызов sendfile() впервые появился в Linux 2.2. Файл заголовков <sys/sendfile.h> появился в glibc 2.1.

## СООТВЕТСТВИЕ СТАНДАРТАМ
Отсутствует в POSIX.1-2001 и других стандартах.

В других системах UNIX вызов sendfile() реализован с другими семантиками и прототипами. Не должен использоваться в переносимых программах.

## ЗАМЕЧАНИЯ
Вызов sendfile() передаст не больше 0x7ffff000 (2 147 479 552) байт, возвращая число байт, переданных на самом деле (это утверждение справедливо как к 32-битным, так и к 64-битным системам).

Если вы планируете использовать sendfile() для отправки файлов через сокет TCP и вам нужно послать некоторые заголовочные данные перед содержимым файла, то обратите внимание на параметр TCP_CORK, описанный в tcp(7), он поможет минимизировать количество пакетов и оптимизировать производительность.

В Linux 2.4 и более ранних, значение out_fd может также указывать на обычный файл; эта возможность была удалена в ядрах Linux 2.6.x, но возвращена в 2.6.33.

Первоначальная версия Linux sendfile() не была приспособлена для работы с большими файловыми смещениями. В последствии в Linux 2.4 был добавлен вызов sendfile64() с более широким диапазоном значений аргумента offset. В glibc sendfile() представляет собой обёрточную функцию, которая делает незаметным разницу между версиями ядер.

Приложение может попытаться воспользоваться read(2)/write(2), если вызов sendfile() завершится с ошибкой EINVAL или ENOSYS.

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

Специальный вызов Linux splice(2) поддерживает пересылку данных между произвольными файловыми дескрипторами, представляющими сокеты (один или оба).

## СМОТРИТЕ ТАКЖЕ
copy_file_range(2), mmap(2), open(2), socket(2), splice(2)


sendmmsg

title: sendmmsg(2)

summary: отправляет несколько сообщений в сокет

text:
# sendmmsg(2)

## ИМЯ
sendmmsg - отправляет несколько сообщений в сокет

## ОБЗОР

```
#define _GNU_SOURCE         /* Смотрите feature_test_macros(7) */
#include <sys/socket.h>
```

```
int sendmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen,
             int flags);
```

## ОПИСАНИЕ
Системный вызов sendmmsg() является расширенной версией sendmsg(2), позволяя вызывающему передавать несколько сообщений из сокета, используя только один системный вызов (в некоторых приложениях это позволяет получить выигрыш в производительности).

Аргумент sockfd представляет собой файловый дескриптор сокета для отправки данных.

Аргумент msgvec является указателем на массив структур mmsghdr. Размер этого массива указывается в vlen.

Структура mmsghdr определена в <sys/socket.h> следующим образом:

```
struct mmsghdr {

    struct msghdr msg_hdr;  /* заголовок сообщения */

    unsigned int  msg_len;  /* кол-во переданных байт */
};
```

Поле msg_hdr представляет собой структуру msghdr, которая описана в sendmsg(2). Поле msg_len используется для возврата количества байт, посланных из сообщения в msg_hdr (т. е., такое же значение, что и возвращаемое значение одиночного вызова sendmsg(2)).

Аргумент flags содержит объединённые с помощью OR флаги. Флаги те же, что и у sendmsg(2).

Блокирование вызова sendmmsg() происходит до тех пор, пока не будет отправлено vlen сообщений. Неблокирующий вызов посылает столько сообщений сколько возможно (максимальное количество указано в vlen) и сразу завершает работу.

При возврате из sendmmsg(), поля msg_len последующих элементов msgvec обновляются и содержат количество байт, переданных из соответствующего msg_hdr. Возвращаемое вызовом значение равно количеству элементов msgvec, которые были обновлены.

## ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном выполнении sendmmsg() возвращает количество сообщений, посланных из msgvec; если это значение меньше чем vlen, то вызывающий может повторить вызов sendmmsg() для отправки оставшихся сообщений.

В случае ошибки возвращается -1, а errno устанавливается в значение ошибки.

## ОШИБКИ
Возникают те же ошибки что и для sendmsg(2). Ошибка возвращается только, если ни одной дейтаграммы не послано. Смотрите также ДЕФЕКТЫ.

## ВЕРСИИ
Системный вызов sendmmsg() был добавлен в ядро Linux версии 3.0. Поддержка в glibc появилась в версии 2.14.

## СООТВЕТСТВИЕ СТАНДАРТАМ
Вызов sendmmsg() есть только в Linux.

## ЗАМЕЧАНИЯ
Значение, указанное в vlen, ограничено UIO_MAXIOV (1024).

## ДЕФЕКТЫ
Если после отправки хотя бы одного сообщения произошла ошибка, то вызов завершается успешно и возвращается количество отправленных сообщений. Код ошибки теряется. Вызывающий может повторить передачу, начиная с первого ошибочного сообщения, но нет гарантии, что возвращаемый код ошибки будет совпадать с потерянным в предыдущем вызове.

## ПРИМЕР
В примере далее sendmmsg() используется для отправки onetwo и three в двух разных дейтаграммах UDP за один системный вызов. Содержимое первой дейтаграммы составляется из пары буферов.

```
#define _GNU_SOURCE
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
int
main(void)
{

    int sockfd;

    struct sockaddr_in addr;

    struct mmsghdr msg[2];

    struct iovec msg1[2], msg2;

    int retval;

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    if (sockfd == -1) {

        perror("socket()");

        exit(EXIT_FAILURE);

    }

    addr.sin_family = AF_INET;

    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);

    addr.sin_port = htons(1234);

    if (connect(sockfd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {

        perror("connect()");

        exit(EXIT_FAILURE);

    }

    memset(msg1, 0, sizeof(msg1));

    msg1[0].iov_base = "one";

    msg1[0].iov_len = 3;

    msg1[1].iov_base = "two";

    msg1[1].iov_len = 3;

    memset(&msg2, 0, sizeof(msg2));

    msg2.iov_base = "three";

    msg2.iov_len = 5;

    memset(msg, 0, sizeof(msg));

    msg[0].msg_hdr.msg_iov = msg1;

    msg[0].msg_hdr.msg_iovlen = 2;

    msg[1].msg_hdr.msg_iov = &msg2;

    msg[1].msg_hdr.msg_iovlen = 1;

    retval = sendmmsg(sockfd, msg, 2, 0);

    if (retval == -1)

        perror("sendmmsg()");

    else

        printf("%d сообщений послано\n", retval);

    exit(0);
}
```

## СМОТРИТЕ ТАКЖЕ
recvmmsg(2), sendmsg(2), socket(2), socket(7)


title: set_mempolicy(2)

text:
# set_mempolicy(2)

## ИМЯ
set_mempolicy - настраивает политику работы с памятью NUMA для потока и его потомков

## ОБЗОР

```
#include <numaif.h>
```

```
long set_mempolicy(int mode, const unsigned long *nodemask,
                   unsigned long maxnode);
```

```
Компонуется при указании параметра -lnuma.
```

## ОПИСАНИЕ
Вызов set_mempolicy() устанавливает политику работы с памятью NUMA для вызывающей нити, которая определяется режимом политики и нулём или более узлами (задаются в аргументах mode, nodemask и maxnode).

Машина с NUMA имеет различные контроллеры памяти с различными расстояниями до определённых ЦП. Политикой памяти задаётся узел, на котором выделяется память для нити.

Этот системный вызов устанавливает в нити политику по умолчанию. Политика нити управляет выделением страниц в адресном пространстве процесса из областей памяти, которые не контролируются специальной политикой, заданной с помощью mbind(2). Политикой нити по умолчанию также контролируется выделение любых страниц для отображаемых в память файлов с помощью вызова mmap(2) с флагом MAP_PRIVATE (которые читаются (загружаются) только этой нитью) и для отображаемых в память файлов с помощью вызова mmap(2) с флагом MAP_SHARED (независимо от типа доступа). Эта политика применяется только когда нить запрашивает выделение новой страницы. Для анонимной памяти это применяется, когда страница впервые затрагивается приложением.

В аргументе mode должно быть указано одно из следующих значений: MPOL_DEFAULT, MPOL_BIND, MPOL_INTERLEAVE, MPOL_PREFERRED или MPOL_LOCAL (описано далее). Для всех режимов, за исключением MPOL_DEFAULT, требуется, чтобы вызывающий указывал в аргументе nodemask узел или узлы, для которых применяется режим.

Аргумент mode также может содержать необязательный флаг режима. Поддерживаемые флаги режима:

MPOL_F_STATIC_NODES (начиная с Linux 2.6.26)

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

MPOL_F_RELATIVE_NODES (начиная с Linux 2.6.26)

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

В nodemask указывается битовая маска идентификаторов узлов, которая может содержать до maxnode бит. Размер битовой маски округляется до следующего значение кратного sizeof(unsigned long), но ядро будет использовать биты только до maxnode. Значение NULL в nodemask или maxnode указывает на пустой набор узлов. Если значение maxnode равно нулю, то аргумент nodemask игнорируется.

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

Аргумент mode должен включать одно из следующих значений:

MPOL_DEFAULT

Данный режим устанавливает, что любые политики памяти нити, не равные политике по умолчанию, будут удалены, и политика памяти «вернётся» к системной политике по умолчанию. Системная политика по умолчанию — это «локальное выделение», то есть выделение памяти на узле с ЦП, на котором возник запрос выделения Значение nodemask должно быть равно NULL. Если на «локальном узле» нет свободной памяти, то система попытается выделить память на «ближайшем» узле.

MPOL_BIND

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

MPOL_INTERLEAVE

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

MPOL_PREFERRED

В этом режиме задаётся предпочтительный узел для выделения памяти. Ядро будет пытаться сначала выделить страницы на этом узле и только после того как на предпочтительном узле не хватит памяти, попробует использовать «ближайшие» узлы. Если в nodemask указано более одного идентификатора узла, то в качестве предпочтительного будет использован первый узел из маски. Если в аргументах nodemask и maxnode указан пустой набор, то политике предписывается использовать «локальное выделение» (как при системной политике по умолчанию, описанной ранее).

MPOL_LOCAL (начиная с Linux 3.8)

Этот режим задаёт «локальное выделение» (local allocation); память, выделяется на узле ЦП, попавшем в выделение («локальный узел»). В аргументах nodemask и maxnode должен быть указан пустой набор. Если на «локальном узле» мало свободной памяти, то ядро будет пытаться выделить память на других узлах. Ядро будет выделять память на «локальном узле» сразу, как только на этом узле освободится память. Если «локальный узел» не разрешён в текущем контексте набора ЦП процесса, то ядро будет пытаться выделить памяти на других узлах. Ядро начнёт выделять память на «локальном узле» сразу, как только это будет разрешено в текущем контексте набора ЦП процесса.

Политика памяти нити сохраняется при вызове execve(2) и наследуется дочерними нитями, созданными с помощью fork(2) или clone(2).

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

## ОШИБКИ
EFAULT

Часть всего диапазона памяти, заданная в nodemask и maxnode, указывает за пределы доступного адресного пространства.

EINVAL

Некорректное значение mode. Или значение mode равно MPOL_DEFAULT и значение nodemask не пусто, или mode равно MPOL_BIND или MPOL_INTERLEAVE и значение nodemask пусто. Или значение maxnode больше чем количество значимых бит страницы. Или в nodemask указан один или более идентификаторов узлов, значения которых больше чем максимальный поддерживаемый идентификатор узла. Или ни один из идентификаторов узлов, указанных в nodemask, не работает или не разрешены для текущего набора контекста процесса, или ни один из узлов не содержит памяти. Или в аргументе mode указаны MPOL_F_STATIC_NODES и MPOL_F_RELATIVE_NODES одновременно.

ENOMEM

Недостаточное количество памяти ядра.

## ВЕРСИИ
Системный вызов set_mempolicy() был добавлен в ядро Linux версии 2.6.7.

## СООТВЕТСТВИЕ СТАНДАРТАМ
Данный вызов есть только в Linux.

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

Информация о библиотеке доступна в numa(7).

## СМОТРИТЕ ТАКЖЕ
get_mempolicy(2), getcpu(2), mbind(2), mmap(2), numa(3), cpuset(7), numa(7), numactl(8)


title: set_thread_area(2)

text:
# set_thread_area(2)

## ИМЯ
get_thread_area, set_thread_area - управляют информацией области локального хранилища нити

## ОБЗОР

```
#include <linux/unistd.h>
#if defined __i386__ || defined __x86_64__
# include <asm/ldt.h>
```

```
int get_thread_area(struct user_desc *u_info);
int set_thread_area(struct user_desc *u_info);
```

```
#elif defined __m68k__
```

```
int get_thread_area(void);
int set_thread_area(unsigned long tp);
```

```
#elif defined __mips__
```

```
int set_thread_area(unsigned long addr);
```

```
#endif
```

Замечание: В glibc нет обёрточных функций для этих системных вызовов; смотрите ЗАМЕЧАНИЯ.

## ОПИСАНИЕ
Эти вызовы предоставляют зависимую от архитектуры реализацию поддержки информации области локального хранилища нити. В настоящее время вызов set_thread_area() доступен для m68k, MIPS и x86 (32-битный и 64-битный вариант); get_thread_area() доступен для m68k и x86.

Для m68k и MIPS, set_thread_area() позволяет сохранить произвольный указатель (указанный в аргументе tp на m68k и в аргументе addr на MIPS) структуре данных ядра, связанной с вызывающей нитью; этот указатель позднее может быть получен с помощью get_thread_area() (информацию о получении указателя нити на MIPS также смотрите ЗАМЕЧАНИЯ).

На x86 в Linux под локальное хранилище нити отдано три элемента глобальной таблицы дескрипторов (GDT). Подробней о GDT читайте в Intel Software Developer's Manual или AMD Architecture Programming Manual.

Этим системным вызовам передаётся указатель на структуру вида:

```
struct user_desc {

    unsigned int  entry_number;

    unsigned long base_addr;

    unsigned int  limit;

    unsigned int  seg_32bit:1;

    unsigned int  contents:2;

    unsigned int  read_exec_only:1;

    unsigned int  limit_in_pages:1;

    unsigned int  seg_not_present:1;

    unsigned int  useable:1;
#ifdef __x86_64__

    unsigned int  lm:1;
#endif
};
```

Вызов get_thread_area() читает элемент GDT, указанный в u_info->entry_number и заполняет оставшиеся поля в u_info.

Вызов set_thread_area() изменяет элемент TLS в GDT.

Элемент массива TLS, устанавливаемый set_thread_area(), соответствует значению u_info->entry_number, которое передал пользователь. Если это значение находится в допустимых пределах, то set_thread_area() записывает дескриптор TLS, на который указывает u_info, в массив TLS нити.

Когда set_thread_area() передаётся entry_number со значением -1, то ищется свободный элемент TLS. Если set_thread_area() находит свободный элемент TLS, то значение u_info->entry_number устанавливается после возврата для показа того, какой же элемент был изменён.

Структура user_desc считается «пустой», если read_exec_only и seg_not_present равны 1, а все остальные поля равны 0. Если «пустой» дескриптор передаётся в set_thread_area(), то соответствующий элемент TLS будет очищен. Дополнительную информацию смотрите в разделе ДЕФЕКТЫ.

Начиная с Linux 3.19, set_thread_area() нельзя использовать для записи отсутствующих сегментов, 16-битных сегментов или сегментов кода, но допускается очистка таких сегментов.

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

На MIPS и m68k вызов set_thread_area() всегда возвращает 0. На m68k вызов get_thread_area() возвращает значение указателя области нити (установленный ране с помощью set_thread_area()).

## ОШИБКИ
EFAULT

u_info является некорректным указателем.

EINVAL

u_info->entry_number вне допустимых границ.

ENOSYS

Вызов get_thread_area() или set_thread_area() был вызван как 64-битный системный вызов.

ESRCH

(set_thread_area()) Невозможно найти свободный элемент TLS.

## ВЕРСИИ
Вызов set_thread_area() появился в версии 2.5.29. Вызов get_thread_area() появился в Linux 2.5.32.

## СООТВЕТСТВИЕ СТАНДАРТАМ
Вызовы set_thread_area() и get_thread_area() есть только в Linux, и они не должны использоваться в переносимых программах.

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

На x86 вызов arch_prctl(2) может влиять на set_thread_area(). Подробней смотрите в arch_prctl(2). Обычно это не вызывает проблем, так как arch_prctl(2) обычно используется только в 64-битных программах.

На MIPS текущее значение указателя области нити можно получить с помощью инструкции:

```
rdhwr dest, $29
```

Эта инструкция ловится и обрабатывается ядром.

## ДЕФЕКТЫ
В 64-битных ядрах до Linux 3.19, если был установлен один из битов заполнения в user_desc, то это приводило к тому, что дескриптор не считался пустым (смотрите modify_ldt(2)). В результате, единственным надёжным способом очистить элемент TLS было задействование memset(3) для обнуления всей структуры user_desc, включая биты заполнения, и затем установка битов read_exec_only и seg_not_present. В Linux 3.19, структура user_desc, полностью состоящая из нулей кроме entry_number, также будет считаться запросом на очистку элемента TLS, что отличается от работы старых ядер.

До Linux 3.19, сегментные регистры DS и ES не должны ссылаться на элементы TLS.

## СМОТРИТЕ ТАКЖЕ
arch_prctl(2), modify_ldt(2), ptrace(2) (PTRACE_GET_THREAD_AREA and PTRACE_SET_THREAD_AREA)


title: set_tid_address(2)

text:
# set_tid_address(2)

## ИМЯ
set_tid_address - устанавливает указатель идентификатора нити

## ОБЗОР

```
#include <linux/unistd.h>
```

```
long set_tid_address(int *tidptr);
```

Замечание: В glibc нет обёрточной функции для данного системного вызова; смотрите ЗАМЕЧАНИЯ.

## ОПИСАНИЕ
В ядре для каждой нити хранится два атрибута (адреса): set_child_tid и clear_child_tid. Их значение по умолчанию равно NULL.

set_child_tid

Если нить запущена с помощью clone(2) с флагом CLONE_CHILD_SETTID, то значение set_child_tid устанавливается равным аргументу системного вызова ctid.

Если set_child_tid присвоено значение, то самое первое действие, которое выполняется для новой нити, это запись ID нити по этому адресу.

clear_child_tid

Если нить запущена с помощью clone(2) с флагом CLONE_CHILD_CLEARTID, то значение clear_child_tid устанавливается равным аргументу системного вызова ctid.

Системный вызов set_tid_address() устанавливает у вызывающей нити значение clear_child_tid равным tidptr.

Если нить, чьё значение clear_child_tid не равно NULL, завершается и если нить использовала общую память с другими нитями, то по адресу, указанному в clear_child_tid, записывается 0 и ядро выполняет следующую операцию:

futex(clear_child_tid, FUTEX_WAKE, 1, NULL, NULL, 0);

Действие этой операции в том, что она пробуждает единственную нить, выполнявшую ожидание futex на расположение памяти. Ошибки операции пробуждения futex игнорируются.

## ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
Вызов set_tid_address() всегда возвращает ID вызывающей нити.

## ОШИБКИ
Вызов set_tid_address() всегда завершается без ошибок.

## ВЕРСИИ
Данный вызов появился в Linux 2.5.48. Представленное здесь описание соответствует вызову, начиная с Linux 2.5.49.

## СООТВЕТСТВИЕ СТАНДАРТАМ
Данный вызов есть только в Linux.

## ЗАМЕЧАНИЯ
В glibc нет обёртки для данного системного вызова; запускайте его с помощью syscall(2).

## СМОТРИТЕ ТАКЖЕ
clone(2), futex(2), gettid(2)


seteuid

title: seteuid(2)

text:
# seteuid(2)

## ИМЯ
seteuid, setegid - устанавливает эффективный идентификатор пользователя или группы

## ОБЗОР
\#include <sys/types.h>

\#include <unistd.h>

int seteuid(uid_t euid);

int setegid(gid_t egid);

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

seteuid(), setegid():

_POSIX_C_SOURCE >= 200112L

|| /* версии glibc <= 2.19: */ _BSD_SOURCE

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

Тоже самое справедливо при работе setegid() с «групповым» идентификатором, а не «пользовательским».

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

Замечание: есть случаи, когда seteuid() может завершиться с ошибкой даже когда UID вызывающего равен 0; это серьёзная ошибка безопасности — не проверять возвращаемое значение seteuid().

## ОШИБКИ
EINVAL

Целевой идентификатор пользователя или группы некорректен в этом пользовательском пространстве имён.

EPERM

Для seteuid(): вызывающий процесс не имеет прав (не имеет мандата CAP_SETUID в своём пространстве имён пользователя) и euid не совпадает с текущим действительным, эффективным или сохранённым идентификатором пользователя.

Для setegid(): вызывающий процесс не имеет прав (не имеет мандата CAP_SETGID в своём пространстве имён пользователя) и egid не совпадает с текущим действительным, эффективным или сохранённым идентификатором группы.

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

## ЗАМЕЧАНИЯ
Установка эффективного идентификатора пользователя (группы) в сохранённый идентификатор пользователя (группы) возможна с версии Linux 1.1.37 (1.1.38). В других системах надо проверять _POSIX_SAVED_IDS.

В glibc 2.0 вызов seteuid(euid)эквивалентен setreuid(-1, euid), и поэтому может изменить сохранённый идентификатор пользователя. В glibc 2.1 и новее он эквивалентен setresuid(-1, euid, -1) и поэтому не изменяет сохранённый идентификатор пользователя. Аналогичные замечания относятся и к setegid(), с той разницей, что изменение в реализации из setregid(-1, egid) в setresgid(-1, egid, -1) произошло в glibc 2.2 или 2.3 (зависит от аппаратной архитектуры).

Согласно POSIX.1, seteuid() (setegid()) необходимо запрещать euid (egid) равный текущему эффективному идентификатору пользователя (группе), и некоторые реализации не позволяют этого.

### Отличия между библиотекой C и ядром
В Linux, seteuid() и setegid() реализованы в виде библиотечных функций, которые вызывают setreuid(2) и setregid(2), соответственно.

## СМОТРИТЕ ТАКЖЕ
geteuid(2), setresuid(2), setreuid(2), setuid(2), capabilities(7), credentials(7), user_namespaces(7)


setfsgid

title: setfsgid(2)

text:
# setfsgid(2)

## ИМЯ
setfsgid - устанавливает идентификатор группы для доступа к файловой системе

## ОБЗОР
\#include <sys/fsuid.h>

int setfsgid(uid_t fsgid);

## ОПИСАНИЕ
В Linux у процесса есть идентификатор группы файловой системы и эффективный (effective) идентификатор группы. Идентификатор группы файловой системы (есть только в Linux) используется при проверки прав доступа к объектам файловой системы, а эффективный идентификатор группы используется для проверки прав доступа к другим различным объектам (смотрите credentials(7)).

Обычно, значение идентификатора группы файловой системы процесса совпадает со значением эффективного идентификатора группы. Более того, при изменении идентификатора эффективного идентификатора группы ядро также изменяет и идентификатор группы файловой системы на новое значение идентификатора эффективного идентификатора группы.Процесс может изменить значение своего идентификатора группы файловой системы на отличное от эффективного идентификатора группы с помощью setfsgid() указав его в параметре fsgid.

Вызов setfsgid() выполняется корректно, только если он был вызван суперпользователем или, если fsgid совпадает с идентификатором действительной, эффективной и сохранённой группы или равен текущему значению идентификатора пользователя файловой системы вызывающего.

## ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном выполнении или при ошибке данный вызов возвращает предыдущий идентификатор группы файловой системы вызвавшего.

## ВЕРСИИ
Данный системный вызов доступен в Linux начиная с версии 1.2.

## СООТВЕТСТВИЕ СТАНДАРТАМ
Вызов setfsgid() есть только в Linux и не должен использоваться в переносимых программах.

## ЗАМЕЧАНИЯ
Понятие идентификатора группы файловой системы и системный вызов setfsgid() были изобретены по историческим причинам, которые больше не применимы к современным ядрам Linux. Смотрите в описании setfsuid(2) почему в использовании setfsuid(2) и setfsgid() теперь нет необходимости.

Первоначальная версия системного вызова Linux setfsgid() поддерживала только 16-битные идентификаторы групп. Позднее в Linux 2.4 был добавлен setfsgid32(), поддерживающий 32-битные идентификаторы. Обёрточная функция glibc setfsgid() скрывает это различие в версиях ядер.

### Отличия между библиотекой C и ядром
В glibc 2.15 и старее, если обёрточная функция этого системного вызова определяет, что аргумент невозможно передать ядру без обрезания целого (так как ядро старое и не поддерживает 32-битные ID группы), то она возвращает -1 и присваивает errno значение EINVAL не пытаясь выполнить системный вызов.

## ДЕФЕКТЫ
Вызывающему не возвращается никаких ошибок, и факт того, что успешном или не успешном выполнении возвращается одинаковое значение, делает невозможным напрямую определить как завершился вызов. Вместо этого, вызывающий должен получить значение из ещё одного вызова — setfsgid(-1) (который всегда завершается с ошибкой), чтобы определить, изменил ли предыдущий вызов setfsgid() идентификатор группы файловой системы. По крайней мере, должно быть получено значение EPERM, если вызов завершится с ошибкой (так как у вызывающего нет мандата CAP_SETGID).

## СМОТРИТЕ ТАКЖЕ
kill(2), setfsuid(2), capabilities(7), credentials(7)


setfsuid

title: setfsuid(2)

text:
# setfsuid(2)

## ИМЯ
setfsuid - устанавливает идентификатор пользователя для доступа к файловой системе

## ОБЗОР
\#include <sys/fsuid.h>

int setfsuid(uid_t fsuid);

## ОПИСАНИЕ
В Linux у процесса есть идентификатор пользователя файловой системы и эффективный (effective) идентификатор пользователя. Идентификатор пользователя файловой системы (есть только в Linux) используется при проверки прав доступа к объектам файловой системы, а эффективный идентификатор пользователя используется для проверки прав доступа к другим различным объектам (смотрите credentials(7)).

Обычно, значение идентификатора пользователя файловой системы процесса совпадает со значением эффективного идентификатора пользователя. Более того, при изменении идентификатора эффективного идентификатора пользователя ядро также изменяет и идентификатор пользователя файловой системы на новое значение идентификатора эффективного идентификатора пользователя.Процесс может изменить значение своего идентификатора пользователя файловой системы на отличное от эффективного идентификатора пользователя с помощью setfsuid() указав его в параметре fsuid.

Вызовы setfsuid() и setfsgid(2) в явном виде, обычно (теперь), используются только в программах Linux типа NFS-сервера, которым требуется изменить идентификатор пользователя и группы для доступа к файлам без изменения действительного и эффективного идентификаторов пользователя и группы. Изменение обычного идентификатора пользователя для программы типа NFS-сервера — это нарушение безопасности (раньше), приводящее к возможному получению нежелательных сигналов от других групп (однако это было в прошлом, читайте далее).

Вызов setfsuid() выполняется корректно, только если он был вызван суперпользователем или, если fsuid совпадает с действительным, эффективным и сохранённым идентификатором пользователя или равен текущему значению идентификатора пользователя файловой системы вызывающего.

## ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном выполнении или при ошибке данный вызов возвращает предыдущий идентификатор пользователя файловой системы вызвавшего.

## ВЕРСИИ
Данный системный вызов доступен в Linux начиная с версии 1.2.

## СООТВЕТСТВИЕ СТАНДАРТАМ
Вызов setfsuid() есть только в Linux и не должен использоваться в переносимых программах.

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

Первоначальная версия системного вызова setfsuid() в Linux поддерживала только 16-битные идентификаторы пользователя. Позднее в Linux 2.4 был добавлен вызов setfsuid32(), поддерживающий 32-битные идентификаторы. В glibc обёрточная функция setfsuid() работает одинаково вне зависимости от версий ядра.

### Отличия между библиотекой C и ядром
В glibc 2.15 и старее, если обёрточная функция этого системного вызова определяет, что аргумент невозможно передать ядру без обрезания целого (так как ядро старое и не поддерживает 32-битные ID пользователя), то она возвращает -1 и присваивает errno значение EINVAL не пытаясь выполнить системный вызов.

## ДЕФЕКТЫ
Вызывающему не возвращается никаких ошибок, и факт того, что успешном или не успешном выполнении возвращается одинаковое значение, делает невозможным напрямую определить как завершился вызов. Вместо этого, вызывающий должен получить значение из ещё одного вызова — setfsuid(-1) (который всегда завершается с ошибкой), чтобы определить, изменил ли предыдущий вызов setfsuid() идентификатор пользователя файловой системы. По крайней мере, должно быть получено значение EPERM, если вызов завершится с ошибкой (так как у вызывающего нет мандата CAP_SETUID).

## СМОТРИТЕ ТАКЖЕ
kill(2), setfsgid(2), capabilities(7), credentials(7)


setgid

title: setgid(2)

text:
# setgid(2)

## ИМЯ
setgid - устанавливает идентификатор группы процесса

## ОБЗОР
\#include <sys/types.h>

\#include <unistd.h>

int setgid(gid_t gid);

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

В Linux setgid() реализован так же, как в версии POSIX где есть возможность _POSIX_SAVED_IDS. Это позволяет set-group-ID-программам (не являющимся программами суперпользователя) сбросить привилегии группы, проделать непривилегированную работу и безопасно вернуть исходный эффективный идентификатор группы.

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

## ОШИБКИ
EINVAL

Идентификатор группы, заданный в gid, некорректен в этом пользовательском пространстве имён.

EPERM

У вызвавшего процесса нет прав (не имеет мандата CAP_SETGID в его пользовательском пространстве имён) и gid не совпадает с эффективным идентификатором группы или сохраненным set-group-ID идентификатором группы вызывающего процесса.

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

## ЗАМЕЧАНИЯ
Первоначальная версия системного вызова setgid() в Linux поддерживала только 16-битные идентификаторы групп. Позднее в Linux 2.4 был добавлен вызов setgid32(), поддерживающий 32-битные идентификаторы. В glibc обёрточная функция gsetgid() работает одинаково вне зависимости от версий ядра.

### Отличия между библиотекой C и ядром
На уровне ядра ID пользователя и группы являются атрибутами нити. Однако в POSIX требуется, чтобы все нити в процессе имели одинаковые права. В реализации нитей NPTL требования POSIX реализованы через обёрточные функции для различных системных вызовов, которые изменяют UID и GID процесса. В этих функциях (включая и для setgid()) используется алгоритмы на основе сигналов, которые следят за тем, что когда у одной нити изменяются права, эти изменения выполняются и для остальных нитей процесса. Подробное описание смотрите в nptl(7).

## СМОТРИТЕ ТАКЖЕ
getgid(2), setegid(2), setregid(2), capabilities(7), credentials(7), user_namespaces(7)