Linux Kill Process Using Timeout in Milliseconds

linux kill process using timeout in milliseconds

You can do something like this:

#!/bin/bash

#execute command in background
<command> &

#get process ID
PROC=$!

#sleep for 10 milliseconds then kill command
(usleep 10000; kill $PROC) &

#bring back the process ID, finish command or kill it
fg $PROC

Preventing Terminated message when killing subprocess

To avoid the message you can disown the process

(./tools/usleep $TIMEOUT ; kill $PROC &> /dev/null) &
disown %

and then you cannot wait on it, or you can put it in a subshell:

( (./tools/usleep $TIMEOUT ; kill $PROC &> /dev/null) & )

but of course then you cannot kill it as $! wont be right.


My fedora 21 timeout takes floating point durations:

$ time timeout .1 sleep 1
real 0m0.103s
user 0m0.002s
sys 0m0.002s

Timeout a command in bash without unnecessary delay

I think this is precisely what you are asking for:

http://www.bashcookbook.com/bashinfo/source/bash-4.0/examples/scripts/timeout3

#!/bin/bash
#
# The Bash shell script executes a command with a time-out.
# Upon time-out expiration SIGTERM (15) is sent to the process. If the signal
# is blocked, then the subsequent SIGKILL (9) terminates it.
#
# Based on the Bash documentation example.

# Hello Chet,
# please find attached a "little easier" :-) to comprehend
# time-out example. If you find it suitable, feel free to include
# anywhere: the very same logic as in the original examples/scripts, a
# little more transparent implementation to my taste.
#
# Dmitry V Golovashkin <Dmitry.Golovashkin@sas.com>

scriptName="${0##*/}"

declare -i DEFAULT_TIMEOUT=9
declare -i DEFAULT_INTERVAL=1
declare -i DEFAULT_DELAY=1

# Timeout.
declare -i timeout=DEFAULT_TIMEOUT
# Interval between checks if the process is still alive.
declare -i interval=DEFAULT_INTERVAL
# Delay between posting the SIGTERM signal and destroying the process by SIGKILL.
declare -i delay=DEFAULT_DELAY

function printUsage() {
cat <<EOF

Synopsis
$scriptName [-t timeout] [-i interval] [-d delay] command
Execute a command with a time-out.
Upon time-out expiration SIGTERM (15) is sent to the process. If SIGTERM
signal is blocked, then the subsequent SIGKILL (9) terminates it.

-t timeout
Number of seconds to wait for command completion.
Default value: $DEFAULT_TIMEOUT seconds.

-i interval
Interval between checks if the process is still alive.
Positive integer, default value: $DEFAULT_INTERVAL seconds.

-d delay
Delay between posting the SIGTERM signal and destroying the
process by SIGKILL. Default value: $DEFAULT_DELAY seconds.

As of today, Bash does not support floating point arithmetic (sleep does),
therefore all delay/time values must be integers.
EOF
}

# Options.
while getopts ":t:i:d:" option; do
case "$option" in
t) timeout=$OPTARG ;;
i) interval=$OPTARG ;;
d) delay=$OPTARG ;;
*) printUsage; exit 1 ;;
esac
done
shift $((OPTIND - 1))

