부모가 종료된 후 자식 프로세스를 죽게 만드는 방법은 무엇입니까?

StackOverflow https://stackoverflow.com/questions/284325

  •  08-07-2019
  •  | 
  •  

문제

정확히 하나의 하위 프로세스를 생성하는 프로세스가 있다고 가정합니다.이제 어떤 이유로든 부모 프로세스가 종료되면(정상적으로든 비정상적으로든, 종료, ^C, 실패 주장 등) 자식 프로세스가 죽기를 원합니다.올바르게 수행하는 방법은 무엇입니까?


stackoverflow에 대한 비슷한 질문이 있습니다.


stackoverflow에 대한 비슷한 질문 윈도우:

도움이 되었습니까?

해결책

어린이는 커널에게 전달하도록 요청할 수 있습니다 SIGHUP (또는 기타 신호) 옵션을 지정하여 부모가 죽을 때 PR_SET_PDEATHSIG 안에 prctl() 다음과 같은 SYSCALL :

prctl(PR_SET_PDEATHSIG, SIGHUP);

보다 man 2 prctl 자세한 내용은.

편집 : 이것은 Linux 전용입니다

다른 팁

동일한 문제를 해결하려고 노력하고 있으며 프로그램이 OS X에서 실행되어야하므로 Linux 전용 솔루션이 효과가 없었습니다.

나는이 페이지의 다른 사람들과 같은 결론을 내렸다. 부모가 죽을 때 아이에게 알리는 posix 호환 방법은 없다. 그래서 나는 아이가 여론 조사를하는 다음으로 가장 좋은 일을 시작했습니다.

부모 과정이 (어떤 이유로 든) 사망 (어떠한 이유로 든) 자녀의 부모 절차가 절차 1이됩니다. 자녀가 단순히 주기적으로 투표하는 경우 부모가 1인지 확인할 수 있습니다.

이것은 훌륭하지는 않지만 작동 하며이 페이지의 다른 곳에서 제안 된 TCP 소켓/잠금 장치 폴링 솔루션보다 쉽습니다.

나는 과거에 "자식"에서 "원본"코드를 실행하여 "부모"에서 "스폰 된"코드를 실행하여 이것을 달성했습니다 (즉, 다음과 같은 테스트의 일반적인 감각을 뒤집습니다. fork()). 그런 다음 "스폰 된"코드에서 Sigchld를 트랩합니다 ...

귀하의 경우에는 불가능할 수 있지만 작동하면 귀엽습니다.

아동 프로세스를 수정할 수없는 경우 다음과 같은 것을 시도 할 수 있습니다.

int pipes[2];
pipe(pipes)
if (fork() == 0) {
    close(pipes[1]); /* Close the writer end in the child*/
    dup2(0, pipes[0]); /* Use reader end as stdin */
    exec("sh -c 'set -o monitor; child_process & read dummy; kill %1'")
}

close(pipes[0]); /* Close the reader end in the parent */

이것은 작업 제어가 활성화 된 쉘 프로세스 내에서 어린이를 실행합니다. 아동 과정은 백그라운드에서 생성됩니다. 껍질은 Newline (또는 EOF)을 기다린 다음 아이를 죽입니다.

부모가 죽을 때 (이유는 무엇이든) 파이프 끝을 닫습니다. Child Shell은 읽기에서 EOF를 얻고 배경 아동 과정을 죽입니다.

완전성을 위해. MacOS에서는 Kqueue를 사용할 수 있습니다.

void noteProcDeath(
    CFFileDescriptorRef fdref, 
    CFOptionFlags callBackTypes, 
    void* info) 
{
    // LOG_DEBUG(@"noteProcDeath... ");

    struct kevent kev;
    int fd = CFFileDescriptorGetNativeDescriptor(fdref);
    kevent(fd, NULL, 0, &kev, 1, NULL);
    // take action on death of process here
    unsigned int dead_pid = (unsigned int)kev.ident;

    CFFileDescriptorInvalidate(fdref);
    CFRelease(fdref); // the CFFileDescriptorRef is no longer of any use in this example

    int our_pid = getpid();
    // when our parent dies we die as well.. 
    LOG_INFO(@"exit! parent process (pid %u) died. no need for us (pid %i) to stick around", dead_pid, our_pid);
    exit(EXIT_SUCCESS);
}


void suicide_if_we_become_a_zombie(int parent_pid) {
    // int parent_pid = getppid();
    // int our_pid = getpid();
    // LOG_ERROR(@"suicide_if_we_become_a_zombie(). parent process (pid %u) that we monitor. our pid %i", parent_pid, our_pid);

    int fd = kqueue();
    struct kevent kev;
    EV_SET(&kev, parent_pid, EVFILT_PROC, EV_ADD|EV_ENABLE, NOTE_EXIT, 0, NULL);
    kevent(fd, &kev, 1, NULL, 0, NULL);
    CFFileDescriptorRef fdref = CFFileDescriptorCreate(kCFAllocatorDefault, fd, true, noteProcDeath, NULL);
    CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack);
    CFRunLoopSourceRef source = CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, fdref, 0);
    CFRunLoopAddSource(CFRunLoopGetMain(), source, kCFRunLoopDefaultMode);
    CFRelease(source);
}

아동 프로세스에 부모 프로세스의 파이프가 있습니까? 그렇다면, 글을 쓰면 시그 파이프를 받거나 읽을 때 EOF를 얻을 수 있습니다. 이러한 조건을 감지 할 수 있습니다.

Linux에서는 아동에 부모 사망 신호를 설치할 수 있습니다.

#include <sys/prctl.h> // prctl(), PR_SET_PDEATHSIG
#include <signal.h> // signals
#include <unistd.h> // fork()
#include <stdio.h>  // perror()

// ...

pid_t ppid_before_fork = getpid();
pid_t pid = fork();
if (pid == -1) { perror(0); exit(1); }
if (pid) {
    ; // continue parent execution
} else {
    int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
    if (r == -1) { perror(0); exit(1); }
    // test in case the original parent exited just
    // before the prctl() call
    if (getppid() != ppid_before_fork)
        exit(1);
    // continue child execution ...

부모 프로세스 ID를 포크 전에 저장하고 그 후 아이의 테스트 prctl() 사이의 인종 조건을 제거합니다 prctl() 그리고 아이를 불렀던 과정의 출구.

또한 자녀의 부모 사망 신호는 새로 창조 된 자녀들로부터 청소됩니다. 그것은 영향을받지 않습니다 execve().

모든 채택을 담당하는 시스템 프로세스가 확실하다면 그 테스트를 단순화 할 수 있습니다. 고아 PID 1이 있습니다 :

pid_t pid = fork();
if (pid == -1) { perror(0); exit(1); }
if (pid) {
    ; // continue parent execution
} else {
    int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
    if (r == -1) { perror(0); exit(1); }
    // test in case the original parent exited just
    // before the prctl() call
    if (getppid() == 1)
        exit(1);
    // continue child execution ...

해당 시스템 프로세스에 의존합니다 init 그러나 PID 1을 갖는 것은 휴대용이 아닙니다. posix.1-2008 지정:

기존의 모든 자식 프로세스 및 호출 프로세스의 좀비 프로세스의 부모 프로세스 ID는 구현 정의 시스템 프로세스의 프로세스 ID로 설정되어야합니다. 즉, 이러한 프로세스는 특수 시스템 프로세스에 의해 상속됩니다.

전통적으로 모든 고아를 채택하는 시스템 프로세스는 PID 1, 즉 init입니다. 이는 모든 프로세스의 조상입니다.

현대 시스템에서 리눅스 또는 freebsd 다른 과정은 그 역할을 할 수 있습니다. 예를 들어 Linux에서 프로세스가 호출 할 수 있습니다. prctl(PR_SET_CHILD_SUBREAPER, 1) 자손의 모든 고아를 물려받는 시스템 프로세스로서 스스로를 확립하기 위해 (참조 : 예시 페도라 25).

여기서 또 다른 답변에서 영감을 얻은 나는 다음과 같은 All-Posix 솔루션을 생각해 냈습니다. 일반적인 아이디어는 부모와 자녀 사이에 중간 과정을 만드는 것입니다. 이는 부모가 죽을 때의 목적이 있습니다.

이 유형의 솔루션은 어린이의 코드를 수정할 수없는 경우 유용합니다.

int p[2];
pipe(p);
pid_t child = fork();
if (child == 0) {
    close(p[1]); // close write end of pipe
    setpgid(0, 0); // prevent ^C in parent from stopping this process
    child = fork();
    if (child == 0) {
        close(p[0]); // close read end of pipe (don't need it here)
        exec(...child process here...);
        exit(1);
    }
    read(p[0], 1); // returns when parent exits for any reason
    kill(child, 9);
    exit(1);
}

이 방법에는 두 가지 작은 경고가 있습니다.

  • 의도적으로 중간 과정을 죽이면 부모가 죽을 때 아이가 죽지 않을 것입니다.
  • 자녀가 부모보다 먼저 나가면, 중간 과정은 원래 자녀 PID를 죽이려고합니다. 이제 다른 과정을 참조 할 수 있습니다. (이것은 중간 프로세스에서 더 많은 코드로 수정 될 수 있습니다.)

제쳐두고, 내가 사용하는 실제 코드는 Python입니다. 여기는 완전성을위한 것입니다.

def run(*args):
    (r, w) = os.pipe()
    child = os.fork()
    if child == 0:
        os.close(w)
        os.setpgid(0, 0)
        child = os.fork()
        if child == 0:
            os.close(r)
            os.execl(args[0], *args)
            os._exit(1)
        os.read(r, 1)
        os.kill(child, 9)
        os._exit(1)
    os.close(r)

표준 POSIX 호출 만 사용한다고 보장 할 수는 없다고 생각합니다. 실생활과 마찬가지로, 일단 아이가 생성되면, 그것은 그 자체의 삶을 가지고 있습니다.

그것 ~이다 부모 절차가 가능한 대부분의 종료 사건을 포착하고 그 시점에서 아동 과정을 죽이려고 시도 할 수 있지만 항상 잡을 수없는 사람들이 항상 있습니다.

예를 들어, 프로세스는 a를 잡을 수 없습니다 SIGKILL. 커널 이이 신호를 처리하면 해당 프로세스에 대한 알림없이 지정된 프로세스를 죽입니다.

비유를 확장하려면 - 다른 표준 방법은 더 이상 부모가 없다는 것을 알 때 자녀가 자살하는 것입니다.

Linux 전용 방법이 있습니다 prctl(2) - 다른 답변을 참조하십시오.

다른 사람들이 지적했듯이, 부모가 나가는 경우 부모 PID에 의존하여 부모가 출구가 없을 때 1이됩니다. 특정 부모 프로세스 ID를 기다리는 대신 ID가 변경 될 때까지 기다리십시오.

pit_t pid = getpid();
switch (fork())
{
    case -1:
    {
        abort(); /* or whatever... */
    }
    default:
    {
        /* parent */
        exit(0);
    }
    case 0:
    {
        /* child */
        /* ... */
    }
}

/* Wait for parent to exit */
while (getppid() != pid)
    ;

전속 속도로 폴링하고 싶지 않은 경우 원하는대로 마이크로 슬리핑을 추가하십시오.

이 옵션은 파이프를 사용하거나 신호에 의존하는 것보다 나에게 간단 해 보입니다.

설치 a 트랩 처리기 Sigint를 잡으려면 자녀가 아직 살아 있다면 자녀 과정을 죽인다. 그러나 다른 포스터는 Sigkill을 잡을 수 없다는 것이 맞다.

독점 액세스 권한을 가진 .lockfile을 열고 아동이 그것을 열려고 노력하는 아동 설문 조사 - 오픈이 성공하면 아동 프로세스가 종료되어야합니다.

이 솔루션은 저에게 효과적이었습니다.

  • Stdin 파이프를 어린이에게 전달하십시오 - 스트림에 데이터를 쓸 필요가 없습니다.
  • 아이는 Stdin에서 eof까지 무기한으로 읽습니다. EOF는 부모가 갔다는 신호입니다.
  • 이것은 부모가 언제 갔는지 탐지하는 완벽하고 휴대용 방법입니다. 부모가 충돌하더라도 OS는 파이프를 닫습니다.

이것은 부모가 살아있을 때만 존재하는 근로자 유형 과정을위한 것이 었습니다.

빠르고 더러운 방법은 어린이와 부모 사이에 파이프를 만드는 것입니다. 부모가 나가면 아이들은 시그 파이프를 받게됩니다.

일부 포스터는 이미 파이프를 언급했습니다 kqueue. 실제로 연결된 한 쌍을 만들 수도 있습니다. 유닉스 도메인 소켓 에 의해 socketpair() 전화. 소켓 유형이 있어야합니다 SOCK_STREAM.

두 소켓 파일 설명자 FD1, FD2가 있다고 가정 해 봅시다. 지금 fork() 어린이 프로세스를 만들려면 FDS를 물려받습니다. 부모에게는 FD2를 닫고 어린이에서는 FD1을 닫습니다. 이제 각 프로세스가 가능합니다 poll() 나머지 오픈 FD는 POLLIN 이벤트. 각면이 명시 적으로는 안되는 한 close() 정상적인 생애 동안의 FD, 당신은 POLLHUP 플래그는 상대방의 종료를 나타냅니다 (깨끗하든 말든). 이 행사에 대한 통보 후, 아이는해야 할 일 (예 : 죽음)을 결정할 수 있습니다.

#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <poll.h>
#include <stdio.h>

int main(int argc, char ** argv)
{
    int sv[2];        /* sv[0] for parent, sv[1] for child */
    socketpair(AF_UNIX, SOCK_STREAM, 0, sv);

    pid_t pid = fork();

    if ( pid > 0 ) {  /* parent */
        close(sv[1]);
        fprintf(stderr, "parent: pid = %d\n", getpid());
        sleep(100);
        exit(0);

    } else {          /* child */
        close(sv[0]);
        fprintf(stderr, "child: pid = %d\n", getpid());

        struct pollfd mon;
        mon.fd = sv[1];
        mon.events = POLLIN;

        poll(&mon, 1, -1);
        if ( mon.revents & POLLHUP )
            fprintf(stderr, "child: parent hung up\n");
        exit(0);
    }
}

위의 개념 증명 코드를 컴파일하려고 시도하고 다음과 같은 터미널에서 실행할 수 있습니다. ./a.out &. 다양한 신호로 부모 PID를 죽이는 것을 실험하는 데 약 100 초가 걸리거나 간단히 종료됩니다. 두 경우 모두 "자식 : 부모가 끊어진"메시지가 표시되어야합니다.

사용 방법과 비교합니다 SIGPIPE 핸들러,이 방법은 시도 할 필요가 없습니다 write() 전화.

이 방법도 있습니다 대칭, 즉, 프로세스는 동일한 채널을 사용하여 서로의 존재를 모니터링 할 수 있습니다.

이 솔루션은 POSIX 기능 만 호출합니다. 나는 이것을 Linux와 FreeBSD에서 시도했다. 다른 유닉스에서 작동해야한다고 생각하지만 실제로 테스트하지 않았습니다.

또한보십시오:

  • unix(7) Linux Man Pages, unix(4) freebsd의 경우, poll(2), socketpair(2), socket(7) Linux에서.

아래에 posix,, exit(), _exit() 그리고 _Exit() 함수는 다음으로 정의됩니다.

  • 프로세스가 제어 프로세스 인 경우, SIGHUP 신호는 호출 프로세스에 속하는 제어 터미널의 전경 프로세스 그룹에서 각 프로세스로 전송되어야합니다.

따라서 부모 프로세스가 프로세스 그룹의 제어 프로세스가되도록 준비하면 부모가 종료 할 때 자녀가 샴페인 신호를 받아야합니다. 부모가 충돌 할 때 그런 일이 일어나지 않지만 그렇게 생각합니다. 확실히, 충돌이 아닌 경우의 경우 잘 작동해야합니다.

기본 정의 (정의) 섹션과 시스템 서비스 정보를 포함하여 많은 작은 글씨를 읽어야 할 수도 있습니다. exit() 그리고 setsid() 그리고 setpgrp() - 완전한 그림을 얻으려면. (나도 그럴거야!)

PID 0에 신호를 보내면 예를 들어

kill(0, 2); /* SIGINT */

이 신호는 전체 프로세스 그룹으로 전송되어 어린이를 효과적으로 죽입니다.

다음과 같이 쉽게 테스트 할 수 있습니다.

(cat && kill 0) | python

그런 다음 ^d를 누르면 텍스트가 표시됩니다. "Terminated" Python 통역사가 실제로 사망했음을 나타내는 것으로, Stdin이 폐쇄 되었기 때문에 방금 빠져 나가는 대신.

다른 사람과 관련이있는 경우, C ++의 Forked Child 프로세스에서 JVM 인스턴스를 생성 할 때, 부모 프로세스가 완료된 후 JVM 인스턴스를 제대로 종료 할 수있는 유일한 방법은 다음을 수행하는 것이 었습니다. 이 작업을 수행하는 가장 좋은 방법이 아니라면 누군가가 의견에 피드백을 제공 할 수 있기를 바랍니다.

1) 전화 prctl(PR_SET_PDEATHSIG, SIGHUP) Java 앱을 통해 제안 된대로 포크 아동 프로세스에서 execv, 그리고

2) 학부모 PID가 1과 같은 경우에 설문 조사를하는 Java 응용 프로그램에 종료 후크를 추가 한 다음 열심히하십시오. Runtime.getRuntime().halt(0). 폴링은 ps 명령 (참조 : Linux에서 Java 또는 Jruby에서 내 PID를 어떻게 찾습니까?).

130118 편집 :

그것은 강력한 해결책이 아닌 것 같습니다. 나는 여전히 무슨 일이 일어나고 있는지에 대한 뉘앙스를 이해하기 위해 조금 어려움을 겪고 있지만, 화면/SSH 세션에서 이러한 응용 프로그램을 실행할 때 때때로 고아 JVM 프로세스를 받고있었습니다.

Java 앱에서 PPID에 대한 폴링 대신, 나는 단순히 셧다운 후크 공연 정리와 위와 같이 단단한 정지가 이어졌습니다. 그런 다음 호출했습니다 waitpid 스폰 된 아동 프로세스의 C ++ 상위 앱에서 모든 것을 종료 할 때였습니다. 아동 프로세스가 종료되도록 보장하는 반면, 부모는 기존 참조를 사용하여 자녀가 종료되도록하는 것이 더 강력한 솔루션 인 것 같습니다. 이것을 부모 과정이 기뻐할 때마다 종료 된 이전 솔루션과 비교하고, 아이들이 종단하기 전에 고아가되었는지 알아 내려고 노력했습니다.

부모가 죽으면 고아의 PPID가 1으로 변경됩니다. 자신의 PPID 만 확인하면됩니다. 어떤면에서, 이것은 위에서 언급 한 여론 조사입니다. 다음은 쉘 조각입니다.

check_parent () {
      parent=`ps -f|awk '$2=='$PID'{print $3 }'`
      echo "parent:$parent"
      let parent=$parent+0
      if [[ $parent -eq 1 ]]; then
        echo "parent is dead, exiting"
        exit;
      fi
}


PID=$$
cnt=0
while [[ 1 = 1 ]]; do
  check_parent
  ... something
done

나는 완벽하지 않은 2 개의 솔루션을 발견했다.

1. SIGTERM 신호를 받으면 모든 어린이 (-pid)에 의해 모든 어린이를 kill니다.
분명히,이 솔루션은 "킬 -9"를 처리 할 수는 없지만 대부분의 경우에 작동하며 모든 아동 프로세스를 기억할 필요가 없기 때문에 매우 간단합니다.


    var childProc = require('child_process').spawn('tail', ['-f', '/dev/null'], {stdio:'ignore'});

    var counter=0;
    setInterval(function(){
      console.log('c  '+(++counter));
    },1000);

    if (process.platform.slice(0,3) != 'win') {
      function killMeAndChildren() {
        /*
        * On Linux/Unix(Include Mac OS X), kill (-pid) will kill process group, usually
        * the process itself and children.
        * On Windows, an JOB object has been applied to current process and children,
        * so all children will be terminated if current process dies by anyway.
        */
        console.log('kill process group');
        process.kill(-process.pid, 'SIGKILL');
      }

      /*
      * When you use "kill pid_of_this_process", this callback will be called
      */
      process.on('SIGTERM', function(err){
        console.log('SIGTERM');
        killMeAndChildren();
      });
    }

동시에, Process.exit 어딘가에 호출하는 경우 위의 길과 같은 'Exit'핸들러를 설치할 수 있습니다. 참고 : CTRL+C 및 갑작스런 충돌은 OS에 의해 자동으로 처리되어 프로세스 그룹을 죽였으므로 더 이상 여기에 없습니다.

2. 사용하십시오 chjj/pty.js 터미널을 제어하여 프로세스를 생성합니다.
어쨌든 현재 프로세스를 죽일 때 -9를 죽일 때, 모든 아동 프로세스도 자동으로 (OS?) 죽을 것입니다. 현재 프로세스가 터미널의 다른 측면을 유지하기 때문에 현재 프로세스가 사라지면 하위 프로세스는 SIGPIPE가 사라집니다.


    var pty = require('pty.js');

    //var term =
    pty.spawn('any_child_process', [/*any arguments*/], {
      name: 'xterm-color',
      cols: 80,
      rows: 30,
      cwd: process.cwd(),
      env: process.env
    });
    /*optionally you can install data handler
    term.on('data', function(data) {
      process.stdout.write(data);
    });
    term.write(.....);
    */

나는 터미널 제어와 세션을 남용하여 3가지 프로세스로 이식 가능한 비폴링 솔루션을 만들었습니다.이것은 정신적 자위이지만 효과가 있습니다.

비결은 다음과 같습니다.

  • 프로세스 A가 시작되었습니다
  • 프로세스 A는 파이프 P를 생성합니다(그리고 결코 읽지 않습니다).
  • 프로세스 A가 프로세스 B로 분기됨
  • 프로세스 B가 새 세션을 생성합니다.
  • 프로세스 B는 새 세션에 가상 터미널을 할당합니다.
  • 프로세스 B는 자식이 종료될 때 죽도록 SIGCHLD 핸들러를 설치합니다.
  • 프로세스 B는 SIGPIPE 핸들러를 설정합니다.
  • 프로세스 B가 프로세스 C로 분기됨
  • 프로세스 C는 필요한 모든 작업을 수행합니다(예:exec()는 수정되지 않은 바이너리를 실행하거나 어떤 로직이든 실행합니다)
  • 프로세스 B는 파이프 P에 기록하고 그런 식으로 차단합니다.
  • 프로세스 A는 프로세스 B에서 대기()를 하고 프로세스가 종료되면 종료됩니다.

그런 식으로:

  • 프로세스 A가 종료되면:프로세스 B는 SIGPIPE를 받고 종료됩니다.
  • 프로세스 B가 종료되면:프로세스 A의 wait()가 반환되고 죽으면 프로세스 C는 SIGHUP을 얻습니다(왜냐하면 터미널이 연결된 세션의 세션 리더가 죽으면 전경 프로세스 그룹의 모든 프로세스가 SIGHUP을 얻기 때문입니다)
  • 프로세스 C가 종료되면:프로세스 B는 SIGCHLD를 받고 종료되므로 프로세스 A는 종료됩니다.

단점:

  • 프로세스 C는 SIGHUP을 처리할 수 없습니다.
  • 프로세스 C는 다른 세션에서 실행됩니다
  • 프로세스 C는 불안정한 설정을 깨뜨릴 수 있으므로 세션/프로세스 그룹 API를 사용할 수 없습니다.
  • 그러한 모든 작업을 위한 터미널을 만드는 것은 최고의 아이디어가 아닙니다.

7 년이 지났지 만 개발 중에 WebPack-Dev-Server를 시작 해야하는 SpringBoot 애플리케이션을 실행 하면서이 문제를 해결하고 백엔드 프로세스가 중단 될 때는 죽여야합니다.

나는 사용하려고 노력한다 Runtime.getRuntime().addShutdownHook 그러나 Windows 10에서는 작동했지만 Windows 7에서는 작동하지 않았습니다.

프로세스가 종료되기를 기다리는 전용 스레드를 사용하도록 변경합니다. InterruptedException 두 Windows 버전에서 올바르게 작동하는 것 같습니다.

private void startWebpackDevServer() {
    String cmd = isWindows() ? "cmd /c gradlew webPackStart" : "gradlew webPackStart";
    logger.info("webpack dev-server " + cmd);

    Thread thread = new Thread(() -> {

        ProcessBuilder pb = new ProcessBuilder(cmd.split(" "));
        pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
        pb.redirectError(ProcessBuilder.Redirect.INHERIT);
        pb.directory(new File("."));

        Process process = null;
        try {
            // Start the node process
            process = pb.start();

            // Wait for the node process to quit (blocking)
            process.waitFor();

            // Ensure the node process is killed
            process.destroyForcibly();
            System.setProperty(WEBPACK_SERVER_PROPERTY, "true");
        } catch (InterruptedException | IOException e) {
            // Ensure the node process is killed.
            // InterruptedException is thrown when the main process exit.
            logger.info("killing webpack dev-server", e);
            if (process != null) {
                process.destroyForcibly();
            }
        }

    });

    thread.start();
}

역사적으로 UNIX V7에서 프로세스 시스템은 프로세스의 상위 ID를 확인하여 프로세스의 고아성을 감지했습니다. 내가 말했듯이, 역사적으로 init(8) 시스템 프로세스는 한 가지 이유만의 특별한 프로세스입니다. 죽을 수 없습니다. 새로운 상위 프로세스 ID를 할당하는 커널 알고리즘 이이 사실에 따라 다르기 때문에 죽을 수 없습니다. 프로세스가 실행될 때 exit(2) 전화 (프로세스 시스템 호출 또는 외부 작업을 통해 신호 등을 보내는 것으로 보입니다.) 커널은이 프로세스의 모든 어린이를 부모 프로세스 ID로 재 할당합니다. 이것은 가장 쉬운 테스트로 이어지고 프로세스가 고아가 있는지 알 수있는 가장 휴대용 방법으로 이어집니다. 결과를 확인하십시오 getppid(2) 시스템 호출 및 그것이 프로세스 ID 인 경우 init(2) 프로세스는 시스템 호출 전에 프로세스가 고아를 얻었습니다.

이 접근법에서 두 가지 문제가 발생하여 문제가 발생할 수 있습니다.

  • 먼저, 우리는 변경할 가능성이 있습니다 init 모든 사용자 프로세스에 대한 프로세스를 통해 INNT 프로세스가 항상 모든 고아 프로세스의 부모가 될 수 있습니까? 글쎄, exit 시스템 통화 코드 호출을 실행하는 프로세스가 INIT 프로세스인지 (PID를 사용한 프로세스)인지 확인하기위한 명시 적 점검이 있으며,이 경우 커널 패닉 (더 이상 프로세스 계층 구조를 유지할 수 없음). 따라서 Init 프로세스가 수행하는 것은 허용되지 않습니다. exit(2) 전화.
  • 둘째, 위에서 노출 된 기본 테스트에는 레이스 조건이 있습니다. INIT 프로세스 'ID는 역사적으로 가정됩니다 1, 그러나 그것은 POSIX 접근법에 의해 보증되지 않습니다. (다른 응답에 노출 된대로) 시스템의 프로세스 ID 만 해당 목적으로 예약되어 있다고 말합니다. 거의 posix 구현이이를 수행하지 않으며, 원래 UNIX 파생 시스템에서 1 응답으로 getppid(2) 시스템 호출은 프로세스가 고아라고 가정하기에 충분합니다. 확인하는 또 다른 방법은 a를 만드는 것입니다 getppid(2) 포크 직후에 해당 값을 새 통화 결과와 비교하십시오. 두 통화 모두 함께 원자가가 아니며 부모 프로세스가 fork(2) 그리고 첫 번째 getppid(2) 시스템 호출. 과정parent id only changes once, when its parent does an출구 (2)call, so this should be enough to check if thegetppid (2)result changed between calls to see that parent process has exit. This test is not valid for the actual children of the init process, because they are always children ofinit (8)`, 그러나 이러한 프로세스를 부모가없는 것으로 안전하게 가정 할 수 있습니다 (INIT 프로세스를 시스템에서 대체 할 때를 제외하고)

이 작업을 수행하는 또 다른 방법은 부모를 새 PID 네임 스페이스로 만들도록하는 것입니다. 그런 다음 네임 스페이스에서 PID 1이 될 것입니다.이를 종료하면 모든 아이들이 즉시 살해 될 것입니다. SIGKILL.

불행히도 새로운 PID 네임 스페이스를 만들려면 CAP_SYS_ADMIN. 그러나이 방법은 매우 효과적이며 부모의 초기 발사를 넘어서 부모 나 어린이에게 실질적인 변화가 필요하지 않습니다.

보다 클론 (2), pid_namespaces (7), 그리고 샤워 방지 (2).

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top