Я только что Ctrlcдважды ударил по своей раковине, пытаясь остановить процесс, который затягивается надолго.
^C
повторилось дважды, но процесс продолжался.
Почему не Ctrlcпрекратили этот процесс, как это обычно происходит?
решение1
Процессы могут выбирать:
- игнорировать сигнал SIGINT, обычно отправляемый при нажатии Ctrl-C(как
trap '' INT
в оболочке), или иметь собственный обработчик для него, который решает не завершать работу (или не завершает работу своевременно). - сообщить терминальному устройству, что символ, который вызывает отправку SIGINT в приоритетную задачу, — это что-то другое (например,
stty int '^K'
в оболочке) - сообщить терминальному устройству, чтобы оно не отправляло никаких сигналов (как
stty -isig
в оболочке).
Или они могут быть непрерываемыми, например, во время системного вызова, который нельзя прервать.
В Linux (с относительно новым ядром) вы можете определить, игнорирует ли процесс и/илиумение обращатьсяSIGINT, просматривая выходные данные
$ kill -l INT
2
$ grep Sig "/proc/$pid/status"
SigQ: 0/63858
SigPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000000002
SigCgt: 0000000000000000
SIGINT равен 2. Второй бит SigIgn выше равен 1, что означает, что SIGINT игнорируется.
Вы можете автоматизировать это с помощью:
$ SIG=$(kill -l INT) perl -lane 'print $1 if $F[0] =~ /^Sig(...):/ &&
$F[1] & (1<<($ENV{SIG}-1))' < "/proc/$pid/status"
Ign
Чтобы проверить текущий intr
символ или его isig
наличие для данного терминала:
$ stty -a < /dev/pts/0
[...] intr = ^C [...] isig
(выше intr
символа - это ^C
(символ, который обычно отправляется вашим терминалом (эмулятором) при нажатии CTRL-Cи входные сигналы не отключены.
$ stty -a < /dev/pts/1
[...] intr = ^K [...] -isig
( intr
персонаж есть ^K
и isig
отключен для /dev/pts/1
).
Для полноты картины, есть еще два способа, которыми процесс может что-то сделать, чтобы прекратить получение SIGINT, хотя это и не то, что вы обычно увидите.
После этого Ctrl+Cсигнал SIGINT отправляется всем процессам вгруппа процессов переднего плана терминала. Обычно именно оболочка помещает процессы в группы процессов (отображаемые в оболочкерабочие места) и сообщите терминальному устройству, какое из них являетсяпередний планодин.
Теперь процесс может:
Покинуть свою группу процессов. Если он переходит в другую группу процессов (любую группу процессов, кроме той, которая являетсяпередний планone), то он больше не будет получать SIGINT Ctrl-C(и другие сигналы, связанные с клавиатурой, такие как SIGTSTP, SIGQUIT). Однако он может быть приостановлен, если попытается прочитать (возможно, и записать в зависимости от настроек терминального устройства) с терминального устройства (как это делают фоновые процессы).
В качестве примера:
perl -MPOSIX -e 'setpgid(0,getppid) or die "$!"; sleep 10'
не может быть прервана с помощью Ctrl-C. Выше
perl
будет сделана попытка присоединиться к группе процессов, идентификатор которой совпадает с идентификатором родительского процесса. В общем случае нет гарантии, что существует такая группа процессов с таким идентификатором. Но здесь, в случае, если этаperl
команда запускается сама по себе по приглашению интерактивной оболочки, ppid будет процессом оболочки, а оболочка, как правило, будет запущена в своей собственной группе процессов.Если команда еще не является лидером группы процессов (лидером этой приоритетной группы процессов), то запуск новой группы процессов будет иметь тот же эффект.
Например, в зависимости от оболочки,
$ ps -j >&2 | perl -MPOSIX -e 'setpgid(0,0) or die "$!"; sleep 10' PID PGID SID TTY TIME CMD 21435 21435 21435 pts/12 00:00:00 zsh 21441 21441 21435 pts/12 00:00:00 ps 21442 21441 21435 pts/12 00:00:00 perl
будет иметь тот же эффект.
ps
иperl
запускаются в группе процессов переднего плана, но в большинстве оболочекps
будет лидером этой группы (как видно изps
выходных данных выше, где pgidps
иperl
является pidps
), поэтомуperl
может запустить свою собственную группу процессов.Или он может изменить группу процессов переднего плана. По сути, сказать устройству tty отправить SIGINT в какую-то другую группу процессов послеCtrl+C
perl -MPOSIX -e 'tcsetpgrp(0,getppid) or die$!; sleep 5'
Там
perl
он остается в той же группе процессов, но вместо этого сообщает терминальному устройству, что группа процессов переднего плана — это та, идентификатор которой совпадает с идентификатором ее родительского процесса (см. примечание выше по этому поводу).