#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/msg.h>
#define MAX_TEXT 512
struct my_msg_st {
long int my_msg_type;
char some_text[MAX_TEXT];
};
int main() {
int running = 1;
struct my_msg_st some_data;
int msgid;
<i> char buffer = [BUFSIZ];</i>
msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
if (msgid == -1) {
fprintf(stderr, "msgget failed with error: %dn", errno);
exit(EXIT_FAILURE);
}
while (running) {
<i> printf("Enter some text: ");</i>
<i> fgets(buffer, BUFSIZ, stdin);</i>
<i> some_data.my_msg_type = 1;</i>
<i> strcpy(some_data.some_text, buffer);</i>
<i> if (msgsnd(msgid, (void*)&some_data, MAX_TEXT, 0)) == -1) {</i>
<i> fpintf(stderr, "msgsnd failedn");</i>
<i> exit(EXIT_FAILURE);</i>
<i> }</i>
<i> if (strncmp(buffer, "end", 3) == 0) {</i>
<i> running = 0;</i>
<i> }</i>
}
exit(EXIT_SUCCESS);
}
В отличие от примера с каналами, процессам нет нужды предоставлять метод их собственной синхронизации. Это существенное преимущество сообщений по сравнению с каналами.
Если в очереди сообщений есть место, отправитель может создать очередь, поместить в нее какие-либо данные и завершить выполнение еще до того, как начнет выполняться приемник. Первой следует запускать программу-отправителя msg2. Далее приведен пример вывода:
$ <b>./msg2</b>
Enter some text: <b>hello</b>
Enter some text: <b>How are you today?</b>
Enter some text: <b>end</b>
$ <b>./msg1</b>
You wrote: hello
You wrote: How are you today?
You wrote: end
Как это работает
Программа-отправитель создает очередь сообщений с помощью функции msgget; далее она добавляет сообщения в очередь, применяя функцию msgsnd. Программа-приемник получает идентификатор очереди сообщений с помощью функции msgget и получает сообщения до тех пор, пока не будет найден специальный текст end. Затем программа приводит все в порядок, удаляя очередь сообщений с помощью функции msgctl.
Приложение для работы с базой данных компакт-дисков
Сейчас подходящий момент для модификации вашего приложения, управляющего базой данных компакт-дисков, с помощью средств IPC, с которыми вы познакомились в этой главе.
Вы могли бы применить множество разных комбинаций трех разновидностей средств IPC, но поскольку информация, которую следует передавать, очень мала по объему, есть смысл реализовать передачу запросов и ответов непосредственно с помощью очередей сообщений.
Если объемы данных, которые вы должны передавать, были бы велики, можно было бы рассмотреть передачу реальных данных в совместно используемой памяти, одновременно применяя семафоры или сообщения для отправки маркера или "опознавательного знака", информирующего другой процесс о наличии данных в совместно используемой памяти.
Интерфейс очереди сообщений устраняет проблему, которая у вас была в главе 11, когда вы нуждались в открытом канале у обоих процессов в момент передачи данных. Применение очередей сообщений позволяет одному процессу поместить сообщения в очередь, даже если этот процесс в данный момент — единственный пользователь очереди.
Вам нужно ответить лишь на один важный вопрос: как возвращать ответы клиентам? Простым решением было бы наличие одной очереди для сервера и по одной очереди для каждого клиента. Если одновременно существует много клиентов, такой подход может вызвать проблемы, т.к. потребуется большое количество очередей. Используя в сообщении поле идентификатора сообщения, вы сможете разрешить всем клиентам пользоваться одной очередью и адресовать ответные сообщения конкретным клиентским процессам с помощью включенного в сообщение идентификатора клиентского процесса. Далее каждый клиент может извлекать сообщения, адресованные только ему, оставляя сообщения для других клиентов в очереди.
Для преобразования приложения, работающего с базой данных компакт-дисков, с помощью средств IPC вам придется заменить только файл pipe_imp.c из сопроводительного программного кода к главе 13. Далее мы рассмотрим важные разделы замещающего файла ipc_imp.c.
Пересмотр функций сервера
Сначала нужно обновить серверные функции.
1. Прежде всего, включите необходимые заголовочные файлы, объявите несколько ключей очередей сообщений и структуру для хранения данных сообщения:
#include "cd_data.h"
#include "cliserv.h"
#include <sys/msg.h>
#define SERVER_MQUEUE 1234
#define CLIENT_MQUEUE 4321
struct msg_passed {
long int msg_key; /* Используется для клиентского pid */
message_db_t real message;
};
2. Две глобальные переменные хранят идентификаторы двух очередей, возвращаемые функцией msgget:
static int serv_qid = -1;
static int cli_qid = -1;