Почему не работает Ctrl-C?

9

Я просто Ctrlcдважды ударил по своей оболочке, пытаясь остановить процесс, который занимает много времени, чтобы закончить.

^C было повторено дважды, но процесс просто продолжал идти.

Почему не Ctrlcвышли из процесса как обычно?

зеркало
источник
4
Мое решение для надоедливых программ, которые не хотят умирать, - обычно приостановить их с помощью CTRL + Z, а затем kill -9 %убить их. Сигнал 9 нельзя игнорировать, как и сигнал приостановки. Последовательность клавиш CTRL + Z можно игнорировать в теории, но не на практике.
Дэвид Сэйнти
@DavidSainty Сигнал приостановки можно игнорировать (или, по крайней мере, не останавливать). Пример: perl -E '$SIG{TSTP} = sub { say "ha ha" }; sleep 1 while 1'. Вы, наверное, думаете о SIGSTOP, это другой сигнал.
Дероберт
Ах, да, вы правы :)
Дэвид Сэйнти

Ответы:

13

Процессы могут выбрать:

  • игнорировать сигнал 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 отправляется всем процессам в группе процессов переднего плана терминала . Обычно это оболочка, которая помещает процессы в группы процессов (сопоставленные с заданиями оболочки ) и сообщает терминальному устройству, которое является приоритетным .

Теперь процесс может:

  • Оставьте свою процессную группу. Если он перемещается в другую группу процессов (любую группу процессов, кроме той, которая является приоритетной ), он больше не будет получать Ctrl-Cсигнал SIGINT (как и другие связанные с клавиатурой сигналы, такие как 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результатов выше, где pgid обоих psи perlpid ps), поэтому perlможет запускать свою собственную группу процессов.

  • Или это может изменить группу процессов переднего плана. В основном, скажите tty устройству отправить SIGINT какой-либо другой группе процессов послеCtrl+C

    perl -MPOSIX -e 'tcsetpgrp (0, getppid) или умереть $ !; спать 5 '

    Там perlостается в той же группе процессов, но вместо этого сообщает оконечному устройству, что приоритетной группой процессов является та, чей идентификатор совпадает с идентификатором его родительского процесса (см. Примечание выше об этом).

Стефан Шазелас
источник
1
Хорошим примером бесперебойного вызова является доступ к аппаратному устройству, которое не отвечает. Например, если вы попытаетесь использовать hdparmили smartctlна неисправном жестком диске, который не отвечает, они будут зависать вечно, и вы не можете убить их с помощью CTRL + C. Вы можете определить, находится ли процесс в непрерывном режиме сна, посмотрев на столбец stat ps auxили столбец S top/ htop- Dозначает непрерывный сон. Хотя это не обязательно плохо, это может означать, что процесс выполняет много операций ввода-вывода.
Мартин фон Виттих