Simple, Simon and Sage signaling.
Gemini 2.5 September 2025

Signal Ping Pong Project

by: burt rosenberg & ChatGPT
at: university of miami and cyberspace
date: sep 2025

Goals

  1. Gain familiarity with system calls.

    Implement and use low-level UNIX system calls such as getpid(), fork(), kill() and pause(). Students should consult appropriate man pages (e.g. man 2 fork).

  2. Understand process creation with fork().

    Using fork(), a process creates a child process that runs concurrently. Students learn how the child inherits the parents environment, and how the parent and child can interact, synchronize, and terminate cleanly.

  3. Understand inter-process communication (IPC) using signals.

    Signals are a basic asynchronous IPC mechanism. Exchanging SIGHUP signals between a parent and child process demonstrates the processes can send and receive messages.

  4. Study how signals are delivered and handled in the context of thread time-slicing.

    Threads are can be run or wait. The scheduler time-slices threads so they get cycles on a hardware thread. Another example is how a signal is delivered to a process. The scheduler either interrupts or uses the normal thread pre-emption and schedules the signal handler code for the thread's next time-slice. The handler exits and then the scheduler resumes scheduling the main thread.

  5. To write a small Makefile

    As part of this project, and graded, is that you have been provided with a Makefile with three targets. Two of the three are not complete, and you must complete them.

Problem Description

Write a C program that creates a child process with fork(). The parent and child alternately send each other the SIGHUP signal. Each time a process receives SIGHUP, it decrements a counter and prints out the value. The counter is initialized to a pre-defined MAX_COUNT. When the counter is decremented to zero, program prints out BOOM!. Both processes are expected to terminate cleanly.

Introduction to Signals

A signal is a limited form of inter-process communication in UNIX/Linux, used to notify a process that a particular event has occurred. Given a PID pid and a signal sig, a thread calling kill(pid,sid) sends a signal to the referenced process. The signal is posted into a vector in the PCB of the receiving process for delivery. A function vector associates a signal handler with the signal.

The sig to use for this project is SIGHUP defined in signal.h. The handler is installed into the function vector with,

   sigaction(SIGHUP, struct sigaction *, NULL)
and the signal is sent by,
   kill(pid_t, SIGHUP)

When a thread for the process is readied to run the signal vector is consulted for any pending signals. If there is a pending signal, rather than return to where the thread left off, the signal handling code is run.

A process can wait for a signal by calling pause() ( see man 2 pause). This call puts the thread in a wait state, waiting for a signal. When the signal is received the thread is moved to the ready state. The scheduler will run the signal handler on that thread before continuing the code after the pause statement.

In this assignment, you will have to install a signal handler, but I (with ChatGPT) have written the signal handler for you. These things are tricky to get right. So the design principle was to make the handler as simple as possible. It simply sets a flag that the signal has been delivered.

SAMPLE RUN

% ./signal-ping-pong
P: started ...
C: started ...
P: 10
C: 10
P: 9
C: 9
P: 8
C: 8
P: 7
C: 7
P: 6
C: 6
P: 5
C: 5
P: 4
C: 4
P: 3
C: 3
P: 2
C: 2
P: 1
C: 1
P: 0
P: BOOM!
C: 0
C: BOOM!
%

Operation

The concept is each the child and parent use pause to wait for the signals. The hup_handler set got_hup_g and exits. The main thread resets got_hup_g. The variable makes sure the HUP code is run only if the wake up was because of a HUP signal.

  1. The child goes first with a HUP to the parent then pauses.
  2. The parent has paused (or would have paused) so it returns (or runs the handler then continues on). It resets got_hup_g, decrements then prints count; then signals the child. It then pauses again.
  3. The child receives the parent's HUP, returns from the pause and does similarly: got_hup_g, decrements then prints count. It then signals the parent and pauses.
  4. When the parent's count becomes 0, it prints BOOM!, HUPs the child one more time and calls wait, to only exit when the child has exited. Else it is possible that the HUP will be lost and the child would be stuck.
  5. When the child's count becomes 0, it prints BOOM! and exits. This exit will release the parent from its wait, and the parent will exit as well.
Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.

author: burton rosenberg
created: 8 sep 2025
update: 9 sep 2025