Lab 3 — Signals, Signal Masks, and Signal Handling

CMPS 3600 • Fall 2025

Overview

This lab introduces POSIX signals on Linux: basic signal handling, signal masks, and using kill(2) to generate signals between a parent and child process. You’ll practice registering handlers with sigaction, masking with sigprocmask/sigset_t, and synchronizing with sigsuspend.

Learning Objectives

Why this matters: Correct signal handling is essential for robust Unix programs and for understanding process coordination and asynchronous events at the OS boundary.

Resources

Use man 7 signal, man 2 sigaction, man 2 kill, etc.

Step 1 — Setup

$ cd ~
$ cd cs3600
$ ./lab-start.sh
$ cd 3
$ cp /home/fac/dfanucchi/public_html/cs3600/examples/3/*.c .
$ cp /home/fac/dfanucchi/public_html/cs3600/examples/3/Makefile .
$ make

Read the headers and comments in each example; run them as directed to build intuition before coding vrlab3.c.

Step 2 — Assignment (vrlab3.c)

Create the vrlab3.c file, it is not included this week.

Your program forks one child. The child logs messages around a sigsuspend wait; the parent sends SIGTERM then SIGUSR1 to the child, and waits for the child to exit.

Required functions (use in your solution)

Headers you will need
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/file.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>

High-level flow

  1. Declare a sigset_t mask; fill it with all signals via sigfillset.
  2. Block every signal in the current process via sigprocmask(SIG_BLOCK, &mask, ...).
  3. Install a SIGUSR1 handler using sigaction before fork().
  4. fork() a child.
    • Child: open log, write "Go CSUB", prepare a mask that blocks all signals except SIGUSR1 (sigdelset(&unblock_usr1, SIGUSR1)), then call sigsuspend(&unblock_usr1). In the handler, append " (got the signal) " to the log and return. After sigsuspend returns, write "Roadrunners!", close the log, and exit(0).
    • Parent: immediately send SIGTERM then SIGUSR1 to the child via kill, then wait(&status). Print the child’s exit status.
Race conditions & deadlock: Blocking all signals before the fork protects both processes until the child intentionally opens SIGUSR1 during sigsuspend. If the child never unmasks SIGUSR1, the parent and child will deadlock (parent waits in wait(); child waits for a blocked signal).

Build & Run

$ make
$ ./vrlab3
child terminated with code 0

$ cat log
Go CSUB (got the signal) Roadrunners!

Trace with strace

$ strace -e trace=signal -f ./vrlab3 2> err
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 [CHILD pid] attached
[pid PARENT] kill(CHILD, SIGTERM) = 0
[pid PARENT] kill(CHILD, SIGUSR1) = 0
Process [PARENT pid] suspended
[pid CHILD ] rt_sigsuspend(~[USR1 RTMIN RT_1]) = ? ERESTARTNOHAND (To be restarted_
[pid CHILD ] --- SIGUSR1 --- (User defined signal 1) @ 0 (0) ---
[pid CHILD ] rt_sigreturn(0x3) = -1 EINTR (Interrupted system call)
Process [PARENT pid] resumed
Process [CHILD pid] 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.

Files to Submit / Check

  • ~/cs3600/3/vrlab3.c
  • ~/cs3600/3/Makefile