0% found this document useful (0 votes)
7 views26 pages

Understanding UNIX Process Management

The document provides an overview of processes in UNIX systems, detailing how processes are created, managed, and controlled. It explains the fork and exec functions used for process creation and execution, along with their attributes and differences between parent and child processes. Additionally, it covers process control mechanisms such as wait and waitpid functions for managing child process termination and retrieving their exit statuses.

Uploaded by

gitty690
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views26 pages

Understanding UNIX Process Management

The document provides an overview of processes in UNIX systems, detailing how processes are created, managed, and controlled. It explains the fork and exec functions used for process creation and execution, along with their attributes and differences between parent and child processes. Additionally, it covers process control mechanisms such as wait and waitpid functions for managing child process termination and retrieving their exit statuses.

Uploaded by

gitty690
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

UNIT-II

PROCES
INTRODUCTION
A Process is a program under execution in a UNIX or POSIX system.
UNIX shell is a process that is created when a user logs on to a system.
When user enters a command shell creates a new process. This is a child process.
The advantages of allowing any process to create new processes in its course of execution are:
1. Any user can create multitasking applications.
2. Because a child process executes in its own virtual address space, its success or failure in execution will not affect its
parent.
3. It is very common for a process to create a child process that will execute a new program. This allows users to write
programs that can call on any other program to extend their functionality without the need to incorporate any new
source code.

UNIX KERNEL SUPPORT FOR


PROCESS
The data structure and execution of processes are dependent on operating system implementation.
A UNIX process consists minimally of a text segment, a data segment and a stack segment. A segment is an area of
memory that is managed by the system as a unit.
 A text segment consists of the program text in machine executable instruction code format.
 The data segment contains static and global variables and their corresponding data.
 A stack segment contains runtime variables and the return addresses of all active functions for a process. UNIX
kernel has a process table that keeps track of all active process present in the system. Some of these processes
belongs to the kernel and are called as “system process”. Every entry in the process table contains pointers to the
text, data and the stack segments and also to U-area of a process. U-area of a process is an extension of the process table
entry and contains other process specific data such as the file descriptor table, current root and working directory inode
numbers and set of system imposed process limits.
All processes in UNIX system expect the process that is created by the system boot code, are created by the fork system
call. After the fork system call, once the child process is created, both the parent and child processes resumes execution.
When a process is created by fork, it contains duplicated copies of the text, data and stack segments of its parent as shown
in the Figure below. Also it has a file descriptor table, which contains reference to the same opened files as the parent,
such that they both share the same file pointer to each opened files.

Figure: Parent & child relationship after fork


The process will be assigned with attributes, which are either inherited from its parent or will be set by the kernel.
 A real user identification number (rUID): the user ID of a user who created the parent process.
 A real group identification number (rGID): the group ID of a user who created that parent process.
 An effective user identification number (eUID): this allows the process to access and create files with the
same privileges as the program file owner.
 An effective group identification number (eGID): this allows the process to access and create files with the
same privileges as the group to which the program file belongs.
 Saved set-UID and saved set-GID: these are the assigned eUID and eGID of the process respectively.
 Process group identification number (PGID) and session identification number (SID): these identify
the process group and session of which the process is member.
 Supplementary group identification numbers: this is a set of additional group IDs for a user who created the
process.
 Current directory: this is the reference (inode number) to a working directory file.
 Root directory: this is the reference to a root directory.
 Signal handling: the signal handling settings.
 Signal mask: a signal mask that specifies which signals are to be blocked.
 Unmask: a file mode mask that is used in creation of files to specify which accession rights should be taken
out.
 Nice value: the process scheduling priority value.
 Controlling terminal: the controlling terminal of the process.
In addition to the above attributes, the following attributes are different between the parent and child processes:
 Process identification number (PID): an integer identification number that is unique per process in an entire
operating system.
 Parent process identification number (PPID): the parent process PID.
 Pending signals: the set of signals that are pending delivery to the parent process.
 Alarm clock time: the process alarm clock time is reset to zero in the child process.
 File locks: the set of file locks owned by the parent process is not inherited by the chid process.
fork and exec are commonly used together to spawn a sub-process to execute a different program. The advantages of
this method are:
 A process can create multiple processes to execute multiple programs concurrently.
 Because each child process executes in its own virtual address space, the parent process is not affected by
the execution status of its child process.

PROCESS CONTROL
INTRODUCTION
Process control is concerned about creation of new processes, program execution, and process termination.

PROCESS Attributes
#include <unistd.h>
#include<sys/types.h>

pid_t getpid(void);
Returns: process ID of calling process
pid_t getppid(void);
Returns: parent process ID of calling process

pid_t getpgrp(void);
Returns: group process ID of calling process

uid_t getuid(void);
Returns: real user ID of calling process
uid_t geteuid(void);
Returns: effective user ID of calling process
gid_t getgid(void);
Returns: real group ID of calling process
gid_t getegid(void);
Returns: effective group ID of calling process

✓ Getpgrp : When a user logs onto a system, the login process becomes a session leader and process
group leader. The session ID and the process group ID of a session leader is the same as its process
ID.
o If the login process creates new child processes to execute jobs, these child processes will be
in the same session and process group as the login process.
fork FUNCTION
An existing process can create a new one by calling the forkfunction.
#include <unistd.h>
pid_t fork(void);
Returns: 0 in child, process ID of child in parent, 1 on error.
 The new process created by fork is called the child process.
 This function is called once but returns twice.
 The only difference in the returns is that the return value in the child is 0, whereas the return value in the
parent is the process ID of the new child.
 The reason the child's process ID is returned to the parent is that a process can have more than one child,
and there is no function that allows a process to obtain the process IDs of its children.
 The reason fork returns 0 to the child is that a process can have only a single parent, and the child can
always call getppidto obtain the process ID of its parent. (Process ID 0 is reserved for use by the kernel, so it's
not possible for 0 to be the process ID of a child.)
 Both the child and the parent continue executing with the instruction that follows the call to fork.
 The child is a copy of the parent.
 For example, the child gets a copy of the parent's data space, heap, and stack.
 Note that this is a copy for the child; the parent and the child do not share these portions of memory.
 The parent and the child share the text segment .

Example programs:
Program 1
/* Program to demonstrate fork function Program name – fork1.c */
#include<sys/types.h>
#include<unistd.h>
int main( )
{
fork( );
printf(“\n hello USP”);
}

Output :
$ cc fork1.c
$ ./[Link] hello
USP hello
USP
Note : The statement hello USP is executed twice as both the child and parent have executed that instruction.

Program
2
/* Program name – fork2.c */
#include<sys/types.h>
#include<unistd.h>
int main( )
{
printf(“\n 6 sem “);
fork( );
printf(“\n hello USP”);
}

Output :
$ cc fork1.c
$ ./[Link]
6 sem hello
USP hello
USP
Note: The statement 6 sem is executed only once by the parent because it is called before fork and statement hello
USP is executed twice by child and parent.

There are numerous other properties of the parent that are inherited by the child:
o Real user ID, real group ID, effective user ID, effective group ID
o Supplementary group IDs
o Process group ID
o Session ID
o Controlling terminal
o The set-user-ID and set-group-ID flags
o Current working directory
o Root directory
o File mode creation mask
o Signal mask and dispositions
o The close-on-exec flag for any open file descriptors
o Environment
o Attached shared memory segments
o Memory mappings
o Resource limits

The differences between the parent and child are


The return value from fork
The process IDs are different
The two processes have different parent process IDs: the parent process ID of the child is the parent; the
parent process ID of the parent doesn't change
The child's tms_utime, tms_stime, tms_cutime, and tms_cstime values are set to 0
File locks set by the parent are not inherited by the child
Pending alarms are cleared for the child
The set of pending signals for the child is set to the empty set

The two main reasons for forkto fail are


(a)ENOMEM if too many processes are already in the system, which usually means that insufficient
memory to create the new process
(b)EAGAIN if the total number of processes for this real user ID exceeds the system's limit.

CHILD_MAX Maximum no. of processes that can be created by a single user. ,<sys/param.h>
MAXPID Maximum no. of processes that can exist concurrently system-wide <limits.h>

Both the child and parent process will be scheduled by the UNIX kernel to run independently and order of execution is
implementation dependent.

There are two uses for fork:


 When a process wants to duplicate itself so that the parent and child can each execute different sections of code
at the same time. This is common for network servers, the parent waits for a service request from a client. When
the request arrives, the parent calls forkand lets the child handle the request. The parent goes back to waiting
for the next service request to arrive.
 When a process wants to execute a different program. This is common for shells. In this case, the child does
an execright after it returns from the fork.

vfork FUNCTION
The function vforkhas the same calling sequence and same return values as fork.
 The vforkfunction is intended to create a new process when the purpose of the new process is to execa new
program.
The vfork function creates the new process, just like fork, without copying the address space of the parent
into the child, as the child won't reference that address space; the child simply calls exec(or exit) right after the
vfork.
Instead, while the child is running and until it calls either exec or exit, the child runs in the address space of the
parent. This optimization provides an efficiency gain on some paged virtual-memory implementations
of the UNIX System.
Another difference between the two functions is that vforkguarantees that the child runs first, until the child
calls execor exit. When the child calls either of these functions, the parent resumes.
✓ Vfork is unsafe to use because if the child process modifies any data of the parent before it calls exec or _exit
those changes will remain when the parent process resumes execution.
✓ Vfork should be used in porting old applications to the new UNIX systems only.
✓ Copy on write : the latest UNIX systems have improved on the efficiency of fork by allowing parent and child
processes to share common virtual address space until the child calls either the exec or _exit function.
If parent or child modifies any data the kernel creates new memory pages that cover virtual address space
modified. The process that has made change s will reference new memory pages and other process will refer
old memory pages. This process is called copy on write.

wait AND waitpid FUNCTIONS

These are used by parent process to wait for its child process to terminate and to retrieve the child exit status.

These calls will deallocate the process table slot of the child process, so that slot can be reused by a new process.

When a process terminates, either normally or abnormally, the kernel notifies the parent by sending the SIGCHLD signal
to the parent. Because the termination of a child is an asynchronous event - it can happen at any time while the parent
is running - this signal is the asynchronous notification from the kernel to the parent. The parent can choose to ignore
this signal, or it can provide a function that is called when the signal occurs: a signal handler.

A process that calls waitor waitpidcan:


 Block, if all of its children are still running
 Return immediately with the termination status of a child, if a child has terminated and is waiting for its
termination status to be fetched
 Return immediately with an error, if it doesn't have any child processes.
#include <sys/wait.h>

pid_t wait(int *statloc);


pid_t waitpid(pid_t pid, int *statloc, int options);

Both return: process ID if OK, 0 (see later), or -1 on error.


The differences between these two functions are as follows.
The waitfunction can block the caller until a child process terminates, where as waitpidhas an option that
prevents it from blocking.
The waitpidfunction doesn't wait for the child that terminates first; it has a number of options that control which
process it waits for.

If a child has already terminated and is a zombie, waitreturns immediately with that child's status. Otherwise, it blocks
the caller until a child terminates.

If the caller blocks and has multiple children, wait returns when one terminates.

