Как Ctrl-C завершает дочерний процесс?
Я пытаюсь понять, как CTRL+C завершает дочерний, но не родительский процесс. Я вижу такое поведение в некоторых оболочках скриптов, таких как bash где вы можете начать какой-то длительный процесс, а затем завершить его, введя CTRL -C и элемент управления возвращается в оболочку.
не могли бы вы объяснить, как это работает и, в частности, почему не родительский процесс (оболочки) прекращается?
имеет ли оболочка чтобы сделать некоторые специальные обработки CTRL+C событие и если да, что именно он делает?
5 ответов:
сигналы по умолчанию обрабатываются ядром. В старых системах Unix было 15 сигналов, теперь их стало больше. Вы можете проверить
</usr/include/signal.h>(или убить-l). CTRL+C сигнал с именемSIGINT.действие по умолчанию для обработки каждого сигнала также определяется в ядре, и обычно оно завершает процесс, который получил сигнал.
все сигналы (но
SIGKILL) может быть обработан программой.и это то, что оболочка делает:
- когда оболочка работает в интерактивном режиме, она имеет специальную обработку сигнала для этого режима.
- при запуске программы, например
findоболочка:
forks сам- и для ребенка установите обработку сигнала по умолчанию
- замените ребенка данной командой (например, найти)
- при нажатии клавиши CTRL+C, родительская оболочка обрабатывает этот сигнал, но ребенок получит его - с действием по умолчанию-прекратить. (ребенок также может реализовать обработку сигналов)
вы можете
trapсигналы в вашем сценарии оболочки тоже...и вы можете установить обработку сигнала для интерактивной оболочки тоже попробовать ввести это в верхней части вы
~/.profile. (Убедитесь, что вы уже вошли в систему и протестировать его с другое терминал - вы можете заблокировать себя)trap 'echo "Dont do this"' 2теперь каждый время вы нажимаете CTRL+C в командной строке, он будет печатать сообщение. Не забудьте удалить линию!
если интересно, вы можете проверить простой старый
/bin/shобработка сигнала в исходном коде здесь.на выше были некоторые дезинформации в комментариях (теперь удалены), так что если кто - то заинтересован здесь очень хорошая ссылка -как работает обработка сигнала.
во-первых, читать статья Википедии о интерфейсе терминала POSIX весь путь до конца.
на
SIGINTсигнал генерируется дисциплиной терминальной линии и транслируется на все процессы в терминале группа процессов переднего плана. Ваша оболочка уже создала новую группу процессов для команды (или конвейера команд), которую вы запустили, и сообщила терминалу, что эта группа процессов является его (терминала) группой процессов переднего плана. Каждый параллельный командный конвейер имеет свою собственную группу процессов, а изображения командный конвейер-это конвейер с группой процессов, которую оболочка запрограммировала в терминал в качестве группы процессов переднего плана терминала. Переключение "заданий" между передним и задним планом (некоторые детали в сторону) - это вопрос оболочки, сообщающей терминалу, какая группа процессов теперь является передним планом.сам процесс оболочки находится в еще одной группе процессов, все свои и так не получает сигнал, когда одна из этих групп процессов находится на переднем плане. Все так просто.
терминал посылает сигнал INT (прерывания) в процесс, который в настоящее время подключен к терминалу. Затем программа получает его и может игнорировать его или выйти.
ни один процесс не обязательно принудительно закрывается (хотя по умолчанию, если вы не обрабатываете sigint, я считаю, что поведение должно вызывать
abort(), но мне нужно посмотреть это).конечно, запущенный процесс изолирован от оболочки, которая его запустила.
Если вы хотел родительская оболочка, чтобы пойти, запустите программу с
exec:exec ./myprogramтаким образом, родительская оболочка заменяется дочерним процессом
CTRL+C - это карта для команды kill. Когда вы нажимаете их, kill посылает сигнал SIGINT, который прерывает процесс.
setpgidPOSIX C Группа процессов минимальный примерэто может быть проще понять с минимальным запускаемым примером базового API.
это иллюстрирует, как сигнал действительно отправляется ребенку, если ребенок не изменил свою группу процессов с
setpgid.тогда, конечно, если ребенок обрабатывает сигнал, то он не убивается, как обычно.
главная.c:
#define _XOPEN_SOURCE 700 #include <assert.h> #include <signal.h> #include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> void signal_handler(int sig) { char sigint_str[] = "sigint\n"; if (sig == SIGINT) { write(STDOUT_FILENO, sigint_str, sizeof(sigint_str)); } signal(sig, signal_handler); } int main(int argc, char **argv) { (void)(argv); pid_t pid, pgid; signal(SIGINT, signal_handler); signal(SIGUSR1, signal_handler); pid = fork(); assert(pid != -1); if (pid == 0) { /* Change the pgid. * The new one is guaranteed to be different than the previous, which was equal to the parent's, * because `man setpgid` says: * > the child has its own unique process ID, and this PID does not match * > the ID of any existing process group (setpgid(2)) or session. */ if (argc == 1) { setpgid(0, 0); } printf("child pid, pgid = %ju, %ju\n", (uintmax_t)getpid(), (uintmax_t)getpgid(0)); assert(kill(getppid(), SIGUSR1) == 0); while (1); exit(EXIT_SUCCESS); } /* Wait until the child sends a SIGUSR1. */ pause(); pgid = getpgid(0); printf("parent pid, pgid = %ju, %ju\n", (uintmax_t)getpid(), (uintmax_t)pgid); /* man kill explains that negative first argument means to send a signal to a process group. */ kill(-pgid, SIGINT); while (1); }скомпилировать и беги:
gcc -g -std=c99 -Wall -Wextra -o setpgid setpgid.c -lpthread ./setpgidитог:
child pid, pgid = 28250, 28249 parent pid, pgid = 28249, 28249 sigint sigintи программа зависает.
pgid обоих процессов одинаковый, так как он наследуется через
fork.затем, если вы нажмете:
Ctrl + Cон снова выходы:
sigint sigintэто как так:
- отправить сигнал всей группе процессов с
kill(-pgid, SIGINT)- Ctrl + C на терминале отправляет убийство всей группе процессов по умолчанию
если вы работаете с аргументом, например:
./setpgid 1ребенок меняет свой pgid, и теперь только один sigint печатается каждый раз: родителя.
выйдите из программы, отправив другой сигнал процессам, например SIGQUIT с
Ctrl + \.если вы изменили идентификатор дочерней группы, то он также не получит этот SIGQUIT, и вы должны убить его явно:
ps aux | grep setpgid kill -9 $PIDэто ясно почему сигнал посылается всем процессам по умолчанию: в противном случае мы получим кучу процессов, чтобы быть очищены вручную.
протестировано на Ubuntu 18.04. GitHub вверх по течению.
Comments