Читать «UNIX: взаимодействие процессов» онлайн - страница 106
Уильям Ричард Стивенс
12 /* считывание имени файла из очереди */
13 mesg.mesg_type = 1;
14 if ((n = Mesg_recv(readid, &mesg)) == 0) {
15 err_msg("pathname missing");
16 continue;
17 }
18 mesg.mesg_data[n] = 40'; /* имя файла */
19 if ((ptr = strchr(mesg.mesg_data, ' ')) = NULL) {
20 err_msg("bogus request: %s", mesg.mesg_data);
21 continue;
22 }
23 *ptr++ = 0; /* ptr = имя файла */
24 writeid = atoi(mesg.mesg_data);
25 if (Fork() == 0) { /* дочерний процесс */
26 if ((fp = fopen(ptr, "r")) == NULL) {
27 /* ошибка: нужно сообщить клиенту */
28 snprintf(mesg.mesg_data + n, sizeof(mesg.mesg_data) – n,
29 ": can't open, %s\n", strerror(errno));
30 mesg.mesg_len = strlen(ptr);
31 memmove(mesg.mesg_data, ptr, mesg.mesg_len);
32 Mesg_send(writeid, &mesg);
33 } else {
34 /* файл открыт, копируем клиенту */
35 while (Fgets(mesg.mesg_data, MAXMESGDATA, fp) != NULL) {
36 mesg.mesg_len = strlen(mesg.mesg_data);
37 Mesg_send(writeid, &mesg);
38 }
39 Fclose(fp);
40 }
41 /* отправка сообщения нулевой длины, указывающего конец файла */
42 mesg.mesg_len = 0;
43 Mesg_send(writeid, &mesg);
44 exit(0); /* завершение дочернего процесса */
45 }
46 /* родительский процесс просто зациклен */
47 }
48 }
Листинг 6.20. Функция-обертка Mesg_recv, обрабатывающая прерванный системный вызов
//svmsgmpxnq/mesg_recv.с
10 ssize_t
11 Mesg_recv(int id, struct mymesg *mptr)
12 {
13 ssize_t n;
14 do {
15 n = mesg_recv(id, mptr);
16 } while (n == –1 && errno == EINTR);
17 if (n == –1)
18 err_sys("mesg_recv error");
19 return(n);
20 }
6.9. Использование select и poll с очередями сообщений
Одним из недостатков очередей сообщений System V является то, что они идентифицируются не дескрипторами, а идентификаторами. Поэтому с ними нельзя использовать функции select и poll (глава 6 [24]).
ПРИМЕЧАНИЕ
На самом деле одна из версий Unix, а именно AIX (созданная IBM), позволяет использовать select с очередями сообщений System V, а не только с дескрипторами. Но эта возможность имеется только в AIX.
Этот недостаток часто всплывает, когда возникает необходимость написать сервер, работающий одновременно с сетевыми соединениями и с IPC. Сетевые соединения с использованием интерфейса сокетов или XTI ([24]) используют дескрипторы, что позволяет вызывать select или poll. Программные каналы и FIFO также идентифицируются дескрипторами, поэтому для них тоже допустимо использование этих функций.
Одним из решений этой проблемы является следующее: сервер должен создать канал и породить процесс, который будет заблокирован при вызове msgrcv. При получении сообщения произойдет возврат из msgrcv, дочерний процесс получит это сообщение из очереди и запишет его в канал. Затем родительский процесс может использовать функцию select для канала совместно с сетевыми соединениями. Недостаток этого подхода в том, что сообщения обрабатываются трижды: при считывании дочерним процессом с помощью msgrcv, при отправке в канал и при считывании из канала родительским процессом. Для ускорения обработки порожденный процесс может создать сегмент совместно используемой с породившим процессом памяти, а канал использовать как флаг (упражнение 12.5).