Что происходит при отправке SIGKILL в Zombie Process в Linux?

В Linux, когда дочерний процесс завершается и его родитель еще не ждал, он становится процессом зомби. Код выхода ребенка сохраняется в дескрипторе pid.

Если SIGKILL отправляется ребенку, не должно быть никакого эффекта.

Означает ли это, что код выхода не будет изменен SIGKILL или будет изменен код выхода, чтобы указать, что ребенок вышел из-за того, что он получил SIGKILL ?

Чтобы ответить на этот вопрос, вы должны понять, как сигналы отправляются процессу и как процесс существует в ядре.

Каждый процесс представлен как task_struct внутри ядра (определение содержится в sched.h заголовка sched.h и начинается здесь ). Эта структура содержит информацию о процессе; например pid. Важная информация находится в строке 1566, где хранится связанный сигнал. Это устанавливается только в том случае, если сигнал отправляется процессу.

У мертвого процесса или процесса зомби все еще есть task_struct . Структура остается до тех пор, пока родительский процесс (естественный или усыновленный) не вызвал wait() после получения SIGCHLD для получения своего дочернего процесса. Когда сигнал отправляется, устанавливается signal_struct . Не имеет значения, является ли сигнал захватывающим или нет, в этом случае.

Сигналы оцениваются каждый раз, когда выполняется процесс. Или, точнее, до того, как процесс запустится. Затем процесс TASK_RUNNING состояние TASK_RUNNING . Ядро запускает процедуру schedule() которая определяет следующий запущенный процесс в соответствии с его алгоритмом планирования. Предполагая, что этот процесс является следующим запущенным процессом. Затем оценивается значение signal_struct есть ли сигнал ожидания для обработки или нет. Если обработчик сигнала определяется вручную (через signal() или sigaction() ), зарегистрированная функция выполняется, если не выполняется действие по умолчанию для сигнала . Действие по умолчанию зависит от отправляемого сигнала.

Например, обработчик по умолчанию сигнала SIGSTOP изменит состояние текущего процесса на TASK_STOPPED а затем выполнит schedule() чтобы выбрать новый процесс для запуска. Обратите внимание: SIGSTOP не увлекателен (например, SIGKILL ), поэтому нет возможности зарегистрировать ручной обработчик сигнала. В случае неконтролируемого сигнала действие по умолчанию всегда будет выполняться.


На ваш вопрос:

Неисправный или мертвый процесс никогда не будет определяться планировщиком снова в состоянии TASK_RUNNING . Таким образом, ядро ​​никогда не будет запускать обработчик сигнала (по умолчанию или определенный) для соответствующего сигнала, в зависимости от того, какой сигнал был. Поэтому exit_signal никогда не будет установлен снова. Сигнал «обрабатывается» процессом, устанавливая signal_struct в task_struct этого процесса, но ничего больше не произойдет, потому что процесс никогда не будет работать снова. Нет кода для запуска, все, что осталось от процесса, это процесс struct.

Если тогда родительский процесс, однако, пожинает своих дочерних элементов wait() , код выхода, который он получает, является тем, когда процесс «изначально» умер. Не имеет значения, есть ли сигнал, ожидающий обработки.

Процесс зомби в основном уже мертв. Единственное, что никто не признал своей смертью, тем не менее, он продолжает занимать запись в таблице процессов, а также блок управления (структура ядра Linux поддерживает для каждого потока активности). Восстановлены другие ресурсы, такие как обязательные блокировки файлов, разделяемых сегментов памяти, семафоров и т. Д.

Вы не можете сигнализировать о них, потому что никто не может воздействовать на этот сигнал. Даже фатальные сигналы, такие как KILL, бесполезны, поскольку процесс уже завершил его выполнение. Вы можете попробовать себя:

 #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include <sys/wait.h> int main(void) { pid_t pid = fork(); if (pid == -1) exit(-1); if (pid > 0) { //parent printf("[parent]: I'm the parent, the pid of my child is %i\n" "I'll start waiting for it in 10 seconds.\n", pid); sleep(10); int status; wait(&status); if (WIFSIGNALED(status)) { printf("[parent]: My child has died from a signal: %i\n", WTERMSIG(status)); } else if (WIFEXITED(status)) { printf("[parent]: My child has died from natural death\n"); } else { printf("[parent]: I don't know what happened to my child\n"); } } else { //child printf("[child]: I'm dying soon, try to kill me.\n"); sleep(5); printf("[child]: Dying now!\n"); } return 0; } 

Здесь я начинаю процесс, который вилки и спит, прежде чем ждать его ребенка. Ребенок ничего не делает, кроме сна. Вы можете убить ребенка, когда он спит, или сразу после его выхода, чтобы увидеть разницу:

 $ make zombie cc zombie.c -o zombie $ ./zombie [parent]: I'm the parent, the pid of my child is 16693 I'll start waiting for it in 10 seconds. [child]: I'm dying soon, try to kill me. # Here, I did "kill -15 16693" in another console [parent]: My child has died from a signal: 15 $ ./zombie [parent]: I'm the parent, the pid of my child is 16717 I'll start waiting for it in 10 seconds. [child]: I'm dying soon, try to kill me. [child]: Dying now! # Here, I did "kill -15 16717" in another console [parent]: My child has died from natural death