Unix Signals Programming Assignment

In this assignment, you are going to learn how to use signals for processes to communicate with one another.

Preliminary Information


A signal is a software interrupt.

There are many conditions that generate signals such as terminal generated signals from certain key combinations, hardware exceptions such as divide by zero and invalid memory references, the kill command, and software conditions such as a write to a pipe after the reader has terminated.

A program can deal with signals in one of three ways:

A signal is sent to a process when the event causing the signal occurs. Upon signal generation, the kernel sets a flag in the process table. The signal is delivered to a process when the action for a signal is taken. The signal is pending between the time of generation and delivery.

Processes can block signal delivery (you will see that we will have to do this during initialization). If a signal that is blocked is generated for a process and the action for the signal is to catch the signal or default, the signal remains pending for the process until the process unblocks the signal or changes the action to ignore.

There are two user-defined signals for use by application programs: SIGUSER1 and SIGUSER2. We are going to be using SIGUSER1 only.

You will be using the kill system call to generate signals.

int kill (pid_t pid, int signno);

There are four different actions based on the value of pid:
 
 
pid action
>0 signal sent to the process whose process id is pid
0 signal sent to all processes whose process group id equals the process groupid of the sender for which the sender has permission to send the signal
<0 signal 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
-1 POSIX.1 does not specify

You will often find examples of programs that handle signals that use the pause function. Pause is a function that will suspend a process until a signal is received and handled. Once a process executes pause, pause only returns if a signal handler is executed and returns. The problem with using pause to wait for a signal to occur is with the type of program you are writing, it is not possible to code it without a race condition. As part of this assignment, you will be given a program in which a race condition exists and you will have to explain the race condition.

Unix does provide system calls that allow a safe implementation, an implementation free of race conditions. It is a bit more complicated to code.

Follow this  link to a simple sample program that represents a safe signal handling approach. It involves blocking the signal and executing an atomic function that pauses and unblocks the signal - preventing a race condition. The program creates a signal handler then forks a child. The parent sends a signal to the child and then waits for the child to terminate. The child terminates after receiving the signal from the parent.

if (signal(SIGUSR1, sig_user) == SIG_ERR)
{
  printf("error creating signal handler \n");
}

This code, executed in the main program before the fork, specifies a function called sig_user as the signal handler.

void sig_user (int signo);

sig_user is a void function that takes the signal number as a parameter.

Remember, when a child is created, it is an exact duplicate of the parent, it knows everything the parent knows at the time of creation. By creating the signal handler in the parent, you eliminate one possible race condition - the parent sending a signal before the signal handler has been created.

A signal mask of a process is the set of signals that are currently blocked for that process.

The sigprocmask function allows us to block and unblock signals. For use with sigprocmask, we need a data type to represent signals. This data type is called a signal set.

int sigemptyset (sigset_t *set)
int sigaddset (sigset_t *set, int signo);

The function sigemptyset initializes the signal set pointed to by set to exclude all signals.  After initializing a signal set, the sigaddset is used to add a single signal to an existing set.

sigaddset(&newmask,SIGUSR1);

After the call to sigaddset, the set newmask includes SIGUSER1.

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

If set is nonnull, the how argument defines how the current signal mask represented by set is modified:

SIG_BLOCK - the new signal mask for the process is the union of its current signal mask and the signal set pointed to by set. In other words, set contains the signals  to block.

SIG_UNBLOCK - the new signal mask for the process is the intersection of its current signal mask and the complement of the signal set pointed to by set - set contains the signals to unblock.

SIG_SETMASK - the new signal mask for the process is the value pointed to by set.

If set is null, the signal mask of the process is not changed.

The current signal mask of the process is returned through oset.

The call:

if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
{
  printf("error in sigprocmask \n");
}

blocks the SIGUSR1 signal.

With the SIGUSR1 signal blocked, the child is created.

The kill function sends a signal to the specified process as described above. After creating the child, the parent sends a signal to the child. The parent then waits for the child to terminate

When the child is created, it calls a function called child. The parent created the signal handler, so the child does not have to worry about creating it. Remember, blocked signals remain pending until the signal is unblocked. In order to receive the signal from the parent, the child must unblock the signal. If this is not done carefully, a race condition can be created such that the signal handler handles the signal before the child has waited for the signal. If that happens, the child will wait indefinitely. The child must be able to reset the signal mask and put itself to sleep in a single atomic operation. The function sigsuspend changes the signal mask and puts the calling process to sleep atomically, eliminating any race conditions.

int sigsuspend (const sigset_t *sigmask);

The sigsuspend function sets the signal mask of the process to the value pointed to by sigmask and suspends the process until a signal is caught or a signal occurs that terminates teh process. If a signal is caught and the signal handler returns, sigsuspend returns the signal mask of the calling process to the value before the call to sigsuspend.

There is no successful return from sigsuspend. If it returns to the caller, it returns -1 with errno set equal to EINTR. This indicates
an interrupted signal call.

The Project - The Twelve Days of Chirstmas

The lyrics of the Christmas song, The Twelve Days of Christmas were taken from:
 http://www.41051.com/xmaslyrics/twelvedays.html

12 Days of Christmas
 

On the first day of Christmas
my true love sent to me:
A Partridge in a Pear Tree

On the second day of Christmas
my true love sent to me:
2 Turtle Doves
and a Partridge in a Pear Tree

On the third day of Christmas
my true love sent to me:
3 French Hens
2 Turtle Doves
and a Partridge in a Pear Tree

On the fourth day of Christmas
my true love sent to me:
4 Calling Birds
3 French Hens
2 Turtle Doves
and a Partridge in a Pear Tree

On the fifth day of Christmas
my true love sent to me:
5 Golden Rings
4 Calling Birds
3 French Hens
2 Turtle Doves
and a Partridge in a Pear Tree

On the sixth day of Christmas
my true love sent to me:
6 Geese a Laying
5 Golden Rings
4 Calling Birds
3 French Hens
2 Turtle Doves
and a Partridge in a Pear Tree

On the seventh day of Christmas
my true love sent to me:
7 Swans a Swimming
6 Geese a Laying
5 Golden Rings
4 Calling Birds
3 French Hens
2 Turtle Doves
and a Partridge in a Pear Tree

On the eighth day of Christmas
my true love sent to me:
8 Maids a Milking
7 Swans a Swimming
6 Geese a Laying
5 Golden Rings
4 Calling Birds
3 French Hens
2 Turtle Doves
and a Partridge in a Pear Tree

On the ninth day of Christmas
my true love sent to me:
9 Ladies Dancing
8 Maids a Milking
7 Swans a Swimming
6 Geese a Laying
5 Golden Rings
4 Calling Birds
3 French Hens
2 Turtle Doves
and a Partridge in a Pear Tree

On the tenth day of Christmas
my true love sent to me:
10 Lords a Leaping
9 Ladies Dancing
8 Maids a Milking
7 Swans a Swimming
6 Geese a Laying
5 Golden Rings
4 Calling Birds
3 French Hens
2 Turtle Doves
and a Partridge in a Pear Tree

On the eleventh day of Christmas
my true love sent to me:
11 Pipers Piping
10 Lords a Leaping
9 Ladies Dancing
8 Maids a Milking
7 Swans a Swimming
6 Geese a Laying
5 Golden Rings
4 Calling Birds
3 French Hens
2 Turtle Doves
and a Partridge in a Pear Tree

On the Twelfth day of Christmas
my true love sent to me:
12 Drummers Drumming
11 Pipers Piping
10 Lords a Leaping
9 Ladies Dancing
8 Maids a Milking
7 Swans a Swimming
6 Geese a Laying
5 Golden Rings
4 Calling Birds
3 French Hens
2 Turtle Doves
and a Partridge in a Pear Tree



Write a program that forks twelve children, sends a signal to the twelfth child, then waits for all twelve children to terminate.

Each child is going to print out one day of the Twelve Days of Christmas after receiving a signal from the child printing the previous day.

Upon receipt of the signal from the parent, the twelfth child must print the first day of Christmas, send a signal to the eleventh child, and terminate.

Upon receipt of the signal from the twelfth child, the eleventh child must print the second day of Christmas, send a signal to the tenth child, and terminate.

Upon receipt of the signal from the eleventh child, the tenth child must print the third day of Christmas, send a signal to the ninth child, and terminate.

. . . and so on

Upon receipt of the signal from the third child, the second child must print the eleventh day of Christmas, send a signal to the first child, and terminate.

Upon receipt of the signal from the second child, the first child must print the twelfth day of Christmas and terminate.

The output should look like:

Child 12:

On the first day of Christmas
my true love sent to me:
A Partridge in a Pear Tree

Child 11:

On the second day of Christmas
my true love sent to me:
2 Turtle Doves
and a Partridge in a Pear Tree

Child 10:

On the third day of Christmas
my true love sent to me:
3 French Hens
2 Turtle Doves
and a Partridge in a Pear Tree

Some Restrictions

All of the functions that you need have been described above. You have been provided with an example of their usage.

The Output: The lyrics to the Twelve Days of Christmas, each day printed by a different child.

Deliverables

  1. Modify the safe sample program  to fork two children instead of one. The parent should signal the second child created and the second child signals the first child. . The signal handler should print out which child (first or second child) it is executing for.

    Important, write a loop to fork the children!!!!! If you write a simple loop here, you have much of the work done for the final deliverable.

    Use turnin to submit your program. Select safesigc, the name should be safesigex.c.

  2. The completed project, The Twelve Days of Christmas, as described above.

    Use turnin to submit your program. Select signalc the name should be twelvedays.c.
    In the header comments of your program, provide an expression for the complexity of the number of total gifts given.

  3. Write a report answering the following questions regarding  the signalex.c and  safesigex.c programs:
     
    1. What is the purpose of the sem_id semaphore. This must be thorough. Why is it needed? What does it accomplish and how?
    2. Explain the race condition that exists in signalex.c. Explain how that race condition is eliminated in safesigex.c
    3. In the final deliverable that displays the twelve days of Xmas song, why must the last child created be the first to signal?

    Come to class prepared to present your report to the class. Students will be selected at random to present their reports.

Advice

Your job has been made much easier by the safe signal example program provided. You did not have to try to figure out race conditions. However, this assignment is not trivial. If you wait until the last minute, you will not be successful.


This page is maintained by Barbara Bracken

This page is copyright © by Barbara Bracken

This page was last modified 11/10/2025