posixsignals: POSIX Signal Example, Using P.I.P.S.

This example demonstrates various signal use cases as supported in P.I.P.S.

Purpose

The example demonstrates the following use cases:

  1. Sending and handling a signal using the default handler

  2. Sending and handling a signal using a customized signal handler

  3. Ignoring an incoming signal

  4. Blocking and releasing a signal

  5. Waiting for a signal

  6. Generating and handling a SIGPIPE signal

  7. Using a signal to gracefully terminate a process

  8. 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.

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.

Download

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

Building and configuring

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

The example builds the following executables :
  • 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.

Running the example

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.