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 приведен текст программы-клиента.
//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)
11 err_quit("usage: client2 name #loops #usec");
12 nloop = atoi(argv[2]);
13 nusec = atoi(argv[3]);
14 /* открытие и отображение объекта разделяемой памяти, созданного сервером заранее */
15 fd = Shm_open(Px_ipc_name(argv[1]), O_RDWR, FILE_MODE);
16 ptr = Mmap(NULL, sizeof(struct shmstruct), PROT_READ | PROT_WRITE,
17 MAP_SHARED, fd, 0);
18 Close(fd);
19 pid = getpid;
20 for (i = 0; i nloop; i++) {
21 Sleep_us(nusec);
22 snprintf(mesg, MESGSIZE, "pid %ld; message %d", (long) pid, i);
23 if (sem_trywait(ptr-nempty) == –1) {
24 if (errno == EAGAIN) {
25 Sem_wait(ptr-noverflowmutex);
26 ptr-noverflow++;
27 Sem_post(ptr-noverflowmutex);
28 continue;
29 } else
30 err_sys("sem_trywait error");
31 }
32 Sem_wait(ptr-mutex);
33 offset = ptr-msgoff[ptr-nput];
34 if (++(ptr-nput) = NMESG)
35 ptr-nput = 0; /* циклический буфер */
36 Sem_post(ptr-mutex);
37 strcpy(ptr-msgdata[offset], mesg);
38 Sem_post(ptr-nstored);
39 }
40 exit(0);
41 }
10-13 Первый аргумент командной строки задает имя объекта разделяемой памяти; второй — количество сообщений, которые должны быть отправлены серверу данным клиентом. Последний аргумент задает паузу перед отправкой очередного сообщения (в микросекундах). Мы сможем получить ситуацию переполнения, запустив одновременно несколько экземпляров клиентов и указав небольшое значение для этой паузы. Таким образом мы сможем убедиться, что сервер корректно обрабатывает ситуацию переполнения.
14-18 Мы открываем объект разделяемой памяти, предполагая, что он уже создан и проинициализирован сервером, а затем отображаем его в адресное пространство процесса. После этого дескриптор может быть закрыт.