2015-10-22 2 views
0

Для инициализации приложения родительский процесс разворачивает 3 дочерних процесса, потом дочерние процессы устанавливают свои обработчики сигналов и возвращают родительскому процессу, чтобы они были готовы начать работу. Для достижения этого используется сигнал SIGUSR1.Сигнал, пропущенный дочерним процессом

Родительский процесс в то же время ожидает этих сигналов от дочерних процессов. Как только будет получен сигнал, родительский элемент соответствует его pid с дочерними pids, которые он сохранил, и увеличивает счетчик. Как только родитель знает, что поступают сигналы доброго от всех дочерних процессов, он начинает посылать каждому из них сигнал SIGUSR1 для указания запуска активности.

Проверяется факт передачи всех сигналов от родителя для каждого ребенка; однако в большинстве случаев один из дочерних процессов пропускает сигнал. В течение нескольких испытаний я определил, что процесс, которому родитель сначала посылает сигнал, пропускает его. Однако иногда также происходит, что все дочерние процессы пропускают свои сигналы. Я также использовал инструмент «strace» для проверки потока всех сигналов, но по-прежнему не может определить, почему дочерние процессы не могут поймать сигналы, отправленные родителем.

Любая обратная связь будет оценена по достоинству.

+2

Описывая текст в тексте очень неоднозначно, попробуйте создать [Минимальный, полный и проверенный пример] (http://stackoverflow.com/help/mcve) и покажите нам, а если есть выход, пожалуйста, предоставьте фактический и ожидаемый результат. –

+1

Есть так много вещей, которые могли бы пойти не так, мы не можем помочь вам, не имея хотя бы MCVE, как сказал Йоахим. Откуда вы знаете, какой ребенок отправил каждый сигнал? Откуда вы знаете, что родитель успешно отправил каждому из детей сигнал? Мы не можем начинать угадывать ответы на такие вопросы, не видя кода. –

+0

его в сигналах природа ненадежна :) – shami

ответ

0

SIGUSR1 и другие сигналы POSIX не поставлены в очередь. Если в процессе уже есть один ожидающий, любые другие сигналы будут отброшены.

Вы можете избежать этого, используя «сигналы в реальном времени». Вы используете их так же, как вы используете стандартные сигналы POSIX; первый - SIGRTMIN+0, а последний - SIGRTMAX-0. Если вы используете sigqueue(), вы можете даже приложить один int (или указатель на пустоту) в качестве полезной нагрузки.

Сигналы в реальном времени POSIX находятся в очереди (до предела), поэтому вы с меньшей вероятностью потеряете их.

Однако я не использовал бы сигналы для отслеживания дочерних процессов. Я использовал бы трубы, у которых были бы концы записи, а родительский с концами чтения и все дескрипторы, помеченные для close-on-exec, используя fcntl(descriptor, O_SETFD, O_CLOEXEC) для каждого.

Дети обновляют родительский статус при помощи однобайтовых сообщений. Если ребенок выйдет или выполнит другую программу, родитель увидит это как условие окончания файла (read(), возвращающий ноль). Если родительский элемент завершен, конец записи станет неприемлемым для конца записи для ребенка, и любая попытка записи в трубу завершится ошибкой с ошибкой EPIPE. (Это будет также поднять SIGPIPE сигнал, так что вы можете захотеть использовать sigaction() игнорировать SIGPIPE сигнал.)

родитель может контролировать процесс ребенка статусы параллельно с использованием select() или poll(). Всякий раз, когда дочерний процесс отправляет данные или выдает или выполняет другую программу (которая закрывает конец записи в трубе), родительский дескриптор (считываемый конец канала) станет читаемым. Лично я также отмечаю, что дескрипторы родительских дескрипторов не блокируются с использованием fcntl(rfd, F_SETFL, O_NONBLOCK), так что если есть глюк, вместо того, чтобы блокировать неверное чтение, чтение на родительском компьютере просто завершится ошибкой с EWOULDBLOCK в errno.

Если вы хотите использовать двунаправленный поток данных, проще всего использовать дополнительный канал для каждого ребенка, родительскую запись и чтение ребенка из него.

Также можно использовать неназванные сокеты datagram домена Unix (созданные через socketpair(AF_UNIX, SOCK_DGRAM, 0, fds). (См. Также man 2 socket и man 7 unix для получения подробной информации о параметрах.) Также используйте fcntl(fds[0], F_SETFL, O_CLOEXEC) и fcntl(fds[1], F_SETFL, O_CLOEXEC), чтобы сделать дескрипторы close-on-exec, как и в случае с корпусом.

Проблема с парами сокет домена Unix (любого типа - SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET), является то, что они могут содержать вспомогательные данные . Эти вспомогательные данные могут содержать дополнительные файловые дескрипторы, которые являются ограниченным товаром. Если есть возможность ненадежного или неприятного дочернего процесса, он может убить своего родителя, отправив ему несколько тысяч дескрипторов файлов. Для обеспечения безопасности родительский процесс должен следить за тем, что он получает от дочернего процесса, и если он содержит вспомогательные данные, немедленно закройте этот дескриптор (поскольку дочерний процесс явно враждебен!), И если были предоставлены какие-либо файловые дескрипторы, закройте их тоже. Вы можете избежать этого, только если вы доверяете своему ребенку процессы, насколько вы доверяете своему первоначальному процессу, чтобы не делать ничего гнусного.

Обеспечение безопасности сокетов домена unix не сложно, но проверка каждой полученной дейтаграммы или получение для вспомогательных данных - это пара десятков дополнительных строк кода. Трубы проще.

Смежные вопросы