# $# should be at least 1 (the command to execute), however it may be strictly
# greater than 1 if the command itself has options.
if (($# == 0 || interval <= 0)); then
printUsage
exit 1
fi

# kill -0 pid Exit code indicates if a signal may be sent to $pid process.
(
((t = timeout))

while ((t > 0)); do
sleep $interval
kill -0 $$ || exit 0
((t -= interval))
done

# Be nice, post SIGTERM first.
# The 'exit 0' below will be executed if any preceeding command fails.
kill -s SIGTERM $$ && kill -0 $$ || exit 0
sleep $delay
kill -s SIGKILL $$
) 2> /dev/null &

exec "$@"

Bash: wait with timeout

Write the PIDs to files and start the apps like this:

pidFile=...
( app ; rm $pidFile ; ) &
pid=$!
echo $pid > $pidFile
( sleep 60 ; if [[ -e $pidFile ]]; then killChildrenOf $pid ; fi ; ) &
killerPid=$!

wait $pid
kill $killerPid

That would create another process that sleeps for the timeout and kills the process if it hasn't completed so far.

If the process completes faster, the PID file is deleted and the killer process is terminated.

killChildrenOf is a script that fetches all processes and kills all children of a certain PID. See the answers of this question for different ways to implement this functionality: Best way to kill all child processes

If you want to step outside of BASH, you could write PIDs and timeouts into a directory and watch that directory. Every minute or so, read the entries and check which processes are still around and whether they have timed out.

EDIT If you want to know whether the process has died successfully, you can use kill -0 $pid

EDIT2 Or you can try process groups. kevinarpe said: To get PGID for a PID(146322):

ps -fjww -p 146322 | tail -n 1 | awk '{ print $4 }'

In my case: 145974. Then PGID can be used with a special option of kill to terminate all processes in a group: kill -- -145974

Un*x shell script: what is the correct way to run a script for at most x milliseconds?

Have a look at this script: http://www.pixelbeat.org/scripts/timeout
Note timeouts of less that one second are pretty much nonsensical on most systems due to scheduling delays etc. Note also that newer coreutils has the timeout command included and it has a resolution of 1 second.

How to keep running a program during a time period?

If you want to use timeout:

timeout 5s ./a.out

You can write a short script and easily set an end time with date -d "date string" +%s to get a future time in seconds. Then just compare current time to end time and break on true. This allows you to capture additional data during your execution time period. For example, the following code sets the end time 5 seconds in the future and then loops until current time equals end.

#!/bin/bash

end=$(date -d "+ 5 seconds" +%s) # set end time with "+ 5 seconds"
declare -i count=0

while [ $(date +%s) -lt $end ]; do # compare current time to end until true
((count++))
printf "working... %s\n" "$count" # do stuff
sleep .5
done

output:

$ bash timeexec.sh
working... 1
working... 2
working... 3
working... 4
working... 5
working... 6
working... 7
working... 8
working... 9

In your case you would do something like

./a.out &                               # start your application in background
apid=$(pidof a.out) # save PID of a.out

while [ $(date +%s) -lt $end ]; do
# do stuff, count, etc.
sleep .5 # something to prevent continual looping
done

kill $apid # kill process after time test true

Using module 'subprocess' with timeout

In Python 3.3+:

from subprocess import STDOUT, check_output

output = check_output(cmd, stderr=STDOUT, timeout=seconds)

output is a byte string that contains command's merged stdout, stderr data.

check_output raises CalledProcessError on non-zero exit status as specified in the question's text unlike proc.communicate() method.

I've removed shell=True because it is often used unnecessarily. You can always add it back if cmd indeed requires it. If you add shell=True i.e., if the child process spawns its own descendants; check_output() can return much later than the timeout indicates, see Subprocess timeout failure.

The timeout feature is available on Python 2.x via the subprocess32 backport of the 3.2+ subprocess module.

WaitForExitAsync with a timeout

You need to use CancellationTokenSource. It has a ctor which accepts a TimeSpan

var timeoutSignal = new CancellationTokenSource(TimeSpan.FromSeconds(3));
try
{
await CMD.WaitForExitAsync(timeoutSignal.Token);
} catch (OperationCanceledException)
{
CMD.Kill();
}

When the CTS signals then the awaited operation will throw an OperationCanceledException. So you need to wrap your await call into a try-catch to handle cancelled operation properly.


UPDATE #1: Capture STDOUT with async wait of exit

Naive approach

First let me share with you the naive version of the code

Console.WriteLine("Launch ping with fifteen retries");
var terminal = Process.Start(new ProcessStartInfo("/sbin/ping")
{
RedirectStandardOutput = true,
Arguments = "-c 15 stackoverflow.com",
UseShellExecute = false,
});

_ = Task.Run(() =>
{
string line = null;
while ((line = terminal.StandardOutput.ReadLine()) != null)
Console.WriteLine(line);
});


var timeoutSignal = new CancellationTokenSource(TimeSpan.FromSeconds(3));
try
{
await terminal.WaitForExitAsync(timeoutSignal.Token);
Console.WriteLine("Ping has been Finished");
}
catch (OperationCanceledException)
{
terminal.Kill();
Console.WriteLine("Ping has been Terminated");
}
  • I'm using .NET on a Macintosh machine so, I don't have ping.exe rather than I can run /sbin/ping command
  • I ping stackoverflow fifteen times to make sure the command runs more than 3 seconds
  • I've moved the StandardOutput reading to a separate thread (Task.Run)
    • Without that, the cancellation signal will not have any effect
  • The rest of the code same as above + debug logging

Suggested approach

The Process class does expose a capability to read data asynchronously from the StandardOutput without the need to do extra tricks

Console.WriteLine("Launch ping with fifteen retries");
var terminal = new Process()
{
StartInfo = new ProcessStartInfo("/sbin/ping")
{
RedirectStandardOutput = true,
Arguments = "-c 15 stackoverflow.com",
UseShellExecute = false,
}
};

terminal.OutputDataReceived += (s, e) => Console.WriteLine(e.Data);
terminal.Start();
terminal.BeginOutputReadLine();

var timeoutSignal = new CancellationTokenSource(TimeSpan.FromSeconds(3));
try
{
await terminal.WaitForExitAsync(timeoutSignal.Token);
Console.WriteLine("Ping has been Finished");
}
catch (OperationCanceledException)
{
terminal.Kill();
Console.WriteLine("Ping has been Terminated");
}

Let me highlight only the differences

  • Rather than starting the process right away, first we create a Process and specify its StartInfo property
  • Then we subscribe to the OutputDataReceived event
    • Its EventArgs' Data property contains the newly available information
  • After the subscription we can call the Start method
  • And finally we need to call the BeginOutputReadLine method to tell the Process fire the above event handler whenever new data is available on the standard output


Related Topics



Leave a reply



Submit