sem14 - Средства System V IPC. Семафоры. Разделяемая память. Очереди сообщений. (1114926), страница 3
Текст из файла (страница 3)
Если указать в аргументе cmd значение IPC_RMID, а аргументом buf передать NULL, сегмент разделяемой памяти будет удалён. Точнее, он будет помечен на удаление,и ни один процесс не сможет его получить или подключить к своему адресному пространству.Окончательно такой сегмент разделяемой памяти будет удалён, когда его отключит последний процесс, который его использовал.2.1.3 Подключение сегмента разделяемой памяти#include <sys/types.h>#include <sys/shm.h>void *shmat(int shmid, const void *shmaddr, int shmflg);8Функция shmat подключает сегмент разделяемой памяти с идентификатором shmid кадресному пространству процесса. Адрес подключения задаётся аргументом shmaddr следующим образом:• Если shmaddr равен 0, система пытается подобрать адрес сегмента, начиная от старших адресов к младшим.• Если shmaddr не равен 0, а в аргументе shmflg указан флаг SHM_RND, указанныйадрес выравнивается вниз по границе страницы памяти процессора, и сегмент памятиподключается по этому адресу.
Если флаг SHM_RND не установлен, адрес подключенияshmaddr должен быть выровнен по границе страницы.Если в параметре shmflg указан флаг SHM_RDONLY, сегмент подключается в режиме«только на чтение», в противном случае сегмент подключается и для чтения, и для записи.Естественно, у процесса должны быть соответствующие права на данный сегмент разделяемой памяти.При успешном завершении функция возвращает адрес начала подключённого сегментаразделяемой памяти.
При ошибке функция возвращает значение (void*) -1, и переменная errno устанавливается в код ошибки.EACCES Процесс не имеет достаточно прав на указанный тип подключения сегмента.EINVAL Недопустимое значение shmid, неверное (невыровненное) значениеshmaddr, или подключение по адресу shmaddr невозможно.ENOMEM Недостаточно памяти ядра.2.1.4 Отключение сегмента разделяемой памяти#include <sys/types.h>#include <sys/shm.h>int shmdt(const void *shmaddr);Функция shmdt отсоединяет сегмент разделяемой памяти с начальным адресомshmaddr от адресного пространства процесса.
В случае успеха функция возвращает0. При неудаче (неправильный адрес shmaddr) возвращается -1, а переменная errnoустанавливается в значение EINVAL.2.2 Пример программыРассмотрим программу, которая проиллюстрирует работу с массивами семафоров и ссегментом разделяемой памяти.Программа порождает два процесса, которые обмениваются друг с другом целым числом. Первый процесс получает от второго некоторое число, печатает, увеличивает его на 1и отправляет обратно второму процессу и наоборот. Для пересылки значения числа используется сегмент разделяемой памяти, а для синхронизации процессов — массив бинарныхсемафоров.
Программа корректно освобождает занятые ресурсы.#include#include#include#include#include#include#include<stdio.h><stdlib.h><unistd.h><sys/types.h><sys/ipc.h><sys/sem.h><sys/shm.h>9#include <sys/wait.h>#include <signal.h>#define FTOK_FILE "sem14_2"#define FTOK_PROJ 0key_t ipc_key;intshm_id = -1;intsem_id = -1;int*mem_ptr = (void *) -1;intpid[2] = { -1, -1 };/* функция освобождает все занятые ресурсы */void cleanup(void){if (pid[0] > 0) kill(pid[0], SIGTERM);if (pid[1] > 0) kill(pid[1], SIGTERM);if (mem_ptr != (void*) -1) shmdt(mem_ptr);if (sem_id >= 0) semctl(sem_id, 0, IPC_RMID);if (shm_id >= 0) shmctl(shm_id, IPC_RMID, 0);}/* обработчик сигналов завершения программы */void handler(int signo){exit(0);}void pexit(char const *str){perror(str);exit(1);}void do_work(int read_ind, int write_ind){// операции, выполняемые при ожидании: ждем появления 0 (открыто)// на семафоре данного процесса, сразу же устанавливаем его// в 1 (закрыто)struct sembuf wait_ops[] = {{ -1, 0, 0 }, {-1, 1, 0}};// операция при разблокировании: семафор должен быть// установлен в 1, тогда переустанавливаем его в 0struct sembuf unlock_ops[] = {{ -1, -1, 0 }};sigset_t s, os;sigfillset(&s);wait_ops[0].sem_num = read_ind;wait_ops[1].sem_num = read_ind;unlock_ops[0].sem_num = write_ind;while (1) {10// блокируем сигналы завершения на время ожидания и печатиsigprocmask(SIG_BLOCK, &s, &os);// ожидаемif (semop(sem_id, wait_ops, 2) < 0) break;printf("%d: %d\n", getpid(), *mem_ptr);fflush(stdout);(*mem_ptr)++;// разблокируем другой процессif (semop(sem_id, unlock_ops, 1) < 0) break;// разблокируем сигналыsigprocmask(SIG_SETMASK, &os, NULL);}}int main(void){int i;// регистрируем функцию, которая будет автоматически вызываться// при завершении программы с помощью exit() или// return из функции mainatexit(cleanup);// устанавливаем обработчики сигналовsignal(SIGINT, handler);signal(SIGTERM, handler);signal(SIGALRM, handler);signal(SIGHUP, handler);// получаем и инициализируем разделяемую памятьipc_key = ftok(FTOK_FILE, FTOK_PROJ);printf("Key = %d\n", ipc_key);if ((shm_id=shmget(ipc_key,sizeof(int),IPC_CREAT|IPC_EXCL|0600))<0)pexit("shmget");mem_ptr = shmat(shm_id, NULL, 0);if (mem_ptr == (void*) -1)pexit("shmat");*mem_ptr = 0;// получаем семафорыif ((sem_id = semget(ipc_key, 2, IPC_CREAT | IPC_EXCL | 0600)) < 0)pexit("semget");// значение семафора [0] - 0 (открыт), [1] - 1 (закрыт)if (semctl(sem_id, 1, SETVAL, 1) < 0)pexit("semctl");for (i = 0; i < 2; i++) {pid[i] = fork();if (pid[i] < 0) pexit("fork");if (!pid[i]) {// сыновние процессы не выполняют никаких особенных действий11// при завершении работы, поэтому обработчики сбрасываютсяsignal(SIGINT, SIG_DFL);signal(SIGTERM, SIG_DFL);signal(SIGALRM, SIG_DFL);signal(SIGHUP, SIG_DFL);do_work(i, 1 - i);_exit(1);}}alarm(5);wait(0); wait(0);return 0;}3Очереди сообщенийПоследний механизм, входящий в состав SysV IPC — это очереди сообщений.
Сама очередь сообщений находится в адресном пространстве ядра и имеет ограниченный размер. Вотличие от каналов, которые обладают теми же самыми свойствами, очереди сообщений сохраняют границы сообщений. Это значит, что ядро ОС гарантирует, что сообщение, поставленное в очередь, не сольётся при чтении из очереди сообщений с предыдущим или следующим сообщением. Кроме того, с каждым сообщением связывается его тип. Процесс, читающий очередь сообщений, может отбирать только сообщения заданного типа или все сообщения кроме сообщений заданного типа.Следует заметить, что, к сожалению, не определены системные вызовы, которые позволяют читать сразу из нескольких очередей сообщений, или из очередей сообщений и файловых дескрипторов. Видимо, отчасти и по-этому очереди сообщений широко не используются.3.1 Создание очереди сообщенийОчередь сообщений создаётся с помощью системного вызова msgget.#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>int msgget(key_t key, int msgflg);Эта функция создаёт новую очередь сообщений и возвращает её идентификатор.
Идентификатор очереди сообщений — неотрицательное целое число. Он используется в дальнейшем во всех операциях с данной очередью сообщений и играет ту же роль, что дескриптороткрытого файла при операциях с файлами. При ошибке функция возвращает значение -1, ав переменную errno записывается код ошибки.Параметр key — ключ объекта. Это - положительное целое число, уникальное в системе. Пространство ключей очередей сообщений не связано с пространством ключей семафоров и областей разделяемой памяти, то есть в системе могут существовать область раз-12деляемой памяти, массив семафоров и очередь сообщений с одним и тем же ключом. Ключвыбирается программистом или генерируется с помощью функции ftok.Допускается специальное значение ключа IPC_PRIVATE, которое обозначает анонимную очередь сообщений.
В этом случае никакой другой процесс не сможет подключиться кэтой очереди сообщений с помощью вызова msgget. При указании ключа IPC_PRIVATEочередь создаётся всегда, не зависимо от флага IPC_CREAT.Параметр msgflg задаёт права доступа к создаваемой очереди и флаги создания очереди. Если установлен флаг IPC_CREAT, очередь будет создана, если до этого она ещё несуществовала.
Указание флага IPC_EXCL совместно с флагом IPC_CREAT приведёт к тому, что очередь сообщений будет создана, если она ещё не существовала, но вызов msggetзавершится ошибкой, если очередь сообщений уже существовала.В случае, когда создаётся новая очередь сообщений, младшие 9 битов определяют правадоступа к создаваемой очереди. Биты интерпретируются точно так же, как и в случае заданияправ доступа к файлам (за исключением того, что бит x не используется).Если очередь сообщений уже существует, и флаг IPC_EXCL не задан, проверяется, чтоочередь сообщений не помечена на удаление, и проверяются права доступа процесса к данной очереди сообщений.Если вызов msgget завершился с результатом -1, переменная errno содержит один изследующих кодов ошибки.EACCESEEXISTEIDRMENOENTENOMEMENOSPCОчередь сообщений уже существует, но процесс не имеет прав для доступа к ней.Очередь сообщений уже существует, а параметр msgflg содержит установленныефлаги IPC_CREAT и IPC_EXCL.Очередь сообщений помечена на удаление.Очередь сообщений не существует, и флаг IPC_CREAT не указан.Очередь сообщений должна быть создана, но система не располагает достаточнымколичеством памяти.Очередь сообщений должна быть создана, но достигнуто максимальное количествоочередей сообщений в системе (параметр MSGMNI ядра ОС).3.2 Удаление очереди сообщенийУдаление очереди сообщений выполняется с помощью системного вызова msgctl указанием команды IPC_RMID в параметр cmd.#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>int msgctl(int msqid, int cmd, struct msqid_ds *buf);Данная функция выполняет операцию над очередью сообщений.