简体   繁体   中英

How to kill a process tree programmatically on Linux using C

I am trying to write a function that spawns a child process, lets it run for a certain amount of time and then kills it if it hasn't finished:

int sysExecTimeout(const char * exePath, int timeoutSec);

In the function, I use fork and execl to spawn the child, and, when it times out, I use kill(pid, SIGTERM) and kill(pid, SIGKILL) after 2 seconds, to ensure the child dies:

pid_t pid = fork();

if(pid == 0) {
    execl("/bin/sh", "sh", "-c", exePath);
    exit(127);
} else if(pid != -1) {
    // timeout code
    if(timeout) {
        kill(pid, SIGTERM);
        sleep(2);
        kill(pid, SIGKILL);
    }
}

I am using Linux, and it seems that when a parent process dies, the child is not killed automatically. So the two kill calls will just kill the /bin/sh process and leave the exePath command running, since it is a child process of /bin/sh .

I am trying to write the sysExecTimeout function such that it kills the entire process tree rooted at pid , where pid is the PID from pid = fork()

I need this because the exePath command will spawn other commands, which can also spawn other commands, which could get stuck and consume resources.

I do not have control over the exePath binaries/scripts that get executed, so I cannot write my own parent-dies-so-kill-the-children logic in them.

I tried using kill(0, SIGTERM) , which almost did the job, except it also killed my own process :)

I am wondering if there is a flag I can turn on programatically in C that says "hey man, when I die, take all my children and kill them, and repeat recursively for their children" such that the entire process tree started from that program dies (assuming the PID/PPID chain can be followed).

I could use that flag here:

if(pid == 0) {
    turnOnRecursiveDeathFlag();

    system(exePath);
    //execl("/bin/sh", "sh", "-c", exePath);
    exit(127);
}

Is there a way to do that? I've been searching for a while, but all I can find are hacks using ps -ef , or modifying the children processes that you are running etc.

Use setpgid in the child to set its GPID equal to its own PID. The parent then can kill(-pid,...) to signal the entire group.

pid_t pid = fork();

if(pid == 0) {
    setpgid(0, 0);
    execl("/bin/sh", "sh", "-c", exePath);
    exit(127);
} else if(pid == -1) {
    // timeout code
    if(timeout) {
        kill(-pid, SIGTERM);
        sleep(2);
        kill(-pid, SIGKILL);
    }
}

That should do it.

One more thing, when you spawn a shell, make sure it doesn't enable job control. Otherwise, it will create its own process groups. Your "/bin/sh -c" is fine.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM