Linux에서는 자식 프로세스가 종료되고 해당 부모 프로세스가 아직 이를 기다리지 않으면 좀비 프로세스가 됩니다. 하위 프로세스의 종료 코드는 pid 설명자에 저장됩니다.
a가 SIGKILL
자식에게 전송되면 아무런 효과가 없습니다.
이는 종료 코드가 수정되지 않는다는 의미입니까, SIGKILL
아니면 종료 코드가 수정되어 하위 프로세스가 종료되었음을 나타내기 위해 수정된다는 의미입니까 SIGKILL
?
답변1
이 질문에 대답하려면 신호가 프로세스로 전송되는 방식과 프로세스가 커널에 존재하는 방식을 이해해야 합니다.
task_struct
각 프로세스는 커널에 의해 내부적으로 sched.h
헤더 파일에 정의되어 시작됩니다.여기). 이 구조는 pid와 같은 프로세스에 대한 정보를 보유합니다. 중요한 정보는 다음 위치에 있습니다.1566라인관련 신호가 저장되는 위치입니다. 이 값은 신호가 프로세스로 전송될 때만 설정됩니다.
죽은 프로세스나 좀비 프로세스에는 여전히 task_struct
.이 구조는 부모 프로세스(자연적 또는 입양)가 wait()
수신 후 SIGCHLD
자녀 획득을 요청할 때까지 유지됩니다. 신호가 전송될 때 signal_struct
설정됩니다 . 이 경우 신호를 포착할 수 있는지 여부는 중요하지 않습니다.
신호는 프로세스가 실행될 때마다 평가됩니다.아니면 정확히 말하면,앞으로프로세스회의달리기. 그런 다음 프로세스가 TASK_RUNNING
상태에 있습니다. 커널은 schedule()
스케줄링 알고리즘을 기반으로 실행할 다음 프로세스를 결정하는 루틴을 실행합니다. 해당 프로세스가 다음 실행 프로세스라고 가정하고 signal_struct
처리해야 할 대기 신호가 있는지 여부를 결정하기 위해 값을 평가합니다 . 신호 처리기를 수동으로 정의하는 경우(다음을 통해)signal()
또는sigaction()
), 그렇지 않은 경우 등록된 기능을 실행합니다.신호에 대한 기본 동작처형되다. 기본 동작은 전송된 신호에 따라 다릅니다.
예를 들어, SIGSTOP
신호에 대한 기본 처리기는 현재 프로세스의 상태를 변경한 TASK_STOPPED
다음 schedule()
실행할 새 프로세스를 선택하기 위해 실행됩니다. 는 SIGSTOP
포착할 수 없으므로(예 SIGKILL
: ) 수동 신호 처리기를 등록할 수 없습니다. 신호를 포착할 수 없으면 항상 기본 작업이 수행됩니다.
귀하의 질문에 대해:
스케줄러는 죽은 프로세스나 죽은 프로세스가 TASK_RUNNING
다시 해당 상태에 있는지 확인하지 않습니다. 따라서 커널은 신호가 무엇이든 상관없이 해당 신호에 대해 신호 처리기(기본값 또는 정의됨)를 실행하지 않습니다. 그러므로exit_signal
다시는 설정되지 않습니다. 신호는 in 을 설정하여 signal_struct
프로세스에 "전달"되지만 task_struct
프로세스가 다시 실행되지 않기 때문에 아무 일도 일어나지 않습니다. 실행할 코드가 없으며 프로세스에 남은 것은 프로세스 구조입니다.
그러나 상위 프로세스가 하위 프로세스를 수집하는 경우 wait()
수신되는 종료 코드는 프로세스가 "원래" 종료되었을 때 가졌던 종료 코드입니다. 처리 대기 중인 신호가 있는지 여부는 중요하지 않습니다.
답변2
좀비 프로세스는 기본적으로 죽었습니다. 유일한 문제는 아직 아무도 그 죽음을 인정하지 않았기 때문에 프로세스 테이블의 항목과 제어 블록(각 활성 스레드에 대해 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