1

I have the following problem: when my big parent process dies (accidentelly!) after it created many child processes, all child processes switched to init.

How can I safely kill all these child processes ?

I thought there is a way: get to know parent process by this command

ps -o ppid= <number of some child process>

and after doing it I can use this answer and kill all child processes.

Problem here is that all child's processes switched to init and this solution does not work...

Is there a way except for htop and kill it all manually?

Thank you in advance!

  • 1
    As you've noted, when a process dies any children get re-parented to init. At that point, there's no way to know which process was the original parent. Moving forward, however, you can update the parent process to ask its children to die when it terminates gracefully. – Andy Dalton Nov 9 '17 at 14:36
  • Thank you very much! Can you provide the code, how to terminate it all gracefully please? – Ivan Nov 9 '17 at 14:47
  • Do you have the setpgrp command, or do you start the parent from an interactive shell? – Mark Plotnick Nov 9 '17 at 15:29
2
  1. Short Term Workaround:  If your big parent process creates all the children around the same time, sort the output of ps by STIME (process start time).  That (along with the process name) will help you identify the orphans from this invocation.
  2. Long Term Fix:  Modify your big parent process program to keep a log of the PIDs of all the processes that it starts.  Then you can use that as a kill list.
  • 1
    I don't like 2, because it might happen that the pid gets reassigned to a new process before you kill them. It's not very likely, but if it does happen it will mean some random process gets killed and possibly no one will know why. – David Knipe Nov 9 '17 at 20:35
  • @NateEldredge: No, I believe David’s right. Remember, we’re talking about cleaning up after the unplanned termination of the parent process.  If process 100 forks process 200, and then process 100 dies, process 200’s PPID becomes 1 (unless you use thrig’s idea) and init (process 1) waits and vaporizes the zombie. – G-Man Nov 19 '17 at 4:17
  • Oh true. And anyway, for 2, the grandparent needs a log not only of the PIDs which it started, but of the PIDs started by its descendants. That would need some sort of IPC. – Nate Eldredge Nov 19 '17 at 6:18
1

Not portable, but Linux allows for not-init reparenting of processes, see PR_SET_CHILD_SUBREAPER in prctl(2).

PR_SET_CHILD_SUBREAPER (since Linux 3.4)

      If arg2 is nonzero, set the "child subreaper" attribute of the calling process; if arg2 is zero, unset the attribute.  When a process is marked as a child subreaper, all of the children that it creates, and their descendants, will be marked as having a subreaper.  In effect, a subreaper fulfills the role of init(1) for its descendant processes.  …

However your subreaper could also die (accidentally). Another option, again on Linux, may be a PID namespace or container. The more usual solution is to make the parent process as simple and robust as possible, so that it is less likely to be kicked over or die.

More complicated would be to link the child processes to the parent, though this may not be possible if the child processes execs something else, or if the child processes cannot be complicated with async I/O as the child will have to check the pipe for EOF to notice that the parent has gone away:

#include <err.h>
#include <errno.h>
#include <unistd.h>

int main(void)
{
    int fd[2];
    char ch;
    ssize_t ret;
    pipe(fd);

    switch (fork()) {
    case -1:
        err(1, "fork failed");
    case 0:
        close(fd[1]);
        warnx("child  %d start", getpid());
        /* this would really need to be done in an event loop so the
         * child can do other things meanwhile */
        ret = read(fd[0], &ch, 1);
        if (ret == 0)
            errx(1, "EOF from parent (child %d)", getpid());
        break;
    default:
        /* and another child process... */
        switch (fork()) {
        case -1:
            err(1, "fork failed");
        case 0:
            close(fd[1]);
            warnx("child  %d start", getpid());
            ret = read(fd[0], &ch, 1);
            if (ret == 0)
                errx(1, "EOF from parent (child %d)", getpid());
            break;
        default:
            sleep(9);
        }
    }
    return 0;
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.