UNIX Processes, Signals
UNIX Processes, Signals
Process creation
o Unless the system is being bootstrapped a process can only come into
existence as the child of another process. This done by the fork
system call.
o The first process created is "hand tooled" by the boot process. This is
the swapper process.
o The swapper process creates the init process, which is the ancestor
of all further processes. In particular, init forks off a process getty,
which monitors terminal lines and allows users to log in.
o Upon login, the command shell is run as the first process. The
command shell for a given user is specified in the /etc/passwd file.
From thereon, any process may fork to produce new processes,
considered to be children of the forking process.
The process table and uarea
o Information about processes is described in two data structures, the
kernel process table and a "uarea" associated with each process.
o The process table holds information required by the kernel
o The uarea holds information required by the process itself.
o The process table entry for a process holds (amongst other things):
Process state
Several process IDs
Several user IDs for determining process priviledges
Pointer to text structure for shared text areas
Pointer to page table for memory management
Scheduling parameters, including the "nice" value which
determines priority
Timers for resource usage
A pointer to the process uarea
o The uarea of a process contains (amongst other things):
Real and effective user IDs
Current working directory
Timer fields to hold accumulated user and kernel mode time
Information an how to react to signals
Identification of any associated control terminal
Identification of data areas relevant to IO activity
Return values and error conditions from system calls
Information on the file system environment of the process
The user file descriptor table
Process IDs
o There are three IDs associated with every process, the ID of the
process itself (the PID), its parent process's ID (the PPID) and its
process group ID (the PGID).
o Every UNIX process has a unique PID in the range 0 to 30000.
o The PID 0 is reserved for the swapper process, and 1 for
the init process.
o A process can get hold of its PID and PPID with
the getpid and getppid calls.
int getpid(void)
int getppid(void)
o Process group IDs
UNIX provides a PGID that is used to group processes
together.
The PGID of a process may be obtained with
int getpgrp(void)
A process initially belongs to its parent's group, but new
groups may be established with
int setpgid(pid_t pid, pid_t pgid)
Sets the process group ID of the process with ID pid to pgid. If
pid is equal to 0, the process ID of the calling process is used.
Groups may have a control terminal which is the first tty
device opened by the group leader. Initially the group leader is
the shell, and the login device is the control terminal. A new
group that is started is not attached to the previous control
terminal (if any). Interupt, quit and hangup signals from a
terminal go to all process associated with that terminal. For
example all processes that start from one given terminal belong
to the same group. Then if the terminal goes down it's possible
to find all its processes.
o ProcessIDs.c - Program to play with PIDs
User IDs
o Every process has a real user ID (the UID), an effective user ID (the
EUID), a real user group ID (the GID), and an effective user group ID
(the EGID). The user group ID is distinct from the process group ID.
o The real IDs are used for accounting and user-user communication,
while the effective IDs are used to control access to files and control
signal sending.
o A user ID is associated with each user login name in the password
file /etc/passwd. This is assigned to the user's shell as its UID and
EUID, and is inherited by processes spawned from the shell.
o The user IDs can be accessed with the system calls:
int getuid(void)
which gets the UID of the process, and
int geteuid(void)
which gets the EUID of the process.
o Each entry in /etc/passwd also has a group ID which is given to the
shell as its initial GID and EGID.
o The group IDs can be accessed with the system calls: int
getgid(void)
which gets the GID of the process and
int getegid()
which gets the EGID of the process.
o Only the superuser may change the user IDs of a process, using the
system calls:
int setuid(int uid)
which sets the UID and EUID, and
int setgid(int gid)
which sets the GID and EGID. Both calls return 0 on success, or -1
on error.
o Ordinary users may change effective IDs to real IDs, by using these
calls with the arguments equal to the real IDs.
o UserIDs.c - Program to play with user IDs
The environment of a process
o The environment of a process includes details of the home directory,
path names to be searched for the program, the type of terminal being
used, the user name, the shell being run, etc.
o The environment of a program is held in strings which have the
format <keyword>=<value>, e.g.
HOME=/usr/spg/geoff
TERM=/tvi950
PATH=/usr/bin:/usr/spg/geoff/bin
o The environment of a program can be examined by looking at the
system external variable environ, which is an array of char pointers:
extern char *environ[]; A NULL pointer signifies the last such string.
o The program may explicitly receive the environment strings as a third
parameter to main. This parameter is an array of char pointers.
int main(int argc,char *argv[],char *env[])
o The value of a environment keyword can be examined with
the getenv call:
char *getenv(char keyword[]); getenv returns a pointer to the string
value, or NULL if no value is specified.
o The value of a environment keyword can be set with the setenv call:
int setenv(char *name,char *value,int overwrite);
o An environmeent variable can be deleted using the unsetenv call:
unsetenv(char *name);
o Environment.c - Program to examine environment strings
The fork system call
o int fork(void)
o The fork call creates an (almost exact) copy of the parent. In
particular they share all open files.
o The fork call returns the new PID to the parent and 0 to the child, or -
ve to the parent if it fails.
o The PID of the new process is different, the open file descriptors of
the parent process are copied, and accumulated execution times are
reset.
o Fork.c - Program that forks
Execution of programs
o The fork call merely duplicates the parent process. To switch to a
different program a process uses one of the exec system calls.
o The exec calls replace the code and data areas of the current program
and jumps to the start of the new program.
o Open files, current directory, inter-process relationships are not
affected by the exec calls.
o void execl(char *program_path,char *arg0,...,char *argn,NULL);
The program_path is the name of the file to execute.
The program_path must be absolute, i.e. start with /.
argi are the arguments to the program, which in C are
accessible through the parameters to main.
Conventionally arg0 is the name of the program.
A NULL argument ends the list.
o Exec.c - Programs that forks and execs
o void execv(char *program_path,char *arguments[]);
Here the arguments are stored as an array of pointers, in the same way
as they are received by main. The last pointer must be NULL.
o The execl and execv calls pass on the current environment to new
programs.
o The environment of a new program can be changed by using
the environment argument to execle and execve.
o The path can be search using execvp or execlp.
o void execle(char *program_path,char *arg0,...,char *argn,NULL,
char *environment[]);
The environment is an array of pointers to strings that describe the
environment to be passed to the new program.
o void execve(char *program_path,char *arguments[],char
*environment[]);
This is the true exec, the others end up calling this one.
o ExecWithArgs.c - Program that forks a child with arguments
Process termination
o void exit(int status)
A process may terminate by explicit use of the exit system
call.
This call is used implicitly when a process terminates
normally.
o A process may terminate when another process sends an appropriate
signal (see later).
o The parent process is notified by sending it a SIGCHLD signal.
o If the parent terminates before any child, then the PPID of the child
process is set to 1, i.e. the init process adopts all orphan processes.
o Terminated processes become zombie processes, which are removed
by the parent process.
o If the parent does not remove zombies, they hang around.
o The wait system call allows parents to wait for their children to
terminate.
int wait(int *status_pointer);
wait suspends the process until a child process terminates and
sends a SIGCHLD signal.
wait returns the id of that process, or -1 immediately if there
are no children.
Exit status information (see below) is returned to the parent
in *status_pointer.
To run a process in background (& in C and Bourne shells) the
parent does not execute wait.
o Wait.c - Program that forks, execs, and waits
o int waitpid(pid_t pid, int *status_pointer, int options);
Can wait for a specified pid. If pid is -1 then waits for any
process.
Various options, including
WNOHANG - Does not suspend execution of the calling
process if status is not immediately available for one of
the child processes specified by pid.
WUNTRACED - The status of any child processes specified
by pid that are stopped, and whose status has not yet
been reported since they stopped, is also reported to the
calling process.
o The reason for the wait to have returned can be detected from
the *status_pointer using WIFEXITED, WIFSIGNALED, and WIFSTOPPED.
If WIFEXITED, the lower 8 bits of the exit parameter in the child
can be obtained from the *status_pointer using WEXITSTATUS.
If WIFSIGNALED, the signal that caused the termination can be
obtained from the *status_pointer using WIFSIGNALED.
If WIFSTOPPED, the signal that caused the stop can be obtained
from the *status_pointer using WSTOPSIG.
o ExecWithArgsAndWait.c - Program that forks a child with arguments
and waits
Signals
o A signal interrupts a process.
o Signals are defined in signal.h. Basic ones are:
SIGHUP: #1 - hangup (modem)
SIGINT: #2 - interrupt (rubout key)
SIGQUIT: #3 - quit (FS control \ key)
SIGILL: #4 - illegal instruction
SIGTRAP: #5 - trace or breakpoint
SIGABRT: #6 - abort()
SIGEMT: #7 - emulator trap instruction
SIGFPE: #8 - floating point exception
SIGKILL: #9 - kill, uncatchable quit
SIGBUS: #10 - bus error
SIGSEGV: #11 - segmentation violation
SIGSYS: #12 - bad system call
SIGPIPE: #13 - end of pipe
SIGALRM: #14 - alarm clock
SIGTERM: #15 - catchable termination
SIGURG: #16 - urgent condition on IO channel
SIGSTOP: #17 - sendable stop signal not from tty
SIGTSTP: #18 - stop signal from tty
SIGCONT: #19 - continue a stopped process
SIGCHLD: #20 - child death (or stopped under Linux)
SIGTTIN: #21 - to readers pgrp upon background tty read
SIGTTOU: #22 - like TTIN for output
SIGIO: #23 - input/output possible signal
SIGXCPU: #24 - exceeded CPU time limit
SIGXFSZ: #25 - exceeded file size limit
SIGVTALRM: #26 - virtual time alarm
SIGPROF: #27 - profiling time alarm
SIGWINCH: #28 - window size changes
SIGINFO: #29 - information request
SIGUSR1: #30 - user defined signal 1
SIGUSR2: #31 - user defined signal 2
o Signals have 6 basic sources
User typing control characters at the keyboard. These signals
are sent to all processes in the group associated with the
terminal - SIGHUP, SIGINT, SIGQUIT.
Software errors
- SIGILL, SIGTRAP, SIGABRT, SIGFPE, SIGSYS, SIGPIPE.
Hardware errors - SIGEMT, SIGBUS, SIGSEGV.
The alarm system call - SIGALRM
The kill system call (see below) - All signals
External events
- SIGCHLD, SIGURG, SIGIO, SIGXCPU, SIGXFSZ, SIGWINCH, SIGINFO.
o Signals either caught, ignored or terminate the process.
o The signal system call The signal system call can be used to instruct
receiving processes on how to deal with a signal. This associates a
function with a given signal.
void (*signal(int TheSignal,void (*Function)(int)))(int);
This is a function that returns a pointer to a function that
returns an int. The second argument is the same.
signal associates TheSignal with the Function.
Function may be supplied by the user or one of SIG_DFL, which
resets to the default, and SIG_IGN, which ignores the signal.
When the signal occurs execution is transferred to the function.
When a function set up to handle interrupts ends it returns to
the point where the interruption occured.
External variables have to be used to pass data into and out of
signal handlers.
signal returns the previous function.
The pause system call
The pause system call
void pause(void);
causes the calling process to suspend until a non-ignored
signal is received.
Signal.c - Program that catches keyboard interrupts
The SIGKILL and SIGSTOP signals cannot be caught or ignored
Child processes inherit signal handling
o The kill system call
int kill(int PID,int Signal);
kill sendsthe Signal to all processes identified by the PID,
where the values are:
+ve - process with the ID
0 - processes with group ID equal to process ID of
sender.
-1 - if the EUID is super user then all processes,
otherwise processes whose UID equals the EUID of
sender.
< -1 - processes whose GID is the absolute of the PID
Returns -1 on error, 0 on success
Kill.c - Program that kills its parent
o The alarm system call
unsigned alarm(unsigned Time)
Causes a signal SIGALRM to occur Time seconds later in the
calling process.
It returns the time left until that signal.
alarm(0) turns off the alarm
Alarm.c - Program that wakes itself up
KillAlarm.c - Program that uses alarm and kill
o With the exception of the SIGCHLD signal, signals are not queued.
SIGCHLD signals are generated by child processes when they
terminate, and when they are stopped by a SIGTSTP or SIGSTOP
ExecManyWithArgs.c - Program that forks many child with
arguments, and reaps
o POSIX signal handling
The signal system call is the traditional way of handling
signals. POSIX has defined a richer signal handling interface.
int sigaction(int signum,struct sigaction *act,struct
sigaction *oldact); - used to install a signal handler
signum specified which signal to handle
*act specifies what to do
*oldact, if not NULL, saves the old action
A struct sigaction contains:
void (*sa_handler)(int) - the signal handler
sigset_t sa_mask - mask of signals which should be
blocked during execution of the signal handler. In
addition, the signal which triggered the handler will be
blocked, unless the SA_NODEFER or SA_NOMASK
flags are used.
int sa_flags - flags which modify the behaviour of the
signal handling process. Useful flags
include SA_NOCLDSTOP, SA_ONESHOT, and SA_NOMASK
int sigprocmask(int how,sigset_t *set,sigset_t
*oldset); - Used to change the list of currently blocked signals
int siginterrupt(int sig,int flag); - Changes the restart
behaviour when a system call is interrupted by the signal
InterruptSystem.c - Program that times out read attempts
Process priority
o The priority of a process is an integer in the range -20 to 20, with -20
being the highest priority.
o Processes start with a priority of 0.
o getpriority(int which, int who); - Gets the priority of a process (or
user, or group)
which is PRIO_PROCESS for process priorities who is the PID, or 0 for the
current process (user, group)
o setpriority(int which, int who, int prio); - Sets the priority of a
process (or user, or group)
prio is the new priority value.
o Priority.c - Program that changes its priority
Process resource usage
o Need to include sys/time.h, sys/resource.h, and unistd.h.
o int getrlimit(int resource, struct rlimit *rlim); - Get the
processes resource limits. resource is one of:
RLIMIT_CPU - CPU time in seconds
RLIMIT_FSIZE - Maximum filesize
RLIMIT_DATA - max data size
RLIMIT_STACK - max stack size
RLIMIT_CORE - max core file size
RLIMIT_RSS - max resident set size
RLIMIT_NPROC - max number of processes
RLIMIT_NOFILE - max number of open files
RLIMIT_MEMLOCK - max locked-in-memory address space
RLIMIT_AS - address space (virtual memory) limit
Exercises
Process management: Write a program that reads a file of commands and
executes them all in background mode. This batch process starter must wait
for all the programs to finish before it exits. A sample file that may be
presented to your program could contain:
date
ls -l
cat fred.c
Notes: