How to run a program and know its PID in Linux?
If I have several shells running each other, will they all have separate PIDs?
Greg's wiki to the rescue:
$! is the PID of the last backgrounded process.
kill -0 $PID checks whether $PID is still running. Only use this for processes started by the current process or its descendants, otherwise the PID could have been recycled.
wait waits for all children to exit before continuing.
Actually, just read the link - It's all there (and more).
$$ is the PID of the current shell.
And yes, each shell will have its own PID (unless it's some homebrewed shell which doesn't fork to create a "new" shell).
1) There is a variable for that, often $$:
edd#max:~$ echo $$ # shell itself
20559
edd#max:~$ bash -c 'echo $$' # new shell with different PID
19284
edd#max:~$ bash -c 'echo $$' # dito
19382
edd#max:~$
2) Yes they do, the OS / kernel does that for you.
the top command in linux(Ubuntu) shows the memory usage of all running programs in linux with their pid. Kill pid can kill the process.
Related
I was trying to get pid of process I ran with setsid and which ought to run in background like this:
test.sh:
#/bin/bash
setsid nohup ./my_program &
echo $!
if I run ./test.sh it will print a pid of my_program process and it's exactly what I need. But if run this commands one by one in my shell like this:
$ setsid nohup ./my_program &
$ echo $!
It will give me a pid of setsid command (or may be something else, but it almost all times gives me pid of my_program minus one).
What is happening here? Why results of commands I ran in terminal by myself differs from results of test.sh script?
Btw, may be you know some easy way of process which I started with setsid and which I need to run in background?
Repost of comments above as an answer:
This is because setsid only forks the current process if it is the process group leader. A detailed explanation can be found here.
To get the pid of a process executed via setsid, the approaches given here may be tried.
setsid will call fork to ensure that it creates a new process group aswell as a new session, hence the resulting pid will not match the pid of setsid. The most clean work-around would be that my_program stores its pid into a file.
When you later want to send kill to my_program, you should check that the pid actually matches a program named my_program via /proc file system or calling the ps command with some magic code around it. (This is a very common method used by many daemons)
I run a shell script inside php (using shell_exec). I want to kill it and all processes it created when it is in operation. I know the shell script pid. Do you have any suggestion?
I am using ubuntu, php as apache module.
Thank you.
Example:
#!/bin/bash
echo hello
sleep 20
When I kill my script (shell_exec("sudo kill -9 $pid")), the sleep process is not killed which is not desired.
use
pkill -TERM -P pid
will kill the child processes
see this answer
Use this kill command instead:
kill -- -$pid
to kill the running script and all its spawned children.
The command kill $$ should kill current bash, but it seems that it doesn't work:
$ ps -p $$
PID TTY TIME CMD
18179 pts/4 00:00:00 bash
$ kill $$
$ ps -p $$
PID TTY TIME CMD
18179 pts/4 00:00:00 bash
Why?
I'm not sure why one would like to kill the current shell. Nevertheless...
kill PID would send SIGTERM when no signal is specified. bash ignores SIGTERM and SIGQUIT in the absence of any traps.
You'll achieve the desired effect if you were to say
kill -9 $$
or
kill -SIGKILL $$
Quoting from the manual:
When Bash is interactive, in the absence of any traps, it ignores
SIGTERM (so that ‘kill 0’ does not kill an interactive shell), and
SIGINT is caught and handled (so that the wait builtin is
interruptible). When Bash receives a SIGINT, it breaks out of any
executing loops. In all cases, Bash ignores SIGQUIT.
When you send a process any signal using kill command, the process can choose to handle the signal as per its wish. (There are a few signals, which cannot be handled.)
When you use kill $$, you are actually passing it signal=15 (SIGTERM). It can be handled by a process.
You can google for linux signal example to know HOW it is implemented.
& To answer YOUR question:
If you pass an un-handlable signal, like SIGKILL(9) or SIGSTOP(19), it will respectively kill/stop a running bash process.
How to set process group of a shell script ? Also I want all the child process to be in the same process group
I expect something similar to setpgid() in C.
As PSkocik points out, it is possible to run a process in its own process group, in most shells, by activating job control (“monitor mode”).
(set -m; exec process_in_its_own_group)
Linux has a setsid utility, which runs the command passed as argument in its own session (using the eponymous system call). This is stronger than running it in its own process group à la setpgrp, but that may be ok for your purpose.
If you want to place the process in an existing group rather than in its own group (i.e. if you want the full power of setpgid), there's no common shell utility. You have to use C/Perl/…
I'll answer part of what I understand:
How to force current bash shell script to be it self process group:
I put this in the beginning of my bash script:
pgid_from_pid() {
local pid=$1
ps -o pgid= "$pid" 2>/dev/null | egrep -o "[0-9]+"
}
pid="$$"
if [ "$pid" != "$(pgid_from_pid $pid)" ]; then
exec setsid "$(readlink -f "$0")" "$#"
fi
Why do I would need this ?
When launching a program from an interactive bash session, it gets its own new process group. But this is not the case if your program is called from a bash script (non-interactive). If your program relies on being the process group owner in both condition you'll need this.
I don't think Bourne, bash, or zsh will let you do that, but you could do it in perl using the built-in setpgrp (note the slight name difference from POSIX). Pass zero as the PID to modify the group of the perl process itself:
setpgrp(0, 12345) || die "$!"
You might think you could use perl from, say, bash to set the bash process's group (by passing $$ to a perl script, for example), but I don't think the perl process would be able to modify the group of a process that it didn't fork.
Depending on what you're trying to do, the job control features in various shells may give you what you need, in a different way, like if you just want to detach from the terminal.
UPDATE: I think it's strange that this answer has received a couple of down-votes without clear explanation why. My guess is that the downvoters are misunderstanding the question, which is asking how to change the process group of the current shell. Or perhaps they know how to do a setpgrp from the shell but are keeping the secret to themselves.
If you turn set -m on, new processes will be spawned in a new process group, and if they're backgrounded, they won't have SIGINT and SIGQUIT ignored.
if [ $$ = $(ps -o pgid -hp $$) ]; then
echo already a process group leader;
else
set -m
$0 "$#" #optionally with &
set +m
fi
The new processes group of programs run after set -m takes over as the foreground process group of the terminal, unless they're run in the background.
The set -m is apparently semi-standard, required by POSIX if the implementation supports "User Portability Utilities".
In practice it works on bash, dash, ksh, pdksh, sh, yash, and zsh. posh doesn't have it.
Here's a late synthesis, taken from several other good answers here, if your intention is to cleanup any spawned subshell processes (even if the script itself is not directly launched from an interactive shell, but from another process, and therefore doesn't automatically becomes its own process group leader), relaunching the current script as a new process group leader if necessary.
# First, obtain the current PGID, by parsing the output of "ps".
pgid=$(($(ps -o pgid= -p "$$")))
# Check if we're already the process group leader; if not, re-launch ourselves.
# Use setsid instead of set -m (...) to avoid having another subshell in between. This helps that the trap gets executed when the script is killed.
[ $$ -eq $pgid ] || exec setsid --wait "${BASH_SOURCE[0]}" "$#"
# Kill any subshell processes when the script exits.
trap "kill -- -$pgid" EXIT
# Note: If the script only starts background jobs, and that's all you care about, you can replace all of the above with this simple trap:
#trap "jobs -p | xargs kill --" EXIT # Kill remaining jobs when the script exits.
Nested commands
Another complication is introduced when one script that does subshell cleanup is invoked by another such script. The process group leadership does not nest; once a script assumes leadership, its lifetime is not controlled any longer by a parent script, so when the parent script gets interrupted or killed, the nested script will linger on. That's not what the user usually wants.
The following script fragments extend the above implementation with a cooperation model, so that only the toplevel script assumes the process group leadership, indicating this to subshells by exporting $PGID. If a subshell finds an existing leader, it will not assume leadership itself, and limits its own cleanup tasks to remaining jobs. Other subshells will only be killed once the toplevel script exits. (So this cooperation model works best when one script only invokes one or only few other scripts.)
if [ -z "$PGID" ]; then # No parent script has become the process group leader yet.
pgid=$(($(ps -o pgid= -p "$$"))) # By defining this, we'll be killing subshell processes of this process group when we're done or interrupted. Any children with the same ambition will defer to us.
if [ $$ -eq $pgid ]; then
export PGID=$pgid # We are (already / after setsid) in our own process group, announce our leadership to any children, so that they don't become leaders themselves and thereby decouple themselves from our lifetime control.
else
exec setsid --wait "${BASH_SOURCE[0]}" "$#" # Use setsid instead of set -m (...) to avoid having another subshell in between.
fi
fi
if [ -n "$pgid" ]; then
trap "kill -- -$pgid" EXIT # If we're the leader, kill subshell processes when the script exits.
else
trap "jobs -p | xargs kill --" EXIT # Someone else is the leader; killing remaining jobs is all we can do here.
fi
As #Rob Davis pointed out in his answer, setting process group is not what you want for shells.
Instead you want to use their process control mechanisms. This answer covers doing this for sh on linux and borne. In short:
#! /bin/sh
# Kill all opened jobs on exit.
trap 'kill $(jobs -p)' EXIT
This will kill any jobs opened in the backrground (e.g. with &).
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:
Why did the sleep command get its own process ID?
How do I easily kill the while loop and the sleep?
Sleep gets its own PID because it is a process running and just waiting. Try which sleep to see where it is.
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.
Because "sleep" is a process, not a build-in function or similar
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
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.
"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)
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 )
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 ${!}