Уильям Стивенс - UNIX: взаимодействие процессов
solaris % shmget shmget 1234
solaris % ipcs –bmo
IPC status from <running system> as of Thu Jan 8 13:17:06 1998
T ID KEY MODE OWNER GROUP NATTCH SEGSZ
Shared Memory:
m 1 0x0000f12a –rw-r--r-- rstevens other1 0 1234
Программу ipcs мы запускаем для того, чтобы убедиться, что сегмент разделяемой памяти действительно был создан и не был удален по завершении программы shmcreate. Количество подключений (хранящееся в поле shm_nattch структуры shmid_ds) равно нулю, как мы и предполагали.
Теперь запустим пpoгрaммy shmwrite, чтобы заполнить содержимое разделяемой памяти последовательностью значений. Затем проверим содержимое сегмента разделяемой памяти программой shmread и удалим этот сегмент:
solaris % shmwrite shmget
solaris % shmread shmget
solaris % shmrmid shmget
solaris % ipes –bmo
IPC status from <running system> as of Thu Jan 8 13:17:06 1998
T ID KEY MODE OWNER GROUP NATTCH SEGSZ
Shared Memory:
Мы используем программу ipcs, чтобы убедиться, что сегмент разделяемой памяти действительно был удален.
ПРИМЕЧАНИЕ
При использовании имени исполняемого файла сервера в качестве аргумента ftok для идентификации какого-либо вида IPC System V обычно передается полное имя этого файла (например, /usr/bin/myserverd), а не часть имени, как сделано у нас (shmget). У нас не возникло проблем в этом примере, потому что все программы запускались из того же каталога, в котором был расположен исполняемый файл сервера. Вы помните, что функция ftok использует номер i-node файла для формирования ключа IPC и ей безразлично, определяется файл своим полным именем или его частью (относительным именем).
14.7. Ограничения, накладываемые на разделяемую память
На разделяемую память System V накладываются определенные ограничения точно так же, как и на семафоры и очереди сообщений System V (раздел 3.8). В табл. 14.1 приведены значения этих ограничений для разных реализаций. В первом столбце приведены традиционные для System V имена переменных ядра, в которых хранятся эти ограничения.
Таблица 14.1. Типичные значения ограничений, накладываемых на разделяемую память System V
Имя Описание DUnix 4.0B Solaris 2.6 shmmax Максимальный размер сегмента в байтах 4194304 1048576 shmmnb Минимальный размер сегмента разделяемой памяти в байтах 1 1 shmmni Максимальное количество идентификаторов разделяемой памяти в системе 128 100 shmseg Максимальное количество сегментов, подключенных к процессу 32 6Пример
Программа в листинге 14.5 определяет значения четырех ограничений, приведенных в табл. 14.1.
Листинг 14.5. Определение системных ограничений на разделяемую память//svshm/limits.c
1 #include "unpipc.h"
2 #define MAX_NIDS 4096
3 int
4 main(int argc, char **argv)
5 {
6 int i, j, shmid[MAX_NIDS];
7 void *addr[MAX_NIDS];
8 unsigned long size;
9 /* проверка максимального количества открываемых идентификаторов */
10 for (i = 0; i <= MAX_NIDS; i++) {
11 shmid[i] = shmget(IPC_PRIVATE, 1024, SVSHM_MODE | IPC_CREAT);
12 if (shmid[i]== –1) {
13 printf("%d identifiers open at oncen", i);
14 break;
15 }
16 }
17 for (j = 0; j < i; j++)
18 Shmctl(shmid[j], IPC_RMID, NULL);
19 /* определяем максимальное количество подключаемых сегментов */
20 for (i=0;i <= MAX_NIDS; i++) {
21 shmid[i] = Shmget(IPC_PRIVATE, 1024, SVSHM_MODE | IPC_CREAT);
22 addr[i] = shmat(shmid[i], NULL, 0);
23 if (addr[i] == (void *) –1) {
24 printf("%d shared memory segments attached at oncen", i);
25 Shmctl(shmid[i], IPC_RMID, NULL); /* удаляем неудачно подключенный сегмент */
26 break;
27 }
28 }
29 for (j = 0; j < i; j++) {
30 Shmdt(addr[j]);
31 Shmcfl(shmid[j], IPC_RMID, NULL);
32 }
33 /* проверка минимального размера сегмента */
34 for (size = 1; ; size++) {
35 shmid[0] = shmget(IPC_PRIVATE, size, SVSHM_MODE | IPC_CREAT);
36 if (shmid[0] != –1) { /* выход при успешном создании */
37 printf("minimum size of shared memory segment = %lun", size);
38 Shmctl(shmid[0], IPC_RMID, NULL);
39 break;
40 }
41 }
42 /* определение максимального размера сегмента */
43 for (size = 65536; ; size += 4096) {
44 shmid[0] = shmget(IPC_PRIVATE, size, SVSHM_MODE | IPC_CREAT);
45 if (shmid[0] == –1) { /* выход при ошибке */
46 printf("maximum size of shared memory segment = %lun", size-4096);
47 break;
48 }
49 Shmctl(shmid[0], IPC_RMID, NULL);
50 }
51 exit(0);
52 }
Запустив эту программу в Digital Unix 4.0B, увидим:
alpha % limits
127 identifiers open at once
32 shared memory segments attached at once
minimum size of shared memory segment = 1
maximum size of shared memory segment = 4194304
Причина, по которой в табл. 14.1 приведено значение 128 для числа идентификаторов, а наша программа выводит значение 127, заключается в том, что один сегмент разделяемой памяти всегда используется системным демоном.
14.8. Резюме
Разделяемая память System V похожа на разделяемую память Posix. Наиболее схожи функции:
■ shmget для получения идентификатора;
■ shmat для подключения сегмента разделяемой памяти к адресному пространству процесса;
■ shmctl с командой IPC_STAT для получения размера существующего сегмента разделяемой памяти;
■ shmctl с командой IPC_RMID для удаления объекта разделяемой памяти.
Одно из отличий состоит в том, что размер объекта разделяемой памяти Posix может быть изменен в любой момент вызовом ftruncate (как мы продемонстрировали в упражнении 13.1), тогда как размер объекта разделяемой памяти System V устанавливается изначально вызовом shmget и не может быть изменен.
Упражнение
Листинг 6.6 содержал измененную версию программы из листинга 6.4. Новая программа использовала для обращения к объекту IPC System V идентификатор вместо полного имени. Таким образом мы показали, что для доступа к очереди сообщений System V достаточно знать только ее идентификатор (если у нас имеются соответствующие разрешения). Сделайте аналогичные изменения в программе из листинга 14.4, продемонстрировав, что это верно и для разделяемой памяти System V.
ЧАСТЬ 5
УДАЛЕННЫЙ ВЫЗОВ ПРОЦЕДУР
ГЛАВА 15
Двери
15.1. Введение
Поговорим о схеме клиент-сервер и вызове процедур. Существуют три различных типа вызова процедур, показанные на рис. 15.1.
Рис. 15.1. Три типа вызова процедур
1. Локальный вызов процедуры (local procedure call) знаком нам по обычному программированию на языке С. Вызываемая и вызывающая процедуры (функции) при этом относятся к одному и тому же процессу. При этом обычно выполняется некая команда процессора, передающая управление новой процедуре, а вызвавшая процедура сохраняет значение регистров процессора и выделяет место в стеке Под свои локальные переменные.
2. Удаленный вызов процедуры (remote procedure call — RPC) происходит в ситуации, когда вызвавшая и вызываемая процедуры относятся к разным процессам. В такой ситуации мы обычно называем вызвавшую процедуру клиентом, а вызванную — сервером. Во втором сценарии на рис. 15.1 клиент и сервер выполняются на одном и том же узле. Это типичный частный случай третьего сценария, и это именно то, что осуществляется с помощью дверей (doors). Итак, двери дают возможность вызывать процедуру (функцию) другого процесса на том же узле. Один из процессов (сервер) делает процедуру, находящуюся внутри него, доступной для вызова другим процессам (клиентам), создавая для этой процедуры дверь. Мы можем считать двери специальным типом IPC, поскольку при этом между процессами (клиентом и сервером) передается информация в форме аргументов функции и возвращаемых значений.
3. RPC в общем случае дает возможность клиенту на одном узле вызвать процедуру сервера на другом узле, если эти два узла связаны каким-либо образом по сети (третий сценарий на рис. 15.1). Такой вид взаимодействия будет описан в главе 16.