system LIST system PROGRAM LIST
Does exactly the same thing as execLIST
, except that a fork is
done first and the parent process waits for the child process to
exit.
-If there is more than one argument in LIST,
or if LIST is an array with more than one value, starts the program
given by the first element of the list with arguments given by the
rest of the list.
-If there is only one scalar argument, the argument
is checked for shell metacharacters, and if there are any, the
entire argument is passed to the system's command shell for parsing
(this is /bin/sh -c on Unix platforms, but varies on other
platforms). If there are no shell metacharacters in the argument,
it is split into words and passed directly to execvp
, which is
more efficient.
A new process is traditionally started using the fork()
system call:
pid_t p;
p = fork();
if (p == (pid_t) -1)
/* ERROR */
else if (p == 0)
/* CHILD */
else
/* PARENT */
This creates a child as a duplicate of its parent.
Parent and child are identical in almost all respects.
In the code they are distinguished by the fact that the parent
learns the process ID of its child, while fork()
returns 0 in the child. (It can find the process ID of its
parent using the getppid() system call.)
Termination
Normal termination is when the process does
exit(n);
or
return n;
from its main() procedure. It returns the single byte n
to its parent.
Abnormal termination is usually caused by a signal.
Collecting the exit code of child process
The parent does
pid_t p;
int status;
p = wait(&status);
The parent will suspend till one of its child process terminates, and collects two bytes:
A process that has terminated but has not yet been waited for
is a zombie. It need only store these two bytes:
exit code and reason for termination.
On the other hand, if the parent dies first, init (process 1)
inherits the child and becomes its parent.
Signals
Stopping
Some signals cause a process to stop:
SIGSTOP (stop!),
SIGTSTP (stop from tty: probably ^Z was typed),
SIGTTIN (tty input asked by background process),
SIGTTOU (tty output sent by background process, and this was
disallowed by stty tostop).
Apart from ^Z there also is ^Y. The former stops the process
when it is typed, the latter stops it when it is read.
Signals generated by typing the corresponding character on some tty
are sent to all processes that are in the foreground process group
of the session that has that tty as controlling tty. (Details below.)
If a process is being traced, every signal will stop it.
Continuing
SIGCONT: continue a stopped process.
Terminating
SIGKILL (die! now!),
SIGTERM (please, go away),
SIGHUP (modem hangup),
SIGINT (^C),
SIGQUIT (^\), etc.
Many signals have as default action to kill the target.
(Sometimes with an additional core dump, when such is
allowed by rlimit.)
The signals SIGCHLD and SIGWINCH
are ignored by default.
All except SIGKILL and SIGSTOP can be
caught or ignored or blocked.
For details, see signal(7).
Every process is member of a unique process group,
identified by its process group ID.
(When the process is created, it becomes a member of the process group
of its parent.)
By convention, the process group ID of a process group
equals the process ID of the first member of the process group,
called the process group leader.
A process finds the ID of its process group using the system call
getpgrp(), or, equivalently, getpgid(0).
One finds the process group ID of process p using
getpgid(p).
One may use the command ps j to see PPID (parent process ID),
PID (process ID), PGID (process group ID) and SID (session ID)
of processes. With a shell that does not know about job control,
like ash, each of its children will be in the same session
and have the same process group as the shell. With a shell that knows
about job control, like bash, the processes of one pipeline. like
% cat paper | ideal | pic | tbl | eqn | ditroff > out
form a single process group.
Creation
A process pid is put into the process group pgid by
setpgid(pid, pgid);
If pgid == pid or pgid == 0 then this creates
a new process group with process group leader pid.
Otherwise, this puts pid into the already existing
process group pgid.
A zero pid refers to the current process.
The call setpgrp() is equivalent to setpgid(0,0).
Restrictions on setpgid()
The calling process must be pid itself, or its parent,
and the parent can only do this before pid has done
exec(), and only when both belong to the same session.
It is an error if process pid is a session leader
(and this call would change its pgid).
This ensures that regardless of whether parent or child is scheduled
first, the process group setting is as expected by both.
Signalling and waiting
One can signal all members of a process group:
killpg(pgrp, sig);
One can wait for children in ones own process group:
waitpid(0, &status, ...);
or in a specified process group:
waitpid(-pgrp, &status, ...);
Foreground process group
Among the process groups in a session at most one can be
the foreground process group of that session.
The tty input and tty signals (signals generated by ^C, ^Z, etc.)
go to processes in this foreground process group.
A process can determine the foreground process group in its session
using tcgetpgrp(fd), where fd refers to its
controlling tty. If there is none, this returns a random value
larger than 1 that is not a process group ID.
A process can set the foreground process group in its session
using tcsetpgrp(fd,pgrp), where fd refers to its
controlling tty, and pgrp is a process group in the
its session, and this session still is associated to the controlling
tty of the calling process.
How does one get fd? By definition, /dev/tty
refers to the controlling tty, entirely independent of redirects
of standard input and output. (There is also the function
ctermid() to get the name of the controlling terminal.
On a POSIX standard system it will return /dev/tty.)
Opening the name of the
controlling tty gives a file descriptor fd.
Background process groups
All process groups in a session that are not foreground
process group are background process groups.
Since the user at the keyboard is interacting with foreground
processes, background processes should stay away from it.
When a background process reads from the terminal it gets
a SIGTTIN signal. Normally, that will stop it, the job control shell
notices and tells the user, who can say fg to continue
this background process as a foreground process, and then this
process can read from the terminal. But if the background process
ignores or blocks the SIGTTIN signal, or if its process group
is orphaned (see below), then the read() returns an EIO error,
and no signal is sent. (Indeed, the idea is to tell the process
that reading from the terminal is not allowed right now.
If it wouldn't see the signal, then it will see the error return.)
When a background process writes to the terminal, it may get
a SIGTTOU signal. May: namely, when the flag that this must happen
is set (it is off by default). One can set the flag by
% stty tostop
and clear it again by
% stty -tostop
and inspect it by
% stty -a
Again, if TOSTOP is set but the background process ignores or blocks
the SIGTTOU signal, or if its process group is orphaned (see below),
then the write() returns an EIO error, and no signal is sent.
Orphaned process groups
The process group leader is the first member of the process group.
It may terminate before the others, and then the process group is
without leader.
A process group is called orphaned when the
parent of every member is either in the process group
or outside the session.
In particular, the process group of the session leader
is always orphaned.
If termination of a process causes a process group to become
orphaned, and some member is stopped, then all are sent first SIGHUP
and then SIGCONT.
The idea is that perhaps the parent of the process group leader
is a job control shell. (In the same session but a different
process group.) As long as this parent is alive, it can
handle the stopping and starting of members in the process group.
When it dies, there may be nobody to continue stopped processes.
Therefore, these stopped processes are sent SIGHUP, so that they
die unless they catch or ignore it, and then SIGCONT to continue them.
Note that the process group of the session leader is already
orphaned, so no signals are sent when the session leader dies.
Note also that a process group can become orphaned in two ways
by termination of a process: either it was a parent and not itself
in the process group, or it was the last element of the process group
with a parent outside but in the same session.
Furthermore, that a process group can become orphaned
other than by termination of a process, namely when some
member is moved to a different process group.
Every process group is in a unique session.
(When the process is created, it becomes a member of the session
of its parent.)
By convention, the session ID of a session
equals the process ID of the first member of the session,
called the session leader.
A process finds the ID of its session using the system call
getsid().
Every session may have a controlling tty,
that then also is called the controlling tty of each of
its member processes.
A file descriptor for the controlling tty is obtained by
opening /dev/tty. (And when that fails, there was no
controlling tty.) Given a file descriptor for the controlling tty,
one may obtain the SID using tcgetsid(fd).
A session is often set up by a login process. The terminal
on which one is logged in then becomes the controlling tty
of the session. All processes that are descendants of the
login process will in general be members of the session.
Creation
A new session is created by
pid = setsid();
This is allowed only when the current process is not a process group leader.
In order to be sure of that we fork first:
p = fork();
if (p) exit(0);
pid = setsid();
The result is that the current process (with process ID pid)
becomes session leader of a new session with session ID pid.
Moreover, it becomes process group leader of a new process group.
Both session and process group contain only the single process pid.
Furthermore, this process has no controlling tty.
The restriction that the current process must not be a process group leader
is needed: otherwise its PID serves as PGID of some existing process group
and cannot be used as the PGID of a new process group.
Getting a controlling tty
How does one get a controlling terminal? Nobody knows,
this is a great mystery.
The System V approach is that the first tty opened by the process
becomes its controlling tty.
The BSD approach is that one has to explicitly call
ioctl(fd, TIOCSCTTY, ...);
to get a controlling tty.
Linux tries to be compatible with both, as always, and this
results in a very obscure complex of conditions. Roughly:
The TIOCSCTTY ioctl will give us a controlling tty,
provided that (i) the current process is a session leader,
and (ii) it does not yet have a controlling tty, and
(iii) maybe the tty should not already control some other session;
if it does it is an error if we aren't root, or we steal the tty
if we are all-powerful.
Opening some terminal will give us a controlling tty,
provided that (i) the current process is a session leader, and
(ii) it does not yet have a controlling tty, and
(iii) the tty does not already control some other session, and
(iv) the open did not have the O_NOCTTY flag, and
(v) the tty is not the foreground VT, and
(vi) the tty is not the console, and
(vii) maybe the tty should not be master or slave pty.
Getting rid of a controlling tty
If a process wants to continue as a daemon, it must detach itself
from its controlling tty. Above we saw that setsid()
will remove the controlling tty. Also the ioctl TIOCNOTTY does this.
Moreover, in order not to get a controlling tty again as soon as it
opens a tty, the process has to fork once more, to assure that it
is not a session leader. Typical code fragment:
if ((fork()) != 0)
exit(0);
setsid();
if ((fork()) != 0)
exit(0);
See also daemon(3).
Disconnect
If the terminal goes away by modem hangup, and the line was not local,
then a SIGHUP is sent to the session leader.
Any further reads from the gone terminal return EOF.
(Or possibly -1 with errno set to EIO.)
If the terminal is the slave side of a pseudotty, and the master side
is closed (for the last time), then a SIGHUP is sent to the foreground
process group of the slave side.
When the session leader dies, a SIGHUP is sent to all processes
in the foreground process group. Moreover, the terminal stops being
the controlling terminal of this session (so that it can become
the controlling terminal of another session).
Thus, if the terminal goes away and the session leader is
a job control shell, then it can handle things for its descendants,
e.g. by sending them again a SIGHUP.
If on the other hand the session leader is an innocent process
that does not catch SIGHUP, it will die, and all foreground processes
get a SIGHUP.
A process can have several threads. New threads (with the same PID
as the parent thread) are started using the clone system
call using the CLONE_THREAD flag. Threads are distinguished
by a thread ID (TID). An ordinary process has a single thread
with TID equal to PID. The system call gettid() returns the
TID. The system call tkill() sends a signal to a single thread.
Example: a process with two threads. Both only print PID and TID and exit.
(Linux 2.4.19 or later.)
No, child processes are not necessarily killed when the parent is killed. On UNIX, there is no enforced relation between parent and child
process's lifetimes. Process will only terminate when
it calls exit() or receives unhandled signal for which default action
is to terminate.
Exceptions:
1. If the child has a pipe open which it is writing to and the
parent is reading from, it will get a SIGPIPE when it next tries to
write to the pipe, for which the default action is to kill it. That is
often what happens in practice.
2. Entire "foreground process group" in a
"controlling terminal" can receive SIGINT, SIGQUIT, etc. signals when
user hits ctrl-C, ctrl-\, etc. on that terminal. Specific
behaviour is partly implemented by login shell (with help from tty
driver), details may be quite complicated: look here and here
You don't say if the tree you want to kill is a
single process group. (This is often the case if the tree is the
result of forking from a server start or a shell command line.) You can
discover process groups using GNU ps as follows:
ps x -o "%p %r %y %x %c "
If it is a process group you want to kill, just use the kill(1) command but instead of giving it a process number, give it the negation of the group number. For example to kill every process in group 5112, use kill -TERM -5112.
To kill a process tree recursively, use killtree.sh:
#!/bin/bash
killtree() {
local _pid=$1
local _sig=${2-TERM}
for _child in $(ps -o pid --no-headers --ppid ${_pid}); do
killtree ${_child} ${_sig}
done
kill -${_sig} ${_pid}
}
if [ $# -eq 0 -o $# -gt 2 ]; then
echo "Usage: $(basename $0) <pid> [signal]"
exit 1
fi
killtree $@
I started to run a bunch of buildbot slaves at work, on various platforms. I encountered some issues that I want to document here for future reference. The version of buildbot I used in all scenarios was 0.7.2. The buildbot master is running on a RHEL3 server. Note that I'm not going to talk about the general buildbot setup -- if you need guidance in configuring buildbot, read this post of mine.
Before I discuss platform-specific issues, I want to mention the issue of timeouts. If you want to run a command that takes a long time on the buildbot slave, you need to increase the default timeout (which is 1200 sec. = 20 min.) for the ShellCommand definitions in the buildmaster's master.cfg file -- otherwise, the master will mark that command as failed after the timeout expires. To modify the default timeout, simply add a keyword argument such as timeout=3600 to the ShellCommand (or derived class) instantiation in master.cfg. I have for example this line in the builders section of my master.cfg file:
where ClientSmokeTests is a class I derived from ShellCommand (if you need details on this, see again my previous post on buildbot.)
Buildbot on Windows
My setup: Windows 2003 server, Active Python 2.4.2
Issue with subprocess module: I couldn't use the subprocess module to run commands on the slave. I got errors such as these:
p = Popen(arglist, stdout=PIPE, stderr=STDOUT) File "C:\Python24\lib\subprocess.py", line 533, in __init__ (p2cread, p2cwrite, File "C:\Python24\lib\subprocess.py", line 593, in _get_handles p2cread = self._make_inheritable(p2cread) File "C:\Python24\lib\subprocess.py", line 634, in _make_inheritable DUPLICATE_SAME_ACCESS) TypeError: an integer is required
I didn't have too much time to spend troubleshooting this, so I ended up replacing calls to subprocess to calls to popen2.popen3(). This solved the problem.
Also, I'm not currently running the buildbot process as a Windows service, although it's on my TODO list. I wrote a simple .bat file which I called startbot.bat:
buildbot start C:\qa\pylts\buildbot\QA
To start buildbot, I launched startbot.bat from the command prompt and I left it running.
Note that on Windows, the buildbot script gets installed in C:\Python24\scripts, and there is also a buildbot.bat batch file in the same scripts directory, which calls the buildbot script.
Issue with buildbot.bat: it contains a hardcoded path to Python23. I had to change that to Python24 so that it correctly finds the buildbot script in C:\Python24\scripts.
Buildbot on Solaris
My setup: one Solaris 9 SPARC server, one Solaris 10 SPARC server, both running Python 2.3.3
Issue with ZopeInterface on Solaris 10: when I tried to install ZopeInterface via 'easy_install http://www.zope.org/Products/ZopeInterface/3.1.0c1/ZopeInterface-3.1.0c1.tgz', a compilation step failed with:
/usr/include/sys/wait.h:86: error: parse error before "siginfo_t"
A google search revealed that this was a gcc-related issue specific to Solaris 10. Based on this post, I ran:
# cd /usr/local/lib/gcc-lib/sparc-sun-solaris2.10/3.3.2/install-tools # ./mkheaders
After these steps, I was able to install ZopeInterface and the rest of the packages required by buildbot.
For reference, here is what I have on the Solaris 10 box in terms of gcc packages: # pkginfo | grep -i gcc system SFWgcc2 gcc-2 - GNU Compiler Collection system SFWgcc2l gcc-2 - GNU Compiler Collection Runtime Libraries system SFWgcc34 gcc-3.4.2 - GNU Compiler Collection system SFWgcc34l gcc-3.4.2 - GNU Compiler Collection Runtime Libraries application SMCgcc gcc system SUNWgcc gcc - The GNU C compiler system SUNWgccruntime GCC Runtime libraries
Here is what uname -a returns:
# uname -a SunOS sunv2403 5.10 Generic sun4u sparc SUNW,Sun-Fire-V240
Issue with exit codes from child processes not intercepted correctly: on both Solaris 9 and Solaris 10, buildbot didn't seem to intercept correctly the exit code from the scripts which were running on the build slaves. I was able to check that I had the correct exit codes by running the scripts at the command line, but within buildbot the scripts just hung as if they hadn't finish.
Some searches on the buildbot-devel mailing list later, I found the solution via this post: I replaced usepty = 1 with usepty = 0 in buildbot.tac on the Solaris slaves, then I restarted the buildbot process on the slaves, and everything was fine.
Buildbot on AIX
My setup: AIX 5.2 on an IBM P510 server, Python 2.4.1
To put kdm into effect, I put create and DISPLAYMANAGER into file /etc/sysconfig/desktop, and also set the default session manager by setting DESKTOP, as follows:
david% foo() { for i in $@; do echo $i; done }; foo bar "baz quux" bar baz quux david% foo() { for i in "$@"; do echo $i; done }; foo bar "baz quux" bar baz quux
if [ ! -e $lockfile ]; then trap "rm -f $lockfile; exit" INT TERM EXIT touch $lockfile critical-section rm $lockfile trap - INT TERM EXIT else echo "critical-section is already running" fi
if ( set -o noclobber; echo "$$" > "$lockfile") 2> /dev/null; then trap 'rm -f "$lockfile"; exit $?' INT TERM EXIT critical-section rm -f "$lockfile" trap - INT TERM EXIT else echo "Failed to acquire lockfile: $lockfile." echo "Held by $(cat $lockfile)" fi
rollback() { del_from_passwd $user if [ -e /home/$user ]; then rm -rf /home/$user fi exit } trap rollback INT TERM EXIT add_to_passwd $user cp -a /etc/skel /home/$user chown $user /home/$user -R trap - INT TERM EXIT
The "align" attribute has been deprecated, however, in favor of CSS (Cascading Style Sheets), and this is a good thing. However, it's not so obvious how to center a table using CSS.
The obvious way might appear to use the CSS "text-align: center;" somewhere, maybe like one of these:
OR
...
OR, if you get really desperate,
...
None of these will work. The table itself will be left-aligned, but all the content in the table cells will be centered.
Why? Because "text-align" applies to inline content, not to a block-level element like "table".
Method 1
To center a table, you need to set the margins, like this: