Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
 iakovlev.org 
 Languages
 С
 GNU С Library 
 Qt 
 STL 
 Threads 
 C++ 
 Samples 
 stanford.edu 
 ANSI C
 Libs
 LD
 Socket
 Pusher
 Pipes
 Encryption
 Plugin
=> Inter-Process
 Errors
 Deep C Secrets
 C + UNIX
 Linked Lists / Trees
 Asm
 Perl
 Python
 Shell
 Erlang
 Go
 Rust
 Алгоритмы
NEWS
Последние статьи :
  Тренажёр 16.01   
  Эльбрус 05.12   
  Алгоритмы 12.04   
  Rust 07.11   
  Go 25.12   
  EXT4 10.11   
  FS benchmark 15.09   
  Сетунь 23.07   
  Trees 25.06   
  Apache 03.02   
 
TOP 20
 Linux Kernel 2.6...5164 
 Trees...935 
 Максвелл 3...861 
 Go Web ...815 
 William Gropp...796 
 Ethreal 3...779 
 Ethreal 4...766 
 Gary V.Vaughan-> Libtool...765 
 Rodriguez 6...756 
 Steve Pate 1...749 
 Ext4 FS...748 
 Clickhouse...748 
 Ethreal 1...736 
 Secure Programming for Li...721 
 C++ Patterns 3...711 
 Ulrich Drepper...693 
 Assembler...687 
 DevFS...655 
 Стивенс 9...644 
 MySQL & PosgreSQL...622 
 
  01.01.2024 : 3621733 посещений 

iakovlev.org

Inter-Process Communication

By Hiran Ramankutty

Обзор

Inter-Process Communication - или IPC - облегчает взаимодействие между процессами. Рассмотрим простой пример - пусть у нас 2 полных стакана , в одном горячая вода , в другом холодная. Что нужно сделать для того , чтобы в обоих стаканах вода стала одинаковая ? Нужен еще один стакан бОльшей емкости для смешивания . В мире программ необходим механизм для взаимодействия между процессами. Как взаимодействуют процессы ? Каждый процесс работает в собственном адресном пространстве. Ядро в данном случае может выступить каналом между этими обособленными участками памяти. Оно по аналогии с предыдущим примером играет роль стакана бОльшей емкости .

Основы IPC

IPC-механизм можно разбить на следующие категории :
  1. pipes
  2. fifos
  3. shared memory
  4. mapped memory
  5. message queues
  6. sockets

Pipes

Пайпы присутствуют в любой юникс-системе . Они обеспечивают передачу данных в одном направлении . Пайп создается системным вызовом pipe , который создает пару файловых дескрипторов . В этой паре filedes[0] используется для чтения и filedes[1] для записи.

Рассмотрим программу , которая читает ввод с клавиатуры . Создадим 2 процесса : один будет читать эти символы , другой будет их проверять .

/***** KEYBOARD HIT PROGRAM *****/
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include <pthread.h>
 #include <ctype.h>
 
 int filedes[2];
 
 void *read_char()
 {
 	char c;
 	printf("Entering routine to read character.........\n");
 	while(1) {
 		/* Get a character in 'c' except '\n'. */
 		c = getchar();
 		if(c == '\n')
 			c = getchar();
 		write(filedes[1], &c, 1);
 		if(isalnum(c)) {
 			sleep(2);
 			exit(1);
 		}
 	}
 }
 
 void *check_hit()
 {
 	char c;
 	printf("Entering routine to check hit.........\n");
 	while(1) {
 		read(filedes[0], &c, 1);
 		if(isalnum(c)) {
 			printf("The key hit is %c\n", c);
 			exit(1);
 		} else {
 			printf("key hit is %c\n", c);
 		}
 	}
 }
 		
 int main()
 {
 	int i;
 	pthread_t tid1, tid2;
 	pipe(filedes);
 	/* Create thread for reading characters. */
 	i = pthread_create(&tid1, NULL, read_char, NULL);
 	/* Create thread for checking hitting of any keyboard key. */
 	i = pthread_create(&tid2, NULL, check_hit, NULL);
 	if(i == 0) while(1);
 	return 0;
 }
 

Компиляция программы - cc filename.c -lpthread.

read_char читает символ и пишет его в filedes[1]. Нам нужен тред check_hit, который проверяет этот символ , и если он символьно-числовой,то программа прекращается .

