[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Help-bash] parallel processing in bash (to replace for loop)
From: |
Greg Wooledge |
Subject: |
Re: [Help-bash] parallel processing in bash (to replace for loop) |
Date: |
Mon, 30 Jan 2012 08:42:29 -0500 |
User-agent: |
Mutt/1.4.2.3i |
> > http://mywiki.wooledge.org/ProcessManagement#I_want_to_process_a_bunch_of_files_in_parallel.2C_and_when_one_finishes.2C_I_want_to_start_the_next._And_I_want_to_make_sure_there_are_exactly_5_jobs_running_at_a_time.
> I assume that this is your wiki. I can not open it as it is too slow.
> Would you please post the solution here?
Someone posted a link to one the pages to a very popular web site over
the weekend, so access to the whole web server was very slow yesterday,
and probably today as well. Patience is a virtue.
<SNIP>
This is still a work in progress. Expect some rough edges.
Contents
1. [21]The basics
2. [22]Simple questions
1. [23]How do I run a job in the background?
2. [24]My script runs a job in the background. How do I get its
PID?
3. [25]OK, I have its PID. How do I check that it's still
running?
4. [26]I want to do something with a process I started earlier
5. [27]How do I kill a process by name? I need to get the PID
out of ps aux | grep ....
6. [28]But I'm on some old legacy Unix system that doesn't have
pgrep! What do I do?
7. [29]I want to run something in the background and then log
out.
8. [30]I'm trying to kill -9 my job but blah blah blah...
9. [31]Make SURE you have run and understood these commands:
3. [32]Advanced questions
1. [33]I want to run two jobs in the background, and then wait
until they both finish.
2. [34]How can I check to see if my game server is still
running? I'll put a script in crontab, and if it's not
running, I'll restart it...
3. [35]How do I make sure only one copy of my script can run at
a time?
4. [36]I want to process a bunch of files in parallel, and when
one finishes, I want to start the next. And I want to make
sure there are exactly 5 jobs running at a time.
5. [37]My script runs a pipeline. When the script is killed, I
want the pipeline to die too.
4. [38]How to work with processes
1. [39]PIDs and parents
2. [40]The risk of letting the parent die
3. [41]The risk of parsing the process tree
4. [42]Doing it right
1. [43]Starting a process and remembering its PID
2. [44]Checking up on your process or terminating it
3. [45]Starting a "daemon" and checking whether it started
successfully
5. [46]On processes, environments and inheritance
The basics
A process is a running instance of a program in memory. Every process
is identified by a number, called the PID, or Process IDentifier. Each
process has its own privately allocated segment of memory, which is
not accessible from any other process. This is where it stores its
variables and other data.
The kernel keeps track of all these processes, and stores a little bit
of basic metadata about them in a process table. However, for the most
part, each process is autonomous, within the privileges allowed to it
by the kernel. Once a process has been started, it is difficult to do
anything to it other than suspend (pause) it, or terminate it.
The metadata stored by the kernel includes a process "name" and
"command line". These are not reliable; the "name" of a process is
whatever you said it is when you ran it, and may have no relationship
whatsoever to the program's file name. (On some systems, running
processes can also change their own names. For example, sendmail uses
this to show its status.) Therefore, when working with a process, you
must know its PID in order to be able to do anything with it. Looking
for processes by name is extremely fallible.
Simple questions
How do I run a job in the background?
command &
By the way, '&' is a command separator in bash and other Bourne
shells. It can be used any place ';' can (but not in addition to ';'
-- you have to choose one or the other). Thus, you can write this:
command one & command two & command three &
which runs all three in the background simultaneously, and is
equivalent to:
command one &
command two &
command three &
Or:
for i in 1 2 3; do some_command $i & done
While both & and ; can be used to separate commands, & runs them in
the background and ; runs them in sequence.
My script runs a job in the background. How do I get its PID?
The $! special parameter holds the PID of the most recently executed
background job. You can use that later on in your script to keep track
of the job, terminate it, record it in a PID file (shudder), or
whatever.
myjob &
jobpid=$!
OK, I have its PID. How do I check that it's still running?
kill -0 $PID will check to see whether a signal is deliverable (i.e.,
the process still exists). If you need to check on a single child
process asynchronously, that's the most portable solution. You might
also be able to use the wait shell command to block until the child
(or children) terminate -- it depends on what your program has to do.
There is no shell scripting equivalent to the select(2) or poll(2)
system calls. If you need to manage a complex suite of child processes
and events, don't try to do it in a shell script. (That said, there
are a few tricks in the [47]advanced section of this page.)
I want to do something with a process I started earlier
Store the PID when you start the process and use that PID later on:
# Bourne
my child process &
childpid=$!
If you're still in the parent process that started the child process
you want to do something with, that's perfect. You're guaranteed the
PID is your child process (dead or alive), for the reasons
[48]explained below. You can use kill to signal it, terminate it, or
just check whether it's still running. You can use wait to wait for it
to end or to get its exit code if it has ended.
If you're NOT in the parent process that started the child process you
want to do something with, that's a shame. Try restructuring your
logic so that you can be. If that's not possible, the things you can
do are a little more limited and a little more risky.
The parent process that created the child process should've written
its PID to some place where you can access it. A PID file is probably
the best place. Read the PID in from wherever the parent stored it,
and hope that no other process has accidentally taken control of the
PID while you weren't looking. You can use kill to signal it,
terminate it, or just check whether it's still running. You cannot use
wait to wait for it or get its exit code; this is only possible from
the child's parent process. If you really want to wait for the process
to end, you can poll kill -0:
while sleep 1; do kill -0 $pid || break; done.
Everything in the preceding paragraph is risky. The PID in the file
may have been recycled before you even got there. The PID could be
recycled after you read it from the file but before you send the
suicide order. The PID could be recycled in the middle of your polling
loop, leaving you in a non-terminating wait.
If you need to write programs that manage a process without
maintaining a parent/child relationship, your best bet is to make sure
that all of those programs run with the same User ID (UID) which is
not used by any other programs on the system. That way, if the PID
gets recycled, your attempt to query/kill it will fail. This is
infinitely preferable to your sending SIGTERM to some innocent
process.
How do I kill a process by name? I need to get the PID out of ps aux | grep
....
No, you don't. Firstly, you probably do NOT want to find a process by
name AT ALL. Make sure you have the PID of the process and do what the
above answer says. If you don't know how to get the PID: Only the
process that created your process knows the real PID. It should have
stored it in a file for you. If you are IN the parent process, that's
even better. Put the PID in a variable (process & mypid=$!) and use
that.
If for some silly reason you really want to get to a process purely by
name, you understand that this is a broken method, you don't care that
this may set your hair on fire, and you want to do it anyway, you
should probably use a command called pkill. You might also take a look
at the command killall if you're on a legacy GNU/Linux system, but be
warned: killall on some systems kills every process on the entire
system. It's best to avoid it unless you really need it.
(Mac OS X comes with killall but not pkill. To get pkill, go to
[49]http://proctools.sourceforge.net/.)
If you just wanted to check for the existence of a process by name,
use pgrep.
Please note that checking/killing processes by name is insecure,
because processes can lie about their names, and there is nothing
unique about the name of a process.
But I'm on some old legacy Unix system that doesn't have pgrep! What do I
do?
As stated above, checking or killing processes by name is an extremely
bad idea in the first place. So rather than agonize about shortcut
tools like pgrep that you don't have, you'd do better to implement
some sort of robust process management using the techniques we'll talk
about later. But people love shortcuts, so let me fill in some legacy
Unix issues and tricks here, even though you should not be using such
things.
A legacy Unix system typically has no tool besides ps for inspecting
running processes as a human system administrator. People then think
that this is an appropriate tool to use in a script, even though it
isn't. They fall into the mental trap of thinking that since this is
the only tool provided by the OS for troubleshooting runaway processes
as a human being, that it must be an appropriate tool for setting up
services.
There are two entirely different ps commands on legacy Unix systems:
System V Unix style (ps -ef) and BSD Unix style (ps auxw). In some
slightly-less-old Unix systems, the two different syntaxes are
combined, and the presence or absence of a hyphen tells ps which set
of option letters is being used. (If you ever see ps -auxw with a
hyphen, throw the program away immediately.) POSIX uses the System V
style, and adds a -o option to tell ps which fields you want, so you
don't have to write things like ps ... | awk '{print $2}' any more.
Now, the second real problem with ps -ef | grep foo (after the fact
that process names are inherently unreliable) is that there is a
[50]RaceCondition in the output. In this pipeline, both the ps and the
grep are spawned either simultaneously or nearly simultaneously.
Depending on just how nearly simultaneously they are spawned, the grep
process might or might not show up in the ps output. And the grep foo
command is going to match both processes -- the foo daemon or
whatever, and the grep foo command as well. Assuming both of them show
up. You might get just one.
There are two workarounds for that issue. The first is to filter out
the grep command. This is typically done by running
ps -ef | grep -v grep | grep foo. Note that the grep -v is done first
so that it is not the final command in the pipeline. This is so that
the final command in the pipeline is the one whose exit status
actually matters. This allows commands like the following to work
properly:
if ps -ef | grep -v grep | grep -q foo; then
The second workaround involves crafting a grep command that will match
the foo process but not the grep itself. There are many variants on
this theme, but one of the most common is:
if ps -ef | grep '[f]oo'; then
You'll likely run into this a few times. The [51]RegularExpression
[f]oo matches only the literal string foo; it does not match the
literal string [f]oo, and therefore the grep command won't be matched
either. This approach saves one forked process (the grep -v), and some
people find it clever.
I've seen one person try to do this:
# THIS IS BAD! DO NOT USE THIS!
if ps -ef | grep -q -m 1 foo; then
Not only does this use a nonstandard GNU extension (grep -m -- stop
after M matches), but it completely fails to avoid the race condition.
If the race condition produces both grep and foo lines, there's no
guarantee the foo one will be first! So, this is even worse than what
we started with.
Anyway, these are just explanations of tricks you might see in other
people's code, so that you can guess what they're attempting to do.
You won't be writing such hacks, I hope.
I want to run something in the background and then log out.
If you want to be able to reconnect to it later, use screen or tmux.
Launch either, then run whatever you want to run in the foreground,
and detach (screen with Ctrl-A d and tmux with Ctrl-B d). You can
reattach (as long as you didn't reboot the server) with screen -x to
screen and with tmux attach to tmux. You can even attach multiple
times, and each attached terminal will see (and control) the same
thing. This is also great for remote teaching situations.
If you can't or don't want to do that, the traditional approach still
works: nohup something &
Bash also has a disown command, if you want to log out with a
background job running, and you forgot to nohup it initially.
sleep 1000
Ctrl-Z
bg
disown
If you need to logout of an ssh session with background jobs still
running, make sure their file descriptors have been redirected so they
aren't holding the terminal open, or [52]the ssh client may hang.
I'm trying to kill -9 my job but blah blah blah...
Woah! Stop right there! Do not use kill -9, ever. For any reason.
Unless you wrote the program to which you're sending the SIGKILL, and
know that you can clean up the mess it leaves. Because you're
debugging it.
If a process is not responding to normal signals, it's probably in
"state D" (as shown on ps u), which means it's currently executing a
system call. If that's the case, you're probably looking at a dead
hard drive, or a dead NFS server, or a kernel bug, or something else
along those lines. And you won't be able to kill the process anyway,
SIGKILL or not.
If the process is ignoring normal SIGTERMs, then get the source code
and fix it!
If you have an employee whose first instinct any time a job needs to
be terminated is to break out the fucking howitzers, then fire him.
Now.
If you don't understand why this is a case of slicing bread with a
chain saw, read [53]Who's [sic] idea was this? and [54]The UUOK9 Form
Letter.
Make SURE you have run and understood these commands:
* help kill
* help trap
* man pkill
* man pgrep
OK, now let's move on to the interesting stuff....
Advanced questions
I want to run two jobs in the background, and then wait until they both
finish.
By default, wait waits for all of your shell's children to exit.
job1 &
job2 &
wait
You can specify one or more jobs (either by PID, or by jobspec -- see
Job Control for that). The help wait page is misleading (implying that
only one argument may be given); refer to the full Bash manual
instead.
There is no way to wait for "child process foo to end, OR something
else to happen", other than [55]setting a trap, which will only help
if "something else to happen" is a signal being sent to the script.
There is also no way to wait for a process that is not your child. You
can't hang around the schoolyard and pick up someone else's kids.
How can I check to see if my game server is still running? I'll put a script
in crontab, and if it's not running, I'll restart it...
We get that question (in various forms) way too often. A user has some
daemon, and they want to restart it whenever it dies. Yes, one could
probably write a bash script that would try to parse the output of ps
(or preferably pgrep if your system has it), and try to guess which
process ID belongs to the daemon we want, and try to guess whether
it's not there any more. But that's haphazard and dangerous. There are
much better ways.
Most Unix systems already have a feature that allows you to respawn
dead processes: init and inittab. If you want to make a new daemon
instance pop up whenever the old one dies, typically all you need to
do is put an appropriate line into /etc/inittab with the "respawn"
action in column 3, and your process's invocation in column 4. Then
run telinit q or your system's equivalent to make init re-read its
inittab.
Some Unix systems don't have inittab, and some system administrators
might want finer control over the daemons and their logging. Those
people may want to look into [56]daemontools, or [57]runit.
This leads into the issue of self-daemonizing programs. There was a
trend during the 1980s for Unix daemons such as inetd to put
themselves into the background automatically. It seems to be
particularly common on BSD systems, although it's widespread across
all flavors of Unix.
The problem with this is that any sane method of managing a daemon
requires that you keep track of it after starting it. If init is told
to respawn a command, it simply launches that command as a child, then
uses the wait() system call; so, when the child exits, the parent can
spawn another one. Daemontools works the same way: a user-supplied run
script establishes the environment, and then execs the process,
thereby giving the daemontools supervisor direct parental authority
over the process, including standard input and output, etc.
If a process double-forks itself into the background (the way inetd
and sendmail and named do), it breaks the connection to its parent --
intentionally. This makes it unmanageable; the parent can no longer
receive the child's output, and can no longer wait() for the child in
order to be informed of its death. And the parent won't even know the
new daemon's process ID. The child has run away from home without even
leaving a note.
So, the Unix/BSD people came up with workarounds... they created "PID
files", in which a long-running daemon would write its process ID,
since the parent had no other way to determine it. But PID files are
not reliable. A daemon could have died, and then some other process
could have taken over its PID, rendering the PID file useless. Or the
PID file could simply get deleted, or corrupted. They came up with
pgrep and pkill to attempt to track down processes by name instead of
by number... but what if the process doesn't have a unique name? What
if there's more than one of it at a time, like nfsd or Apache?
These workarounds and tricks are only in place because of the original
hack of self-backgrounding. Get rid of that, and everything else
becomes easy! Init or daemontools or runit can just control the child
process directly. And even the most raw beginner could write their own
[58]wrapper script:
#!/bin/sh
while :; do
/my/game/server -foo -bar -baz >> /var/log/mygameserver 2>&1
done
Then simply arrange for that to be executed at boot time, with a
simple & to put it in the background, and voila! An instant one-shot
respawn.
Most modern software packages no longer require self-backgrounding;
even for those where it's the default behavior (for compatibility with
older versions), there's often a switch or a set of switches which
allows one to control the process. For instance, Samba's smbd now has
a -F switch specifically for use with daemontools and other such
programs.
If all else fails, you can try using [59]fghack (from the daemontools
package) to prevent the self-backgrounding.
How do I make sure only one copy of my script can run at a time?
First, ask yourself why you think that restriction is necessary. Are
you using a temporary file with a fixed name, rather than
[60]generating a new temporary file in a secure manner each time? If
so, correct that bug in your script. Are you using some system
resource without locking it to prevent corruption if multiple
processes use it simultaneously? In that case, you should probably use
file locking, by rewriting your application in a language that
supports it.
The naive answer to this question, which is given all too frequently
by well-meaning but inexperienced scripters, would be to run some
variant of ps -ef | grep -v grep | grep "$(basename "$0")" | wc -l to
count how many copies of the script are in existence at the moment. I
won't even attempt to describe how horribly wrong that approach is...
if you can't see it for yourself, you'll simply have to take my word
for it.
Unfortunately, bash has no facility for locking a file. [61]Bash FAQ
#45 contains examples of using a directory, a symlink, etc. as a means
of mutual exclusion; but you cannot lock a file directly.
* I believe you can use (set -C; >lockfile) to atomically create a
lockfile, please verify this. (see: [62]Bash FAQ #45) --Andy753421
You could also run your program or shell script under the [63]setlock
program from the daemontools package. Presuming that you use the same
lockfile to prevent concurrent or simultaneous execution of your
script(s), you have effectively made sure that your script will only
run once. Here's an example where we want to make sure that only one
"sleep" is running at a given time.
$ setlock -nX lockfile sleep 100 &
[1] 1169
$ setlock -nX lockfile sleep 100
setlock: fatal: unable to lock lockfile: temporary failure
If environmental restrictions require the use of a shell script, then
you may be stuck using that. Otherwise, you should seriously consider
rewriting the functionality you require in a more powerful language.
I want to process a bunch of files in parallel, and when one finishes, I
want to start the next. And I want to make sure there are exactly 5 jobs
running at a time.
Many xargs allow running tasks in parallel, including FreeBSD, OpenBSD
and GNU (but not POSIX):
find . -print0 | xargs -0 -n 1 -P 4 command
One may also choose to use GNU Parallel (if available) instead of
xargs, as GNU Parallel makes sure the output from different jobs do
not mix.
find . -print0 | parallel -0 command | use_output_if_needed
A C program could fork 5 children and manage them closely using
select() or similar, to assign the next file in line to whichever
child is ready to handle it. But bash has nothing equivalent to select
or poll.
In a script where the loop is very big you can use sem from GNU
Parallel. Here 10 jobs are run in parallel:
for i in *.log ; do
echo "$i"
[...do other needed stuff...]
sem -j10 gzip $i ";" echo done
done
sem --wait
If you do not have GNU Parallel installed you're reduced to lesser
solutions. One way is to divide the job into 5 "equal" parts, and then
just launch them all in parallel. Here's an example:
[64] 1 #!/usr/local/bin/bash
[65] 2 # Read all the files (from a text file, 1 per line) into an array.
[66] 3 IFS=$'\n' read -r -d '' -a files < inputlist
[67] 4
[68] 5 # Here's what we plan to do to them.
[69] 6 do_it() {
[70] 7 for f; do [[ -f $f ]] && my_job "$f"; done
[71] 8 }
[72] 9
[73] 10 # Divide the list into 5 sub-lists.
[74] 11 i=0 n=0 a=() b=() c=() d=() e=()
[75] 12 while ((i < ${#files[*]})); do
[76] 13 a[n]=${files[i]}
[77] 14 b[n]=${files[i+1]}
[78] 15 c[n]=${files[i+2]}
[79] 16 d[n]=${files[i+3]}
[80] 17 e[n]=${files[i+4]}
[81] 18 ((i+=5, n++))
[82] 19 done
[83] 20
[84] 21 # Process the sub-lists in parallel
[85] 22 do_it "address@hidden" > a.out 2>&1 &
[86] 23 do_it "address@hidden" > b.out 2>&1 &
[87] 24 do_it "address@hidden" > c.out 2>&1 &
[88] 25 do_it "address@hidden" > d.out 2>&1 &
[89] 26 do_it "address@hidden" > e.out 2>&1 &
[90] 27 wait
See [91]reading a file line-by-line and [92]arrays and
[93]ArithmeticExpression for explanations of the syntax used in this
example.
Even if the lists aren't quite identical in terms of the amount of
work required, this approach is close enough for many purposes.
Another approach involves using a [94]named pipe to tell a "manager"
when a job is finished, so it can launch the next job. Here is an
example of that approach:
[95] 1 #!/bin/bash
[96] 2
[97] 3 # FD 3 will be tied to a named pipe.
[98] 4 mkfifo pipe; exec 3<>pipe
[99] 5
[100] 6 # This is the job we're running.
[101] 7 s() {
[102] 8 echo Sleeping $1
[103] 9 sleep $1
[104] 10 }
[105] 11
[106] 12 # Start off with 3 instances of it.
[107] 13 # Each time an instance terminates, write a newline to the named pipe
.
[108] 14 { s 5; echo >&3; } &
[109] 15 { s 7; echo >&3; } &
[110] 16 { s 8; echo >&3; } &
[111] 17
[112] 18 # Each time we get a line from the named pipe, launch another job.
[113] 19 while read; do
[114] 20 { s $((RANDOM%5+7)); echo >&3; } &
[115] 21 done <&3
If you need something more sophisticated than these, you're probably
looking at the wrong language.
My script runs a pipeline. When the script is killed, I want the pipeline to
die too.
One approach is to set up a [116]signal handler (or an EXIT trap) to
kill your child processes right before you die. Then, you need the
PIDs of the children -- which, in the case of a pipeline, is not so
easy. You can use a [117]named pipe instead of a pipeline, so that you
can collect the PIDs yourself:
[118] 1 #!/bin/bash
[119] 2 unset kids
[120] 3 fifo=/tmp/foo$$
[121] 4 trap 'kill "address@hidden"; rm -f "$fifo"' EXIT
[122] 5 mkfifo "$fifo" || exit 1
[123] 6 command 1 > "$fifo" & kids+=($!)
[124] 7 command 2 < "$fifo" & kids+=($!)
[125] 8 wait
This example sets up a FIFO with one writer and one reader, and stores
their PIDs in an array named kids. The EXIT trap sends SIGTERM to them
all, removes the FIFO, and exits. See [126]Bash FAQ #62 for notes on
the use of temporary files.
Another approach is to enable job control, which allows whole
pipelines to be treated as units.
[127] 1 #!/bin/bash
[128] 2 set -m
[129] 3 trap 'kill %%' EXIT
[130] 4 command1 | command2 &
[131] 5 wait
In this example, we enable job control with set -m. The %% in the EXIT
trap refers to the current job (the most recently executed background
pipeline qualifies for that). Telling bash to kill the current job
takes out the entire pipeline, rather than just the last command in
the pipeline (which is what we would get if we had stored and used $!
instead of %%).
How to work with processes
The best way to do process management in Bash is to start the managed
process(es) from your script, remember its PID, and use that PID to do
things with your process later on.
If at ALL possible, AVOID ps, pgrep, killall, and any other process
table parsing tools. These tools have no clue what process YOU WANT to
talk to. They only guess at it based on filtering unreliable
information. These tools may work fine in your little test
environment, they may work fine in production for a while, but
inevitably they WILL fail, because they ARE a broken approach to
process management.
PIDs and parents
In UNIX, processes are identified by a number called a PID (for
Process IDentifier). Each running process has a unique identifier. You
cannot reliably determine when or how a process was started purely
from the identifier number: for all intents and purposes, it is
random.
Each UNIX process also has a parent process. This parent process is
the process that started it, but can change to the init process if the
parent process ends before the new process does. (That is, init will
pick up orphaned processes.) Understanding this parent/child
relationship is vital because it is the key to reliable process
management in UNIX. A process's PID will NEVER be freed up for use
after the process dies UNTIL the parent process waits for the PID to
see whether it ended and retrieve its exit code. If the parent ends,
the process is returned to init, which does this for you.
This is important for one major reason: if the parent process manages
its child process, it can be absolutely certain that, even if the
child process dies, no other new process can accidentally recycle the
child process's PID until the parent process has waited for that PID
and noticed the child died. This gives the parent process the
guarantee that the PID it has for the child process will ALWAYS point
to that child process, whether it is alive or a "zombie". Nobody else
has that guarantee.
The risk of letting the parent die
Why is this all so important? Why should you care? Consider what
happens if we use a "PID file". Assume the following sequence of
events:
1. You're a boot script (for example, one in /etc/init.d). You are
told to start the foodaemon.
2. You start a foodaemon child process in the background and grab its
PID.
3. You write this PID to a file.
4. You exit, assuming you've done your job.
5. Later, you're started up again and told to kill the foodaemon.
6. You look for the child process's PID in a file.
7. You send the SIGTERM signal to this PID, telling it to clean up
and exit.
There is absolutely no way you can be certain that the process you
told to exit is actually the one you started. The process you wanted
to check up on could have died and another random new process could
have easily recycled its PID that was released by init.
The risk of parsing the process tree
UNIX comes with a set of handy tools, among which is ps. This is a
very helpful utility that you can use from the command line to get an
overview of what processes are running on your box and what their
status is.
All too many people, however, assume that computers and humans work
the same way. They think that "I can read ps and see if my process is
in there, why shouldn't my script do the same?". Here's why: You are
(hopefully) smarter than your script. You see ps output and you see
all sorts of information in context. Your brain determines, "Is this
the process I'm looking for?" and based on what you see it guesses
"Yeah, it looks like it.". Firstly, your script can't process context
the way your brain can (no, awk'ing out column 4 and seeing if that
contains your process's command name isn't good enough). Secondly,
even if it could do a good job, your script shouldn't be doing any
guessing whatsoever. It shouldn't need to.
ps output is unpredictable, highly OS-dependent, and not built for
parsing. It is next to impossible for your script to distinguish ping
as a command name from another process's command line which may
contain a similar word like piping, or a user named ping, etc.
The same goes for almost any other tool that parses the process list.
Some are worse than others, but in the end, they all do the wrong
thing.
Doing it right
As mentioned before, the right way to do something with your child
process is by using its PID, preferably (if at all possible) from the
parent process that created it.
You may have come here hoping for a quick hint on how to finish your
script only to find these recommendations don't apply to any of your
existing code or setup. That's probably not because your code or setup
is an exception and you should disregard this; but more likely because
you need to take the time and re-evaluate your existing code or setup
and rework it. This will require you to think for a moment. Take that
moment and do it right.
Starting a process and remembering its PID
To start a process asynchronously (so the main script can continue
while the process runs in the "background"), use the & operator. To
get the PID that was assigned to it, expand the ! parameter. You can,
for example, save it in a variable:
# Bourne shell
myprocess -o myfile -i &
mypid=$!
Checking up on your process or terminating it
At a later time, you may be interested in whether your process is
still running and if it is, you may decide it's time to terminate it.
If it's not running anymore, you may be interested in its exit code to
see whether it experienced a problem or ended successfully.
To [132]send a process a signal, we use the kill command. Signals can
be used to tell a process to do something, but kill can also be used
to check if the process is still alive:
# Bourne
kill -0 $mypid && echo "My process is still alive."
kill $mypid ; echo "I just asked my process to shut down."
kill sends the SIGTERM signal, by default. This tells a program it's
time to terminate. You can use the -0 option to kill if you don't want
to terminate the process but just check up on whether it's still
running. In either case, the kill command will have a 0 exit code
(success) if it managed to send the signal (or found the process to
still be alive).
Unless you intend to send a very specific signal to a process, do not
use any other kill options; in particular, avoid using -9 or SIGKILL
at all cost. The KILL signal is a very dangerous signal to send to a
process and using it is almost always a bug. Send the default SIGTERM
instead and have patience.
To wait for a child process to finish or to read in the exit code of a
process that you know has already finished (because you did a kill -0
check, for example), use the wait built-in command:
# Bash
night() { sleep 10; } # Define 'night' as a function that take
s 10 seconds.
# Adjust seconds according to current se
ason and latitude
# for a more realistic simulation.
night & nightpid=$!
sheep=0
while sleep 1; do
kill -0 $nightpid || break # Break the loop when we see the process
has gone away.
echo "$(( ++sheep )) sheep jumped over the fence."
done
wait $nightpid; nightexit=$?
echo "The night ended with exit code $nightexit. We counted $sheep sheep."
Starting a "daemon" and checking whether it started successfully
This is a very common request. The problem is that there is no answer!
There is no such thing as "the daemon started up successfully", and if
your specific daemon were to have a relevant definition to that
statement, it would be so completely daemon-specific, that there is no
generic way for us to tell you how to check for that condition.
What people generally resort to in an attempt to provide something
"good enough", is: "Let's start the daemon, wait a few seconds, check
whether the daemon process is still running, and if so, let's assume
it's doing the right thing.". Ignoring the fact that this is a totally
lousy check which could easily be defeated by a stressed kernel,
timing issues, latency or delay in the daemon's operations, and many
other conditions, let's just see how we would implement this if we
actually wanted to do this:
# Bash
mydaemon -i eth0 & daemonpid=$!
sleep 2
if kill -0 $daemonpid ; then
echo "Daemon started successfully. I think."
else
wait $daemonpid; daemonexit=$?
echo "Daemon process disappeared. I suppose something may have gone wr
ong. Its exit code was $daemonexit."
fi
To be honest, this problem is much better solved by doing a
daemon-specific check. For example, say you're starting a web server
called httpd. The sensible thing to check in order to determine
whether the web server started successfully... is whether it's
actually serving your web content! Who'd have thought!
# Bourne(?)
httpd -h 127.0.0.1 & httpdpid=$!
while sleep 1; do
nc -z 127.0.0.1 80 && break # See if we can establish a TCP
connection to port 80.
done
echo "httpd ready for duty."
If something goes wrong, though, this will wait forever trying to
connect to port 80. So let's check whether httpd died unexpectedly or
whether a certain "timeout" time elapsed:
# Bash
httpd -h 127.0.0.1 & httpdpid=$!
time=0 timeout=60
while sleep 1; do
nc -z 127.0.0.1 80 && break # See if we can establish a TCP
connection to port 80.
# Connection not yet available.
if ! kill -0 $httpdpid; then
wait $httpdpid; httpdexit=$?
echo "httpd died unexpectedly with exit code: $httpdexit"
exit $httpdexit
fi
if (( ++time > timeout )); then
echo "httpd hasn't gotten ready after $time seconds. Something mus
t've gone wrong.."
# kill $httpdpid; wait $httpdpid # You could terminate httpd her
e, if you like.
exit
fi
done
echo "httpd ready for duty."
On processes, environments and inheritance
Every process on a Unix system (except init) has a parent process from
which it inherits certain things. A process can change some of these
things, and not others. You cannot change things inside another
process other than by being its parent, or attaching (attacking?) it
with a debugger.
It is of paramount importance that you understand this model if you
plan to use or administer a Unix system successfully. For example, a
user with 10 windows open might wonder why he can't tell all of his
shells to change the contents of their PATH variable, short of going
to each one individually and running a command. And even then, the
changed PATH variable won't be set in the user's window manager or
desktop environment, which means any new windows he creates will still
get the old variable.
The solution, of course, is that the user needs to edit a shell
[133]dot file, then logout and back in, so that his top-level
processes will get the new variable, and can pass it along to their
children.
Likewise, a system administrator might want to tell her in.ftpd to use
a default [134]umask of 002 instead of whatever it's currently using.
Achieving that goal will require an understanding of how in.ftpd is
launched on her system, either as a child of inetd or as a standalone
daemon with some sort of [135]boot script; making the appropriate
modifications; and restarting the appropriate daemons, if any.
So, let's take a closer look at how processes are created.
The Unix process creation model revolves around two system calls:
fork() and exec(). (There is actually a family of related system calls
that begin with exec which all behave in slightly different manners,
but we'll treat them all equally for now.) fork() creates a child
process which is a duplicate of the parent who called fork() (with a
few exceptions). The parent receives the child process's PID (Process
ID) number as the return value of the fork() function, while the child
gets a "0" to tell it that it's the child. exec() replaces the current
process with a different program.
So, the usual sequence is:
* A program calls fork() and checks the return value of the system
call. If the status is greater than 0, then it's the parent
process, so it calls wait() on the child process ID (unless we
want it to continue running while the child runs in the
background).
* If the status is 0, then it's the child process, so it calls
exec() to do whatever it's supposed to be doing.
* But before that, the child might decide to close() some file
descriptors, open() new ones, set environment variables, change
resource limits, and so on. All of these changes will remain in
effect after the exec() and will affect the task that is executed.
* If the return value of fork() is negative, something bad happened
(we ran out of memory, or the process table filled up, etc.).
Let's take an example of a shell command:
echo hello world 1>&2
The process executing this is a shell, which reads commands and
executes them. For external commands, it uses the standard
fork()/exec() model to do so. Let's show it step by step:
* The parent shell calls fork().
* The parent gets the child's process ID as the return value of
fork() and waits for it to terminate.
* The child gets a 0 from fork() so it knows it's the child.
* The child is supposed to redirect standard output to standard
error (due to the 1>&2 directive). It does this now:
+ Close file descriptor 1.
+ Duplicate file descriptor 2, and make sure the duplicate is
FD 1.
* The child calls
exec("echo", "echo", "hello", "world", (char *)NULL) or something
similar to execute the command. (Here, we're assuming echo is an
external command.)
* Once the echo terminates, the parent's wait call also terminates,
and the parent resumes normal operation.
There are other things the child of the shell might do before
executing the final command. For example, it might set environment
variables:
http_proxy=http://tempproxy:3128/ lynx http://someURL/
In this case, the child will put http_proxy=http://tempproxy:3128/
into the environment before calling exec(). The parent's environment
is unaffected.
A child process inherits many things from its parent:
* Open file descriptors. The child gets copies of these, referring
to the same files.
* Environment variables. The child gets its own copies of these, and
[136]changes made by the child do not affect the parent's copy.
* Current working directory. If the child changes its working
directory, [137]the parent will never know about it.
* User ID, group ID and supplementary groups. A child process is
spawned with the same privileges as its parent. Unless the child
process is running with superuser UID (UID 0), it cannot change
these privileges.
* System resource limits. The child inherits the limits of its
parent. A process that runs as superuser UID can raise its
resource limits (setrlimit(2)). A process running as non-superuser
can only lower its resource limits; it can't raise them.
* [138]umask.
An active Unix system may be perceived as a tree of processes, with
parent/child relationships shown as vertical ("branch") connections
between nodes. For example,
(init)
|
(login)
|
startx
|
xinit
|
bash .xinitrc
/ | \
rxvt rxvt fvwm2
| | \
bash screen \____________________
/ | \ | | \
bash bash bash xclock xload firefox ...
| |
mutt rtorrent
This is a simplified version of an actual set of processes run by one
user on a real system. I have omitted many, to keep things readable.
The root of the tree, shown as (init), as well as the first child
process (login), are running as root (superuser UID 0). Here is how
this scenario came about:
* The kernel (Linux in this case) is hard-coded to run /sbin/init as
process number 1 when it has finished its startup. init never
dies; it is the ultimate ancestor of every process on the system.
* init reads /etc/inittab which tells it to spawn some getty
processes on some of the Linux virtual terminal devices (among
other things).
* Each getty process presents a bit of information plus a login
prompt.
* After reading a username, getty exec()s /bin/login to read the
password. (Thus, getty no longer appears in the tree; it has
replaced itself.)
* If the password is valid, login fork()s the user's login shell (in
this case bash). Presumably, it hangs around (instead of using
exec()) because it wants to do some clean-up after the user's
shell has terminated.
* The user types exec startx at the bash shell prompt. This causes
bash to exec() startx (and therefore the login shell no longer
appears in the tree).
* startx is a wrapper that launches an X session, which includes an
X server process (not shown -- it runs as root), and a whole slew
of client programs. On this particular system, .xinitrc in the
user's home directory is a script that tells which X client
programs to run.
* Two rxvt terminal emulators are launched from the .xinitrc file
(in the background using &), and each of them runs a new copy of
the user's shell, bash.
+ In one of them, the user has typed exec screen (or something
similar) to replace bash with screen. Screen, in turn, has
three bash child processes of its own, two of which have
terminal-based programs running in them (mutt, rtorrent).
* The user's window manager, fvwm2, is run in the foreground by the
.xinitrc script. A window manager or desktop environment is
usually the last thing run by the .xinitrc script; when the WM or
DE terminates, the script terminates, and brings down the whole
session.
* The window manager runs several processes of its own (xclock,
xload, firefox, ...). It typically has a menu, or icons, or a
control panel, or some other means of launching new programs. We
will not cover window manager configurations here.
Other parts of a Unix system use similar process trees to accomplish
their goals, although few of them are quite as deep or complex as an X
session. For example, inetd runs as a daemon which listens on several
UDP and TCP ports, and launches programs (ftpd, telnetd, etc.) when it
receives network connections. lpd runs as a managing daemon for
printer jobs, and will launch children to handle individual jobs when
a printer is ready. sshd listens for incoming SSH connections, and
launches children when it receives them. Some electronic mail systems
(particularly [139]qmail) use relatively large numbers of small
processes working together.
Understanding the relationship among a set of processes is vital to
administering a system. For example, suppose you would like to change
the way your FTP service behaves. You've located a configuration file
that it is known to read at startup time, and you've changed it. Now
what? You could reboot the entire system to be sure your change takes
effect, but most people consider that overkill. Generally, people
prefer to restart only the minimal number of processes, thereby
causing the least amount of disruption to the other services and the
other users of the system.
So, you need to understand how your FTP service starts up. Is it a
standalone daemon? If so, you probably have some system-specific way
of restarting it (either by running a [140]BootScript, or manually
killing and restarting it, or perhaps by issuing some special service
management command). More commonly, an FTP service runs under the
control of inetd. If this is the case, you don't need to restart
anything at all. inetd will launch a fresh FTP service daemon every
time it receives a connection, and the fresh daemon will read the
changed configuration file every time.
On the other hand, suppose your FTP service doesn't have its own
configuration file that lets you make the change you want (for
example, changing its umask for the default [141]Permissions of
uploaded files). In this case, you know that it inherits its umask
from inetd, which in turn gets its umask from whatever boot script
launched it. If you would like to change FTP's umask in this scenario,
you would have to edit inetd's boot script, and then kill and restart
inetd so that the FTP service daemons (inetd's children) will inherit
the new value. And by doing this, you are also changing the default
umask of every other service that inetd manages! Is that acceptable?
Only you can answer that. If not, then you may have to change how your
FTP service runs, possibly moving it to a standalone daemon. This is a
system administrator's job.
_________________________________________________________________
[142]CategoryShell [143]CategoryUnix
ProcessManagement (last edited 2011-10-24 16:08:10 by [144]GreyCat)
* [145]Edit (Text)
* [146]Comments
* [147]Info
* [148]Attachments
* More Actions:
[Raw Text................] Do
* [149]MoinMoin Powered
* [150]Python Powered
* [151]GPL licensed
* [152]Valid HTML 4.01
References
1. http://mywiki.wooledge.org/ProcessManagement?action=edit
2. http://mywiki.wooledge.org/EnglishFrontPage
3. http://mywiki.wooledge.org/ProcessManagement?action=raw
4. http://mywiki.wooledge.org/ProcessManagement?action=print
5. http://mywiki.wooledge.org/FindPage
6. http://mywiki.wooledge.org/TitleIndex
7. http://mywiki.wooledge.org/WordIndex
8. http://mywiki.wooledge.org/HelpOnFormatting
9. http://mywiki.wooledge.org/EnglishFrontPage
10. http://mywiki.wooledge.org/ProcessManagement?action=login
11.
http://mywiki.wooledge.org/ProcessManagement?action=fullsearch&value=linkto%3A%22ProcessManagement%22&context=180
12. http://mywiki.wooledge.org/EnglishFrontPage
13. http://mywiki.wooledge.org/RecentChanges
14. http://mywiki.wooledge.org/FindPage
15. http://mywiki.wooledge.org/HelpContents
16. http://mywiki.wooledge.org/ProcessManagement
17. http://mywiki.wooledge.org/ProcessManagement?action=edit&editor=text
18. http://mywiki.wooledge.org/ProcessManagement
19. http://mywiki.wooledge.org/ProcessManagement?action=info
20. http://mywiki.wooledge.org/ProcessManagement?action=AttachFile
21. http://mywiki.wooledge.org/ProcessManagement#The_basics
22. http://mywiki.wooledge.org/ProcessManagement#Simple_questions
23.
http://mywiki.wooledge.org/ProcessManagement#How_do_I_run_a_job_in_the_background.3F
24.
http://mywiki.wooledge.org/ProcessManagement#My_script_runs_a_job_in_the_background.__How_do_I_get_its_PID.3F
25.
http://mywiki.wooledge.org/ProcessManagement#OK.2C_I_have_its_PID.__How_do_I_check_that_it.27s_still_running.3F
26.
http://mywiki.wooledge.org/ProcessManagement#I_want_to_do_something_with_a_process_I_started_earlier
27.
http://mywiki.wooledge.org/ProcessManagement#How_do_I_kill_a_process_by_name.3F__I_need_to_get_the_PID_out_of_ps_aux_.7C_grep_....
28.
http://mywiki.wooledge.org/ProcessManagement#But_I.27m_on_some_old_legacy_Unix_system_that_doesn.27t_have_pgrep.21__What_do_I_do.3F
29.
http://mywiki.wooledge.org/ProcessManagement#I_want_to_run_something_in_the_background_and_then_log_out.
30.
http://mywiki.wooledge.org/ProcessManagement#I.27m_trying_to_kill_-9_my_job_but_blah_blah_blah...
31.
http://mywiki.wooledge.org/ProcessManagement#Make_SURE_you_have_run_and_understood_these_commands:
32. http://mywiki.wooledge.org/ProcessManagement#Advanced_questions
33.
http://mywiki.wooledge.org/ProcessManagement#I_want_to_run_two_jobs_in_the_background.2C_and_then_wait_until_they_both_finish.
34.
http://mywiki.wooledge.org/ProcessManagement#How_can_I_check_to_see_if_my_game_server_is_still_running.3F__I.27ll_put_a_script_in_crontab.2C_and_if_it.27s_not_running.2C_I.27ll_restart_it...
35.
http://mywiki.wooledge.org/ProcessManagement#How_do_I_make_sure_only_one_copy_of_my_script_can_run_at_a_time.3F
36.
http://mywiki.wooledge.org/ProcessManagement#I_want_to_process_a_bunch_of_files_in_parallel.2C_and_when_one_finishes.2C_I_want_to_start_the_next._And_I_want_to_make_sure_there_are_exactly_5_jobs_running_at_a_time.
37.
http://mywiki.wooledge.org/ProcessManagement#My_script_runs_a_pipeline.__When_the_script_is_killed.2C_I_want_the_pipeline_to_die_too.
38. http://mywiki.wooledge.org/ProcessManagement#How_to_work_with_processes
39. http://mywiki.wooledge.org/ProcessManagement#PIDs_and_parents
40.
http://mywiki.wooledge.org/ProcessManagement#The_risk_of_letting_the_parent_die
41.
http://mywiki.wooledge.org/ProcessManagement#The_risk_of_parsing_the_process_tree
42. http://mywiki.wooledge.org/ProcessManagement#Doing_it_right
43.
http://mywiki.wooledge.org/ProcessManagement#Starting_a_process_and_remembering_its_PID
44.
http://mywiki.wooledge.org/ProcessManagement#Checking_up_on_your_process_or_terminating_it
45.
http://mywiki.wooledge.org/ProcessManagement#Starting_a_.22daemon.22_and_checking_whether_it_started_successfully
46.
http://mywiki.wooledge.org/ProcessManagement#On_processes.2C_environments_and_inheritance
47. http://mywiki.wooledge.org/ProcessManagement#advanced
48. http://mywiki.wooledge.org/ProcessManagement#parents
49. http://proctools.sourceforge.net/
50. http://mywiki.wooledge.org/RaceCondition
51. http://mywiki.wooledge.org/RegularExpression
52. http://mywiki.wooledge.org/BashFAQ/063
53.
http://web.archive.org/web/20080801151452/http://speculation.org/garrick/kill-9.html
54. http://partmaps.org/era/unix/award.html#uuk9letter
55. http://mywiki.wooledge.org/SignalTrap
56. http://cr.yp.to/daemontools.html
57. http://smarden.org/runit/
58. http://mywiki.wooledge.org/WrapperScript
59. http://cr.yp.to/daemontools/fghack.html
60. http://mywiki.wooledge.org/BashFAQ/062
61. http://mywiki.wooledge.org/BashFAQ/045
62. http://mywiki.wooledge.org/BashFAQ/045
63. http://cr.yp.to/daemontools/setlock.html
64.
http://mywiki.wooledge.org/ProcessManagement#CA-6d491e954a7a0eb56a5b0dc4f314c2cbf501e20e_1
65.
http://mywiki.wooledge.org/ProcessManagement#CA-6d491e954a7a0eb56a5b0dc4f314c2cbf501e20e_2
66.
http://mywiki.wooledge.org/ProcessManagement#CA-6d491e954a7a0eb56a5b0dc4f314c2cbf501e20e_3
67.
http://mywiki.wooledge.org/ProcessManagement#CA-6d491e954a7a0eb56a5b0dc4f314c2cbf501e20e_4
68.
http://mywiki.wooledge.org/ProcessManagement#CA-6d491e954a7a0eb56a5b0dc4f314c2cbf501e20e_5
69.
http://mywiki.wooledge.org/ProcessManagement#CA-6d491e954a7a0eb56a5b0dc4f314c2cbf501e20e_6
70.
http://mywiki.wooledge.org/ProcessManagement#CA-6d491e954a7a0eb56a5b0dc4f314c2cbf501e20e_7
71.
http://mywiki.wooledge.org/ProcessManagement#CA-6d491e954a7a0eb56a5b0dc4f314c2cbf501e20e_8
72.
http://mywiki.wooledge.org/ProcessManagement#CA-6d491e954a7a0eb56a5b0dc4f314c2cbf501e20e_9
73.
http://mywiki.wooledge.org/ProcessManagement#CA-6d491e954a7a0eb56a5b0dc4f314c2cbf501e20e_10
74.
http://mywiki.wooledge.org/ProcessManagement#CA-6d491e954a7a0eb56a5b0dc4f314c2cbf501e20e_11
75.
http://mywiki.wooledge.org/ProcessManagement#CA-6d491e954a7a0eb56a5b0dc4f314c2cbf501e20e_12
76.
http://mywiki.wooledge.org/ProcessManagement#CA-6d491e954a7a0eb56a5b0dc4f314c2cbf501e20e_13
77.
http://mywiki.wooledge.org/ProcessManagement#CA-6d491e954a7a0eb56a5b0dc4f314c2cbf501e20e_14
78.
http://mywiki.wooledge.org/ProcessManagement#CA-6d491e954a7a0eb56a5b0dc4f314c2cbf501e20e_15
79.
http://mywiki.wooledge.org/ProcessManagement#CA-6d491e954a7a0eb56a5b0dc4f314c2cbf501e20e_16
80.
http://mywiki.wooledge.org/ProcessManagement#CA-6d491e954a7a0eb56a5b0dc4f314c2cbf501e20e_17
81.
http://mywiki.wooledge.org/ProcessManagement#CA-6d491e954a7a0eb56a5b0dc4f314c2cbf501e20e_18
82.
http://mywiki.wooledge.org/ProcessManagement#CA-6d491e954a7a0eb56a5b0dc4f314c2cbf501e20e_19
83.
http://mywiki.wooledge.org/ProcessManagement#CA-6d491e954a7a0eb56a5b0dc4f314c2cbf501e20e_20
84.
http://mywiki.wooledge.org/ProcessManagement#CA-6d491e954a7a0eb56a5b0dc4f314c2cbf501e20e_21
85.
http://mywiki.wooledge.org/ProcessManagement#CA-6d491e954a7a0eb56a5b0dc4f314c2cbf501e20e_22
86.
http://mywiki.wooledge.org/ProcessManagement#CA-6d491e954a7a0eb56a5b0dc4f314c2cbf501e20e_23
87.
http://mywiki.wooledge.org/ProcessManagement#CA-6d491e954a7a0eb56a5b0dc4f314c2cbf501e20e_24
88.
http://mywiki.wooledge.org/ProcessManagement#CA-6d491e954a7a0eb56a5b0dc4f314c2cbf501e20e_25
89.
http://mywiki.wooledge.org/ProcessManagement#CA-6d491e954a7a0eb56a5b0dc4f314c2cbf501e20e_26
90.
http://mywiki.wooledge.org/ProcessManagement#CA-6d491e954a7a0eb56a5b0dc4f314c2cbf501e20e_27
91. http://mywiki.wooledge.org/BashFAQ/001
92. http://mywiki.wooledge.org/BashFAQ/005
93. http://mywiki.wooledge.org/ArithmeticExpression
94. http://mywiki.wooledge.org/NamedPipes
95.
http://mywiki.wooledge.org/ProcessManagement#CA-ad2a5f7a2dc492371d37005b8b75f047913537e1_1
96.
http://mywiki.wooledge.org/ProcessManagement#CA-ad2a5f7a2dc492371d37005b8b75f047913537e1_2
97.
http://mywiki.wooledge.org/ProcessManagement#CA-ad2a5f7a2dc492371d37005b8b75f047913537e1_3
98.
http://mywiki.wooledge.org/ProcessManagement#CA-ad2a5f7a2dc492371d37005b8b75f047913537e1_4
99.
http://mywiki.wooledge.org/ProcessManagement#CA-ad2a5f7a2dc492371d37005b8b75f047913537e1_5
100.
http://mywiki.wooledge.org/ProcessManagement#CA-ad2a5f7a2dc492371d37005b8b75f047913537e1_6
101.
http://mywiki.wooledge.org/ProcessManagement#CA-ad2a5f7a2dc492371d37005b8b75f047913537e1_7
102.
http://mywiki.wooledge.org/ProcessManagement#CA-ad2a5f7a2dc492371d37005b8b75f047913537e1_8
103.
http://mywiki.wooledge.org/ProcessManagement#CA-ad2a5f7a2dc492371d37005b8b75f047913537e1_9
104.
http://mywiki.wooledge.org/ProcessManagement#CA-ad2a5f7a2dc492371d37005b8b75f047913537e1_10
105.
http://mywiki.wooledge.org/ProcessManagement#CA-ad2a5f7a2dc492371d37005b8b75f047913537e1_11
106.
http://mywiki.wooledge.org/ProcessManagement#CA-ad2a5f7a2dc492371d37005b8b75f047913537e1_12
107.
http://mywiki.wooledge.org/ProcessManagement#CA-ad2a5f7a2dc492371d37005b8b75f047913537e1_13
108.
http://mywiki.wooledge.org/ProcessManagement#CA-ad2a5f7a2dc492371d37005b8b75f047913537e1_14
109.
http://mywiki.wooledge.org/ProcessManagement#CA-ad2a5f7a2dc492371d37005b8b75f047913537e1_15
110.
http://mywiki.wooledge.org/ProcessManagement#CA-ad2a5f7a2dc492371d37005b8b75f047913537e1_16
111.
http://mywiki.wooledge.org/ProcessManagement#CA-ad2a5f7a2dc492371d37005b8b75f047913537e1_17
112.
http://mywiki.wooledge.org/ProcessManagement#CA-ad2a5f7a2dc492371d37005b8b75f047913537e1_18
113.
http://mywiki.wooledge.org/ProcessManagement#CA-ad2a5f7a2dc492371d37005b8b75f047913537e1_19
114.
http://mywiki.wooledge.org/ProcessManagement#CA-ad2a5f7a2dc492371d37005b8b75f047913537e1_20
115.
http://mywiki.wooledge.org/ProcessManagement#CA-ad2a5f7a2dc492371d37005b8b75f047913537e1_21
116. http://mywiki.wooledge.org/SignalTrap
117. http://mywiki.wooledge.org/NamedPipes
118.
http://mywiki.wooledge.org/ProcessManagement#CA-ac149892fb9c94367815a32142b308353ab03dc8_1
119.
http://mywiki.wooledge.org/ProcessManagement#CA-ac149892fb9c94367815a32142b308353ab03dc8_2
120.
http://mywiki.wooledge.org/ProcessManagement#CA-ac149892fb9c94367815a32142b308353ab03dc8_3
121.
http://mywiki.wooledge.org/ProcessManagement#CA-ac149892fb9c94367815a32142b308353ab03dc8_4
122.
http://mywiki.wooledge.org/ProcessManagement#CA-ac149892fb9c94367815a32142b308353ab03dc8_5
123.
http://mywiki.wooledge.org/ProcessManagement#CA-ac149892fb9c94367815a32142b308353ab03dc8_6
124.
http://mywiki.wooledge.org/ProcessManagement#CA-ac149892fb9c94367815a32142b308353ab03dc8_7
125.
http://mywiki.wooledge.org/ProcessManagement#CA-ac149892fb9c94367815a32142b308353ab03dc8_8
126. http://mywiki.wooledge.org/BashFAQ/062
127.
http://mywiki.wooledge.org/ProcessManagement#CA-e98d2857b35ab4c3a68921f28b744a872a4f0495_1
128.
http://mywiki.wooledge.org/ProcessManagement#CA-e98d2857b35ab4c3a68921f28b744a872a4f0495_2
129.
http://mywiki.wooledge.org/ProcessManagement#CA-e98d2857b35ab4c3a68921f28b744a872a4f0495_3
130.
http://mywiki.wooledge.org/ProcessManagement#CA-e98d2857b35ab4c3a68921f28b744a872a4f0495_4
131.
http://mywiki.wooledge.org/ProcessManagement#CA-e98d2857b35ab4c3a68921f28b744a872a4f0495_5
132. http://mywiki.wooledge.org/SignalTrap
133. http://mywiki.wooledge.org/DotFiles
134. http://mywiki.wooledge.org/Permissions#umask
135. http://mywiki.wooledge.org/BootScript
136. http://mywiki.wooledge.org/BashFAQ/060
137. http://mywiki.wooledge.org/BashFAQ/060
138. http://mywiki.wooledge.org/Permissions#umask
139. http://mywiki.wooledge.org/CategoryQmail
140. http://mywiki.wooledge.org/BootScript
141. http://mywiki.wooledge.org/Permissions
142. http://mywiki.wooledge.org/CategoryShell
143. http://mywiki.wooledge.org/CategoryUnix
144. http://mywiki.wooledge.org/GreyCat
145. http://mywiki.wooledge.org/ProcessManagement?action=edit&editor=text
146. http://mywiki.wooledge.org/ProcessManagement
147. http://mywiki.wooledge.org/ProcessManagement?action=info
148. http://mywiki.wooledge.org/ProcessManagement?action=AttachFile
149. http://moinmo.in/
150. http://moinmo.in/Python
151. http://moinmo.in/GPL
152. http://validator.w3.org/check?uri=referer