Семафор с индексом 1 управляет доступом к файлу устройства, обозначенному переменной среды PRINTER1. Семафор с индексом 2 управляет устройством, на которое ссылается PRINTER2.
Первые четыре буквы имени команды определят, должен вывод идти на принтер, связанный с PRINTER1 или с PRINTER2. Если имя команды начинается с line, должен быть использован PRINTER1. Если оно начинается с lase, нужно использовать PRINTER2.
Создание набора семафоров - Пример
Этот пример использует набор семафоров для управления всеми принтерами, трактуемыми как общий ресурс печати. Этот код показывает, как создать и инициализировать набор из более чем одного семафора.
Эта программа использует следующий файл заголовка:
printer. h
1 #define DUMMY 0
2 #define NUMPR 2/* number of printers */
3 #define ACQUIRE -1
4 #define RELEASE 1
Фрагмент программы работает так:
15 Объявление массива, содержащего инициализационные значения всех семафоров в наборе.
... Переменные среды PRINTER1 и PRINTER2 содержат имена "принтеров", ассоциированных с индексами 1 и 2 набора семафоров. Эти "принтеры" могут быть как файлами, так и устройствами.
31 Формирование ключа на основании имени программы и буквы s. Так как два имени программы laserprnt и lineprnt представляют собой ссылки на один и тот же файл, ftok(3), используя эти имена в качестве параметров, выдает одно и то же устройство и номер инода.
32-35 Здесь создается набор из NUMPR + 1 семафоров. Дополнительный семафор с индексом 0 управляет ресурсом печати в целом.
45-54 Первый семафор в наборе инициализируется количеством принтеров в общем ресурсе печати. Остальные семафоры инициализируются в 1. Заметьте, что второй аргумент semctl(2) не используется.
Файл: printer. c
СОЗДАНИЕ НАБОРА СЕМАФОРОВ - ПРИМЕР
1 #include <stdlib. h>
2 #include <string. h>
3 #include <unistd. h>
4 #include <fcntl. h>
5 #include <sys/types. h>
6 #include <sys/ipc. h>
7 #include <sys/sem. h>
8 #include <stdio. h>
9 #include "printer. h"
10
11 main(int argc, char *argv[])
12 {
...
14 key_t ipckey;
15 ushort initial[NUMPR + 1], prntnum;
...
31 ipckey = ftok(argv[0], 's');
32 if ((semid = semget(ipckey, NUMPR + 1,
33 IPC_CREAT | IPC_EXCL | 0666)) != -1) {
34 creator = 1;
35 }
...
45 if(creator) { /* initialize semaphore set */
46 initial[0] = NUMPR;
47 for (n = 1; n <= NUMPR; n++)
48 initial[n] = 1;
49 if(semctl(semid, DUMMY, SETALL, initial) == -1) {
50 sprintf(errmsg,"%s - SETALL", argv[0]);
51 perror(errmsg);
52 exit(3);
53 }
54 }
...
Операции над набором семафоров — Пример (продолжение)
Код, приведенный в этом примере, представляет собой часть той же программы, что и предыдущий пример. Он демонстрирует, как оперировать с двумя семафорами в наборе одновременно.
Фрагмент программы работает так:
... Имя команды определяет принтер, который нужно использовать.
56-61 Две командные структуры установлены так, чтобы уменьшить на 1 первый семафор и семафор, связанный с принтером с номером prnnum. Заметьте, что используется флаг SEM_UNDO. Вспомните, что номер семафора, над которым должна быть выполнена операция, содержится в командной структуре.
62-66 Здесь выполняется операция над двумя семафорами одновременно. Эта операция захватит принтер, который должен быть использован. Другой процесс, исполняющий этот же код, будет заблокирован до тех пор, пока значение семафора не увеличится.
79-85 Командные структуры для обоих семафоров изменены так, чтобы увеличить значение семафоров на 1. Оба семафора изменяются "одновременно", т. е. ни один другой процесс не получит управления в промежутке между их изменениями.
Эта программа демонстрируется так:
$ ln printer lineprntr
$ ln printer laseprntr
$ PRINTER1=/dev/tty05
$ PRINTER2=/tmp/xyz
$ export PRINTER1 PRINTER2
$ lineprntr data & lineprntr data & wait
3909
3910
1ABCDEFGHIJKLMNOPQRSTUVWXYZ
2ABCDEFGHIJKLMNOPQRSTUVWXYZ
3ABCDEFGHIJKLMNOPQRSTUVWXYZ
4ABCDEFGHIJKLMNOPQRSTUVWXYZ
1ABCDEFGHIJKLMNOPQRSTUVWXYZ
2ABCDEFGHIJKLMNOPQRSTUVWXYZ
3ABCDEFGHIJKLMNOPQRSTUVWXYZ
4ABCDEFGHIJKLMNOPQRSTUVWXYZ
$ ipcs - s
IPC status from /dev/kmem as of Tue Mar 3 11:18:53 1987
T ID KEY MODE OWNER GROUP
Semaphores:
s 100 0x7304001a --ra-ra-ra- jeg unixc
$ ipcrm - s 100
Файл: printer. c
ОПЕРАЦИИ НАД НАБОРОМ СЕМАФОРОВ - ПРИМЕР
1 #include <stdlib. h>
2 #include <string. h>
3 #include <unistd. h>
4 #include <fcntl. h>
5 #include <sys/types. h>
6 #include <sys/ipc. h>
7 #include <sys/sem. h>
8 #include <stdio. h>
9 #include "printer. h"
10
11 main(int argc, char *argv[])
12 {
...
17 struct sembuf operation[2];
...
56 operation[1].sem_num = prntnum;
57 operation[1].sem_op = ACQUIRE;
58 operation[1].sem_flg = SEM_UNDO;
59 operation[0].sem_num = 0;
60 operation[0].sem_op = ACQUIRE;
61 operation[0].sem_flg = SEM_UNDO;
62 if(semop(semid, operation, 2) == -1) {
63 sprintf(errmsg,"%s - ACQUIRE",argv[0]);
64 perror(errmsg);
65 exit(4);
66 }
...
79 operation[1].sem_op = RELEASE;
80 operation[0].sem_op = RELEASE;
81 if(semop(semid, operation, 2) == -1) {
82 sprintf(errmsg,"%s - RELEASE",argv[0]);
83 perror(errmsg);
84 exit(7);
85 }
...
87 }
Разделяемая память
Разделяемая память System V IPC позволяет двум или более процессам разделять память и, следовательно, находящиеся в ней данные. Это достигается помещением в виртуальное адресное пространство процессов одной и той же физической памяти.
Того же эффекта можно достичь, отобразив в память при помощи mmap(2) доступный на запись файл в режиме MAP_SHARED. Как и в случае mmap(2), разделяемые сегменты не обязательно будут отображены на одни и те же адреса в разных процессах.
Главным практическим отличием разделяемой памяти System V IPC от mmap(2) является то, что для mmap(2) нужен файл, а память System V IPC ни к какому файлу не привязана. Кроме того, использование mmap(2) с флагом MAP_SHARED приводит к тому, что система синхронизует содержимое памяти с диском, что может снизить общую производительность системы. Если вам нужно сохранить содержимое разделяемой памяти после завершения работы всех процессов приложения, mmap(2) оказывается удобнее, но на практике такая потребность возникает довольно редко. Поэтому разделяемая память System V IPC до сих пор широко применяется многими приложениями.
В последние годы, разделяемая память вытесняется многопоточными программами. С определенными оговорками, с точки зрения программиста, потоки можно рассматривать как процессы, у которых вся память разделяется.
Этот раздел показывает, как создавать и использовать сегмент разделяемой памяти. Это выполняется следующими системными вызовами.
. shmget
. shmat
. shmdt
. shmctl
Разделяемая память
Это средство IPC позволяет двум или более процессам разделять память.
Такая память используется для хранения данных, которые нужны нескольким процессам. Это могут быть поисковые таблицы, критерии правильности данных и т. д. Разделяемая память представляет собой самый быстрый способ обмена данными между процессами.
Как правило, один процесс создает сегмент разделяемой памяти вызовом shmget(2) с флагом IPC_CREAT, отображает его в свое адресное пространство и инициализирует. Отображения разделяемого сегмента в адресное пространство процесса называют присоединением сегмента. Затем другие процессы могут присоединять этот сегмент и использовать его содержимое. Создатель задает права чтения/записи для хозяина/группы/других пользователей. Обычно сегмент удаляется тем же процессом, который создал его.
Конфигурация системы определяет минимальный и максимальный размеры сегмента, а также максимальное общее количество и общий объем сегментов в системе. Если система использует страничную подкачку, общий объем сегментов может превышать объем физической памяти, но крайне нежелательно, чтобы он превышал объем файла подкачки. Также ограничено количество сегментов, присоединенных к одному процессу.
Через разделяемую память могут взаимодействовать неродственные процессы. Все, что нужно процессу для присоединения разделяемого сегмента, это соответствующие права доступа для своих идентификаторов пользователя и группы.
Когда сегмент больше не нужен процессу, он отсоединяет его вызовом shmdt(2). Если это не было сделано явным образом, это произойдет при завершении процесса (как по exit(2), так и по сигналу) или при exec(2) . Отсоединение разделяемой памяти похоже на закрытие файла. Отсоединение не удаляет разделяемый сегмент, оно только логически исключает этот сегмент из виртуального пространства данных программы.
В отличие от остальных средств IPC, у разделяемой памяти происходит отложенное удаление. Вызов shmctl(2) с командой IPC_RMID приводит только к тому, что новые процессы не могут присоединиться к этому сегменту, но не к отсоединению этих сегментов у процессов, которые их используют. Запрос на удаление помечает сегмент, но само удаление происходит только когда количество присоединившихся процессов станет равно 0. Флаг ожидания удаления виден как D в выводе команды ipcs(1).
Родительский и порожденные процессы также могут использовать сегменты разделяемой памяти. После fork(2) порожденный процесс наследует присоединенные разделяемые сегменты. Обычные сегменты памяти данных после fork(2) копируются при записи, а сегменты разделяемой памяти остаются общими, так что родитель и потомок видят изменения, вносимые другим процессом, и могут взаимодействовать через них. При этом, в родителе и в потомке сегмент будет отображен на одни и те же виртуальные адреса.
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 9 |


