9-10 Существует вероятность того, что клиент не сможет отправить сообщение из-за отсутствия свободного места для него. Если программа-клиент представляет собой сервер для других приложений (например, сервер FTP или HTTP), она не должна блокироваться в ожидании освобождения места для сообщения. Поэтому программа-клиент будет написана таким образом, чтобы она не блокировалась, но увеличивала счетчик переполнений (noverflow). Поскольку этот счетчик также является общим для всех процессов, он также должен быть защищен взаимным исключением, чтобы его значение не было повреждено.
11-12 Массив msgoff содержит сдвиги сообщений в массиве msgdata, в котором сообщения хранятся подряд. Таким образом, сдвиг первого сообщения msgoff[0] = 0, msgoff [1] = 256 (значение MESGSIZE), msgoff [2] = 512 и т. д.
Нужно понимать, что при работе с разделяемой памятью использовать сдвиг в таких случаях необходимо, поскольку объект разделяемой памяти может быть отображен в разные области адресного пространства процесса (может начинаться с разных физических адресов). Возвращаемое mmap значение для каждого процесса может быть индивидуальным. Поэтому при работе с объектами разделяемой памяти нельзя использовать указатели, содержащие реальные адреса переменных в этом объекте.
В листинге 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
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, после чего дескриптор объекта закрывается.