Почему SIGKILL не завершает остановленную программу (да)?


8

Я использую Ubuntu 14.04 и у меня такое поведение, которое я не могу понять:

  1. Запустите yesкоманду (в оболочке по умолчанию: Bash )
  2. Тип, CtrlZчтобы остановитьyes
  3. Беги jobs. Вывод:
    [1]+ Stopped yes
  4. Беги, kill -9 %1чтобы остановиться yes. Вывод:
    [1]+ Stopped yes
  5. Беги jobs. Вывод:
    [1]+ Stopped yes

Это на Ubuntu, 3.16.0-30-genericработающем в виртуальной машине Parallels.

Почему моя kill -9команда не завершила команду yes ? Я думал, что SIGKILL не может быть пойман или проигнорирован? И как я могу прекратить команду да ?


1
Это интересно. SIGKILL должен работать, и он работает на моем Linux Mint 17. Для любого другого сигнала вам обычно нужно отправить его SIGCONT впоследствии, чтобы убедиться, что сигнал получен остановленной целью.
— PSkocik

Действительно ли bash печатает «Остановлено» для приостановленного процесса ?
— edmz

Версия ядра ( uname -a) пожалуйста
— roaima

Linux ubuntu 3.16.0-30-generic #40~14.04.1-Ubuntu SMP Thu Jan 15 17:43:14 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux, Я использую Ubuntu в Parallels Desktop.
— s1m0n

1
@ Чёрное большинство снарядов говорят "Остановлено". tcsh говорит «приостановлено», а zsh говорит «приостановлено». Косметическая разница. Несколько более важным является тот факт, что bash печатает идентичные сообщения для STOP и TSTP, где все другие оболочки помечают аннотацию сообщением STOP, (signal)чтобы вы могли заметить разницу.

Ответы:


10

Сигналы заблокированы для приостановленных процессов. В терминале:

$ yes
...
y
y
^Zy

[1]+  Stopped                 yes

Во втором терминале:

$ killall yes

В первом терминале:

$ jobs
[1]+  Stopped                 yes

$ fg
yes
Terminated

Однако SIGKILLне может быть заблокирован. Выполнение того же действия со killall -9 yesвторым терминалом немедленно дает это в yesтерминале:

[1]+  Killed                  yes

Следовательно, если kill -9 %1процесс не завершается сразу же, либо bashвы фактически не отправляете сигнал, пока не завершите fgпроцесс, либо вы обнаружили ошибку в ядре.


4
Некоторые общие сведения: при вводе Ctrl + Z в вашем терминале bash отправляет SIGTSTP(который является блокируемой версией SIGSTOP) активному процессу. Это переводит процесс в замороженное состояние, когда ядро ​​не будет его планировать. Это также препятствует обработке сигнала (за исключением SIGCONTсигнала, который размораживает процесс) и, следовательно, предотвращает немедленное завершение процесса.
— mreithub

1
SIGKILL, в отличие от других сигналов, не блокируется для приостановленных процессов. Посылка сигнала KILL приостановленному процессу убивает его - асинхронно, но на практике в основном немедленно.
— Жиль "ТАК - перестань быть злым"

1
@Gilles Вот что я пытался проиллюстрировать выше: SIGTERMзаблокировано, но SIGKILLнет. В любом случае, согласно комментарию от OP, проблема, похоже, заключается в том, jobsчто не обнаруживается, что процесс умер, а не процесс не был убит kill -9 %1.
— lcd047

1
Но я могу воспроизвести поведение s1m0n в моей системе (Debian, amd64, bash 4.3.30).
— Жиль "ТАК - перестань быть злым"

1
Хотя SIGKILLне может быть заблокировано, нет никакой гарантии, что оно будет доставлено в любое значимое время. Например, если процесс приостановлен в ожидании блокирующего ввода-вывода, SIGKILLон не поступит, пока процесс не проснется. Это потенциально может быть никогда, если не происходит ввода-вывода.
— Сапи

7

Не паникуйте.

В этом нет ничего прикольного. Здесь нет ошибки ядра. Это совершенно нормальное поведение оболочки Bourne Again и многозадачной операционной системы.

Следует помнить, что процесс убивает себя , даже в ответ SIGKILL. Здесь происходит то, что оболочка Bourne Again перебирает вещи до того, как процесс, который он только что велел убить сам, доходит до самоубийства.

Подумайте, что происходит с точки, где yesвы остановились, SIGTSTPи вы только что выполнили killкоманду с оболочкой Bourne Again:

  1. Оболочка отправляет SIGKILLв yesпроцесс.
  2. Параллельно :
    1. yesПроцесс планируется запустить и сразу же убивает себя.
    2. Оболочка Bourne Again продолжается и выдает еще один запрос.

Причина, по которой вы видите одно, а другие видят другое, заключается в простой гонке между двумя готовыми к запуску процессами, победителем которой являются исключительно вещи, которые варьируются как от машины к машине, так и со временем. Загрузка системы имеет значение, как и факт, что ваш процессор является виртуальным.

В интересном случае деталь шага # 2 такова:

  1. Оболочка Bourne Again продолжается.
  2. Как часть внутренних функций встроенной killкоманды, она помечает запись в своей таблице заданий как нуждающуюся в уведомлении, напечатанном в следующей доступной точке.
  3. Он завершает killкоманду и непосредственно перед печатью снова проверяет, следует ли печатать уведомления о каких-либо заданиях.
  4. У yesпроцесса еще не было возможности покончить с собой, поэтому, что касается оболочки, задание все еще находится в остановленном состоянии. Таким образом, оболочка печатает строку состояния задания «Остановлено» для этого задания и сбрасывает флаг ожидания уведомления.
  5. yesПроцесс получает регулярные и убивает себя.
  6. Ядро сообщает оболочке, которая занята своим редактором командной строки, о том, что процесс покончил с собой. Оболочка отмечает изменение статуса и снова помечает задание как ожидающее уведомления.
  7. Простое нажатие enterдля повторного выполнения печати подсказок дает оболочке возможность распечатать новое состояние задания.

Важными моментами являются:

  • Процессы убивают себя. SIGKILLне волшебно Процессы проверяют наличие ожидающих сигналов при возврате в режим приложения из режима ядра, что происходит в конце ошибок страниц, (не вложенных) прерываний и системных вызовов. Единственная особенность заключается в том, что ядро ​​не позволяет действию в ответ SIGKILLна что-либо, кроме немедленного и безусловного самоубийства, без возврата в режим приложения. Важно отметить, что процессы должны как переходить из режима ядра в режим приложения, так и быть запланированы для запуска, чтобы реагировать на сигналы.
  • Виртуальный процессор - это просто поток в операционной системе хоста. Нет гарантии, что хост запланировал запуск виртуального процессора. Операционные системы хоста тоже не волшебны.
  • Уведомления не распечатываются, когда происходят изменения состояния задания (если вы не используете set -o notify). Они печатаются, когда следующая оболочка достигает точки в своем цикле выполнения, которая проверяет, ожидают ли какие-либо уведомления.
  • Флаг ожидания уведомления устанавливается дважды, один раз killи один раз SIGCHLDобработчиком сигнала. Это означает, что можно увидеть два сообщения, если оболочка запущена раньше yesзапланированного процесса, чтобы убить себя; одно сообщение «Остановлено» и одно сообщение «Убито».
  • Очевидно, что /bin/killпрограмма не имеет доступа к внутренней таблице заданий оболочки; так что вы не увидите такого поведения с /bin/kill. Флаг ожидания уведомления устанавливается SIGCHLDобработчиком только один раз .
  • По той же причине вы не увидите такого поведения, если вы killвыполняете yesпроцесс из другой оболочки.

3
Это интересная теория, но ОП набирает текст, jobsи оболочка все еще видит процесс как живой. Это было бы одно необычно длинное расписание гонки. :)
— lcd047

3
Прежде всего, спасибо за ваш подробный ответ! Я, конечно, имеет смысл и проясняю немало вещей ... Но, как указано выше, я могу запускать jobsкоманды умножения, после killкоторых все еще указывают, что процесс просто остановлен. Однако вы вдохновили меня на продолжение экспериментов, и я обнаружил следующее: сообщение [1]+ Terminated yesпечатается, как только я запускаю другую внешнюю команду (а не встроенную оболочку, подобную echoили jobs). Так что я могу бегать jobsстолько, сколько мне нравится, и он продолжает печатать [1]+ Stopped yes. Но как только я бегу, lsнапример, Bash печатает[1]+ Terminated yes
— s1m0n

lcd047 не прочитал ваш комментарий к вопросу; это было важно и должно было быть правильно отредактировано в начале вопроса. Операционную систему хоста легко перегрузить, чтобы гости выглядели очень странно изнутри. Просто так и многое другое. (Однажды мне удалось вызвать довольно странное планирование с помощью беглого рабочего стола Bing, потребляющего большую часть процессорного времени хоста.)
— JdeBP

1
@ Жиль Проблема, похоже, заключается в том, jobsчто не замечает, что процесс на самом деле умер ... Не уверен, что делать с обновлением статуса, выполнив другую команду.
— lcd047

