[c] 부모가 종료 된 후 자식 프로세스를 죽이는 방법은 무엇입니까?

정확히 하나의 자식 프로세스를 생성하는 프로세스가 있다고 가정하십시오. 이제 부모 프로세스가 어떤 이유로 든 (일반적으로 또는 비정상적으로 kill, ^ C, assert failure 등) 종료 될 때 자식 프로세스가 죽기를 원합니다. 올바르게하는 방법?


stackoverflow에 대한 비슷한 질문 :


Windows의 stackoverflow에 대한 비슷한 질문 :



답변

아이가 제공하는 커널을 요청할 수 있습니다 SIGHUP(또는 다른 신호) 때 옵션을 지정하여 부모 다이 PR_SET_PDEATHSIGprctl()이 같은 콜 :

prctl(PR_SET_PDEATHSIG, SIGHUP);

보다 man 2 prctl 을 참조하십시오.

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


답변

같은 문제를 해결하려고하는데 프로그램이 OS X에서 실행되어야하기 때문에 Linux 전용 솔루션이 작동하지 않았습니다.

이 페이지의 다른 사람들과 같은 결론에 도달했습니다. 부모가 죽을 때 아이에게 알리는 POSIX 호환 방법은 없습니다. 그래서 나는 다음으로 가장 좋은 일을 멈췄습니다.

어떤 이유로 든 부모 프로세스가 종료되면 (어떤 이유로 든) 자식의 부모 프로세스가 프로세스 1이됩니다. 자식이 단순히 정기적으로 폴링하는 경우 부모가 1인지 여부를 확인할 수 있습니다.

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


답변

과거에는 “자식”에서 “원본”코드를 실행하고 “부모”에서 “스폰 된”코드를 실행하여이 작업을 수행했습니다. fork() )이를 달성했습니다. 그런 다음 “spawned”코드에서 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 */

작업 제어가 사용 가능한 쉘 프로세스에서 하위를 실행합니다. 자식 프로세스는 백그라운드에서 생성됩니다. 쉘은 개행 (또는 EOF)을 기다린 다음 아이를 죽입니다.

이유가 무엇이든 부모가 죽으면 파이프 끝이 닫힙니다. 자식 셸은 읽기에서 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 ...

그러나 시스템 프로세스에 의존하고 initPID 1을 갖는 것은 이식성이 없습니다. POSIX.1-2008은 다음을 지정합니다 .

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

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

Linux 또는 FreeBSD 와 같은 최신 시스템에서는 다른 프로세스가 그 역할을 수행 할 수 있습니다. 예를 들어, Linux에서는 프로세스가 prctl(PR_SET_CHILD_SUBREAPER, 1)자신의 자손 중 모든 고아를 상속하는 시스템 프로세스로 설정하기 위해 호출 할 수 있습니다 (참조, Fedora 25 의 ).


답변

완전성을 위해. 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);
}


답변

하위 프로세스에 상위 프로세스와의 파이프가 있습니까? 그렇다면 글을 쓰면 SIGPIPE를 받거나 읽을 때 EOF를 받게됩니다. 이러한 조건이 감지 될 수 있습니다.