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

Пример: одна очередь на приложение

Пример: одна очередь на приложение

Вспомните наш простой пример с одним процессом-сервером и одним процессом-клиентом. Если применять программные каналы или FIFO, необходимо наличие двух каналов IPC для передачи данных в обоих направлениях, поскольку эти типы IPC являются однонаправленными. Очереди сообщений позволяют передавать данные в обоих направлениях, причем поле type может использоваться для указания адресата (клиента или сервера).


Рис. 6.2. Мультиплексирование сообщений между несколькими клиентами и одним сервером

Рассмотрим усложненный вариант: один сервер и несколько клиентов. В этом случае можно использовать значение типа 1, например, для обозначения сообщений от любого клиента серверу. Если клиент передаст серверу свой идентификатор процесса в качестве части сообщения, сервер сможет отсылать клиенту сообщения, используя его идентификатор в качестве значения типа сообщения. Каждый клиент будет использовать свой PID в качестве аргумента type при вызове msgrcv. На рис. 6.2 приведен пример использования очереди для мультиплексирования этих сообщений между несколькими клиентами и одним сервером.

ПРИМЕЧАНИЕ

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

Теперь мы можем переделать наш пример с клиентом и сервером, используя одну очередь сообщений с различными типами для разных адресатов. Эти программы используют следующее соглашение: сообщения с типом 1 адресованы серверу, а все остальные сообщения имеют тип, соответствующий идентификатору процесса адресата. При этом запрос клиента должен содержать его PID вместе с полным именем запрашиваемого файла, аналогично программе в разделе 4.8.

В листинге 6.12 приведен текст функции main сервера. Заголовочный файл svmsg.h был приведен в листинге 6.7. Создается единственная очередь сообщений (если она существует, ошибки не возникнет). Идентификатор этой очереди сообщений используется в качестве обоих аргументов при вызове функции server.

Листинг 6.12. Функция main сервера

//svmsgmpx1q/server_main.с
1  #include "svmsg.h"
2  void server(int, int);
3  int
4  main(int argc, char **argv)
5  {
6   int msqid;
7   msqid = Msgget(MQ_KEY1, SVMSG_MODE | IPC_CREAT);
8   server(msqid, msqid); /* одна очередь в обе стороны */
9   exit(0);
10 }

Функция server обеспечивает работу сервера. Ее текст приведен в листинге 6.13. Эта функция представляет собой комбинацию листинга 4.10 — нашего сервера FIFO, считывавшего команды, состоявшие из идентификатора процесса и полного имени файла, — и листинга 4.16, в котором использовались функции mesg_send и mesg_recv. Обратите внимание, что идентификатор процесса, отправляемый клиентом, используется в качестве типа для всех сообщений, отправляемых сервером этому клиенту. Эта функция представляет собой бесконечный цикл, в котором считываются запросы клиентов и отсылаются запрошенные файлы. Этот сервер является последовательным (см. раздел 4.9).

В листинге 6.14 приведен текст функции main клиента. Клиент открывает очередь сообщений, которая должна была быть создана сервером заранее.

Функция client, текст которой дан в листинге 6.15, обеспечивает всю обработку со стороны клиента. Эта функция представляет собой комбинацию программ из листингов 4.11 и 4.15. В первой программе клиент отсылал свой идентификатор и полное имя файла, а во второй программе использовались функции mesg_send и mesg_recv. Обратите внимание, что тип сообщений, запрашиваемых функцией mesg_recv, совпадает с идентификатором процесса клиента.

Функции client и server используют функции mesg_send и mesg_recv из листингов 6.9 и 6.11.

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

//svmsgmpx1q/server.c
1  #include "mesg.h"
2  void
3  server(int readfd, int writefd)
4  {
5   FILE *fp;
6   char *ptr;
7   pid_t pid;
8   ssize_t n;
9   struct mymesg mesg;
10  for (;;) {
11   /* считывание полного имени из канала IPC */
12   mesg.mesg_type = 1:
13   if ((n = Mesg_recv(readfd, &mesg)) == 0) {
14    err_msg("pathname missing");
15    continue;
16   }
17   mesg.mesg_data[n] = ''; /* полное имя */
18   if ((ptr = strchr(mesg.mesg_data, ' ')) == NULL) {
19    err_msg("bogus request: %s", mesg.mesg_data);
20    continue;
21   }
22   *ptr++ =0; /* ptr = полное имя */
23   pid = atol(mesg.mesg_data);
24   mesg.mesg_type = pid; /* для обратных сообщений */
25   if ((fp = fopen(ptr, "r")) == NULL) {
26    /* 4error: must tell client */
27    snprintf(mesg.mesg_data + n, sizeof(mesg.mesg_data) – n,
28     ": can't open. %sn", strerror(errno));
29    mesg.mesg_len – strlen(ptr);
30    memmove(mesg.mesg_data, ptr, mesg.mesg_len);
31    Mesg_send(writefd, &mesg);
32   } else {
33    /* файл открыт, копируем клиенту */
34    while (Fgets(mesg.mesg_data, MAXMESGDATA, fp) != NULL) {
35     mesg.mesg_len = strlen(mesg.mesg_data);
36     Mesg_send(writefd, &mesg);
37    }
38    Fclose(fp);
39   }
40   /* сообщение нулевой длины заканчивает связь */
41   mesg.mesg_len = 0;
42   Mesg_send(writefd, &mesg);
43  }
44 }

Листинг 6.14. Функция main клиента

//svmsgmpx1q/client_main.c
1  #include "svmsg.h"
2  void client(int, int);
3  int
4  main(int argc, char **argv)
5  {
6   int msqid;
7   /* сервер должен был создать очередь */
8   msqid = Msgget(MQ_KEY1, 0);
9   client(msqid, msqid); /* одна очередь в обе стороны */
10  exit(0);
11 }

Листинг 6.15. Функция client

//svmsgmpx1q/client.с
1  #include "mesg.h"
2  void
3  client(int readfd, int writefd)
4  {
5   size_t len;
6   ssize_t n;
7   char *ptr;
8   struct mymesg mesg;
9   /* инициализируем буфер идентификатором процесса и пробелом */
10  snprintf(mesg.mesg_data, MAXMESGDATA. "%ld ", (long) getpid());
11  len = strlen(mesg.mesg_data);
12  ptr = mesg.mesg_data + len;
13  /* считываем полное имя файла */
14  Fgets(ptr, MAXMESGDATA – len, stdin);
15  len = strlen(mesg.mesg_data);
16  if (mesg.mesg_data[len-1] == 'n')
17   len--; /* удаляем перевод строки fgets() */
18  mesg.mesg_len = len;
19  mesg.mesg_type = 1;
20  /* записываем PID и имя файла в канал IPC */
21  Mesg_send(writefd, &mesg);
22  /* считываем из канала IPC, записываем в stdout */
23  mesg.mesg_type = getpid();
24  while ((n = Mesg_recv(readfd, &mesg)) > 0)
25   Write(STDOUT_FILENO, mesg.mesg_data, n);
26 }
 

Оглавление книги


Генерация: 1.469. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз