signals · sigaction, masks, alarm
Signals are interrupts at the userspace level. Press Ctrl-C five times. The fifth one trips a threshold that arms SIGALRM. If you don't respond in 5 seconds, the kernel sends the alarm and the process exits.
SIGINT 5 times to trigger the prompt. Then either respond before the 5-second alarm fires or let it expire. Then restart and observe what happens when you fire SIGINT during the prompt — the count keeps ticking.Source
// signals · sigaction(SIGINT), SIGTSTP, SIGALRM
int ctrl_c_count = 0;
#define CTRL_C_THRESHOLD 5
void catch_int(int sig) {
if (++ctrl_c_count >= CTRL_C_THRESHOLD) {
alarm(5); // arm a 5-second SIGALRM
fgets(answer, sizeof(answer), stdin);
alarm(0); // cancel if user responded in time
}
}
void ctch_alarm(int sig) { exit(0); }
int main() {
sigaction(SIGINT, &(struct sigaction){.sa_handler=catch_int}, NULL);
sigaction(SIGTSTP, &(struct sigaction){.sa_handler=catch_tstp}, NULL);
sigaction(SIGALRM, &(struct sigaction){.sa_handler=ctch_alarm}, NULL);
while (1) pause(); // wait for signals
}What's really happening
A signal is the kernel's way of telling a process that something interesting just happened — keyboard interrupt, timer expiry, illegal memory access. Each process has a per-signal handler table (set via sigaction), a pending mask (signals delivered but not yet run), and a blocked mask (signals temporarily ignored).
SIGINT ships when you press Ctrl-C in the terminal. SIGTSTP is Ctrl-Z (job control). SIGALRM is delivered by the kernel when an alarm() timer expires. The handlers above are async-signal-safe in spirit — they only set state and call simple I/O.
Reentrancy: a signal can fire while another signal handler is still running. That's why this lab counts SIGINTs delivered during the prompt — they still execute catch_int, still increment the counter, but the prompt's fgets blocks the main loop until the user types or the alarm fires.