Код, который вы предоставляете, не компилируется, и попытка исправить это показывает, что вы многое пропустили. Я только догадываюсь.
Чтобы привести вас в порядок, я укажу на ряд фактов, которые вы, возможно, неправильно поняли. Надеюсь, что вместе с парой ссылок на документацию это будет полезно.
Обработка ошибок
Во-первых: имейте привычку обрабатывать ошибки, особенно если вы знаете, что есть что-то, чего вы не понимаете , Например, родитель (ваша оболочка) ждет, пока завершится дочерний процесс,
waitpid(0, &status, WUNTRACED);
Вы говорите,
Когда, например, я выполняю «сон 5», а затем Я нажимаю Ctrl- C для SIGINT, приглашение «shell>» появляется, как и ожидалось, но процесс все еще выполняется в фоновом режиме.
На самом деле происходит, когда вы нажимаете Ctrl- C, родитель ( не дочерний; см. Ниже, почему) получает SIGINT
(терминальная подсистема ядра обрабатывает ввод с клавиатуры, видит, что кто-то одновременно удерживает клавиши «Ctrl» и «C») время и приходит к выводу, что все процессы с этим управляющим терминалом должны быть отправлены SIGINT
).
Изменить родительскую ветвь на,
int error = waitpid(0, &status, WUNTRACED);
if (error != 0)
perror("waitpid");
С этим вы perror()
напечатайте что-то вроде:
waitpid: interrupted system call
Вы хотите SIGINT
до go для ребенка, поэтому что-то должно быть не так.
Обработчики сигналов, fork()
и exec()
Далее, что происходит с вашими обработчиками сигналов через fork()
и exec()
?
Th e справочная страница обзора сигналов состояния,
Дочерний элемент, созданный с помощью fork (2), наследует копию расположения сигналов своего родителя. Во время execve (2) расположение обработанных сигналов сбрасывается до значения по умолчанию; расположение игнорируемых сигналов остается неизменным.
Итак, в идеале это означает, что:
- Родитель (оболочка) видит
SIGINT
, как отмечено выше и печатает «прерванный системный вызов». - Дочерние обработчики сигналов возвращаются к своим значениям по умолчанию. Для
SIGINT
это означает завершение.
Вы не возитесь с управляющим терминалом , поэтому дочерний объект наследует управляющий терминал родителя. Это означает, что SIGINT
доставляется как родительскому , так и дочернему. Учитывая, что поведение SIGINT
ребенка должно завершаться, я бы поспорил, что ни один процесс не остался запущенным.
За исключением случаев, когда вы используете setpgid()
для создания новой группы процессов.
Процесс Группы, сеансы и управляющий терминал
Кто-то однажды назвал меня UNIX седобородым. Хотя это верно с визуальной точки зрения, я должен отказаться от этого комплимента, потому что я редко бываю в одном из самых темных углов UNIX - терминальной подсистеме. Авторы командной оболочки тоже должны это понимать.
В этом контексте это раздел "NOTES" справочной страницы setpgid()
. Я предлагаю вам прочитать, особенно там, где написано:
В любое время одна (и только одна) из групп процессов в сеансе может быть основной группой процессов для терминала; (...)
Оболочка (bash возможно), из которой вы запускаете программу оболочки, сделала это для вызова вашей программы на переднем плане и пометила это как "группа процессов переднего плана". По сути это означает: «Пожалуйста, уважаемый терминал, всякий раз, когда кто-то нажимает Ctrl- C, отправьте SIGINT
всем процессам в этой группе. Я (ваш родитель) просто сижу и жду (waitpid()
), пока все не закончится, и снова получит контроль. ".
Вы создаете группу процессов для дочернего элемента, но не говорите об этом терминалу. Вам нужно
- Отсоединить родительский элемент от терминала.
- Установить дочернюю группу процессов в качестве передней группы процессов терминала.
- Подождите, пока child (у вас уже есть).
- Восстановить передний план терминала.
Далее в разделе «ЗАМЕЧАНИЯ» на этой странице руководства, они дают ссылки на то, как это делается. Следуйте им, внимательно читайте, пробуйте и убедитесь, что вы справляетесь с ошибками. В большинстве случаев такие ошибки являются признаками недопонимания. И в большинстве случаев такие ошибки исправляются путем перечитывания документации.