How to make child process die after parent exits?

后端 未结 24 1755
天涯浪人
天涯浪人 2020-11-22 05:31

Suppose I have a process which spawns exactly one child process. Now when the parent process exits for whatever reason (normally or abnormally, by kill, ^C, assert failure o

相关标签:
24条回答
  • 2020-11-22 06:04

    Does the child process have a pipe to/from the parent process? If so, you'd receive a SIGPIPE if writing, or get EOF when reading - these conditions could be detected.

    0 讨论(0)
  • 2020-11-22 06:05

    If parent dies, PPID of orphans change to 1 - you only need to check your own PPID. In a way, this is polling, mentioned above. here is shell piece for that:

    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
    
    0 讨论(0)
  • 2020-11-22 06:09

    I managed to do a portable, non-polling solution with 3 processes by abusing terminal control and sessions. This is mental masturbation, but works.

    The trick is:

    • process A is started
    • process A creates a pipe P (and never reads from it)
    • process A forks into process B
    • process B creates a new session
    • process B allocates a virtual terminal for that new session
    • process B installs SIGCHLD handler to die when the child exits
    • process B sets a SIGPIPE handler
    • process B forks into process C
    • process C does whatever it needs (e.g. exec()s the unmodified binary or runs whatever logic)
    • process B writes to pipe P (and blocks that way)
    • process A wait()s on process B and exits when it dies

    That way:

    • if process A dies: process B gets a SIGPIPE and dies
    • if process B dies: process A's wait() returns and dies, process C gets a SIGHUP (because when the session leader of a session with a terminal attached dies, all processes in the foreground process group get a SIGHUP)
    • if process C dies: process B gets a SIGCHLD and dies, so process A dies

    Shortcomings:

    • process C can't handle SIGHUP
    • process C will be run in a different session
    • process C can't use session/process group API because it'll break the brittle setup
    • creating a terminal for every such operation is not the best idea ever
    0 讨论(0)
  • 2020-11-22 06:11

    For completeness sake. On macOS you can use 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);
    }
    
    0 讨论(0)
  • 2020-11-22 06:12

    I'm trying to solve the same problem, and since my program must run on OS X, the Linux-only solution didn't work for me.

    I came to the same conclusion as the other people on this page -- there isn't a POSIX-compatible way of notifying a child when a parent dies. So I kludged up the next-best thing -- having the child poll.

    When a parent process dies (for any reason) the child's parent process becomes process 1. If the child simply polls periodically, it can check if its parent is 1. If it is, the child should exit.

    This isn't great, but it works, and it's easier than the TCP socket/lockfile polling solutions suggested elsewhere on this page.

    0 讨论(0)
  • 2020-11-22 06:13

    Even though 7 years have passed I've just run into this issue as I'm running SpringBoot application that needs to start webpack-dev-server during development and needs to kill it when the backend process stops.

    I try to use Runtime.getRuntime().addShutdownHook but it worked on Windows 10 but not on Windows 7.

    I've change it to use a dedicated thread that waits for the process to quit or for InterruptedException which seems to work correctly on both Windows versions.

    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();
    }
    
    0 讨论(0)
提交回复
热议问题