This example demonstrates various signal use cases as supported in P.I.P.S.
The example demonstrates the following use cases:
Sending and handling a signal using the default handler
Sending and handling a signal using a customized signal handler
Ignoring an incoming signal
Blocking and releasing a signal
Waiting for a signal
Generating and
handling a SIGPIPE
signal
Using a signal to gracefully terminate a process
Using a signal to handle an asynchronous event
The example delivers 3 sub projects:
basicSignals: This project demonstrates basic signal use cases. It shows the first
six use cases mentioned in the list above. The use cases are demonstrated
through basicsignals.exe
.
sigtermSignal: This project demonstrates the graceful termination of a process
using the SIGTERM
signal. The use case is demonstrated
through sigtermsignal.exe
and raisesignal.exe
.
asyncSignal: This project demonstrates asynchronous signal handling using the SIGUSR1
signal and the SIGUSR2
signal.
The use case is demonstrated using sigusr1.exe
and sigusr2.exe
.
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.
Click on the following link to download the example: BasicSignals.zip
Click on the following link to download the example: SigtermSignal.zip
Click on the following link to download the example: AsyncSignal.zip
To view the BasicSignal example source click: browseBasicSignals
To view the SigtermSignal example source click: browseSigtermSignal
To view the AsyncSignal example source click: browseAsyncSignal
You can build the example from your IDE or the command line:
If you
use an IDE, import the bld.inf
file of the example
into your IDE, and use the build command of the IDE.
If you use the command line, open a command prompt, and set the current directory to the source code directory of the example. You can then build the example with the SBSv1 build tools using the following commands:
bldmake bldfiles
abld build
basicsignals.exe
: for basic signal use
cases demonstration.
sigtermsignal.exe
: for demonstrating
graceful termination of process .
raisesignal.exe
: for sending SIGTERM
signal.
sigusr1.exe
: for demonstrating asynchronous
event handling , sending SIGUSR2
signal and receiving SIGUSR1
signal.
sigusr2.exe
: for sending SIGUSR1
signal and receiving SIGUSR2
signal.
in the epoc32\release\winscw\<udeb or urel>\
folder.
NOTE :
basicsignals.exe
should be executed first for running the first six uses cases mentioned
above.
sigtermsignal.exe
should be executed
for running the seventh use case.
sigusr1.exe
should be executed for running the last use case.
The sigtermsignal
process internally spawns the raisesignal
process for taking an input from the user and sending the SIGTERM
signal. You should not run the raisesignal
process explicitly. You should only run sigtermsignal.exe
as the sigtermsignal
process takes proper care
of launching the raisesignal
process.
The sigusr1
process also internally spawns the sigusr2
process.
The sigusr1
process sends the SIGUSR2
signal to the sigusr2
process and receives the SIGUSR1
signal from it.
The sigusr2
process
sends the SIGUSR1
signal to the sigusr1
process and receives the SIGUSR2
signal from it.
As the sigusr1
process spawns the sigusr2
process, hence you should run sigusr1.exe
only.
In order to toggle between the processes use Alt+ctrl+shift+T
and observe the behavior.