Linux: How to Kill Sleep

How to kill a sleep process using a script

I can't find the exact dup I was looking for so here is a short example. If I understand what you are wanting, you want to start a sleep process and then at any time you want to be able to quit the sleep process returning you to your prompt. You say "background" the sleep process, but I'm not sure whether you actually need to background it to let it expire on its own in the future, or if just need a way out of the sleep whenever you choose (that would make more sense)

While you can simply use Ctrl + C to interrupt the process, you won't be returned to your script. So if you need a way to return to your script, you need to trap SIGINT. When you set a trap 'some command' SIGINT your script will intercept and process the interrupt and control will return to the point following the sleep command, e.g.

#!/bin/bash

## set trap
trap 'printf "\n[Ctrl + C] SIGINT intercepted\n"' SIGINT

printf "sleeping 500...\n"

sleep 500

## rest of script after sleep
printf "control returned to script\ncontinuing to process commands\n"

Example Running Script Above

$ bash trap_int.sh
sleeping 500...
^C
[Ctrl + C] SIGINT intercepted
control returned to script
continuing to process commands

That may or may not satisfy what you need. However, you can build on that to control what happens after an interrupt a bit further. When you set the trap you can have it call a separate function within your script that can conditionally execute code and end the script on that separate branch by calling exit. That has the effect of transferring control back to your script to only execute commands from the path that starts with the function you specify. This provides away to avoid executing code that follows the sleep, while providing a separate execution path in your script to run whatever is needed after interrupt from sleep.

Conditionally Executing Commands After Interrupt

Rather than just sleep 500, in the next example we will repeatedly call two functions sleep_a which calls a nested sleep function sleep_b. Following SIGINT control will be passed to a function named handle_interrupt where additional commands can be executed calling exit to exit the script and return you to your prompt. An additional function called actual_exit is called to perform the exit.

#!/bin/bash

## function called to exit, returning you to prompt
function actual_exit
{
printf " Exiting after '%s'\n\n" "$1"
exit
}

## function called after SIGINT, calls actual_exit to disply signal caught
function handle_interrupt
{
printf "\n Signal SIGINT Caught! [ctrl + c]\n\n"
actual_exit 'SIGINT'
}

## set trap
trap handle_interrupt SIGINT # calls handle_interrupt on [ctrl + c]

declare -i idx=0 ## loop counter

function sleep_b
{
printf "\nentering sleep_b\n"
sleep 2
printf "executing post sleep_b index: %s\n" $((idx++))
}

function sleep_a
{
printf "\nentering sleep_a\n"
sleep 2
printf "executing post sleep_a index: %s\n" $((idx++))
sleep_b
}

## repeatedly call nested sleep until signal received
while :; do
sleep_a
done

## rest of script after sleep
printf "control returned to script\ncontinuing to process commands\n"

When the script is interrupted, the command following below the while loop are never reached as the script exits through the code path provided by the function called on SIGINT.

Providing Common Commands On exit

You can add yet another layer of execution by setting a trap on EXIT. If you have a collection of signal you set a trap for, you can have all end though a common function. For example if you monitored multiple signals each returning to a function ultimately calling exit, you can set a trap on EXIT calling a final function holding commands to be executed before the script exits.

For example, you could do:

trap final_function EXIT  # calls final_function on exit() from script

where a call to exit anywhere within the script would invoke the trap on EXIT calling final_function before returning control back to you.

Let me know if something like one of the incremental levels of handling canceling sleep though an interrupt will do what you need. Technically it doesn't "background" sleep, but does allow you to cancel the sleep at any moment conditionally returning control within your script to wherever you choose.

How to kill a process in Unix that is often sleeping?

The shell subprocess invoked by the ( ... ) & construct, and all its children, will be in their own process group.

$ ps -e -o pid,pgid,ppid,tty,comm
PID PGID PPID TT COMMAND
...
2827 2827 2147 pts/1 bash
2832 2827 2827 pts/1 sleep
...

The entire process group can be killed in a single action by specifying a negative number as the process ID to kill. (To do this you must also specify a signal number.)

$ kill -15 -2827
[2]+ Terminated ( trap "" HUP; exec 2> /dev/null; ...

The PGID to kill is guaranteed to be equal to the PID of its process group leader, which in this case is the shell subprocess. So you can modify your code along the lines of

(
# Trap the HUP signal so it doesn't kill me
trap "" 1
# ...
) &

# the special shell variable $! contains the PID of the most recently
# started background process
SUBPROCESS="$!"

# and later when you want to shut it all down again
kill -15 "-$SUBPROCESS"

# ensuring the subprocess is killed with the script would also be a good idea
trap "kill -15 '-$SUBPROCESS'" 0 1 2 3 15

(Note: kill -NAME and trap "..." NAME are not portable shell; however, the meanings of signal numbers 1 through 15 are portable all the way back to V7. If total portability is not an overriding concern, don't write a shell script; the moment you are tempted to reach for an unportable feature, instead stop and rewrite the entire thing in Perl, which is not only a superior programming language, it's more likely to be available on a randomly chosen Unix box than Bash is. Your future self will thank you.)

(Note to pedants: sadly, no readily available version of POSIX.1 can be taken as the reference for what is and is not portable shell, because several major proprietary-Unix vendors froze their shell environments in 1995 plus or minus two years. For complete portability, as e.g. required for autoconf scripting, I'm not aware of a reliable test other than "does this work with Solaris /bin/sh?" (Just be glad you no longer have to dig up access to HP-UX, IRIX, and AIX as well.) However, I am under the impression that you can code to POSIX.1-2001, although not -2008, if you're only interested in portability to the open-source BSDs, full-scale desktop or server Linux, and OSX. I am also under the impression that Android, busybox, and various other embedded environments do not provide all of -2001.)

How do I know which process (PID) to kill to kill all sleeping processes?

Please use the following command to find the PID of the process you wish to kill, e.g. 1811 is the child process.

ps -f 1811

OR

ps -o ppid= 1811

This will return the parent process id which you can then use.

bash: sleep process not getting killed

kill -15 22880 will send a signal to the shell executing the script, but not the sleep command. To send the signal to every process in the process group, you should be able to specify a negative process ID.

kill -15 -22880

Alternately, ensure that the script kills its children before exiting.

trap 'kill $(jobs -p)' EXIT
echo "Sleeping..."
sleep 180s & wait

If you leave sleep in the foreground when the signal is received, the shell must wait until it exits before running the trap; sleep is uninterruptible. The workaround is to run it in the background, then wait on it. wait, being a shell built-in, can be interrupted, so that the trap runs immediately and kills any background processes still in progress.

Reliably kill sleep process after USR1 signal

As the background job is a fork of the foreground one, they share the same name (trap-test.sh); so pkill matches and signals both. This, in an uncertain order, kills the background process (leaving sleep alive, explained below) and triggers the trap in the foreground one, hence the race condition.

Besides, in the examples you linked, the background job is always a mere sleep x, but in your script it is sleep 10 && echo 'doing some work'; which requires the forked subshell to wait sleep to terminate and conditionally execute echo. Compare these two:

$ sleep 10 &
[1] 9401
$ pstree 9401
sleep
$
$ sleep 10 && echo foo &
[2] 9410
$ pstree 9410
bash───sleep

So let's start from scratch and reproduce the principal issue in a terminal.

$ set +m
$ sleep 100 && echo 'doing some work' &
[1] 9923
$ pstree -pg $$
bash(9871,9871)─┬─bash(9923,9871)───sleep(9924,9871)
└─pstree(9927,9871)
$ kill $!
$ pgrep sleep
9924
$ pkill -e sleep
sleep killed (pid 9924)

I disabled job control to partly emulate a non-interactive shell's behavior.

Killing the background job didn't kill sleep, I needed to terminate it manually. This happened because a signal sent to a process is not automatically broadcast to its target's children; i.e. sleep didn't receive the TERM signal at all.

To kill sleep as well as the subshell, I need to put the background job into a separate process group —which requires job control to be enabled, otherwise all jobs are put into the main shell's process group as seen in pstree's output above—, and send the TERM signal to it, as shown below.


$ set -m
$ sleep 100 && echo 'doing some work' &
[1] 10058
$ pstree -pg $$
bash(9871,9871)─┬─bash(10058,
10058)───sleep(10059,10058)
└─pstree(10067,10067)
$ kill --
-$!
$
[1]+ Terminated sleep 100 && echo 'doing some work'
$ pgrep sleep
$

With some refinement and adaptation of this concept, your script looks like:

#!/bin/bash -
set -m

usr1_handler() {
kill -- -$!
echo 'doing some work'
}

do_something() {
trap '' USR1
sleep 10 && echo 'doing some work'
}

trap usr1_handler USR1 EXIT

echo "my PID is $$"

while true; do
do_something &
wait
done

This will print my PID is xxx (where xxx is the PID of foreground process) and start looping. Sending a USR1 signal to xxx (i.e kill -USR1 xxx) will trigger the trap and cause the background process and its children to terminate. Thus wait will return and the loop will continue.

If you use pkill instead it'll work anyway, as the background process ignores USR1.

For further information, see:

  • Bash Reference Manual § Special Parameters ($$ and $!),
  • POSIX kill specification (-$! usage),
  • POSIX Definitions § Job Control (how job control is implemented in POSIX shells),
  • Bash Reference Manual § Job Control Basics (how job control works in bash),
  • POSIX Shell Command Language § Signals And Error Handling,
  • POSIX wait specification.

How to kill a TASK_KILLABLE process?

Firstly, like most versions of Unix, Linux has two fundamental ways in which a process can be put to sleep.

A process which is placed in the TASK_INTERRUPTIBLE state will sleep until either (1) something explicitly wakes it up, or (2) a non-masked signal is received.

The TASK_UNINTERRUPTIBLE state, instead, ignores signals; processes in that state will require an explicit wakeup before they can run again.

TASK_KILLABLE is a state of task_struct which comes from TASK_UNINTERRUPTIBLE, but this can be wake up by signals.

So, I think this process is killable if it sets as TASK_KILLABLE. Here is some source that can help you get deep understand.

In C, linux, about kill signal and sleep() in loop

  1. In general, signals can only be "caught" by your application when it makes a system call into the kernel. If you do a plain while(1) { if (flag==1){...} }, that will never call into the kernel. Theoretically, when you do your outer printf in the while(1) loop, that should call into the kernel and so the signal could be caught.
  2. sleep() is interrupted by any signal. Check the man page for sleep(3).
  3. check the man page for alarm(2).
  4. You cannot change the signal handler for SIGKILL nor for SIGSTOP. These signal effects are hardcoded into the kernel. From sigaction(2):

    signum specifies the signal and can be any valid signal except SIGKILL and SIGSTOP.

    The kill command with no arguments does not generate a SIGKILL signal; it generates SIGTERM.



Related Topics



Leave a reply



Submit