Система межпроцессного взаимодействия IPC
Если процесс завершился/закрыл файловые дескрипторы, неименованный канал прекращает существовать.
- Система IPC предлагает использовать системы доступа (к общим ресурсам) трёх типов:
- Очередь сообщений:
- Процесс асинхоронно отправляет информацию другому через ядро системы. (процессы не обязятельно активны одновременно)
- Сообщения структурированы (имеют определённый формат).
- Сообщения имеют тип long.
- Сообщения хранятся в очереди до момента их прочтения.
- Автоматическая синхронизация - ядро управляет доступом к очереди. (могут использоваться различные языки программирования)
- Разделяемая память:
- Можно создать ресурс, называемый разделяемой или общей памятью. При подключении к ней в адресное пространство процесса, который к нему подключился, добавляется общая память. (минус - практически отсутствует синхронизация)
- Массив семафоров: (не те, что семафоры Дейкстры)
- Объект, состоящий из нескольких семафоров.
- Семафоры отличаются от тех, что у Дейкстры
- Операции можно выполнять одновременно над несколькими семафорами (зачем? - так есть. не очевидно.)
- Очередь сообщений:
Общие концепции
Для верификации и избежания конфликтов нумерации существуют уникальные ключи.
#include <sys/types.h>
#include <sys/ipc.h>
Генерация уникального ключа по имени и характеристикам файла и символьной константе. (два разных обращения с одинаковыми вводными дадут разные ключи).
key_t ftok(char *filename, char proj)filename- строка, содержащая имя файлаproj- добавочный символ
Создание дескриптора или подключение к нему
<ResName>get(key, ..., flags)key- ключflags- см manual:IPC_PRIVATE- доступность только породившему процессуIPC_CREAT- подключиться/создать (без флага будет просто подключиться)IPC_EXCL- в паре сIPC_CREATтолько создаёт новый ресурсIPC_RMID- удалить ресурс (может выполнить процесс, эффективный идентификатор которого совпадает с эффективным идентификатором процесса, создавшего ресурс, либо процесс-администратор)
- Возвращает имя дескриптора, к которому мы подключились.
IPC: Очередь сообщений
Очередь сообщений - механизм межпроцессного взаимодействия, позволяющий процессам обмениваться структурированными данными асинхронно через ядро ОС. Принцип fifo.
Очередь может рассматриваться как подочередь сообщений заданного типа
Пишем всегда в конец очереди
msgget() - Создание очереди сообщений / доступ к ней
int msgget(key_t key, int msgflg)key- идентификатор очереди сообщений ассоциирован с ключом.msflg- флаги управляющие поведением вызова.
Возвращает дескриптор очереди или -1.
msgsnd() - Отправка сообщений
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)msqid- дескриптор очереди, полученный в результате msgget().msgp- указатель на буфер.long msgtype- тип сообщения.char msgtext[]- тело сообщения.
msgsz- размер тела сообщения.msgflg- флаги (блокинующий/не блокирующий режим. send будет блокировать процесс, если ресурса не хватает)
Возвращает 0 или -1.
msgrcv() - Получение сообщений
int msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg)msqid- дескриптор очереди.msgp- указатель на буфер.msgsz- размер тела сообщения.msgtyp- тип ожидаемого сообщения.msgflg- побитовое сложение флагов.IPC_NOWAIT- вернуть -1, если сообщения в очереди нет.MSG_NOERROR- разрешение принимать сообщение, даже если его тело больше ёмкости буфера.
Ожидая сообщение, которое ещё не поступило, процесс блокируется.
Возвращает количество прочитанных байтов или -1.
msgctl() - Управление учередью сообщений
int msgctl(int msqid, int cmd, struct msgid_ds *buf)msgid- дескриптор очереди.cmd- команда.IPC_STAT- скопировать структуру, описывающую управляющие параметры очереди, по адресу из buf.IPS_SET- заменить структуру, описывающую управляющие параметры очереди по адресу, указанному в buf.IPC_RMID- удалить очередь
buf- струтура, описывающая параметры очереди. (там хранятся права доступа к очереди, статистика обращений к очереди, её размер и т.п.)
Возвращает 0 в случае успеха.
Использование очереди сообщений
Есть несколько процессов. Основной и дополнительные.
Основной процесс считывает со стандартного ввода строку.
Отправляет процессу A, если строка начинается с ‘a’. Для B так же с ‘b’.
‘q’ - завершение.
Главный процесс
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/message.h>
#include <stdio.h>
struct {
long mtype;
char Data[256];
} Message;
int main(int argc, char **argv) {
key_t key;
int msgid;
char str[256];
key = ftok("/usr/mash", 's');
msgid = mgget(key, 0666|IPC_CREAT|IPC_EXCL);
for(;;) {
gets(str);
strcpy(Message.Data, str);
switch (str[0]) {
case 'a':
case 'A':
Message.mtype = 1;
msgsnd(msgid, (struct msgbuf*) (&Message), strlen(str) + 1, 0);
break;
case 'b':
case 'B':
Message.mtype = 2;
msgsnd(msgid, (struct msgbuf*) (&Message), strlen(str) + 1 , 0);
break;
case 'q':
case 'Q':
Message.mtype = 1;
msgsnd(msgid, (struct msgbuf*) (&Message), strlen(str) + 1, 0);
Message.mtype = 2;
msgsnd(msgid, (struct msgbuf*) (&Message), strlen(str) + 1, 0);
sleep(10);
msgctl(msgid, IPC_RMID, NULL);
exit(0);
default:
break;
}
}
}Процесс-приёмник A (B такой же)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/message.h>
#include <stdio.h>
struct {
long mtype;
char Data[256];
} Message;
int main(int argc, char **argv) {
key_t key;
int mshid;
key = ftok("/usr/mash", 's');
msgid = msgget(key, 0666);
for(;;) {
masgrcv(msgid, (struct msgbuf*) (&Message), 256, 1, 0);
printf("%s", Message.Data);
if (Message.Data[0] == 'q' || Message.Data[0] == 'Q') break;
}
exit(0);
}Сделать синхронное завершение процессов через сигналы и
wait(). Также рассмотреть опасность в момент подключения к разделяемому ресурсу.
Пример: “Клиент - сервер”
Модель клиент-сервер характеризуется тем, что клиенты сервер “знают”, а серверу, по сути, без разницы, кому отвечать.
Сервер создаёт очередь. Клиенты подключаются к этой очереди.
В теле сообщения процессы будут передавать свой PID. Сервер будет отправлять в очередь сообщения с типом PID, равным ему.
Сервер
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv) {
struct {
long mestype;
char mes[100];
} messageto;
struct {
long mestype;
long mes;
} messageform;
key_t key;
int mesid;
key = ftok("example", 'r');
mesid = msgget(key, 0666|IPC_CREAT|IPC_EXECL);
while (1) {
msgrcv(mesid, &messagefrom, sizeof(messageform) - sizeof(long), 1, 0);
messageto.mestype = messagefrom.mes;
strcpy(messageto.mes, "Message for client");
msgsnd(mesid, &messageto, sizeof(messageto) - sizeof(long)), 0);
}
return 0;
}Клиент
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int main(int argc, char **argv) {
struct {
long mestype;
char mes;
} messageto;
struct {
long mestype;
long mes[100];
} messageform;
key_t key;
int mesid;
long pid = getpid();
key = ftok("example", 'r');
mesid = msgget(key, 0666);
messageto.mestype = 1;
messageto.mes = pid;
msgsnd(mesid, &messageto, sizeof(messageto) - sizeof(long), 0);
msgrcv(mesid, &messagefrom, sizeof(messagefrom) - sizeof(long), 0);
printf("%s", messagefrom.mes);
return 0;
}IPC: Разделяемая память Mark
Семинар по разделяемой памяти.
Создать ресурс
int shmget(tey_t, int size, int shmflg);key- ключ для доступа к разделяемой памяти (если процессу нужен разделяемый ресурс, он должен к нему подключиться).size- размер области памяти.shmflg- флаги, управляющие поведением вызова.
Доступ к разделяемой памяти
Подключить ресурс
char *shmat(int shmid, char* shmaddr, int shmflg);shmid- дескриптор области памятиshmaddr-shmflg-- Возвращает адрес (указатель) области памяти, к которому подключился процесс.
Отключение от разделяемой памяти
Отключить ресурс
int shmdt(char *shmaddr);shmaddr- адрес прикреплённой к процессу памяти, который был получен при вызовеshmat()
Управление разделяемой памятью
int shmctl(int shmid, int cmd, struct shmid_ds *buf);shmid- дескриптор области памятиcmd:IPC_STAT-IPC_SET-IPC_RMID- удалить (может использовать совпадающий effective PID/administrator)
Пример (для одного процесса)
IPC: Массив семафоров
Создание подключение
int semget(key_t key, int nsems, int semflag);key- ключ.nsems- количество семафоров.semflag- флаги.
Возвращает дескриптор или -1.
Операции над семафором
Можно выполнять операции с одним или несколькими семафорами (не обязательно всеми. это указывается в
nops).
int semop(int semid, struct sembyf *cmd_buf, size_t nops);semid- дескриптор ресурса.cmd_buf- массив из элементов типа (описан ниже).nops- количество элементов в массивеcmd_buf.
Каждый семафор имеет своё целочисленное значение. Над этим значением выполняется операция.
struct sembuf {
short sem_num;
short sem_op;
short sem_flg;
}- Если
sem_op0:- Пока (val + sem_op < 0) [процесс блокирован]
- val = val + sem_op
- Если
sem_op0:- Пока (val ) [процесс блокирован]
- …
Управление массивом семафоров
int semetl(int semid, int num, int cmd, union semun arg);semidnumcmd:IPC_SETSETVALIPC_RMID- …
arg- управляющие параметры
Использование разделяемой памяти и семафоров Mark
1-й процесс
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <string.h>
#define NMAX 256
int main(int argc, char **argv) {
key_t key;
int semid, shmid;
struct sembuf sops;
char *shmaddr;
char str[NMAX];
key = ftok("/etc/passwd", 'S');
semid = semget(key, NMAX, 0666|IPC_CREAT|IPC_EXCL);
shmadr = shmat(shmid, NULL, 0);
semctl(semid, 0, SETVAL, (int) 0);
sops.sem_num = 0;
sops.sem_flg = 0;
do {
printf("Введите строку: ");
if (fgets(str, NMAX, stdin) == NULL) strcpy(str, "Q");
strcpy(shmaddr, str);
sops.sem_op = 3;
semop(semid, &sops, 1);
sops.sem_op = 0;
semop(semid, &sops, 1);
} while (str[0] != 'Q');
shmdt(shmaddr);
shmctl(shmid, IPC_RMID, NULL);
shmctl(semid, IPC_RMID, (int) 0);
return 0;
}
2-й процесс
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <string.h>
#define NMAX 256
int main(int argc, char **argv) {
return 0;
}Как корректно создать ресурсы в задаче?
