Ваша проблема была правильно диагностирована Грэмом Коулом в комментарий:
- Каждый дочерний элемент имеет свою собственную копию
car_finished
, поэтому счетчик в любом дочернем элементе всегда достигает только 1
, поэтому, если существует более одного дочернего элемента, сигнал никогда не отправляется.
Как это решить?
Вы можете использовать общую память и поместить car_finished
в общую память. Однако вам придется синхронизировать доступ к общей памяти между процессами, поэтому это быстро становится громоздким (при условии, что это не считалось громоздким, когда упоминалась общая память).
Возможно, можно будет использовать файл для подсчета дочерних элементов. Потомки будут использовать (рекомендательную) блокировку файла для предотвращения одновременного доступа, и каждый из них будет уменьшать счетчик перед выходом, а тот, который уменьшает счетчик до нуля, удаляет файл и отправляет сигнал родительскому процессу. Это довольно надежно, но не так быстро (но, вероятно, будет достаточно быстро). Основная проблема возникает, если дочерний процесс завершается без уменьшения счетчика (например, из-за сбоя с ошибкой памяти или отправки SIGKILL). Тогда счетчик никогда не станет равным нулю, поэтому родительский элемент никогда не получит сигнал.
Если родитель просто ждет смерти детей, он должен выполняться в wait()
цикле:
int corpse;
int status;
while ((corpse = wait(&status)) > 0)
{
/* …optionally… */
printf("%d: PID %d exited with status 0x.4X\n", (int)getpid(), corpse, status);
}
exit(EXIT_SUCCESS);
Если родитель выполняет некоторую обработку, дочерний элемент не должен отправлять SIGKILL; это не дает родителям времени на уборку. Каждый дочерний элемент может отправить родительскому элементу сигнал SIGUSR1, а обработчик родительского сигнала может подсчитать количество полученных сигналов и выйти, когда он получит их все. Это сложнее продемонстрировать полностью, но для настройки обработчика сигналов используется sigaction()
.
Иногда родитель может создать канал, закрыть конец записи и попытаться прочитать из него. Каждый дочерний элемент закрывал бы конец канала для чтения при его запуске (гигиена кода - это можно опустить). Когда дочерний элемент завершен, он завершает работу, явно или неявно закрывая конец записи канала. Когда последний дочерний элемент выйдет из read()
, родитель получит возврат «нулевое прочтение байтов» и может закрыть канал (или просто завершить работу).
Эти последние решения полагаются на сотрудничество родительского процесса. Обычно это беспроигрышный вариант (если семья не является неблагополучной, родитель по мере возможности помогает своим детям).
person
Jonathan Leffler
schedule
14.12.2019
getpid()
перед циклом for, в котором вы вызываетеfork
, и просто использовать это? (Кроме того, несколько более устоявшаяся терминология относится к дочерним и родительским процессам, а не к сыну / дочери и / или отцу / матери.) - person gstukelj   schedule 14.12.2019