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

ЖАНРЫ

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

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

Шрифт:

Нам нужно обработать такой возврат из вызванной функции, поэтому мы пишем новую функцию-обертку Mesg_recv, приведенную в листинге 6.20. Эта программа допускает возвращение ошибки с кодом EINTR функцией mesg_recv (которая просто вызывает msgrcv), и, если это происходит, мы просто еще раз вызываем mesg_recv.

Листинг 6.19. Функция server

//svmsgmpxnq/server.c

1 #include "mesg.h"

2 void

3 server(int readid, int writeid)

4 {

5 FILE *fp;

6 char *ptr;

7 ssize_t n;

8 struct mymesg mesg;

9 void sig_chld(int);

10 Signal(SIGCHLD, sig_chld);

11 for (;;) {

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).

ПРИМЕЧАНИЕ

В листинге 5.12 мы привели решение с использованием очередей сообщений Posix, которое не требовало вызова fork. Для очередей сообщений Posix можно было обойтись одним процессом, поскольку они предусматривают уведомление о появлении нового сообщения с помощью сигнала. Для очередей System V такая возможность не предусмотрена, поэтому приходится порождать процесс, который будет блокироваться при вызове msgrcv.

Другим недостатком очередей сообщений System V по сравнению с сетевым интерфейсом является невозможность считывания сообщений из оперативной памяти (возможность, предоставляемая флагом MSG_PEEK для функций recv, recvfrom, recvmsg [24, с. 356]). Если бы такая возможность имелась, в предложенной только что схеме клиент-сервер (для обхода проблемы с select) можно было бы сделать работу более эффективной, указав флаг peek при вызове msgrcv дочерним процессом и записав 1 байт в канал при приходе сообщения, а родительский процесс тогда просто считывал бы сообщение из очереди.

6.10. Ограничения, накладываемые на очереди сообщений

Как отмечалось в разделе 3.8, на очереди сообщений часто накладываются системные oгрaничeния. В табл. 6.2 приведены значения этих oгрaничeний для двух конкретных реализаций. Первая колонка представляет собой традиционное имя System V для переменной ядра, хранящей это ограничение.

Таблица 6.2. Характерные значения ограничений для очередей сообщений

Имя Описание DUnix 4.0B Solaris 2.6
msgmax Максимальное количество байтов в сообщении 8192 2048
msgmnb Максимальное количество байтов в очереди сообщений 16384 4096
msgmni Максимальное количество очередей сообщений в системе 64 50
msgtlq Максимальное количество сообщений в системе 40 40 
Поделиться с друзьями: