Skip to content

Latest commit

 

History

History
96 lines (58 loc) · 11.8 KB

README.md

File metadata and controls

96 lines (58 loc) · 11.8 KB

Средства межпроцессного взаимодействия POSIX

Разделяемая память

Для создания сегментов разделяемой памяти используется механизм отображаемых файлов mmap. Выполнение системного вызова mmap с параметрами MAP_SHARED и указанием файлового дескпритора позволяет использовать некоторое имя в файловой системе для взаимодействия между собой неродственных процессов.

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

Каждый ключ - это некоторая строка длиной до NAME_MAX байт, которая должна начинаться с символа '/'. У ключей могут быть права доступа и владелец, по аналогии с обычными файлами.

Такие разделяемые объекты существуют до перезагрузки компьютера, либо до их явного удаления одним из процессов.

Функции для работы с разделяемыми файлами

  • int shm_open(const char *name, int flags, mode_t mode) - по аналогии с системным вызовом open, открывает ключ по имени, как обычный файл. Если среди флагов в flags присутствует опция O_CREAT, то третий аргумент подразумевает права доступа, в противном случае его значение игнорируется. Возвращает файловый дескриптор, который можно передать в системный вызов mmap.
  • int shm_unlink(const char *path) - удаляет имя из памяти. Если разделяемый файл был имеет отображение в одном или нескольких процессах, то он продолжает быть доступным и занимать место в памяти.

Права доступа можно настраивать с помощью системных вызовов fchmod и fchown, которые, в отличии от команд shell'а и системных вызовов, работающих с именами, позволяют работать с файловыми дескрипторами.

При создании объекта, он имеет размер 0, и может быть изменен с помощью ftruncate.

Особенности реализации в Linux

В операционной системе Linux (в отличии, например, от FreeBSD), объекты разделяемой памяти - это самые обычные файлы, которые располагаются в файловой системе tmpfs, примонтированной в /dev/shm.

Кроме того, есть дополнительные особенности:

  • для использования функций разделяемых объектов POSIX, нужно указывать опцию -lrt, посколько glibc разбита на несколько частей;
  • требование про символ / в начале имени ключа является не обязательным; тем не менее, это противоречит стандарту POSIX и в BSD системах приводит к ошибке EINVAL, а в системе QNX просто создаст обычный файл на диске в текущем каталоге.

Семафоры

Семафор - это беззнаковая целочисленная переменная, которая обладает дополнительными свойтсвами:

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

В теоретической литературе операция захвата семафора (уменьшения значения) обычно обозначается буквой P (proberen), а операция освобождения (увеличение значения) - буквой V (verhogen), - названия операций заимствованы из нидерландского языка, т.к. семафоры изобрел Дейкстра.

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

Семафоры POSIX

Семафоры POSIX определяются некотроым типом sem_t, объявленным в файле <semaphore.h>, реализация которого, в общем случае, считается неопределенной, и зависит не только от конкретной операционной системы, но и от процессора.

Функции работы с семафорами обычно принимают его по указателю:

  • sem_wait(sem_t *sem) - захватить семафор (операция P);
  • sem_post(sem_t *sem) - освободить семафор (операция V);
  • sem_trywait(sem_t *sem) - попытаться захватить семафор, если он равен нулю, то процесс не блокируется, а функция вовзращает значение -1, прописывая значение EAGAIN в errno;
  • sem_timedwait(sem_t *sem, struct timespec *timeout) - захватывает семафор, но если за указанный промежуток времени он не был разблокирован, то функция завершает свою работу с ошибкой ETIMEDOUT;
  • sem_getvalue(sem_t *sem, int *out_var) - читает численное значение семафора, не блокируя его; эта функция бывает полезна при отладке.

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

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

Анонимные семафоры

Анонимные семафоры - это семафоры, которые доступны только в рамках одного адресного пространства (для многопоточности), либо родственным процессам.

Создаются с попощью функции sem_init:

int sem_init(sem_t *sem,    // указатель на семафор в памяти
             int pshared,   // 0 - если предназначен для использования
                            // в рамках одного адресного пространства,
                            // 1 - если разными процессами
             unsinged value // начальное значение
            )

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

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

Именованные семафоры

Именованные семафоры - это реализация семафоров совместно с механизмом разделяемой памяти POSIX. Имена семафоров подчиняются тем же правилам, что имена сегментов разделяемой памяти, за одним исключением: максимальная длина имени на 4 байта короче, т.к. к имени семафора автоматически приписывается (в зависимости от реализации) суффикс .sem или префикс sem..

Создаются именованные семафоры с помощью sem_open:

sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag,
                mode_t mode, unsigned int value);

По аналогии с open, если присутствует флаг O_CREAT, то нужно указать права доступа, и кроме того, - начальное значение. В отличии от обычных файлов, не нужно указывать флаги, определяющие режим открытия на чтение/запись. Если открывается существующий семафор, то oflag=0.

Закрытие именованного семафора с помощью sem_close, в отличии от анонимного, никак не влияет на процессы, которые могут быть им заблокированы: значение счетчика остается неизменным, и может быть изменено после повторного открытия.

Применять операцию sem_destroy для именованных семафоров запрещено, так же как и операцию sem_close - для анонимных.

Для удаления имени семафора используется функция sem_unlink(const char *name). Как и в случае обычного файла, значение семафора сохраняется даже после удаления до тех пор, пока он не будет закрыт всеми использующими его процессами.