Description The
+use cases demonstrated in this example are described below.
Note: For
+clarity in the documentation for this example, signal names have been given
+in capitals whilst process names have been given in lower case.
1.
+Sending and handling a signal using the default handler
The default
+implementation of the signals supported in P.I.P.S. will either terminate
+a process or ignore a process. Signals are generated using the Kill() method
+and they are handled as per the implementation in the default handler.
To
+demonstrate this use case we use SIGCHLD and SIGALRM signals. SIGCHLD by
+default gets ignored whenever it is raised, whereas SIGALRM causes
+a process termination when raised. As a result the example terminates whenever SIGALRM is
+raised, whereas an info message is printed when SIGCHLD is
+raised.
2. Sending and handling a signal using a customized signal
+handler
To override the default implementation of a signal a
+customized handler can be defined. This customized handler can be set either
+by using sigaction() or signal(). The sigaction() method
+takes struct sigaction as one of its parameters (the sa_handler member
+of this structure is filled with the custom handler). Now whenever a signal
+is generated the custom handler is executed.
For the demonstration
+of this particular use case, SIGCHLD and SIGALRM signals
+are used. These signals are assigned custom handler functions. The handlers
+for these signals contains a simple user message. Thus, whenever the signals
+are raised, the customized signal handlers get invoked instead of the default
+handlers.
3. Ignoring an incoming signal
A signal
+can be ignored by setting SIG_IGN in the sa_handler member
+of struct sigaction. The demonstration of this use case also
+uses SIGCHLD and SIGALRM signals, and as
+a result of setting SIG_IGN in sa_handler the
+signals are ignored when raised.
4. Blocking and releasing a signal
A
+signal can be blocked by first adding it to the blocking set (a list of signals
+we want to block, when a signal is executing) by using the sigaddset() method
+and then calling the sigprocmask() function. Once a signal
+is blocked it will always be ignored upon generation. The sigrelse() method
+is used to unblock a signal.
Demonstration of this use case involves
+the SIGUSR1 and SIGUSR2 signals. Both SIGUSR1 and SIGUSR2 are
+user-defined signals. We first block the SIGUSR1 signal by
+adding it to the blocking set and making a call to the sigprocmask() function.
+Now whenever SIGUSR1 is raised it will get ignored as it
+is blocked. SIGUSR1 will keep waiting in the pending queue
+until it is released. The release of SIGUSR1 happens in the SIGUSR2 signal
+handler. Once SIGUSR1 is released, it is removed from the
+pending queue and its handler function is called to handle it.
5.
+Waiting for a signal
Before a process can wait on a particular
+signal, it has to add the signal to the mask set (which is a list of signals
+we want to block) and then call the sigprocmask() method.
+The process then sets the timeout value using struct timespec.
+Once this is done the process waits on a signal for a specified time period
+using thesigtimedwait() method. If the signal is not received
+within the specified time period, an error message is generated.
For
+the demonstration of this use case we are setting a timeout of 5 seconds.
+The SIGALRM signal is raised by a call to alarm() as
+and when the timer expires. There are two instances in the example where SIGALRM signal
+is raised, one after a duration of 4 seconds and one after 6 seconds. When SIGALRM is
+raised after 4 seconds, it is well within the timeout limit (of 5 seconds)
+and hence the signal gets received but not handled. But when the alarm is
+raised after 6 seconds, timeout happens and an error is generated. Now as
+the SIGALRM signal was added to the mask set it never gets
+handled even though it is received. To handle the signal we need to move it
+from block state to unblock state, which we do using the sigprocmask() method.
+Once the signal gets unblocked its custom handler gets called.
6.
+Generating and handling a SIGPIPE signal
The
+ SIGPIPE signal is generated when writing to a broken pipe.
+To achieve this broken pipe condition the read end of the pipe (obtained using
+the pipe() function call) is closed and then write to the
+pipe is done. The associated handler function is executed in response to the
+raised SIGPIPE signal .
7. Using a signal to gracefully
+terminate a process
Graceful termination of process can be achieved
+by using the SIGTERM signal. In the handler function of the
+signal all the opened file descriptors need to be closed before exiting.
This
+use case is demonstrated using the sigtermSignal project.
+The project consists of two processes: the sigtermsignal process
+and the raisesignal process.
+-
The sigtermsignal process
+first defines a custom handler for the SIGTERM signal that
+carries out the closing of all the open file descriptors when the signal is
+raised. This is done in order to achieve the graceful termination of the process.
+The sigtermsignal process then opens a file and obtains
+names from user to be written in to it. It then simultaneously spawns a raisesignal process
+and starts reading from the file. When the raisesignal process
+sends a SIGTERM signal to the sigtermsignal process,
+the sigtermsignal process closes all the open file descriptors
+and prepares to exit.
+-
The raisesignal process
+sends the SIGTERM signal to the sigtermsignal process.
+The custom handler of the SIGTERM signal takes care of properly
+closing all opened file descriptors and then terminating the process. If the
+custom handler is not implemented, the default handler will get called, which
+will result in process termination without closing any opened file descriptors.
+
8. Using a signal to handle an asynchronous event
The
+SIGUSR1 and SIGUSR2 signals are used to demonstrate
+the handling of an asynchronous event. These signals are sent from one process
+to another. On reception of these signals, respective custom handlers are
+called and any necessary action is taken. The action taken is purely implementation
+dependent .
This use case demonstration is performed using the asyncSignal project.
+The project consists of two processes: the sigusr1 process
+and the sigusr2 process, where the sigusr1 process
+handles the SIGUSR1 signal and sends the SIGUSR2 signal
+to the sigusr2 process. Whereas the sigusr2 process
+handles the SIGUSR2 signal and sends the SIGUSR1 signal
+to the sigusr1 process.
+The sigusr1 process
+assigns a custom handler for the SIGUSR1 signal. It then
+opens a file in read and write mode and write some content into the open file.
+Once write operation is done the sigusr1 process spawns sigusr2 process
+and waits for SIGUSR1 signal from sigusr2 process.
+On receiving SIGUSR1 signal sigusr1 process
+starts reading from the file. Once reading from the file is done and its contents
+are displayed on the console, sigusr1 process sends SIGUSR2 signal
+to sigusr2 process. It then closes all its open file
+descriptor and prepares to exit.
+-
The sigusr2 process
+is spawned from sigusr1 process and it assigns a custom
+handler for SIGUSR2 signal. It sends SIGUSR1 signal
+to sigusr1 process in order to start file read and then
+waits for SIGUSR2 signal from sigusr1 process.
+When sigusr2 process receives SIGUSR2 signal
+it prepares to exit.
+
Hence, the communication between the two processes happens through
+the SIGUSR1 and SIGUSR2 signals and asynchronous
+signal handling happens.
+