POSIX Signal Handling
Signal, or a software interrupt, is a notification to a process that an event has occurred. Generally, signals are asynchronous, because a process doesn't know that or when a signal will occur.
Signals are sent to a process by - another process, or itself, or the kernel.
Example, SIGCHLD is sent by kernel to parent process when child process terminates.
Disposition, or action of a signal is set by sigaction function. There are three choices for disposition -
- Creating a signal handler function. This method is also known as catching the signal. void handler (int signo); -function returns nothing, single integer as parameter contains signal number. SIGKILL and SIGSTOP cannot be caught.
- Ignore signal by setting its disposition to SIG_IGN. SIGKILL and SIGSTOP cannot be ignored.
- Set default disposition for a signal by setting its disposition to SIG_DFL. Usually, default is to terminate process, and sometimes save a core image of the process in its current working directory.
Signal function - an easier but outdated alternative to sigaction, contains two parameters - first is signal number, second is handler function/SIG_IGN/SIG_DFL.
When a signal handler is installed, it remains installed.
When a handler is executing, the caught signal is blocked. Any additional signals set in the mask are also blocked.
If signal is generated one or more time while it is blocked, it is delivered only once after the signal is unblocked. Unix signals are not queued.
Handling SIGCHLD signals
Zombie state is used to maintain information of child for the parent, such as process ID of child, its termination status, information on resource utilisation of child (CPU time, memory etc.). If process terminates and that process has children in zombie state, parent process ID of all children is set to 1 (init process), which inherits the children and cleans them up (i.e., init will wait for them, which removes the zombie). Some UNIX systems show the COMMAND for zombie process as defunct.
Handling Zombies
Zombies take space and we can run out of processes. When we fork children, we must wait for them by calling wait in handler of SIG_CHLD to prevent them from becoming zombies.
void sig_chld ( int signo ) {
pid_t pid ;
int stat ;
pid = wait ( & stat ) ;
printf ( "Child %d terminated." , pid ) ;
return ;
}
Sequence of Steps
- Terminate client by typing EOF character. Client TCP sends FIN to server and server TCP responds with ACK.
- Receipt of FIN delivers EOF to child's pending readline. Child terminates.
- Parent is blocked in its call to accept when SIG_CHLD signal is delivered. Our signal handler sig_chld above executes, wait fetches the child's PID and status. Signal handler returns.
- accept was a slow system call, and the kernel causes it to return an error of EINTR (interrupted system call). Parent does not handle this error, so it aborts.