How to kill a child process after a given timeout in Bash?
(As seen in:
BASH FAQ entry #68: "How do I run a command, and have it abort (timeout) after N seconds?")
If you don't mind downloading something, use timeout
(sudo apt-get install timeout
) and use it like: (most Systems have it already installed otherwise use sudo apt-get install coreutils
)
timeout 10 ping www.goooooogle.com
If you don't want to download something, do what timeout does internally:
( cmdpid=$BASHPID; (sleep 10; kill $cmdpid) & exec ping www.goooooogle.com )
In case that you want to do a timeout for longer bash code, use the second option as such:
( cmdpid=$BASHPID;
(sleep 10; kill $cmdpid) \
& while ! ping -w 1 www.goooooogle.com
do
echo crap;
done )
Does the Unix command Timeout also kill any children of the process?
The GNU coreutils version of timeout
can have its implementation viewed here.
Whether a process group is created (and killed as a whole) does indeed depend on whether --foreground
is passed, as you have inferred from the documentation.
To be a bit more explicit:
- When
--foreground
is not used, we callsetpgid()
to create a new process group, putting both thetimeout
command itself and the invoked command inside that group. When the timeout later occurs,kill()
is passed0
as the PID to kill, specifying that the entire process group should be targeted. - When
--foreground
is used, only the immediately forked PID is signaled, and not other members of the process group.
How to kill a process after a given real running time in Bash?
On Bash+Linux, you can use ulimit -t
. Here's from help ulimit
:
-t the maximum amount of cpu time in seconds
Here's an example:
$ time bash -c 'ulimit -t 5; while true; do true; done'
Killed
real 0m8.549s
user 0m4.983s
sys 0m0.008s
The infinite loop process was scheduled (i.e. actually ran) for a total of 5 seconds before it was killed. Due to other processes competing for the CPU at the same time, this took 8.5 seconds of wall time.
A command like sleep 3600
would never be killed, since it doesn't use any CPU time.
kill process using sigterm and escalate to sigkill after timeout
There's the timeout
command, which allows you to cap a process' execution time and escalate to a SIGKILL if it doesn't respond promptly to the initial signal (SIGTERM by default). This isn't quite what you're asking for, but it might be sufficient.
To do what you're actually describing (send a signal, briefly await, then send a kill) you may have to do a bit of bookkeeping yourself, as this question details.
One option would be to use Upstart (or I imagine other service managers), which provides a kill timeout n
command that does what you want.
As an aside, many systems would treat 30 minutes as much too long to wait for SIGTERM. Linux does something akin to what you're describing on shutdown, for instance, but gives processes barely a few seconds to clean up and exit before SIGKILLing them. For other use cases you certainly can have a long-lived termination like you describe (e.g. with Upstart), but YMMV.
How to stop a child process if not completed after a timeout
In order to kill the child, you need to know its pid. You can get it if you start the program with fork
and exec
instead of system
.
In addition to a signal handler for SIGALRM
, set up one for SIGCHLD
(received when a child process finishes) as well. After calling alarm
to set the timer, call pause
. This function will return when you get either of the two signals.
In the signal handlers you should only set a global flag. Calling printf
from within a signal handler can lead to undefined behavior.
After pause
returns, check each of the two flags. If the timeout flag is set, you can terminate the child with kill
.
In either case, call wait
to reap the child process's pid.
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#define TIME_LIMIT 1 //determine time limit
void alarm_handler(int);
void child_handler(int);
int timeout = 0;
int child_done = 0;
int main()
{
pid_t pid = fork();
if (pid == -1) {
perror("fork failed");
exit(1);
} else if (pid == 0) {
// child process
execl("./r.out","r.out", NULL);
perror("exec failed");
_exit(1);
}
// set up the signal handlers after forking so the child doesn't inherit them
signal(SIGALRM, alarm_handler);
signal(SIGCHLD, child_handler);
alarm(TIME_LIMIT); // install an alarm to be fired after TIME_LIMIT
pause();
if (timeout) {
printf("alarm triggered\n");
int result = waitpid(pid, NULL, WNOHANG);
if (result == 0) {
// child still running, so kill it
printf("killing child\n");
kill(pid, 9);
wait(NULL);
} else {
printf("alarm triggered, but child finished normally\n");
}
} else if (child_done) {
printf("child finished normally\n");
wait(NULL);
}
return 0;
}
void child_handler(int sig)
{
child_done = 1;
}
void alarm_handler(int sig)
{
timeout = 1;
}
Related Topics
What's the Magic of "-" (A Dash) in Command-Line Parameters
How to Parse a CSV File in Bash
Tool to Trace Local Function Calls in Linux
Redirect Stderr/Stdout of a Process After It's Been Started, Using Command Line
How to Kill All Processes With a Given Partial Name
Retrieve Cpu Usage and Memory Usage of a Single Process on Linux
Using the "Alternate Screen" in a Bash Script
What Is the Meaning of a Double Dollar Sign in Bash/Makefile
Glibc Scanf Segmentation Faults When Called from a Function That Doesn't Align Rsp
What Is Double Dot(..) and Single Dot(.) in Linux
How to Debug the Linux Kernel With Gdb and Qemu
More Elegant "Ps Aux | Grep -V Grep"
Command Not Found When Using Sudo
How to Replace a String in Multiple Files in Linux Command Line
Curl to Access a Page That Requires a Login from a Different Page
Why Are the Backslash and Semicolon Required With the Find Command'S -Exec Option