Интерфейс межпроцессного взаимодействия System V IPC позволяет манипулировать классическими набором объектов, таких как: очередь сообщений, семафор и разделяемая память.
Объекты System V IPC идентифицируется 32-битным ключом IPC, играющего роль, аналогичную имени файла. При создании объекта IPC ему присваивается уникальный 32-битный идентификатор IPC, аналогичный inode файла. Идентификаторы IPC назначаются ядром, ключи IPC произвольно выбираются программистами.
У каждого объект IPC есть атрибуты "пользователь-владелец", "группа-владельца", "пользователь-создатель", "группа-создателя", а так же права на чтение и запись для владельца, группы-владельца и остальных, аналогичные файловым. Например: rw- r-- ---.
Впервые System V IPC появился во внутренних версиях Unix, использовавшихся в компании Bell Laboratories, но массовое распространение получил вместе с самой продаваемой версией Unix - System V, что и отражено в названии.
System V IPC имеет некоторые недостатки, в частности, плохую масштабируемость, в связи с тем, что создаваемые объекты не привязаны к процессам, и потому могут оставаться в памяти до перезагрузки ОС .
В последующем, System V IPC с небольшими изменениями вошёл в стандарт POSIX. Логика работы с объектами сохранилась, однако была проведена некоторая унификация вызовов System V IPC с вызовами файловой системы. Соответственно, поменялись имена функций.
В System V IPC для именования объекта создаётся особый 32-битный ключ, часто формируемый из inode существующего файла и целого числа, в POSIX имена объектов имитируют имена файловой системы в формате /object_name.
Набор функций для работы с разделяемой памятью из shmget(), shmat(), shmdt(), shmctl() превратился в shm_open(), mmap(), shm_unlink()/
shmget() возвращает идентификатор IPC, а shm_open() возвращает файловый дескриптор.
shmat(), shmdt() работают только с разделяемой памятью, а mmap() одинаково успешно работает как с объектом "разделяемая память", так и с файлами, отображаемыми в память.
Размер сегмента разделяемой памяти System V IPC неизменен, а в POSIX может изменяться вызовом * ftruncate() * с последующим обновлением отображения в память процесса вызовами munmap() и mmap() .
Наличие сообщений в очередях POSIX может мониториться через callback, который устанавливается вызовом mq_notify().
Объекты System V IPC идентифицируется 32-битным ключом IPC, играющего роль, аналогичную имени файла. При создании объекта IPC ему присваивается уникальный 32-битный идентификатор IPC, аналогичный inode файла. Идентификаторы IPC назначаются ядром, ключи IPC произвольно выбираются программистами.
Особое значение ключа IPC_PRIVATE адресует объект, доступный только данному процессу и его потомкам. Данное значение позволяет избежать случайного доступа к посторонним объектам из-за конфликта ключей.
В случае совпадения идентификаторов несколько программ будут пытаться получить доступ к одному и тому же объекту. Не существует механизма, гарантирующего выбор уникального ключа IPC, поэтому была создана специальная функция ftok(), генерирующая ключ на основе уникального имени файла в файловой системе (точнее номера его Inode) и дополнительного байта (номера проекта). При выборе файла, для формирования ключа, необходимо обеспечить достаточную длительность его жизни. Если файл удалён, то новые процессы не смогут сгенерировать ключи доступа к объектам IPC на основе данного файла. Удаление файла и создание нового с тем же именем так же нарушит доступ к объектам IPC поскольку новый файл будет иметь отличный номер Inode.
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok("/home/bob/key_base", 'Z');
Реализация ftok() в glibc
if (__xstat64 (_STAT_VER, pathname, &st) < 0)
return (key_t) -1;
key = ((st.st_ino & 0xffff) | ((st.st_dev & 0xff) << 16)
| ((proj_id & 0xff) << 24));
return key;
Объекты IPC создаются функциями msgget(), semget(), shmget().
При создании используются флаги
и права доступа. Права доступа аналогичны файлам (mode - rw-rw-rw-)
Управление объектами SysV IPC (в том числе их уничтожение) производится c помощью функций msgctl(), semctl(), shmctl(). Параметр cmd этих функций может принимать значения:
Во всех операциях с объектами может быть указан дополнительный флаг
При создании очереди она ассоциируется с буфером размером MSGMNB (16384 байт в Linux).
Сообщение состоит из заголовка фиксированной длины и текста переменной длины. Размер текста не должен превышать MSGMAX (8192 байта в Linux).
Каждое сообщение поступает в очередь, где хранится до тех пор, пока кто-нибудь не прочитает его. После прочтения сообщение удаляется из очереди сообщений. Следовательно, только один процесс может получить конкретное сообщение.
Сообщение может быть помечено целочисленным значением (типом сообщения), которое позволяет процессу избирательно извлекать сообщения из очереди. Правила чтения по типам:
если msgp меньше нуля, то из очереди берется первое сообщение со значением, меньшим, чем абсолютное значение msgp.
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h>
int msgget(key_t key, int msgflg); int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
struct msgbuf { long mtype; /* message type, must be > 0 / char mtext[1]; / message data */ };
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
Каждый семафор IPC представляет собой набор из одного или нескольких целочисленных счётчиков. Таким образом один семафор может защищать несколько объектов. Обычно, нулевое значение счётчика означает, что объект свободен, положительное - занят, но можно договориться о других значениях. Механизм семафоров ничего не знает про их использование, а только обеспечивает атомарные операции изменения значений.
Существуют ограничения как на количество семафоров (по умолчанию 128), так и на количество счётчиков внутри одного семафора (по умолчанию 250). Эти данные в Linux доступны в /proc/sys/kernel/sem.
При завершении процесса все операции, которые он проводил над семафорами сбрасываются, что позволяет избежать неприятностей в случае краха процесса, изменившего значения счётчиков.
Операции над семафорами в вызове semop():
Если величина sembuf[n].semop отрицательна, процесс ожидает, пока значение счётчика n не станет большим или равным абсолютной величине sembuf.semop. Затем абсолютная величина sembuf[n].semop вычитается из значения счётчика n.
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h>
int semget(key_t key, int nsems, int semflg); int semop(int semid, struct sembuf *sops, unsigned nsops);
struct sembuf { unsigned short sem_num; /* semaphore number / short sem_op; / semaphore operation / short sem_flg; / operation flags */ }
int semctl(int semid, int semnum, int cmd, ...);
Вызов shmget() резервирует участок физической памяти (фала подкачки)
Вызов shmat() (attach) отображает зарезервированный участок физической памяти в виртуальное адресное пространство процесса.
Вызов shmdt() (detach) разрывает связь между зарезервированным участок физической памяти и виртуальным адресным пространством процесса.
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
Просмотр информации о подсистеме System V IPC
lsipc
Создание объекта
# Допустимые опции
# -Q - создание очереди
# -M size - создание разделяемой памяти
# -S number - создание семафора
ipcmk -Q
Просмотр объектов
ipcs
Удаление объектов
ipcrm --shmem-id 9601039
ipcrm --all