logo
Конспект Граур

Пример. Совместное использование сигналов и каналов – «пинг-понг».

Следующий пример - совместное использования сигналов и каналов.

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

Первым делом смотрим на обработчик сигнала. Обработчик сигнала, в данном случае будет использоватьcя сигнал SIGUSER1 для синхронизации, т.е. посылать его процессу всякий раз когда пришла его очередь читать из канала, т.к. взаимные скорости выполнения отца и сына неизвестны. SIGUSER1 - это сигнал, который не соответствует какому-либо определенному событию в ОС, а его семантика определяется пользователями, т.е. это как раз тот случай, когда семантика сигнала отдается на усмотрение программисту.

#include <signal.h>

#include <sys/types.h>

#include <sys/wait.h>

#include <unistd.h>

#include <stdlib.h>

#include <stdio.h>

#define MAX_CNT 100

int target_pid, cnt;

int fd[2];

int status;

void SigHndlr(int s)

{

/* в обработчике сигнала происходит и чтение, и запись */

signal(SIGUSR1, SigHndlr);

if (cnt < MAX_CNT)

{

read(fd[0], &cnt, sizeof(int));

printf("%d \n", cnt);

cnt++;

write(fd[1], &cnt, sizeof(int));

/* посылаем сигнал второму: пора читать из канала */

kill(target_pid, SIGUSR1);

}

else

if (target_pid == getppid())

{

/* условие окончания игры проверяется потомком */

printf("Child is going to be terminated\n");

close(fd[1]); close(fd[0]);

/* завершается потомок */

exit(0);

} else

kill(target_pid, SIGUSR1);

}

int main(int argc, char **argv)

{

pipe(fd); /* организован канал */

signal (SIGUSR1, SigHndlr);

/* установлен обработчик сигнала для обоих процессов */

cnt = 0;

if (target_pid = fork())

{

/* Предку остается только ждать завершения потомка */

while(wait(&status) == -1);

printf("Parent is going to be terminated\n");

close(fd[1]); close(fd[0]);

return 0;

}

else

{

/* процесс-потомок узнает PID родителя */

target_pid = getppid();

/* потомок начинает пинг-понг */

write(fd[1], &cnt, sizeof(int));

kill(target_pid, SIGUSR1);

for(;;); /* бесконечный цикл */

}

}

Процесс – сын начинает игру. Во-первых, устанавливается target_pid, который в процессе-отце был установлен в fork(), в процессе-сыне он устанавливается с помощью вызова getppid(), который возвращает pid по требованию. И он записывает текущее значение в канал и посылает сигнал SIGUSR1 своему предку. Далее происходит бесконечный цикл. Т.е. вся обработка «пинг-понг», которая заключается в чтении данных из канала, увеличения на 1, проверки значения (не достигло ли оно своего максимума) и записи нового увеличенного значения в канал – все это происходит в обработчике канала, т.е. здесь функция main() в процессе-сыне - бесконечный цикл, а в процессе – отце wait() – ожидание завершения потомка. Процесс-сын завершается когда нашел максимум, а процесс-отец в этом случае выходит из wait(), закрывает канал и тоже выходит.

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