This lab introduces
A solution to this lab will likely include the following functions: a handler - your own function definition. sigfillset - initialize and fill a signal set sigprocmask - examine and change blocked signals (here is where you block all signals) sigaction - examine and change a signal action (register a handler, required) sigdelset - delete a signal from a signal set sigsuspend - wait for a signal kill - send a signal to a process wait - wait for a child process to end open - open a file write - write to a file close - close a file The general idea is this... 1. declare a mask 2. fill the mask 3. block every signal in the mask 4. remove one signal from the blocked mask
Lab-3 introduces the use of signals, signal masks and signal handling by using kill(2) to send signals from parent to child. You will be introduced to the challenges involved in synchronizing multiple processes. Refer to the reference links above for detailed help. There are several signal handling intefaces available under Linux (BSD and ANSI-C/POSIX). We will use the POSIX (Portable Operating System Interface) signal handling interface (which is a newer one) although you may see some code written in older interfaces.
You will communicate between a parent and its child through signals delivered by the kill system call. Delivering a signal is done in kernel mode. The kernel determines whether one process has permission to send a signal to another process. Kill is also a shell command; i.e., from the shell a user can send signals SIGTOP, SIGCONT and SIGTERM to a background process. For example:
$ ./a.out & Child PID is 32360 [1] 32359 $ kill -STOP 32360 stopped by signal 19 $ kill -CONT 32360 continued $ kill -TERM 32360 killed by signal 15 [1]+ Done ./a.out $Before starting to code understand the concepts covered in the example files for this week. Copy all files including the Makefile into your 3600/3 directory. Then compile all source files:
$ cd $ cd 3600/3 $ cp /home/fac/gordon/public_html/3600/examples/3/* . $ makeFollow the instructions in the header of each file to execute and test. Read the code and the comments in each file.
LAB INSTRUCTIONS.
Your job is to write a program mylab3.c that forks one child. The child
opens log and writes "Go CSUB" to the file.
The child then goes into a ~~ deep sleep ~~ with sigsuspend(2).
Sigsuspend is a system call that waits until a signal wakes it up.
While in sigsuspend block all signals except SIGUSR1. You can
achieve this because the mask that you pass to sigsuspend goes into
effect at the time of the call and then the original mask goes back into
effect as soon as sigsuspend returns. The mask that you pass
needs to be full except for SIGUSR1. This ensures that
the only signal that will wake the child up is SIGUSR1.
Note:
The SIGUSR1 and SIGUSR2 signals are set aside for you to use any way you want.
triggering SIGUSR1 & 2
Next, code a SIGUSR1 signal handler for the child. In the signal handler the child writes " (got the signal) " to its log and returns; it does not call exit(). The child writes "Roadrunners!", closes its log file and exits with status code 0. Here is a snippet of the child's code:
write(logfd, "Go CSUB", 7); # line 1 sigsuspend(&mask); # line 2 write(logfd, "Roadrunners!", 12); # line 3 close(logfd); # line 4 exit(0); # line 5
Meanwhile,
after the fork the parent is going to immediately send two signals to its child:
SIGTERM followed by SIGUSR1, call wait(2), harvest the child's code, display
the code on the screen and exit. There are a few things
that can go very wrong in this simple scenario.
First, since the child does not have a handler for SIGTERM the child will
immediately terminate unless the child's process blocks this signal.
Second, the child will also terminate prematurely if the parent sends
SIGUSR1 to its child before the child has a chance to setup its
SIGUSR1 handler, open its log file and go into sigsuspend.
In the snippet of the child's code above,
these scenarios occur if either signal is delivered before line #2.
In either case the child will immediately
terminate since by default SIGUSR1 and SIGTERM terminate the process.
It is a race condition as to which line of code in parent and child executes first. The solution to this problem is to protect the parent and the child by blocking all signals before the fork. Since the child inherits its parent's process mask the child is immediately protected.
You should also setup the SIGUSR1 handler before the fork. The child will have a handler ready to activate.
To ensure that SIGTERM does not sneak in while the handler is being executed, block all signals in the handler. If you do all this you are safe.
But there is another potential problem to be solved...
Let's say the child's process mask is immediately
blocking all signals. Right after the fork
the parent sends the two signals and then
goes into wait().
If the child does not change its process mask to allow
SIGUSR1 in before it goes into sigsuspend,
the parent and child will be deadlocked;
i.e., the child is waiting on a signal
from the parent that it will never receive because it is blocked
and the parent is waiting on
a signal from its child that it will never receive because the child
is waiting too.....
To prevent deadlock you must pass a process mask to sigsuspend that opens up SIGUSR1 only (if you open up everything then SIGTERM will sneak in and the child will terminate prematurely).
If all is working properly, you should be able to run your code through strace and see the process mask change, the signal handler setup, the two signals from parent to child and the SIGUSR1 signal received from the child after the sigsuspend call. Output from program runs:
$ ./lab3 child terminated with code 0 $ cat log Go CSUB (got the signal) Roadrunners!
$ strace -e trace=signal -f ./lab3 2> err # -f means trace child child terminated with code 0 $ cat err rt_sigprocmask(SIG_BLOCK, ~[RTMIN RT_1], NULL, 8) = 0 rt_sigaction(SIGUSR1, {0x400ba5, ~[RTMIN RT_1], SA_RESTORER|SA_RESTART, 0x7fdbcbbd21e0}, NULL, 8) = 0 Process 8100 attached [pid 8099] kill(8100, SIGTERM) = 0 [pid 8099] kill(8100, SIGUSR1) = 0 Process 8099 suspended [pid 8100] rt_sigsuspend(~[USR1 RTMIN RT_1]) = ? ERESTARTNOHAND (To be restarted) [pid 8100] --- SIGUSR1 (User defined signal 1) @ 0 (0) --- [pid 8100] rt_sigreturn(0x3) = -1 EINTR (Interrupted system call) Process 8099 resumed Process 8100 detached $ cat log Go CSUB (got the signal) Roadrunners!
Note. In the above output two realtime signals are not blocked (RTMIN and RT_1). To manipulate realtime signals you need different system calls: Linux 2.2 and later: rt_sigaction(2), rt_sigpending(2), rt_sigprocmask(2), rt_sigreturn(2), rt_sigsuspend(2), rt_sigtimedwait(2). Don't be concerned about realtime signals for this lab.
What to turn in...
At 9:50am I will get a snapshot of your files 3600/3/mylab3.c 3600/3/Makefile