Что происходит при системном вызове pipe ? Ядро поддерживает пайпы через файловую систему . Создав пру файловых дескрипторов и разместив их в специальной таблице , ядро позволяет пользователю использовать по отношению к ним обычные файловые операции на чтение-запись, причем один из них только на чтение и другой только на запись.

FIFO

FIFOs (first in, first out - первый входит-первый выходит) похож на пайп. Отличие в том , что фифо-файл имеет имя . Поэтому можно сказать , что фифо - это именованный пайп. Другое отличие в том , что пайпы живут внутри процесса , который их создал,а фифо - внутри всей системы. Посмотрим , как фифо можно приспособить для предыдущей задачи .

/***** PROGRAM THAT READS ANY KEY HIT OF THE KEYBOARD*****/
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include <pthread.h>
 #include <ctype.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <errno.h>
 
 extern int errno;
 
 void *read_char()
 {
 	char c;
 	int fd;
 	printf("Entering routine to read character.........\n");
 	while(1) {
 		c = getchar();
 		fd = open("fifo", O_WRONLY);
 		if(c == '\n')
 			c = getchar();
 		write(fd, &c, 1);
 		if(isalnum(c)) {
 			exit(1);
 		}
 		close(fd);
 	}
 }
 
 int main()
 {
 	int i;
 	pthread_t tid1;
 	i = mkfifo("fifo", 0666);
 	if(i < 0) {
 		printf("Problems creating the fifo\n");
 		if(errno == EEXIST) {
 			printf("fifo already exists\n");
 		}
 		printf("errno is set as %d\n", errno);
 	}
 	i = pthread_create(&tid1, NULL, read_char, NULL);
 	if(i == 0) while(1);
 	return 0;
 }
 

Откомпилируем - cc -o write_fifo filename.c. Программа читает нажимаемые символы и пишет их в файл fifo. Для создания fifo используется функция mkfifo. Тред read_char читает символы с клавиатуры. fifo открыт с O_WRONLY (write only) флагом . Прочитанный символ при этом дописывается в конец fifo.

/***** KEYBOARD HIT PROGRAM *****/
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include <pthread.h>
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <sys/stat.h>
 
 extern int errno;
 
 void *check_hit()
 {
 	char c;
 	int fd;
 	int i;
 	printf("Entering routine to check hit.........\n");
 	while(1) {
 		fd = open("fifo", O_RDONLY);
 		if(fd < 0) {
 			printf("Error opening in fifo\n");
 			printf("errno is %d\n", errno);
 			continue;
 		}
 		i = read(fd, &c, 1);
 		if(i < 0) {
 			printf("Error reading fifo\n");
 			printf("errno is %d\n", errno);
 		}
 		if(isalnum(c)) {
 			printf("The key hit is %c\n", c);
 			exit(1);
 		} else {
 			printf("key hit is %c\n", c);
 		}
 	}
 }
 		
 int main()
 {
 	int i;
 	i = mkfifo("fifo", 0666);
 	if(i < 0) {
 		printf("Problems creating the fifo\n");
 		if(errno == EEXIST) {
 			printf("fifo already exists\n");
 		}
 		printf("errno is set as %d\n", errno);
 	}
 	pthread_t tid2;
 	i = pthread_create(&tid2, NULL, check_hit, NULL);
 	if(i == 0) while(1);
 	return 0;
 }
 

Эту программу откомпилируйте как cc -o detect_hit filename.c. Теперь запустите 2 последних программы в разных терминалах,но в одном каталоге. Наберите в первом терминале фразу . Ее же вы прочитаете во 2-м терминале. То же самое можно проделать и внутри одной программы. 2 программы были созданы специально , чтобы показать , что фифо можно использовать для общения между разными процессами. Если выйти из обоих программ и потом запустить 2-ю,будет получено сообщение,которое было набрано в прошлый раз. Это говорит о том,что фифо живет на протяжении жизни всей системы. Недостатком фифо является то,что их можно использовать только на одной машине.

Shared Memory

Расшареная память - это один из 3 основных механизмов System V IPC. System V IPC можно описать в 4 шага:

  • Инициализация идентификатора shared memory get - shmget
  • Инициализация идентификатора shared memory attach - shmat
  • Отсоединение памяти после использования - shmdt
  • Финальный контроль shared memory control - shmctl

Напишем еще один вариант программы выше.В ней мы создадим участок памяти, в которой будет храниться информация о нажатых клавишах.

