- •Часть 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.4. Функция msgrcv
Сообщение может быть считано из очереди с помощью функции msgrcv.
#include <sys/msg.h>
int msgrcv (int msqid, void *ptr, size_t length, long type, int flag);
/* возвращает количество данных в сообщении, -1 – в случае ошибки */
Аргумент ptrуказывает, куда следует помещать принимаемые данные. Как и дляmsgsnd, он указывает на поле данных типаlong, которое непосредственно предшествует полезным данным.
Аргумент lengthзадает размер относящейся к полезным данным части буфера, на который указываетptr. Это максимальное количество данных, которое может быть возвращено функцией. Поле типаlongне входит в эту длину.
Аргумент typeопределяет тип сообщения, которое нужно считать из очереди:
если значение typeравно 0, возвращается первое сообщение в очереди (то есть при указании типа 0 возвращается старейшее сообщение);
если тип больше нуля, возвращается первое сообщение, тип которого равен указанному;
если тип меньше нуля, возвращается первое сообщение с наименьшим типом, значение которого меньше либо равно модулю аргумента type.
Рассмотрим пример очереди сообщений, изображенный на рис. 17.1. В этой очереди имеются три сообщения:
первое сообщение имеет тип 100 и длину 1;
второе сообщение имеет тип 200 и длину 2;
третье сообщение имеет тип 300 и длину 3.
Таблица 17.1 показывает, какое сообщение будет возвращено при различных значениях аргумента type.
Таблица 17.1
Возвращаемое значение в зависимости от аргумента type
|
type |
Тип возвращаемого сообщения |
|
0 |
100 |
|
100 |
100 |
|
200 |
200 |
|
300 |
300 |
|
-100 |
100 |
|
-200 |
100 |
|
-300 |
100 |
Аргумент flagуказывает, что делать, если в очереди нет сообщения с запрошенным типом. Если установлен битIPC_NOWAIT, происходит немедленный возврат из функцииmsgrcvс кодом ошибкиENOMSG. В противном случае вызвавший процесс блокируется до тех пор, пока не произойдет одно из следующих событий:
появится сообщение с запрошенным типом;
очередь с идентификатором msqidбудет удалена из системы (в этом случае будет возвращена ошибка с кодомEIDRM);
вызвавший поток будет прерван перехватываемым сигналом (в этом случае возвращается ошибка EINTR).
Если установлен бит MSG_EXCEPT, то мы прочитаем первое сообщение из очереди, тип которого не равенtype. Этот бит используется в том случае, когда при вызове функцииmsgrcvв аргументеtypeпередается положительное значение.
В аргументе flagможно указать дополнительный битMSG_NOERROR. При установке этого бита данные, превышающие размер буфера (аргументlength), будут просто обрезаться до его размера без возвращения кода ошибки. Если этот бит не установить, то при превышении сообщением размера приемного буфера будет возвращена ошибкаE2BIG.
В случае успешного завершения работы msgrcvвозвращает количество байтов в принятом сообщении. Оно не включает байты, нужные для хранения типа сообщения (long), который также возвращается через указательptr.
18.5. Функция msgctl
Функция msgctlпозволяет управлять очередями сообщений.
#include <sys/msg.h>
int msgctl (int msqid, int cmd, struct msqid_ds *buff);
/* возвращает 0 в случае успешного завершения, -1 – в случае ошибки */
Команд (аргумент cmd) может быть три:
IPC_RMID– удаление очереди с идентификаторомmsqidиз системы. Все сообщения, имеющиеся в этой очереди, будут утеряны. Для этой команды третий аргумент функции игнорируется;
IPC_SET– установка значений четырех полей структурыmsqid_dsданной очереди равными значениям соответствующих полей структуры, на которую указывает аргументbuff:msg_perm.uid,msg_perm.gid,msg_perm.mode,msg_qbytes;
IPC_STAT– возвращает вызвавшему процессу (через аргументbuff) текущее содержимое структурыmsqid_dsдля указанной очереди.
Пример. Программа влистинге 17.1создает очередь сообщений, помещает в нее сообщение с 1 байтом информации, вызывает функциюmsgctlс командойIPC_STAT, выполняет командуipcs, используя функциюsystem, а затем удаляет очередь, вызвав функциюmsgctlс командойIPC_RMID.
Листинг 17.1. Пример использования функции msgctl
#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)
struct msgbuf
{
long int mtype; /* тип сообщения, должен быть > 0 */
char mtext[1]; /* данные */
};
int main(int argc, char **argv)
{
int msqid;
struct msqid_ds info;
struct msgbuf buf;
if ((msqid = msgget(IPC_PRIVATE, SVMSG_MODE | IPC_CREAT)) == -1)
{
fprintf(stderr, "Ошибка вызова функции msgget: %s\n", strerror(errno));
exit(1);
}
buf.mtype = 1;
buf.mtext[0] = 1;
if (msgsnd(msqid, &buf, 1, 0) == -1)
{
fprintf(stderr, "Ошибка вызова функции msgsnd: %s\n", strerror(errno));
exit(1);
}
if (msgctl(msqid, IPC_STAT, &info) == -1)
{
fprintf(stderr, "Ошибка вызова функции msgctl(IPC_STAT): %s\n",
strerror(errno));
exit(1);
}
printf("read-write: %03o, cbytes = %lu, qnum = %lu, qbytes = %lu\n",
info.msg_perm.mode & 0777, (unsigned long) info.msg_cbytes,
(unsigned long) info.msg_qnum, (unsigned long) info.msg_qbytes);
system("ipcs -q");
if(msgctl(msqid, IPC_RMID, NULL) == -1)
{
fprintf(stderr, "Ошибка вызова функции msgctl(IPC_RMID): %s\n",
strerror(errno));
exit(1);
}
exit(0);
}
Вообще говоря, для получения доступа к очереди сообщений System V вовсе не обязательно вызывать функцию msgget: все, что нужно, – это знать идентификатор очереди сообщений, который легко получить с помощью командыipcs, и, конечно, иметь необходимые права доступа к очереди сообщений.
