리눅스는 프로세스를 어떻게 "죽이는가"?


91

수십 년 동안 컴퓨터와 10 년 동안 리눅스를 전문적으로 사용해 왔지만 실제로는 OS와는 달리 마술과는 달리 블랙 박스로 취급한다고 생각합니다.

오늘 저는 그 kill명령 에 대해 생각했고 , 하루에 여러 번 ( '정상적인' -9풍미 와 풍미로) 여러 번 사용하는 동안 어떻게 명령이 배후에서 작동하는지 전혀 모른다는 것을 인정해야합니다.

내 관점에서, 실행중인 프로세스가 "중지"되면 killPID를 호출 한 다음 갑자기 더 이상 실행되지 않습니다. 마법!

실제로 어떤 일이 발생합니까? 맨 페이지는 "신호"에 대해 이야기하지만 반드시 추상화 일뿐입니다. kill -9프로세스로 전송 하는 것은 신호 처리와 같은 프로세스의 협력이 필요하지 않으며 프로세스를 종료시킵니다.

  • 리눅스는 어떻게 프로세스가 CPU 시간을 계속 차지하지 못하게합니까?
  • 예약에서 제거 되었습니까?
  • 열린 파일 핸들에서 프로세스를 분리합니까?
  • 프로세스의 가상 메모리는 어떻게 릴리스됩니까?
  • 리눅스가 프로세스가 차지하는 모든 리소스에 대한 참조를 유지하는 메모리에 글로벌 테이블과 같은 것이 있습니까? 프로세스를 "킬"할 때 Linux는 단순히 해당 테이블을 통과하고 리소스를 하나씩 해제합니다.

정말 모든 것을 알고 싶습니다!



SIGKILL에 관한 질문에 대한 나의 대답은 여기에서도 특히 관련이 있습니다.
telcoM

답변:


72

프로세스에 kill -9를 전송하면 프로세스의 협력 (예 : 신호 처리)이 필요하지 않으며 프로세스를 종료합니다.

일부 신호는 포착되어 무시 될 수 있기 때문에 모두 협력이 필요하다고 가정합니다. 그러나에 따르면 man 2 signal" SIGKILL 및 SIGSTOP 신호 는 포착하거나 무시할 수 없습니다". SIGTERM이 포착 될 수 있습니다. 이것이 평범한 kill것이 항상 효과적인 것은 아닙니다. 일반적으로 이것은 프로세스 처리기의 무언가가 잘못되었음을 의미합니다. 1

프로세스가 주어진 신호에 대한 핸들러를 정의 할 수 없거나 정의 할 수없는 경우 커널은 기본 동작을 수행합니다. SIGTERM 및 SIGKILL의 경우 프로세스가 종료됩니다 (PID가 1이 아니고 커널이 종료되지 않는 한 init) 2 파일 핸들이 닫히고 메모리가 시스템 풀로 리턴되고 상위가 SIGCHILD, 고아를 수신함을 의미합니다. 자식은 마치 호출 한 것처럼 init 등으로 상속됩니다 exit(참조 man 2 exit). 좀비로 끝나지 않는 한 프로세스는 더 이상 존재하지 않습니다.이 경우 프로세스는 여전히 커널의 프로세스 테이블에 몇 가지 정보와 함께 나열됩니다. 그것은 부모가하지 않을 때 발생wait이 정보를 올바르게 처리하십시오. 그러나 좀비 프로세스에는 더 이상 할당 된 메모리가 없으므로 계속 실행할 수 없습니다.

리눅스가 프로세스가 차지하는 모든 리소스에 대한 참조를 유지하고 프로세스를 "킬"할 때 리눅스가 단순히 해당 테이블을 통과하고 리소스를 하나씩 해제하는 전역 테이블과 같은 것이 있습니까?

