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
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 Мы открываем объект разделяемой памяти, предполагая, что он уже создан и проинициализирован сервером, а затем отображаем его в адресное пространство процесса. После этого дескриптор может быть закрыт.
19-31 Клиент работает по простому алгоритму программы-производителя, но вместо вызова sem_wait(nempty), который приводил бы к блокированию клиента в случае отсутствия места в буфере для следующего сообщения, мы вызываем sem_trywait — эта функция не блокируется. Если значение семафора нулевое, возвращается ошибка EAGAIN. Мы обрабатываем эту ошибку, увеличивая значение счетчика переполнений.