#include <stdio.h>
 #include <stdlib.h>
 #include <sys/types.h>
 #include <sys/ipc.h>
 #include <sys/shm.h>
 #include <errno.h>
 #include <string.h>
 #include <ctype.h>
 
 extern int errno;
 
 #define SIZE	1
 
 char *read_key;
 int shmid;
 
 int shared_init()
 {
 	if((shmid = shmget(9999, SIZE, IPC_CREAT | 0666)) < 0) {
 		printf("Error in shmget. errno is: %d\n", errno);
 		return -1;
 	}
 	if((read_key = shmat(shmid, NULL, 0)) < 0) {
 		printf("Error in shm attach. errno is: %d\n", errno);
 		return -1;
 	}
 	return 0;
 }
 
 void read_char()
 {
 	char c;
 	while(1) {
 		c = getchar();
 		if(c == '\n') {
 			c = getchar();
 		}
 		strncpy(read_key, &c, SIZE);
 		printf("read_key now is %s\n", read_key);
 		if(isalnum(*read_key)) {
 			shmdt(read_key);
 			shmctl(shmid, IPC_RMID, NULL);
 			exit(1);
 		}
 	}
 }
 
 int main()
 {
 	if(shared_init() < 0) {
 		printf("Problems with shared memory\n");
 		exit(1);
 	}
 	read_char();
 	return 0;
 }
 

Обратите внимание на переменную,названную read_key,которая инициализирует создаваемую shared memory,это делается с помощью системного вызова shmget. 1-й параметр которой-9999,это ключ.Второй-SIZE=1.Указывает на размер хранимой информации. 3-й параметр - флаг IPC_CREAT указывает на read-write permissions. Возвращается участок памяти,и его идентификатор хранится в errno. Ключ генерится произвольно с помощью ftok. Далее shared memory segment приаттачивается к конкретному адресу с помощью shmat system call, который в качестве 1-го параметра использует сегментный идентификатор shmid. 2-й параметр - адрес и равен NULL для того,чтобы ядро само выбрало память. После выделения памяти мы вызываем вызывается read_char и прочитанный символ копируется в выделенную память.

Теперь напишем другую программу , которую можно запустить вообще из другого каталога и которая будет читать данные из shared memory.

#include <stdio.h>
 #include <stdlib.h>
 #include <sys/types.h>
 #include <sys/ipc.h>
 #include <sys/shm.h>
 #include <errno.h>
 #include <string.h>
 #include <ctype.h>
 
 extern int errno;
 
 #define SIZE	1
 
 char *detect_key;
 int shmid;
 
 int shared_init()
 {
 	if((shmid = shmget(9999, SIZE, 0444)) < 0) {
 		printf("Error in shmget. errno is: %d\n", errno);
 		return -1;
 	}
 	if((detect_key = shmat(shmid, NULL, SHM_RDONLY)) < 0) {
 		printf("Error in shm attach. errno is: %d\n", errno);
 		return -1;
 	}
 //	detect_key = NULL;
 	return 0;
 }
 
 void detect_hit()
 {
 	char c;
 	c = *detect_key;
 	while(1) {
 		if(c != *detect_key) {
 			if(isalnum(detect_key[0])) {
 				printf("detect_key is %s\n", detect_key);
 				shmdt(detect_key);
 				shmctl(shmid, IPC_RMID, NULL);
 				exit(1);
 			} else {
 				printf("detect_key is %s\n", detect_key);
 			}
 			c = *detect_key;
 		}
 	}
 }
 
 int main()
 {
 	if(shared_init() < 0) {
 		printf("Problems with shared memory\n");
 		exit(1);
 	}
 	detect_hit();
 	return 0;
 }
 

В этой программе флаг IPC_CREAT указывает на то,что shared memory не создается. Вместо этого имеется идентификатор,который приаттачивается к уже существующей. Режим 0444 ограничивает доступ как 'read only'. Ищется shared memory segment с ключом 9999.

Функция detect_hit проверяет нажимаемую клавишу. Сначала запускаем 1-ю программу,потом 2-ю,которая выдаст ошибку инициализации,которую игнорируем. Путь /proc/sysvipc/shm дает список задействованных shared mermory. Описанный механизм предполагает , что один процесс читает из shared memory параллельно с другим,который туда пишет. Но этот же механизм можно использовать для процессов,которые выпоняются не обязательно параллельно во времени.
Оставьте свой комментарий !

Ваше имя:
Комментарий:
Оба поля являются обязательными

 Автор  Комментарий к данной статье