programming

Система межпроцессного взаимодействия 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_op 0:
    • Пока (val + sem_op < 0) [процесс блокирован]
    • val = val + sem_op
  • Если sem_op 0:
    • Пока (val ) [процесс блокирован]
    • …

Управление массивом семафоров

int semetl(int semid, int num, int cmd, union semun arg);
  • semid
  • num
  • cmd:
    • IPC_SET
    • SETVAL
    • IPC_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;
}

Как корректно создать ресурсы в задаче?