Читать интересную книгу UNIX: взаимодействие процессов - Уильям Стивенс

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 36 37 38 39 40 41 42 43 44 ... 128

//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, %sn", 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 

В этом разделе мы хотели показать типичные значения ограничений, чтобы помочь в планировании переносимых программ. При выполнении приложений, активно использующих очереди сообщений, обычно требуется настройка этих (или аналогичных) параметров ядра (что описано в разделе 3.8).

Пример

В листинге 6.21 приведен текст программы, которая определяет четыре ограничения, показанные в табл. 6.2.

Листинг 6.21. Определение системных ограничений для очередей сообщений System V

//svmsg/limits.c

1  #include "unpipc.h"

2  #define MAX_DATA 64*1024

3  #define MAX_NMESG 4096

4  #define MAX_NIDS 4096

5  int max_mesg;

6  struct mymesg {

7   long type;

8   char data[MAX_DATA];

9  } mesg;

10 int

11 main(int argc, char **argv)

12 {

13  int i, j, msqid, qid[MAX_NIDS];

14  /* определение максимального размера сообщения */

15  msqid = Msgget(IPC_PRIVATE, SVMSG_MODE | IPC_CREAT);

16  mesg.type = 1;

17  for (i = MAX_DATA; i > 0; i –= 128) {

18   if (msgsnd(msqid, &mesg, i, 0) == 0) {

19    printf("maximum amount of data per message = %dn", i);

20    max_mesg = i;

21    break;

22   }

23   if (errno != EINVAL)

24    err_sys("msgsnd error for length %d", i);

25  }

26  if (i == 0)

27   err_quit("i == 0");

28  Msgct(lmsqid, IPC_RMID, NULL);

29  /* количество сообщений в очереди */

30  mesg.type = 1;

31  for (i = 8; i <= max_mesg; i *= 2) {

32   msqid = Msgget(IPC_PRIVATE, SVMSG_MODE | IPC_CREAT);

33   for (j = 0; j < MAX_NMESG; j++) {

34    if (msgsnd(msqid, &mesg, i, IPC_NOWAIT) != 0) {

35     if (errno == EAGAIN)

36      break;

37     err_sys("msgsnd error, i = %d, j = %d", i, j);

38     break;

39    }

40   }

41   printf("%d %d-byte messages were placed onto queue,", j, i);

42   printf(" %d bytes totaln". i*j);

43   Msgctl(msqid, IPC_RMID, NULL);

44  }

45  /* максимальное количество идентификаторов */

46  mesg.type = 1;

47  for (i = 0; i <= MAX_NIDS; i++) {

48   if ((qid[i] = msgget(IPC_PRIVATE, SVMSG_MODE | IPC_CREAT)) == –1) {

49    printf("%d identifiers open at oncen", i);

50    break;

51   }

52  }

53  for (j = 0; j < i; j++)

54   Msgctl(qid[j], IPC_RMID, NULL);

55  exit(0);

56 }

Определение максимального размера сообщения

14-28 Для определения максимально возможного размера сообщения мы пытаемся послать сообщение, в котором будет 65 536 байт данных, и если эта попытка оказывается неудачной, уменьшаем этот объем до 65 408, и т.д., пока вызов msgsnd не окажется успешным.

1 ... 36 37 38 39 40 41 42 43 44 ... 128
На этом сайте Вы можете читать книги онлайн бесплатно русская версия UNIX: взаимодействие процессов - Уильям Стивенс.

Оставить комментарий