For both functions, the argument statloc(status_p) is a pointer to an integer. If this argument is not a null
pointer, the termination status of the terminated process is stored in the location pointed to by the argument.

If this argument is a null no child exit status to be queried.

The interpretation of the pid argument for waitpid depends on its value:
pid == 1 Waits for any child process. In this respect, waitpid is equivalent to wait.
pid > 0 Waits for the child whose process ID equals pid.
pid == 0 Waits for any child whose process group ID equals that of the calling process.
pid < 1 Waits for any child whose process group ID equals the absolute value of pid.

Parent can check the exit status code with the following macros as defined in <sys/wait.h>

WIFEXITED(*STATUS_P) Returns a nonzero value if child was terminated via an


_exit call and zero otherwise

WEXITSTATUS(*STATUS_P) Returns a child exit status code that was assigned to


_exit call. This should be called only if WIFEXITED
returns a nonzero value.

WIFSIGNALED(*STATUS_P) Returns a nonzero value if child was terminated due to


signal interruption

WTERMSIG(*STATUS_P) Returns a signal number that had terminated a child


process. This should be called only if WIFSIGNALED
returns a nonzero value

WIFSTOPPED(*STATUS_P) Returns a nonzero value if child has stopped due to job


control.

WSTOPSIG(*STATUS_P) Returns a signal number that has stopped child


process. This should be called only if WIFSTOPPED
returns a nonzero value
RERURN VALUE :

Positive integer : child PID


-1 : no child satisfied the wait criteria or the function was interrupted by a caught signal.

Errno has following values :

Wait or waitpid returns because the system call was interrupted


EINTR by signal

Wait : calling process has no unwaited for child process


ECHILD Waitpid : either the child_pid value is illegal or process can’t be
in a state as defined by the options value.

EFAULT Status_p points to an illegal address

Option value is illegal


EINVAL

The waitpidfunction provides three features that aren't provided by the waitfunction.
 The waitpidfunction lets us wait for one particular process, whereas the waitfunction returns the status of any
terminated child.
 The waitpid function provides a nonblocking version of wait. There are times when we want to fetch a
child's status, but we don't want to block.
 The waitpidfunction provides support for job control with the WUNTRACEDand WCONTINUEDoptions.

execFUNCTIONS
When a process calls one of the execfunctions, that process is completely replaced by the new program, and the new
program starts executing at its mainfunction. The process ID does not change across an exec, because a new process is
not created; exec merely replaces the current process - its text, data, heap, and stack segments - with a brand new
program from disk.
There are 6 exec functions:
#include <unistd.h>
int execl(const char *pathname, const char *arg0,... /* (char *)0 */ );
int execv(const char *pathname, char *const argv []);
int execle(const char *pathname, const char *arg0,... /*(char *)0, char
*const envp */ );
int execve(const char *pathname, char *const argv[], char *const envp[]);
int execlp(const char *filename, const char *arg0, ... /* (char *)0 */ );
int execvp(const char *filename, char *const argv []);
All six return: -1 on error, no return on success.
 The first difference in these functions is that the first four take a pathname argument, whereas the last two take
a filename argument. When a filename argument is specified
 If filename contains a slash, it is taken as a pathname.
 Otherwise, the executable file is searched for in the directories specified by the PATH environment
variable.
 The next difference concerns the passing of the argument list ( lstands for list and vstands for vector). The
functions execl, execlp, and execlerequire each of the command-line arguments to the new program to be specified
as separate arguments. For the other three functions (execv, execvp, and execve), we have to build an array of
pointers to the arguments, and the address of this array is the argument to these three functions.
 The final difference is the passing of the environment list to the new program. The two functions whose
names end in an e (execle and execve) allow us to pass a pointer to an array of pointers to the
environment strings. The other four functions, however, use the environvariable in the calling process to copy
the existing environment for the new program.

Function pathname filename Arg list argv[] environ envp[]


execl • • •
execlp • • •
execle • • •
execv • • •
execvp • • •
execve • • •
(letter in name) p l v e
The above table shows the differences among the 6 exec functions.
We've mentioned that the process ID does not change after an exec, but the new program inherits additional
properties from the calling process:
o Process ID and parent process ID
o Real user ID and real group ID
o Supplementary group IDs
o Process group ID
o Session ID
o Controlling terminal
o Time left until alarm clock o
Current working directory o
Root directory
o File mode creation mask
o File locks
o Process signal mask
o Pending signals
o Resource limits
o Values for tms_utime, tms_stime, tms_cutime, and tms_cstime.
1. The three functions in the top row specify each argument string as a separate argument to the exec function, with a null pointer
terminating the variable number of arguments. The three functions in the second row have an argv array, containing pointers
to the argument strings. This argv array must contain a null pointer to specify its end, since a count is not specified.
2. The two functions in the left column specify a filename argument. This is converted into a pathname using the current PATH
environment variable. If the filename argument to execlp or execvp contains a slash (/) anywhere in the string, the PATH
variable is not used. The four functions in the right two columns specify a fully qualified pathname argument.
3. The four functions in the left two columns do not specify an explicit environment pointer. Instead, the current value of the
external variable environ is used for building an environment list that is passed to the new program. The two functions in the
right column specify an explicit environment list. The envp array of pointers must be terminated by a null pointer.

PROCESS TERMINATION
There are eight ways for a process to terminate.
Normal termination occurs in five ways:
 Return from main
 Calling exit
 Calling _exitor _Exit
 Return of the last thread from its start routine
 Calling pthread_exit from the last thread
Abnormal termination occurs in three ways:
Calling abort

Receipt of a signal

Response of the last thread to a cancellation request


exit
FUNCTIONS
A process can terminate normally in five ways:
Executing a return from the main function.
Calling the exit function.
Calling the _exit or _Exit function.
In most UNIX system implementations, exit(3) is a function in the standard C library, whereas _exi t(2) is a system call.
 Executing a return from the start routine of the last thread in the process. When the last thread returns from its
start routine, the process exits with a termination status of 0.
 Calling the pthread_exit function from the last thread in the process.

The three forms of abnormal termination are as follows:


 Calling abort. This is a special case of the next item, as it generates the SIGABRT signal.
When the process receives certain signals. Examples of signals generated by the kernel include the process
referencing a memory location not within its address space or trying to divide by 0.
The last thread responds to a cancellation request. By default, cancellation occurs in a deferred manner: one
thread requests that another be canceled, and sometime later, the target thread terminates.

Exit Functions
Three functions terminate a program normally: _exit and _Exit, which return to the kernel immediately, and
exit, which performs certain cleanup processing and then returns to the kernel.
#include <stdlib.h> void exit(int
status); void _Exit(int status);

#include <unistd.h>
void _exit(int status);

All three exit functions expect a single integer argument, called the exit status. Returning an integer value from the
main function is equivalent to calling exit with the same value. Thus
exit(0);
is the same as
return(0);
from the main function.

In the following situations the exit status of the process is undefined.


 any of these functions is called without an exit status.
 main does a return without a return value
 main “falls off the end”, i.e if the exit status of the process is undefined.
Example:
$ cc hello.c
$ ./[Link] hello,
world
$ echo $? // print the exit status
13

atexitFunction
With ISO C, a process can register up to 32 functions that are automatically called by exit. These are called exit
handlers and are registered by calling the atexitfunction.
#include <stdlib.h>
int atexit(void (*func)(void));
Returns: 0 if OK, nonzero on error
This declaration says that we pass the address of a function as the argument to atexit. When this function is called, it is
not passed any arguments and is not expected to return a value. The exitfunction calls these functions in reverse
order of their registration. Each function is called as many times as it was registered.
Example of exit handlers
#include "apue.h"

static void my_exit1(void);


static void my_exit2(void);

int main(void)
{
if (atexit(my_exit2) != 0)
err_sys("can't register my_exit2");

if (atexit(my_exit1) != 0)
err_sys("can't register my_exit1");

if (atexit(my_exit1) != 0)
err_sys("can't register my_exit1");

printf("main is done\n");
return(0);
}

static void
my_exit1(void)
{
printf("first exit handler\n");
}

static void
my_exit2(void)
{
printf("second exit handler\n");
}
Output:
$ ./[Link]
main is done
first exit handler first exit
handler
second exit handler

The below figure summarizes how a C program is started and the various ways it can terminate.

❖ system Function:
▪ It is convenient to execute a command string from within a program.
#include <stdlib.h>
int system(const char *cmdstring);
▪ If cmdstring is a null pointer, system returns nonzero only if a command processor is available.
This feature determines whether the system function is supported on a given operating system.
Under the UNIX System, system is always available.
▪ The system is implemented by calling fork, exec, and waitpid, there are three types of return
values.
▪ If either the fork fails or waitpid returns an error ,system returns 1 with errno set to indicate the
error.
▪ If the exec fails, implying that the shell can't be executed, the return value is as if the shell had
executed exit(127).
▪ Otherwise, all three functions fork, exec, and waitpid succeed, and the return value from system
is the termination status of the shell, in the format specified for waitpid.

❖ Zombie process :
Process whose parents don’t wait for their death move to zombie state.
When the process dies, its parent picks up the child’s exit status (the reason for waiting) from the
process table and frees up the process table entry. However, when the parent doesn’t wait (but is still
alive), the child turns into a zombie. A zombie is a harmless dead child that reserves the process table
slot.

The parent is sent a SIGCHLD signal indicating that a child has died; the handler for this signal will
typically execute the wait system call, which reads the exit status and removes the zombie. The
zombie's process ID and entry in the process table can then be reused. However, if a parent ignores
the SIGCHLD, the zombie will be left in the process table.

◼ To avoid creating zombie processes, the parent must:


1. Handle the SIGCHLD signal.
2. In this signal handling function, the parent must wait for the child process.

$ ps
PID TTY TIME CMD
1074 pts/2 00:00:00 bash
1280 pts/2 00:00:00 [Link]
1281 pts/2 00:00:00 [Link] <defunct>
1288 pts/2 00:00:00 ps

$ ps -l
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
000 S 561 1074 1073 0 76 0 - 628 11a418 pts/2 00:00:00 bash
000 S 561 1301 1074 0 70 0 - 436 11f22b pts/2 00:00:00 parentTes
004 Z 561 1302 1301 0 70 0 - 0 119ffb pts/2 00:00:00 childTest
000 R 561 1320 1074 0 77 0 - 646 - pts/2 00:00:00 ps

❖ Orphan process :

• A process whose parent has exited.


• Orphaned processes are inherited by init
• Its slot in the process table is immediately released when an orphan terminates.

SIGNALS
▪ Signals are triggered by events and are posted on a process to notify it that something has happened
and requires some action.
▪ An event can be generated by process, user, or the UNIX kernel. Ex: divide by zero
▪ Signals are software version of hardware interrupts.
▪ Signals provide a way of handling asynchronous events: a user at a terminal typing the interrupt key
to stop a program or the next program in a pipeline terminating prematurely.
▪ Signals are defined as integer flags, and the <signal.h> header depicts the list of signals defined for a
UNIX system.
▪ The table below lists the signals.
Name Description Default action
SIGABRT abnormal termination (abort) terminate+core
SIGALRM timer expired (alarm) terminate
SIGBUS hardware fault terminate+core
SIGCANCEL threads library internal use ignore
SIGCHLD change in status of child ignore
SIGCONT continue stopped process continue/ignore
SIGEMT hardware fault terminate+core
SIGFPE arithmetic exception terminate+core
SIGFREEZE checkpoint freeze ignore
SIGHUP hangup terminate
SIGILL illegal instruction terminate+core
SIGINFO status request from keyboard ignore
SIGINT terminal interrupt character terminate
SIGIO asynchronous I/O terminate/ignore
SIGIOT hardware fault terminate+core
SIGKILL termination terminate
SIGLWP threads library internal use ignore
SIGPIPE write to pipe with no readers terminate
SIGPOLL pollable event (poll) terminate
SIGPROF profiling time alarm (setitimer) terminate
SIGPWR power fail/restart terminate/ignore
SIGQUIT terminal quit character terminate+core
SIGSEGV invalid memory reference terminate+core
SIGSTKFLT coprocessor stack fault terminate
SIGSTOP stop stop process
SIGSYS invalid system call terminate+core
SIGTERM termination terminate
SIGTHAW checkpoint thaw ignore
SIGTRAP hardware fault terminate+core
SIGTSTP terminal stop character stop process
SIGTTIN background read from control tty stop process
SIGTTOU background write to control tty stop process
SIGURG urgent condition (sockets) ignore
SIGUSR1 user-defined signal terminate
SIGUSR2 user-defined signal terminate
SIGVTALRM virtual time alarm (setitimer) terminate
SIGWAITING threads library internal use ignore
SIGWINCH terminal window size change ignore
SIGXCPU CPU limit exceeded (setrlimit) terminate+core/ignore
SIGXFSZ file size limit exceeded (setrlimit) terminate+core/ignore
SIGXRES resource control exceeded Ignore

▪ When a signal is sent to a process, it is pending on the process to handle it. The process can react to
pending signals in one of three ways:
Accept the default action of the signal, which for most signals will terminate the process.
Ignore the signal. The signal will be discarded and it has no affect whatsoever on the
recipient process.
Invoke a user-defined function. The function is known as a signal handler routine and the
signal is said to be caught when this function is called.

▪ The default action for most signals is to terminate a recipient process.


▪ Some signals will generate a core file for the aborted process so that users can trace back the
state of the process when it was aborted.
▪ Most signals are ignored or caught except the SIGKILL AND SIGSTOP.
▪ SIGKILL : generated by user via kill -9
▪ SIGSTOP : generated by kernel if ctrl-z is pressed
▪ A process is allowed to ignore certain signals. Like mission critical work

THE UNIX KERNEL SUPPORT OF SIGNALS


▪ In UNIX system each entry in the kernel process table slot has an array of signal flags, one for each
defined in the system.
▪ When a signal is generated for a process, the kernel will set the corresponding signal flag in the process
table slot of the recipient process.
▪ If the recipient process is asleep, the kernel will awaken the process by scheduling it.
▪ When the recipient process runs, the kernel will check the process U-area that contains an array of
signal handling specifications.
▪ If array entry contains a zero value, the process will accept the default action of the signal.
▪ If array entry contains a 1 value, the process will ignore the signal and kernel will discard it.
▪ If array entry contains any other value, it is used as the function pointer for a user-defined signal
handler routine.
▪ If there are different signals pending on a process, the order in which they are sent to a recipient process
is undefined.
▪ If multiple instances of signal are pending on a process, it is implementation-dependent on whether a
single instance or multiple instances of the signal will be delivered to the process.
▪ The way caught signals are handled by UNIX system V.2 and earlier version has been criticized as
unreliable because when a signal is caught, the kernel first reset signal handler and then call user signal
handling function specified for that signal. Thus if there are multiple instances of a signal being sent to a
process at different points, only the first instance will be catched. All subsequent instances of the signal
will be handled in the default manner.
▪ For a process to continuously catch multiple occurrences of a signal, a process must reinstall the handler
function every time the signal is caught. But still there is no guarantee that process will catch the signal
every time : between time a signal handler is invoked for a caught signal X and the time the handler
method is reestablished, the process is in a state of accepting the default action for signal X. If another
instance of signal X delivered during that interval, the process will have to handle the signal in default
manner. This is race condition, where two events occur simultaneously and which event will take
effect first is unpredictable.
▪ To remedy the unreliability of signal handling in system V.2, BSD UNIX 4.2 and POSIX.1 use a different
method: when a signal is caught, the kernel does not reset signal handler, so there is no need for the
process to reestablish the signal handling method.
▪ Furthermore kernel will block further delivery of the same signal to the process until the signal handler
function has completed execution. This ensures that the signal handler function will not be invoked
recursively for multiple instances of the same signal. System V.3 sigset API behaves in a reliable manner.
SIGNAL
The function prototype of the signal API is:
#include <signal.h>
void (*signal(int sig_no, void (*handler)(int)))(int);

▪ The formal argument of the API are: sig_no is a signal identifier like SIGINT or SIGTERM. The
handler argument is the function pointer of a user-defined signal handler function.
▪ The function should take an integer formal argument and does not return any value.
▪ The following example attempts to catch the SIGTERM signal, ignores the SIGINT signal, and
accepts the default action of the SIGSEGV signal. The pause API suspends the calling process until it
is interrupted by a signal and the corresponding signal handler does a return:
#include<iostream.h>
#include<signal.h>
/*signal handler function*/
void catch_sig(int sig_num)
{
signal (sig_num,catch_sig);
cout<<”catch_sig:”<<sig_num<<endl;
}

/*main function*/
int main()
{
signal(SIGTERM,catch_sig);
signal(SIGINT,SIG_IGN);
signal(SIGSEGV,SIG_DFL);
pause( ); /*wait for a signal interruption*/
}

The SIG_IGN specifies a signal is to be ignored, which means that if the signal is generated to the process,
it will be discarded without any interruption of the process.
The SIG_DFL specifies to accept the default action of a signal.

Unreliable Signals
• Unreliable Signals. Earlier versions of Unix.
• Signals could get lost without notice by process
• Why?
• The action for a signal was reset to its default each time the signal occurred.
• The process could only ignore signals, instead of turning off the signals.
• Example of “old” programming style for signal handling:
int sig_int(); /* signal handler */
….
signal(SIGINT, sig_INT);
….
sig_int()
{ ….
signal(SIGINT, sig_INT); /* re-establish handler */
….
}
Question: What happens if 2nd signal occurs after 1 st signal but before re-establishing signal handler? ➔ default
action !

• Processes cannot turn off signals (disable). Processes can only ignore signals (signals will be lost when
they arrive!!). Signals were not memorized when they occur.

Reliable Signals
• A signal is generated when
• the event that causes the signal occurs: divide by 0, alarm…
• a flag is set in the process table by kernel.
• A signal is delivered when
• the action for the signal is taken.
• A signal is pending during the time between its delivery and generation.
• A process has option to block a signal. A signal is blocked (if the action is either default or a with handler)
until
• the process unblocks the signal, or
• the corresponding action become “ignore”.
• System determines what to do with a blocked signal when signal is delivered (not when it is generated).
Allows process to change action for signal before it is delivered.
• A process may know which signals are blocked and pending using the function sigpending()
Questions: what happens if a blocked signal is generated more that once before process unblocks signal? What
happens if more that one signal is ready to be delivered to a process?
• A signal mask defined for each process. Defines which signals are blocked. sigpromask() allows to
examine and modify process mask.
• Signals are queued when
• a blocked signal is generated more than once.
• POSIX allows signal queuing, but many Unix systems do not implement this feature.
• Delivery order of signals
• No order under POSIX.1, but its rationale states that signals related to the current process state,
e.g., SIGSEGV, should be delivered first.

KILL
A process can send a signal to a related process via the kill API. This is a simple means of
inter-process communication or control. The function prototype of the API is:
#include<signal.h>
int kill(pid_t pid, int signal_num);
Returns: 0 on success, -1 on failure.
The signal_num argument is the integer value of a signal to be sent to one or more processes designated by pid.
The possible values of pid and its use by the kill API are:
pid > 0 The signal is sent to the process whose process ID is pid.

pid == 0 The signal is sent to all processes whose process group ID equals the process group ID of the
sender and for which the sender has permission to send the signal.

pid < 0 The signal is sent to all processes whose process group ID equals the absolute value of pid and for
which the sender has permission to send the signal.

pid == 1 The signal is sent to all processes on the system for which the sender has permission to send the
signal.

The UNIX kill command invocation syntax is:


Kill [ -<signal_num> ] <pid>......
Where signal_num can be an integer number or the symbolic name of a signal. <pid> is process ID.

raise Functions:

• The raise function allows a process to send a signal to itself.

#include <signal.h>
int raise(int signo);

return: 0 if OK,-1 on error.


ALARM
The alarm API can be called by a process to request the kernel to send the SIGALRM signal after a certain
number of real clock seconds. The function prototype of the API is:
#include<signal.h>
Unsigned int alarm(unsigned int time_interval);
Returns: 0 or number of seconds until previously set alarm

The seconds value is [Link] clock seconds in the future when the signal should be generated.

The alarm API can be used to implement the sleep API:


#include<signal.h>
#include<stdio.h>
#include<unistd.h>

void wakeup( ) { };

unsigned int sleep (unsigned int timer )


{
struct sigaction action;
action.sa_handler=wakeup;
action.sa_flags=0;
sigemptyset(&action.sa_mask);
if(sigaction(SIGALARM,&action,0)==-1)
{
perror(“sigaction”);
return -1;
}
(void) alarm (timer);
(void) pause( );
return 0;
}

• If, when we call alarm, a previously registered alarm clock for the process has not yet expired, the
number of seconds left for that alarm clock is returned as the value of this function.

• If a previously registered alarm clock for the process has not yet expired and if the seconds value is 0,
the previous alarm clock is canceled. The number of seconds left for that previous alarm clock is still
returned as the value of the function.

pause function:
• The pause function suspends the calling process until a signal is caught.

#include <unistd.h>

int pause(void);

Returns: 1 with errno set to EINTR

The only time pause returns is if a signal handler is executed and that handler returns

abort Function:

• The abort function causes abnormal program termination.

#include <stdlib.h>

void abort(void);

This function never returns.

• This function sends the SIGABRT signal to the caller.

sleep Function:

#include <unistd.h>

unsigned int sleep(unsigned int seconds);

Returns: 0 or number of unslept seconds.

This function causes the calling process to be suspended until either

• The amount of wall clock time specified by seconds has elapsed. The return value is 0.

• A signal is caught by the process and the signal handler returns. Return value is the number of unslept
seconds.
signal set
Name
sigemptyset, sigfillset, sigaddset, sigdelset, sigismember - POSIX signal set operations.

Synopsis
#include <signal.h>

int sigemptyset(sigset_t *set);

int sigfillset(sigset_t *set);

int sigaddset(sigset_t *set, int signum);

int sigdelset(sigset_t *set, int signum);

int sigismember(const sigset_t *set, int signum);

Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

sigemptyset(), sigfillset(), sigaddset(), sigdelset(), sigismember():


_POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE

Description
These functions allow the manipulation of POSIX signal sets.

sigemptyset() initializes the signal set given by set to empty, with all signals excluded from the set.

sigfillset() initializes set to full, including all signals.

sigaddset() and sigdelset() add and delete respectively signal signum from set.

sigismember() tests whether signum is a member of set.

Objects of type sigset_t must be initialized by a call to either sigemptyset() or sigfillset() before being passed
to the functions sigaddset(), sigdelset() and sigismember() or the additional glibc functions described below
(sigisemptyset(), sigandset(), and sigorset()). The results are undefined if this is not done.

Return Value
sigemptyset(), sigfillset(), sigaddset(), and sigdelset() return 0 on success and -1 on error.

sigismember() returns 1 if signum is a member of set, 0 if signum is not a member, and -1 on error.

Errors
EINVAL

sig is not a valid signal.


sigaction
Name
sigaction - examine and change a signal action

Synopsis
#include <signal.h>

int sigaction(int signum, const struct sigaction *act,


struct sigaction *oldact);
Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
sigaction(): _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE

siginfo_t: _POSIX_C_SOURCE >= 199309L

Description
The sigaction() system call is used to change the action taken by a process on receipt of a specific signal.
(See signal(7) for an overview of signals.)

signum specifies the signal and can be any valid signal except SIGKILL and SIGSTOP.

If act is non-NULL, the new action for signal signum is installed from act. If oldact is non-NULL, the previous
action is saved in oldact.

The sigaction structure is defined as something like:

struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
On some architectures a union is involved: do not assign to both sa_handler and sa_sigaction.

The sa_restorer element is obsolete and should not be used. POSIX does not specify a sa_restorer element.

sa_handler specifies the action to be associated with signum and may be SIG_DFL for the default
action, SIG_IGN to ignore this signal, or a pointer to a signal handling function. This function receives the
signal number as its only argument.

If SA_SIGINFO is specified in sa_flags, then sa_sigaction (instead of sa_handler) specifies the signal-
handling function for signum. This function receives the signal number as its first argument, a pointer to
a siginfo_t as its second argument and a pointer to a ucontext_t (cast to void *) as its third argument.
(Commonly, the handler function doesn't make any use of the third argument. See getcontext(3) for further
information about ucontext_t.)

sa_mask specifies a mask of signals which should be blocked (i.e., added to the signal mask of the thread in
which the signal handler is invoked) during execution of the signal handler. In addition, the signal which
triggered the handler will be blocked, unless the SA_NODEFER flag is used.
sa_flags specifies a set of flags which modify the behavior of the signal. It is formed by the bitwise OR of zero
or more of the following:

SA_NOCLDSTOP
If signum is SIGCHLD, do not receive notification when child processes stop (i.e., when they receive
one of SIGSTOP, SIGTSTP, SIGTTIN or SIGTTOU) or resume (i.e., they receive SIGCONT)
(see wait(2)). This flag is only meaningful when establishing a handler for SIGCHLD.
SA_NOCLDWAIT (since Linux 2.6)
If signum is SIGCHLD, do not transform children into zombies when they terminate. See
also waitpid(2). This flag is only meaningful when establishing a handler for SIGCHLD, or when
setting that signal's disposition to SIG_DFL.

If the SA_NOCLDWAIT flag is set when establishing a handler for SIGCHLD, POSIX.1 leaves it
unspecified whether a SIGCHLD signal is generated when a child process terminates. On Linux,
a SIGCHLD signal is generated in this case; on some other implementations, it is not.

SA_NODEFER
Do not prevent the signal from being received from within its own signal handler. This flag is only
meaningful when establishing a signal handler. SA_NOMASK is an obsolete, nonstandard synonym for
this flag.
SA_ONSTACK
Call the signal handler on an alternate signal stack provided by sigaltstack(2). If an alternate stack is
not available, the default stack will be used. This flag is only meaningful when establishing a signal
handler.
SA_RESETHAND
Restore the signal action to the default upon entry to the signal handler. This flag is only meaningful
when establishing a signal handler. SA_ONESHOT is an obsolete, nonstandard synonym for this flag.
SA_RESTART
Provide behavior compatible with BSD signal semantics by making certain system calls restartable
across signals. This flag is only meaningful when establishing a signal handler. See signal(7) for a
discussion of system call restarting.
SA_SIGINFO (since Linux 2.2)
The signal handler takes three arguments, not one. In this case, sa_sigaction should be set instead
of sa_handler. This flag is only meaningful when establishing a signal handler.

sigpending
Name
sigpending - examine pending signals

Synopsis
#include <signal.h>

int sigpending(sigset_t *set);

Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

sigpending(): _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE


Description
sigpending() returns the set of signals that are pending for delivery to the calling thread (i.e., the signals which
have been raised while blocked). The mask of pending signals is returned in set.

Return Value
sigpending() returns 0 on success and -1 on error.

Errors
EFAULT

set points to memory which is not a valid part of the process address space.

sigprocmask
Name
sigprocmask - examine and change blocked signals

Synopsis
#include <signal.h>

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

sigprocmask(): _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE

Description
sigprocmask() is used to fetch and/or change the signal mask of the calling thread. The signal mask is the set
of signals whose delivery is currently blocked for the caller (see also signal(7) for more details).

The behavior of the call is dependent on the value of how, as follows.

SIG_BLOCK
The set of blocked signals is the union of the current set and the set argument.
SIG_UNBLOCK
The signals in set are removed from the current set of blocked signals. It is permissible to attempt to
unblock a signal which is not blocked.
SIG_SETMASK
The set of blocked signals is set to the argument set.
If oldset is non-NULL, the previous value of the signal mask is stored in oldset.