나는 그것이 정확하다고 생각합니다. 실제 메모리는 페이지 (일반적으로 4KB 청크와 같은 한 페이지)별로 추적되며 해당 페이지는 전역 풀에서 가져 와서 반환됩니다. 포함 된 데이터가 다시 필요한 경우 (즉, 기존 파일에서 읽은 데이터) 사용 가능한 일부 페이지가 캐시되는 것이 조금 더 복잡합니다.

맨 페이지는 "신호"에 대해 이야기하지만 반드시 추상화 일뿐입니다.

물론 모든 신호는 추상화입니다. 그것들은 "프로세스"와 같은 개념입니다. 나는 의미론을 약간 연주하고 있지만 SIGKILL이 SIGTERM과 질적으로 다르다는 것을 의미한다면, 그렇습니다. 잡을 수 없다는 의미에서 그렇습니다. 그러나 둘 다 신호라는 의미에서는 아닙니다. 유사하게, 사과는 오렌지가 아니지만 사과와 오렌지는 선입관에 따르면 과일입니다. SIGKILL은 잡을 수 없기 때문에 추상적 인 것처럼 보이지만 여전히 신호입니다. SIGTERM 처리의 예는 다음과 같습니다.

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>

void sighandler (int signum, siginfo_t *info, void *context) {
    fprintf (
        stderr,
        "Received %d from pid %u, uid %u.\n",
        info->si_signo,
        info->si_pid,
        info->si_uid
    );
}

int main (void) {
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_sigaction = sighandler;
    sa.sa_flags = SA_SIGINFO;
    sigaction(SIGTERM, &sa, NULL);
    while (1) sleep(10);
    return 0;
}

이 과정은 영원히 잠들 것입니다. 당신은 터미널에서 그것을 실행하고 함께 SIGTERM을 보낼 수 있습니다 kill. 그것은 다음과 같은 것들을 뱉어냅니다.

Received 15 from pid 25331, uid 1066.

1066은 내 UID입니다. PID kill는 실행 되는 쉘 의 PID이거나 포크하면 킬의 PID입니다 ( kill 25309 & echo $?).

다시 말하지만 SIGKILL의 핸들러는 작동하지 않기 때문에 아무런 의미가 없습니다. 3 내가 kill -9 25309프로세스를 종료합니다. 그러나 그것은 여전히 ​​신호입니다. 커널은 누가 신호 를 보냈 는지, 어떤 종류의 신호 인지 등에 대한 정보를 가지고 있습니다 .


1. 가능한 신호 목록을 보지 않은 경우을 참조하십시오 kill -l.

2. Tim Post가 아래에 언급 한 것처럼 또 다른 예외는 무정전 수면 프로세스에 적용됩니다 . 근본적인 문제가 해결 될 때까지 깨울 수 없으므로 지속 기간 동안 모든 신호 (SIGKILL 포함)가 지연됩니다. 그러나 프로세스는 이러한 상황을 의도적으로 만들 수 없습니다.

3. 이것이 kill -9실제로 사용 하는 것이 더 좋은 것을 의미하지는 않습니다 . 내 예제 핸들러는로 이어지지 않는다는 점에서 나쁜 것입니다 exit(). SIGTERM 핸들러의 실제 목적은 프로세스에 임시 파일 정리와 같은 작업을 수행 한 다음 자발적으로 종료하는 것입니다. 를 사용 kill -9하면이 기회를 얻지 못하므로 "자발적으로 종료"한 부분이 실패한 것만 가능합니다.


좋아, 그러나 프로세스를 죽이는 -9이유는 누가 이것이 죽어야 하는지를 실제 문제이기 때문입니다! ;)
Kiwy

@Kiwy : 커널. 신호를 포함한 IPC가 통과합니다. 커널은 기본 동작을 구현합니다.
goldilocks

12
프로세스가 해당 상태에있는 동안 디스크 절전 (D)이 모든 신호를 선점한다는 점을 언급 할 가치가 있습니다. 따라서 kill -9특정 I / O 바운드 프로세스를 시도하는 것은 적어도 즉시 작동하지 않습니다.
Tim Post

7
나는 kill -9잡을 수 없기 때문에 그것을받는 프로세스는 종료하기 전에 정리 (예 : 임시 파일 제거, 공유 메모리 해제 등)를 수행 할 수 없다고 덧붙입니다. 따라서 kill -9(일명 kill -kill)을 최후의 수단으로 만 사용하십시오 . a kill -hup및 / 또는 kill -term먼저 시작한 다음 kill -kill최종 타격으로 사용하십시오.
JR 페 거슨

"이 프로세스는 더 이상 존재하지 않습니다. 좀비로 끝나지 않으면 커널의 프로세스 테이블에 일부 정보가 표시됩니다."실제로 모든 프로세스는 죽을 때 좀비 상태가되고 좀비가 사라지면 사라집니다. 부모는 아이를 기다립니다. 일반적으로 너무 빨리
영리한

3

각 프로세스는 예약 된 시간 동안 실행 된 다음 하드웨어 타이머에 의해 중단되어 다른 작업에 CPU 코어를 제공합니다. 그렇기 때문에 CPU 코어보다 훨씬 많은 프로세스가 있거나 단일 코어 CPU에서 많은 프로세스로 모든 운영 체제를 실행할 수 있습니다.

프로세스가 중단 된 후 제어는 커널 코드로 돌아갑니다. 그런 다음 해당 코드는 프로세스 측의 협조없이 중단 된 프로세스의 실행을 재개하지 않기로 결정할 수 있습니다. kill -9는 프로그램의 모든 라인에서 실행될 수 있습니다.


0

다음은 프로세스 종료 방법에 대한 이상적인 설명입니다. 실제로 모든 유닉스 변형에는 많은 추가 합병증과 최적화가 있습니다.

커널은 각 프로세스에 대한 데이터 구조를 가지고 있으며, 매핑하는 메모리, 가지고있는 스레드 및 예약 된 시간, 열려있는 파일 등에 대한 정보를 저장합니다. 커널이 프로세스를 종료하기로 결정한 경우 프로세스가 종료 될 프로세스의 데이터 구조 (및 각 스레드의 데이터 구조)

프로세스의 스레드 중 하나가 현재 다른 CPU에서 예약 된 경우 커널은 다른 CPU에서 인터럽트를 트리거하여 해당 스레드의 실행이 더 빨리 중지되도록 할 수 있습니다.

스케줄러가 스레드가 종료되어야하는 프로세스에 있음을 발견하면 더 이상 스케줄하지 않습니다.

프로세스의 스레드가 더 이상 예약되지 않으면 커널은 프로세스의 리소스 (메모리, 파일 설명자 등)를 해제하기 시작합니다. 커널이 리소스를 해제 할 때마다 소유자가 여전히 라이브 리소스를 가지고 있는지 확인합니다. 프로세스에 더 이상 라이브 리소스 (메모리 매핑, 열린 파일 설명자 등)가 없으면 프로세스 자체의 데이터 구조를 해제하고 해당 항목을 프로세스 테이블에서 제거 할 수 있습니다.

일부 리소스는 즉시 해제 될 수 있습니다 (예 : I / O 작업에서 사용하지 않는 메모리 할당 해제). 다른 리소스 (예 : I / O 작업이 진행되는 동안 ( DMA 가 진행 중이거나 액세스중인 메모리가 사용 중이며 DMA 를 취소하는 동안) I / O 작업을 설명하는 데이터를 해제 할 수 있어야합니다. 주변 장치에 접촉). 그러한 자원의 운전자에게 통지하고 취소를 서두르려고 시도 할 수 있습니다. 작업이 더 이상 진행되지 않으면 드라이버는 해당 리소스 해제를 완료합니다.

(프로세스 테이블의 항목은 실제로 프로세스가 종료 되고 부모가 이벤트를 승인하면 해제되는 상위 프로세스에 속하는 자원입니다 .)

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.