Sleep in a while loop gets its own pid
Asked Answered
K

6

5

I have a bash script that does some parallel processing in a loop. I don't want the parallel process to spike the CPU, so I use a sleep command. Here's a simplified version.

(while true;do sleep 99999;done)&

So I execute the above line from a bash prompt and get something like: [1] 12345

Where [1] is the job number and 12345 is the process ID (pid) of the while loop. I do a kill 12345 and get:

[1]+  Terminated              ( while true; do
    sleep 99999;
done )

It looks like the entire script was terminated. However, I do a ps aux|grep sleep and find the sleep command is still going strong but with its own pid! I can kill the sleep and everything seems fine. However, if I were to kill the sleep first, the while loop starts a new sleep pid. This is such a surprise to me since the sleep is not parallel to the while loop. The loop itself is a single path of execution.

So I have two questions:

  1. Why did the sleep command get its own process ID?
  2. How do I easily kill the while loop and the sleep?
Knowall answered 15/12, 2010 at 16:29 Comment(0)
H
5
  1. Sleep gets its own PID because it is a process running and just waiting. Try which sleep to see where it is.
  2. You can use ps -uf to see the process tree on your system. From there you can determine what the PPID (parent PID) of the shell (the one running the loop) of the sleep is.
Heredia answered 15/12, 2010 at 16:42 Comment(3)
I see. I was thinking sleep was a built-in command in bash, not some process. It makes more sense now.Knowall
If you're using bash, type sleep or type -a sleep is preferable to which. which is an external command; it can't detect aliases, functions, or built-in commands. Compare the output of which echo vs. type echo.Fichu
@User1: Things like echo are built-in commands because that makes them more efficient (and in some cases, like cd, because they have semantics that can't be achieved by running an external command). The efficiency of sleep is unimportant, so there's not much point in making it a built-in.Fichu
A
2
  1. Because "sleep" is a process, not a build-in function or similar

  2. You could do the following:

    (while true;do sleep 99999;done)&
    whilepid=$!
    kill -- -$whilepid
    

The above code kills the process group, because the PID is specified as a negative number (e.g. -123 instead of 123). In addition, it uses the variable $!, which stores the PID of the most recently executed process.

Note: When you execute any process in background on interactive mode (i.e. using the command line prompt) it creates a new process group, which is what is happening to you. That way, it's relatively easy to "kill 'em all", because you just have to kill the whole process group. However, when the same is done within a script, it doesn't create any new group, because all new processes belong to the script PID, even if they are executed in background (jobs control is disabled by default). To enable jobs control in a script, you just have to put the following at the beginning of the script:

#!/bin/bash

set -m
Alexalexa answered 17/8, 2016 at 18:51 Comment(0)
U
1

Have you tried doing kill %1, where 1 is the number you get after launching the command in background?

I did it right now after launching (while true;do sleep 99999;done)& and it correctly terminated it.

Ulm answered 15/12, 2010 at 16:37 Comment(0)
P
1

"ps --ppid" selects all processes with the specified parent pid, eg:

$ (while true;do sleep 99999;done)&
[1] 12345

$ ppid=12345 ; kill -9 $ppid $(ps --ppid $ppid -o pid --no-heading)
Pragmatic answered 15/12, 2010 at 16:42 Comment(0)
E
1

You can kill the process group.

To find the process group of your process run:

ps --no-headers -o "%r" -p 15864

Then kill the process group using:

kill -- -[PGID]

You can do it all in one command. Let's try it out:

$ (while true;do sleep 99999;done)&
[1] 16151

$ kill -- -$(ps --no-headers -o "%r" -p 16151)
[1]+  Terminated              ( while true; do
    sleep 99999;
done )
Enchain answered 15/12, 2010 at 16:59 Comment(0)
R
0

To kill the while loop and the sleep using $! you can also use a trap signal handler inside the subshell.

(trap 'kill ${!}; exit' TERM; while true; do sleep 99999 & wait ${!}; done)&
kill -TERM ${!}
Rockandroll answered 11/3, 2013 at 16:10 Comment(1)
Cannot do this after reloginLevison

© 2022 - 2024 — McMap. All rights reserved.