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:
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.
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
const char *gifts[DAYSOFCHRISTMAS]={
"Twelve Drummers Drumming",
"Eleven Pipers Piping",
"Ten Lords a Leaping",
"Nine Ladies Dancing",
"Eight Maids a Milking",
"Seven Swans a Swimming",
"Six Geese a Laying",
"Five Golden Rings",
"Four Calling Birds",
"Three French Hens",
"Two Turtle Doves",
"A Partridge in a Pear Tree"};
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.
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.
Come to class prepared to present your report to the class. Students will be selected at random to present their reports.
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