1
Даже Жиль не видел комментарий. Вот почему вы должны ставить такие важные вещи в вопросе , а не хоронить их в комментариях. Жиль, ответ ясно говорит о задержках в доставке сигнала, а не о задержках в его отправке . Вы перепутали их. Кроме того, прочитайте комментарий спрашивающего (и действительно пункт, который он здесь приведен) и увидите очень важное неверное фундаментальное предположение, которое вы делаете. Виртуальные процессоры не обязательно работают в режиме блокировки и не могут магически работать всегда на полной скорости.
— JdeBP

2

Что-то странное может происходить в вашей системе, по моему ваш рецепт работает хорошо как с, так и без -9:

> yes
...
^Z
[1]+  Stopped                 yes
> jobs
[1]+  Stopped                 yes
> kill %1
[1]+  Killed                  yes
> jobs
> 

Получить Пид с jobs -pи попытаться убить его как root.


Могу я спросить, какую версию дистрибутива / ядра / bash вы используете? Может быть, внутренняя killкоманда вашего bash делает лишнюю милю и проверяет, заморожено ли задание (вы можете попытаться узнать PID задания и уничтожить его, используя env kill <pid>. Таким образом, вы будете использовать реальную killкоманду, а не встроенную в bash.
— mreithub

bash-4.2-75.3.1.x86_64 на opensuse 13.2. Команда kill не является внутренней:which kill /usr/bin/kill
— Dan Cornilescu

1
whichне является встроенным в bash, поэтому which <anything>всегда будет указывать путь к фактической команде. Но попробуйте сравнить kill --helpс /usr/bin/kill --help.
— mreithub

Ах, верно. Действительно, это встроенный kill.
— Дэн Корнилеску

2

То, что вы наблюдаете, является ошибкой в ​​этой версии bash.

kill -9 %1убивает работу немедленно. Вы можете наблюдать это с ps. Вы можете отслеживать процесс bash, чтобы видеть, когда killвызывается системный вызов, и отслеживать подпроцесс, чтобы видеть, когда он получает и обрабатывает сигналы. Более того, вы можете пойти и посмотреть, что происходит с процессом.

bash-4.3$ sleep 9999
^Z
[1]+  Stopped                 sleep 9999
bash-4.3$ kill -9 %1

[1]+  Stopped                 sleep 9999
bash-4.3$ jobs
[1]+  Stopped                 sleep 9999
bash-4.3$ jobs -l
[1]+  3083 Stopped                 sleep 9999
bash-4.3$ 

В другом терминале:

% ps 3083
  PID TTY      STAT   TIME COMMAND
 3083 pts/4    Z      0:00 [sleep] <defunct>

Подпроцесс - это зомби . Он мертв: от него остается только запись в таблице процессов (но без памяти, кода, открытых файлов и т. Д.). Запись остается до тех пор, пока ее родитель не заметит и не получит статус выхода, вызвав waitсистемный вызов или одного из своих братьев и сестер .

Предполагается, что интерактивная оболочка проверяет наличие мертвых детей и собирает их перед печатью приглашения (если не указано иное). Эта версия bash не может сделать это при некоторых обстоятельствах:

bash-4.3$ jobs -l
[1]+  3083 Stopped                 sleep 9999
bash-4.3$ true
bash-4.3$ /bin/true
[1]+  Killed                  sleep 9999

Вы можете ожидать, что bash сообщит «Killed», как только напечатает приглашение после killкоманды, но это не гарантировано, потому что есть условие гонки. Сигналы доставляются асинхронно: killсистемный вызов возвращается, как только ядро ​​выяснило, в какой процесс (ы) доставить сигнал, не дожидаясь его фактической доставки. Возможно, и на практике это происходит, когда bash успевает проверить состояние своего подпроцесса, обнаружить, что он все еще не мертв ( wait4не сообщает о смерти ребенка), и напечатать, что процесс все еще остановлен. Что неправильно, так это то, что перед следующим приглашением сигнал был доставлен ( psсообщает, что процесс мертв), но bash все еще не вызвалwait4(мы видим это не только потому, что он по-прежнему сообщает о задании как «Остановлено», но и потому, что зомби все еще присутствует в таблице процессов). На самом деле, bash получает зомби только в следующий раз wait4, когда ему нужно позвонить , когда он запускает какую-то другую внешнюю команду.

Ошибка периодически, и я не мог воспроизвести ее, пока отслеживается bash (возможно, потому что это состояние гонки, где bash должен реагировать быстро). Если сигнал доставляется до bash-проверок, все происходит как положено.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.