
- •Часть 4. Локальное взаимодействие процессов
- •Глава 16. Блокирование записей 89
- •12.2. Процессы, потоки и общий доступ к информации
- •12.3. Живучесть объектов ipc
- •12.4. Пространства имен
- •12.5. Действие команд fork, exec и exit на объекты ipc
- •12.6. Комментарии к примерам ipc
- •12.7. Выводы по главе 12
- •12.8. Упражнения по главе 12
- •Глава 13. Именованные и неименованные каналы
- •13.1. Введение
- •13.2. Приложение типа клиент-сервер
- •13.3. Программные каналы
- •13.4. Функции popen и pclose
- •13.5. Именованные каналы (fifo)
- •13.6. Некоторые свойства именованных и неименованных каналов
- •13.7. Один сервер, несколько клиентов
- •13.8. Последовательные и параллельные серверы
- •13.9. Ограничения программных каналов и fifo
- •13.10. Выводы по главе 13
- •13.11. Упражнения по главе 13
- •Глава 14. Программные потоки
- •14.1. Введение
- •14.2. Концепция потоков
- •14.3. Идентификация потоков
- •14.4. Создание потока
- •14.5. Завершение потока
- •Функции управления процессами и потоками
- •14.6. Установка атрибутов потока
- •14.7. Реентерабельность
- •Альтернативные версии функций, безопасные в многопоточной среде
- •14.8. Локальные данные потоков
- •14.9. Принудительное завершение потоков
- •Некоторые точки выхода, определенные стандартом Posix.1
- •14.10. Потоки и сигналы
- •14.11. Выводы по главе 14
- •14.12. Упражнения по главе 14 Глава 15. Средства синхронизации потоков
- •15.1. Введение
- •15.2. Взаимные исключения: установка и снятие блокировки
- •15.2.1. Схема производитель-потребитель
- •15.2.2. Блокирование и опрос
- •15.2.3. Предотвращение тупиковых ситуаций
- •15.3. Условные переменные
- •15.3.1. Ожидание и сигнализация
- •15.3.2. Исключение состояния гонок
- •15.4. Блокировки чтения-записи
- •15.5. Атрибуты средств синхронизации потоков
- •15.5.1. Атрибуты взаимных исключений
- •Поведение взаимных исключений различных типов
- •15.5.2. Атрибуты условных переменных
- •15.5.3. Атрибуты блокировок чтения-записи
- •15.6. Выводы по главе 15
- •15.7. Упражнения по главе 15
- •Глава 16. Блокирование записей
- •16.1. Введение
- •16.2. Блокирование записей и файлов
- •16.3. Блокирование записей с помощью fcntl по стандарту Posix
- •16.4. Рекомендательная блокировка
- •16.5. Обязательная блокировка
- •16.6. Приоритет чтения и записи Выводы по главе 16
- •Упражнения по главе 16 Глава 17. System V ipc
- •17.1. Введение
- •17.2. Ключи типа key_t и функция ftok
- •17.3. Структура ipc_perm
- •17.4. Создание и открытие каналов ipc
- •17.5. Разрешения ipc
- •17.6. Программы ipcs и ipcrm
- •17.7. Ограничения ядра
- •17.8. Выводы по главе 17
- •17.9. Упражнения по главе 17
- •Глава 18. Очереди сообщений System V
- •18.1. Введение
- •18.2. Функция msgget
- •18.3. Функция msgsnd
- •18.4. Функция msgrcv
- •18.5. Функция msgctl
- •18.6. Пример программы клиент-сервер
- •18.7. Мультиплексирование сообщений
- •18.7.1. Пример: одна очередь на приложение
- •18.7.2. Пример: одна очередь для каждого клиента
- •18.8. Ограничения, накладываемые на очереди сообщений
- •18.9. Выводы по главе 18
- •18.10. Упражнения по главе 18
- •Глава 19. Семафоры System V
- •19.1. Введение
- •19.2. Функция semget
- •19.3. Функция semop
- •19.4. Функция semctl
- •19. . Ограничения семафоров System V
- •19. . Выводы по главе 19
- •19. . Упражнения по главе 19 Глава 20. Введение в разделяемую память
- •20.1. Введение
- •20.2. Функции mmap, munmap и msync
- •20.3. Увеличение счетчика в отображаемом в память файле
- •20.4. Неименованное отображение в память
- •20.5. Обращение к объектам, отображенным в память
- •20.6. Выводы по главе 20
- •20.7. Упражнения по главе 20
- •Глава 21. Разделяемая память System V
- •21.1. Введение
- •21.2. Функция shmget
- •21.3. Функция shmat
- •21.4. Функция shmdt
- •21.5. Функция shmctl
- •21.6. Ограничения, накладываемые на разделяемую память
- •21.7. Выводы по главе 21
- •21.8. Упражнения по главе 21
18.6. Пример программы клиент-сервер
Перепишем наш пример программы типа клиент-сервер из раздела 13.2 с использованием двух очередей сообщений. Одна из очередей предназначена для передачи сообщений от клиента серверу, а другая – в обратную сторону.
Программный код сервера приведен в листинге 17.2. Программа создает обе очереди сообщений, и не беда, если какая-нибудь из них уже существует, потому что мы не указываем флагIPC_EXCL. Далее программа вызывает функциюserver. По окончании передачи данных сервер отправляет сообщение, содержащее только заголовок (без данных) для обозначения конца связи.
Листинг 17.2. Программа-сервер, использующая очереди сообщений
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/msg.h>
#define MSG_R S_IRUSR
#define MSG_W S_IWUSR
#define SVMSG_MODE (MSG_R | MSG_W | MSG_R>>3 | MSG_R>>6)
#define MQ_KEY1 1234L
#define MQ_KEY2 2345L
#define MAXMESGDATA (4096 - 2*sizeof(long))
struct mymesg
{
long mesg_type; /* тип сообщения, должен быть > 0 */
long mesg_len; /* количество байтов в массиве mesg_data, может быть 0 */
char mesg_data[MAXMESGDATA];
};
void server(int readid, int writeid)
{
FILE *fp;
ssize_t n;
struct mymesg mesg;
char buff[MAXMESGDATA];
/* чтение полного имени файла из канала IPC */
if ((n = msgrcv(readid, &mesg, sizeof(mesg.mesg_len) + MAXMESGDATA, 1, 0))
== -1)
{
fprintf(stderr, "Сервер: ошибка чтения полного имени файла из канала IPC:
%s\n", strerror(errno));
exit(1);
}
if (n == sizeof(mesg.mesg_len))
{
fprintf(stderr, "Сервер: получено пустое имя файла, завершение работы\n");
exit(1);
} /* добавляем признак конца строки к полному имени */
mesg.mesg_data[n - sizeof(mesg.mesg_len)] = '\0';
strncpy(buff, mesg.mesg_data, sizeof(mesg.mesg_data));
mesg.mesg_type = 1;
if ((fp = fopen(buff, "r")) == NULL)
{ /* сообщаем клиенту об ошибке открытия файла */
snprintf(mesg.mesg_data, sizeof(mesg.mesg_data), "Сервер: ошибка открытия
файла %s: %s\n", buff, strerror(errno));
mesg.mesg_len = strlen(mesg.mesg_data);
if (msgsnd(writeid, &mesg, sizeof(mesg.mesg_len) + strlen(mesg.mesg_data),
0) == -1)
{
fprintf(stderr, "Сервер: ошибка записи в канал IPC: %s\n",
strerror(errno));
exit(1);
}
}
else /* файл успешно открыт; копируем его в канал IPC */
{
while (fgets(mesg.mesg_data, MAXMESGDATA, fp) != NULL)
{
mesg.mesg_len = strlen(mesg.mesg_data);
if (msgsnd(writeid, &mesg, sizeof(mesg.mesg_len) + strlen(mesg.mesg_data),
0) == -1)
{
fprintf(stderr, "Сервер: ошибка записи содержимого файла %s в канал IPC:
%s\n", buff, strerror(errno));
exit(1);
}
}
if (ferror(fp))
{
snprintf(mesg.mesg_data, sizeof(mesg.mesg_data), "Сервер: ошибка чтения
содержимого файла %s: %s\n", buff, strerror(errno));
mesg.mesg_len = strlen(mesg.mesg_data);
if (msgsnd(writeid, &mesg, sizeof(mesg.mesg_len) + strlen(mesg.mesg_data),
0) == -1)
{
fprintf(stderr, "Сервер: ошибка записи в канал IPC: %s\n",
strerror(errno));
exit(1);
}
}
fclose(fp);
}
/* отправка сообщения без данных для обозначения конца связи */
mesg.mesg_len = 0;
if (msgsnd(writeid, &mesg, sizeof(mesg.mesg_len), 0) == -1)
{
fprintf(stderr, "Сервер: ошибка отправки признака конца связи по каналу IPC:
%s\n", strerror(errno));
exit(1);
}
}
int main(int argc, char **argv)
{
int readid, writeid;
if ((readid = msgget(MQ_KEY1, SVMSG_MODE | IPC_CREAT)) == -1)
{
fprintf(stderr, "Сервер: ошибка вызова функции msgget(%lu): %s\n", MQ_KEY1,
strerror(errno));
exit(1);
}
if ((writeid = msgget(MQ_KEY2, SVMSG_MODE | IPC_CREAT)) == -1)
{
fprintf(stderr, "Сервер: ошибка вызова функции msgget(%lu): %s\n", MQ_KEY2,
strerror(errno));
exit(1);
}
server(readid, writeid);
exit(0);
}
В листинге 17.3приведен программный код клиента. Программа открывает две очереди сообщений и вызывает функциюclient.
Листинг 17.3. Программа-клиент, использующая очереди сообщений
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/msg.h>
#define MQ_KEY1 1234L
#define MQ_KEY2 2345L
#define MAXMESGDATA (4096 - 2*sizeof(long))
struct mymesg
{
long mesg_type; /* тип сообщения, должен быть > 0 */
long mesg_len; /* количество байтов в массиве mesg_data, может быть 0 */
char mesg_data[MAXMESGDATA];
};
int readid, writeid;
void msg_del(void)
{
/* удаление очередей сообщений */
if (msgctl(writeid, IPC_RMID, NULL) == -1)
fprintf(stderr, "Клиент: ошибка вызова функции msgctl(%lu, IPC_RMID): %s\n",
MQ_KEY1, strerror(errno));
if (msgctl(readid, IPC_RMID, NULL) == -1)
fprintf(stderr, "Клиент: ошибка вызова функции msgctl(%lu, IPC_RMID): %s\n",
MQ_KEY2, strerror(errno));
}
void client(int readid, int writeid)
{
size_t len;
ssize_t n;
struct mymesg mesg;
/* чтение полного имени файла из стандартного потока ввода */
if (fgets(mesg.mesg_data, MAXMESGDATA, stdin) == NULL)
{
fprintf(stderr, "Клиент: ошибка чтения полного имени файла из стандартного
потока ввода\n");
exit(1);
} /* fgets() гарантирует завершающий нулевой байт */
len = strlen(mesg.mesg_data);
if (mesg.mesg_data[len-1] == '\n')
len--; /* удаление символа перевода строки (если есть) */
if (len == 0)
{
fprintf(stderr, "Клиент: введено пустое имя файла, завершение работы\n");
exit(1);
}
mesg.mesg_type = 1;
mesg.mesg_len = len;
/* запись полного имени файла в канал IPC */
if (msgsnd(writeid, &mesg, sizeof(mesg.mesg_len) + len, 0) == -1)
{
fprintf(stderr, "Клиент: ошибка записи полного имени файла в канал IPC:
%s\n", strerror(errno));
exit(1);
}
/* считывание из канала IPC, запись в стандартный поток вывода */
while ((n = msgrcv(readid, &mesg, sizeof(mesg.mesg_len) + MAXMESGDATA, 1, 0))
> 0)
{
if (n == sizeof(mesg.mesg_len))
break; /* принято сообщение без данных; конец связи */
if (write(STDOUT_FILENO, mesg.mesg_data, n - sizeof(mesg.mesg_len)) !=
n - sizeof(mesg.mesg_len))
{
fprintf(stderr, "Клиент: ошибка записи содержимого файла в стандартный поток вывода: %s\n", strerror(errno));
exit(1);
}
}
if (n == -1)
{
fprintf(stderr, "Клиент: ошибка чтения содержимого файла из канала IPC:
%s\n", strerror(errno));
exit(1);
}
}
int main(int argc, char **argv)
{
if (atexit(msg_del))
{
fprintf(stderr, "Клиент: невозможно зарегистрировать msg_del: %s\n",
strerror(errno));
exit(1);
}
/* очереди сообщений должны быть созданы сервером */
if ((writeid = msgget(MQ_KEY1, 0)) == -1)
{
fprintf(stderr, "Клиент: ошибка вызова функции msgget(%lu): %s\n", MQ_KEY1,
strerror(errno));
exit(1);
}
if ((readid = msgget(MQ_KEY2, 0)) == -1)
{
fprintf(stderr, "Клиент: ошибка вызова функции msgget(%lu): %s\n", MQ_KEY2,
strerror(errno));
exit(1);
}
client(readid, writeid);
exit(0);
}