Signals are a simple yet powerful way for Linux processes to communicate in pre-defined ways. They are ideally suited for inter-process event handling during system operation. Signals can be sent to any process in the system, and can be recieved by defining special functions called signal handlers, which are executed upon receipt of the signal.
In this studio, you will:
Please complete the required exercises below, as well as any optional enrichment exercises that you wish to complete.
As you work through these exercises, please record your answers, and when finished email your results to dferry@email.wustl.edu with the phrase Linux Signals in the subject line.
Make sure that the name of each person who worked on these exercises is listed in the first answer, and make sure you number each of your responses so it is easy to match your responses with each exercise.
dense_mm
program that we used earlier. Build this program and
run it.
While this program is executing, send it the keyboard interrupt signal
(SIGINT
) by highlighting its terminal and pressing CTRL+C
(hold down the CTRL button and press C). As the answer to this exercise,
copy and paste the result from successfully interrupting your program.
man 7 signal
. Some commonly used signals are SIGINT
(keyboard interrupt), SIGQUIT
(quit from keyboard), and
SIGTERM
(terminate process). Every signal in the system is
associated with a default action (which typically terminates the recieving
process), but this can be overridden simply by defining
your own signal handling function, which is what we will now do.
The modern, portable way to define a signal handler is to use the function
sigaction()
which is documented at man sigaction
. Use
this function to define a new handler for SIGINT
. You will need to:
void function_name (int arg)
struct sigaction
in your main()
function
sa_handler
field of your struct sigaction
to your signal handling function
sa_flags
field of your struct sigaction
to SA_RESTART
sigaction()
function with SIGINT
as the signum argument and a pointer to your struct sigaction
for the act argument.
Declare a global counter and increment it when the handler function is
called. At the end of your main()
function print the value of
this counter. Copy and paste a sample program output as the answer to this
exercise.
NOTE: Overriding the SIGINT
handler may make it
difficult to terminate your program. You can alternately use CTRL+\ to send
the SIGQUIT
signal or the kill
program to terminate
your processes.
Even library functions are not immune to problems with asynchronicity.
Download the program print5000.c
, build
and run it. This is a very simple program that defines a signal handler for
SIGINT
, and then prints the numbers from 1 to 5000 in order.
Try running this program and interrupting it with SIGINT
. Inspect
the program output where it was possibly interrupted by a signal handler.
What happens? Copy and paste evidence.
printf()
are not guaranteed to execute
correctly when called from within a handler, since they may be called
concurrently from different contexts.
Fortunately, there is a list of functions that is guaranteed to be safe
when called from a handler. These functions are given in the signal documentation
at man 7 signal
under "Async-signal-safe functions".
Going back to your original program, use the write()
function
documented at man 2 write
to produce output from within a signal
handler. In particular, you must:
char* message = "error message\n"
)
write()
function with argument fd equal to zero to write to standard output (e.g. write(0, message, num_chars);
)