C Language
Interprocess Communication (IPC)
Поиск…
Вступление
Механизмы межпроцессной коммуникации (МПК) позволяют различным независимым процессам взаимодействовать друг с другом. Стандарт C не предусматривает каких-либо механизмов МПК. Следовательно, все такие механизмы определяются операционной системой хоста. POSIX определяет обширный набор механизмов МПК; Windows определяет другой набор; и другие системы определяют их собственные варианты.
семафоры
Семафоры используются для синхронизации операций между двумя или несколькими процессами. POSIX определяет два разных набора функций семафора:
- «System V IPC» -
semctl()
,semop()
,semget()
. - «Семафоры POSIX» -
sem_close()
,sem_destroy()
,sem_getvalue()
,sem_init()
,sem_open()
,sem_post()
,sem_trywait()
,sem_unlink()
.
В этом разделе описываются семафоры System V IPC, так называемые, потому что они возникли в Unix System V.
Во-первых, вам нужно будет включить нужные заголовки. Для старых версий POSIX требуется #include <sys/types.h>
; современный POSIX, и большинство систем не требуют этого.
#include <sys/sem.h>
Затем вам нужно определить ключ как у родителя, так и у ребенка.
#define KEY 0x1111
Этот ключ должен быть одинаковым в обеих программах или они не будут ссылаться на одну и ту же структуру IPC. Существуют способы создания согласованного ключа без жесткого кодирования его значения.
Далее, в зависимости от вашего компилятора, вам может понадобиться или не нужно делать этот шаг: объявить объединение для операций семафора.
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
Затем определите структуры try ( semwait
) и raise ( semsignal
). Имена P и V происходят от голландских
struct sembuf p = { 0, -1, SEM_UNDO}; # semwait
struct sembuf v = { 0, +1, SEM_UNDO}; # semsignal
Теперь начните с получения идентификатора для вашего семафора IPC.
int id;
// 2nd argument is number of semaphores
// 3rd argument is the mode (IPC_CREAT creates the semaphore set if needed)
if ((id = semget(KEY, 1, 0666 | IPC_CREAT) < 0) {
/* error handling code */
}
В родителе инициализируйте семафор, чтобы иметь счетчик из 1.
union semun u;
u.val = 1;
if (semctl(id, 0, SETVAL, u) < 0) { // SETVAL is a macro to specify that you're setting the value of the semaphore to that specified by the union u
/* error handling code */
}
Теперь вы можете уменьшать или увеличивать семафор по мере необходимости. В начале вашего критического раздела вы уменьшаете счетчик с помощью функции semop()
:
if (semop(id, &p, 1) < 0) {
/* error handling code */
}
Чтобы увеличить семафор, вы используете &v
вместо &p
:
if (semop(id, &v, 1) < 0) {
/* error handling code */
}
Обратите внимание, что каждая функция возвращает 0
при успехе и -1
при сбое. Не проверять эти статусы возврата могут привести к разрушительным проблемам.
Пример 1.1: Гонки с потоками
В приведенной ниже программе будет выполняться процесс fork
для дочернего элемента, а родительский и дочерний объекты будут пытаться печатать символы на терминале без какой-либо синхронизации.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main()
{
int pid;
pid = fork();
srand(pid);
if(pid < 0)
{
perror("fork"); exit(1);
}
else if(pid)
{
char *s = "abcdefgh";
int l = strlen(s);
for(int i = 0; i < l; ++i)
{
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
}
}
else
{
char *s = "ABCDEFGH";
int l = strlen(s);
for(int i = 0; i < l; ++i)
{
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
}
}
}
Выход (1-й прогон):
aAABaBCbCbDDcEEcddeFFGGHHeffgghh
(2-й прогон):
aabbccAABddBCeeCffgDDghEEhFFGGHH
Компиляция и запуск этой программы должны давать вам разные результаты каждый раз.
Пример 1.2: Избегайте гонок с помощью семафоров
Изменив пример 1.1 для использования семафоров, мы имеем:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define KEY 0x1111
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
struct sembuf p = { 0, -1, SEM_UNDO};
struct sembuf v = { 0, +1, SEM_UNDO};
int main()
{
int id = semget(KEY, 1, 0666 | IPC_CREAT);
if(id < 0)
{
perror("semget"); exit(11);
}
union semun u;
u.val = 1;
if(semctl(id, 0, SETVAL, u) < 0)
{
perror("semctl"); exit(12);
}
int pid;
pid = fork();
srand(pid);
if(pid < 0)
{
perror("fork"); exit(1);
}
else if(pid)
{
char *s = "abcdefgh";
int l = strlen(s);
for(int i = 0; i < l; ++i)
{
if(semop(id, &p, 1) < 0)
{
perror("semop p"); exit(13);
}
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
putchar(s[i]);
fflush(stdout);
if(semop(id, &v, 1) < 0)
{
perror("semop p"); exit(14);
}
sleep(rand() % 2);
}
}
else
{
char *s = "ABCDEFGH";
int l = strlen(s);
for(int i = 0; i < l; ++i)
{
if(semop(id, &p, 1) < 0)
{
perror("semop p"); exit(15);
}
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
putchar(s[i]);
fflush(stdout);
if(semop(id, &v, 1) < 0)
{
perror("semop p"); exit(16);
}
sleep(rand() % 2);
}
}
}
Выход:
aabbAABBCCccddeeDDffEEFFGGHHgghh
Компиляция и запуск этой программы даст вам один и тот же результат каждый раз.