If set is NULL, then the signal mask is unchanged (i.e., how is ignored), but the current value of the signal
mask is nevertheless returned in oldset (if it is not NULL).

The use of sigprocmask() is unspecified in a multithreaded process; see pthread_sigmask(3).


Return Value
sigprocmask() returns 0 on success and -1 on error.

Errors
EFAULT

the set or oldset argument points outside the process's allocated address space.

EINVAL

The value specified in how was invalid.

sigsuspend
Name
sigsuspend - wait for a signal

Synopsis
#include <signal.h>

int sigsuspend(const sigset_t *mask);

Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

sigsuspend(): _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE

Description
sigsuspend() temporarily replaces the signal mask of the calling process with the mask given by mask and then
suspends the process until delivery of a signal whose action is to invoke a signal handler or to terminate a
process.

If the signal terminates the process, then sigsuspend() does not return. If the signal is caught, then sigsuspend()
returns after the signal handler returns, and the signal mask is restored to the state before the call
to sigsuspend().

It is not possible to block SIGKILL or SIGSTOP; specifying these signals in mask, has no effect on the
process's signal mask.

Return Value
sigsuspend() always returns -1, normally with the error EINTR.

Errors
EFAULT

mask points to memory which is not a valid part of the process address space.
EINTR

The call was interrupted by a signal.

Common questions

Powered by AI

UNIX processes manage signal blocking using system calls like 'sigprocmask', which changes the signal mask of the calling thread to control which signals are blocked . The 'sigpending' function is used to examine pending signals, providing information on signals raised while blocked . These mechanisms ensure that signals are not lost and allow the process to handle signals in a controlled, deferred manner, maintaining process stability and reliability .

The 'fork' function creates a new process by copying the address space of the parent into the child, allowing both to execute independently with their own memory space segments like data, stack, and heap . In contrast, 'vfork' does not copy the address space of the parent but allows the child to run in the parent's space until it calls 'exec' or 'exit', thereby preventing unnecessary memory allocation. This makes 'vfork' more efficient in scenarios where an immediate 'exec' is expected, but it introduces risk if the child modifies any of the parent's data before executing 'exec' or '_exit' .

The 'sigaction' function in UNIX is a more powerful and versatile alternative to the older 'signal' API for signal handling. It allows specifying detailed handler behavior through the 'sa_flags', including options for signal masking and handling of specific signal types (like SIGCHLD). Unlike 'signal', 'sigaction' does not reset signal behavior to default after handling, enhancing reliability. Additionally, it supports extended information delivery to handlers with 'sa_sigaction', enabling more complex signal handling strategies . This makes 'sigaction' preferable for writing modern, robust UNIX applications .

Session and process group management in UNIX involve maintaining organized process hierarchies where processes can communicate and be collectively managed, for example, through terminal job control. Challenges arise in ensuring synchronization and isolation between sessions while allowing the system to efficiently manage resources like terminals among groups . Mismanagement can lead to issues like resource contention and signal handling complexities. These concepts are crucial for process organization as they define how processes relate within the operating environment, affecting resource allocation, signal distribution, and overall system behavior .

In UNIX, user IDs (UIDs) and group IDs (GIDs) are crucial for determining process ownership and permissions. Upon process creation with 'fork', the child inherits its parent's real UID, effective UID, real GID, and effective GID, ensuring permission levels are maintained . This inheritance mechanism allows the child process to access system resources based on the same security credentials as the parent, preserving the security model within process hierarchies and ensuring that child processes operate under the same permission scope as their parent .

Reliable signals in UNIX systems ensure that signals are queued and not lost, allowing the process to handle each signal appropriately upon delivery. Unreliable signals, prevalent in older UNIX systems, could be lost if the process did not handle them before another instance occurred . This difference impacts program execution significantly; reliable signals ensure that signal-driven events do not go unnoticed, thereby improving robustness and stability in signal-sensitive applications . With reliable signaling, processes can establish signal handlers and even block signals, knowing that all instances will be accounted for when unblocked .

UNIX systems use 'wait' and 'waitpid' calls to efficiently handle child process termination by allowing parent processes to retrieve the exit status of their children and ensuring that resources used by terminating child processes are deallocated. 'wait' blocks until a child process exits if no terminated children exist, whereas 'waitpid' allows for non-blocking operation and selection of specific child processes . These calls ensure that process table slots are freed by the kernel, preventing resource leaks, and are fundamental to proper resource management and process control in UNIX environments .

'vfork' is designed for situations where a child process immediately calls 'exec' or '_exit' after creation, optimizing memory usage by not duplicating the parent's memory space. However, it should be avoided in modern systems unless absolutely necessary because if the child modifies any data, it affects the parent's memory space, leading to potential data corruption . Modern systems utilizing 'Copy on Write' with fork provide similar efficiencies without the risks associated with 'vfork', making it preferable in most cases .

UNIX processes use 'wait' and 'waitpid' functions to wait for child processes to terminate and retrieve their exit status. The 'wait' function blocks the calling process until a child process terminates, whereas 'waitpid' can be configured not to block using options, allowing more flexible process control. 'waitpid' also allows selection of specific child processes to wait for, based on process ID . These functions are crucial for synchronized process termination and resource deallocation .

'Copy on Write' is a memory management optimization technique used in UNIX systems where the address space of the parent process is shared with the child until modifications are made. Once either process attempts to write to a shared segment, the kernel creates a new copy for that segment, ensuring isolation while minimizing initial memory usage. This technique enhances efficiency during process creation using fork by avoiding unnecessary memory duplication until it is needed . The main advantage is reduced memory usage and improved performance in process creation tasks .

You might also like