Чтение онлайн

ЖАНРЫ

UNIX: взаимодействие процессов

Стивенс Уильям Ричард

Шрифт:
Счетчик переполнений

9-10 Существует вероятность того, что клиент не сможет отправить сообщение из-за отсутствия свободного места для него. Если программа-клиент представляет собой сервер для других приложений (например, сервер FTP или HTTP), она не должна блокироваться в ожидании освобождения места для сообщения. Поэтому программа-клиент будет написана таким образом, чтобы она не блокировалась, но увеличивала счетчик переполнений (noverflow). Поскольку этот счетчик также является общим для всех процессов, он также должен быть защищен взаимным исключением, чтобы его значение не было повреждено.

Сдвиги
сообщений и их содержимое

11-12 Массив msgoff содержит сдвиги сообщений в массиве msgdata, в котором сообщения хранятся подряд. Таким образом, сдвиг первого сообщения msgoff[0] = 0, msgoff [1] = 256 (значение MESGSIZE), msgoff [2] = 512 и т. д.

Нужно понимать, что при работе с разделяемой памятью использовать сдвиг в таких случаях необходимо, поскольку объект разделяемой памяти может быть отображен в разные области адресного пространства процесса (может начинаться с разных физических адресов). Возвращаемое mmap значение для каждого процесса может быть индивидуальным. Поэтому при работе с объектами разделяемой памяти нельзя использовать указатели, содержащие реальные адреса переменных в этом объекте.

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

Листинг 13.9. Сервер, считывающий сообщения из разделяемой памяти

//pxshm/server2.c

1 #include "cliserv2.h"

2 int

3 main(int argc, char **argv)

4 {

5 int fd, index, lastnoverflow, temp;

6 long offset;

7 struct shmstruct *ptr;

8 if (argc != 2)

9 err_quit("usage: server2 <name>");

10 /* создание объекта разделяемой памяти, установка размера, отображение в память, закрытие дескриптора */

11 shm_unlink(Px_ipc_name(argv[1])); /* ошибка игнорируется */

12 fd = Shm_open(Px_ipc_name(argv[1]), O_RDWR | O_CREAT | O_EXCL, FILE_MODE);

13 ptr = Mmap(NULL, sizeof(struct shmstruct), PROT_READ | PROT_WRITE,

14 MAP_SHARED, fd, 0);

15 Ftruncate(fd, sizeof(struct shmstruct));

16 Close(fd);

17 /* инициализация массива сдвигов */

18 for (index = 0; index < NMESG; index++)

19 ptr->msgoff[index] = index * MESGSIZE;

20 /* инициализация семафоров в разделяемой памяти */

21 Sem_init(&ptr->mutex, 1, 1);

22 Sem_init(&ptr->nempty, 1, NMESG);

23 Sem_init(&ptr->nstored, 1, 0);

24 Sem_init(&ptr->noverflowmutex, 1, 1);

25 /* программа-потребитель */

26 index = 0;

27 lastnoverflow = 0;

28 for (;;) {

29 Sem_wait(&ptr->nstored);

30 Sem_wait(&ptr->mutex);

31 offset = ptr->msgoff[index];

32 printf("index = %d: %s\n", index, &ptr->msgdata[offset]);

33 if (++index >= NMESG)

34 index =0; /*
циклический буфер */

35 Sem_post(&ptr->mutex);

36 Sem_post(&ptr->nempty);

37 Sem_wait(&ptr->noverflowmutex);

38 temp = ptr->noverflow; /* не выводим, пока не снимем блокировку */

39 Sem_post(&ptr->noverflowmutex);

40 if (temp != lastnoverflow) {

41 printf("noverflow = %d\n", temp);

42 lastnoverflow = temp;

43 }

44 }

45 exit(0);

46 }

Создание объекта разделяемой памяти

10-16 Сначала делается вызов shm_unlink, чтобы удалить объект с тем же именем, который мог остаться после другого приложения. Затем объект разделяемой памяти создается вызовом shm_open и отображается в адресное пространство процесса вызовом mmap, после чего дескриптор объекта закрывается.

Инициализация массива сдвигов

17-19 Массив сдвигов инициализируется сдвигами сообщений.

Инициализация семафоров

20-24 Инициализируются четыре семафора, размещаемые в объекте разделяемой памяти. Второй аргумент sem_init всегда делается ненулевым, поскольку семафоры будут использоваться совместно несколькими процессами.

Ожидание сообщения, вывод его содержимого

25-36 Первая половина цикла for написана по стандартному алгоритму потребителя: ожидание изменения семафора nstored, установка блокировки для семафора mutex, обработка данных, увеличение значения семафора nempty.

Обработка переполнений

37-43 При каждом проходе цикла мы проверяем наличие возникших переполнений. Сравнивается текущее значение noverflows с предыдущим. Если значение изменилось, оно выводится на экран и сохраняется. Обратите внимание, что значение считывается с заблокированным взаимным исключением noverflowmutex, но блокировка снимается перед сравнением и выводом значения. Идея в том, что нужно всегда следовать общему правилу минимизации количества операций, выполняемых с заблокированным взаимным исключением. В листинге 13.10 приведен текст программы-клиента.

Листинг 13.10. Клиент, помещающий сообщения в разделяемую память

//pxshm/client2.c

1 #include "cliserv2.h"

2 int

3 main(int argc, char **argv)

4 {

5 int fd, i, nloop, nusec;

6 pid_t pid;

7 char mesg[MESGSIZE];

8 long offset;

9 struct shmstruct *ptr;

10 if (argc != 4)

Поделиться с друзьями: