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!

3
  • 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. Nov 9, 2017 at 14:36
  • Thank you very much! Can you provide the code, how to terminate it all gracefully please?
    – Ivan
    Nov 9, 2017 at 14:47
  • Do you have the setpgrp command, or do you start the parent from an interactive shell? Nov 9, 2017 at 15:29

3 Answers 3

3
  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.
3
  • 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. Nov 9, 2017 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. Nov 19, 2017 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. Nov 19, 2017 at 6:18
2

You can make a reasonable shortlist using:

ps -ef | awk '$1 == "myUser" && $3 == 1'

Maybe refine that by also filtering on other fields, like STIME not for your session startup, and TTY not ?, and CMD not starting with /usr/lib or /usr/bin or /lib. Put the list into a file for a final check, and then awk out column 2 | xargs | kill.

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;
}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .