Course contentsShow
← Linux
Lesson 491 of 1,8195. Process Management and SignalingFree lesson

Atomic Signal Handling

Use atomic signal handling techniques to ensure system integrity during high-frequency interrupts.

In this lesson, you will learn how to use atomic signal handling to maintain data integrity when your program is bombarded by high-frequency interrupts.

The Problem: Partial Updates

When a signal arrives, the operating system interrupts your main program flow to execute your signal handler. If your main code is halfway through updating a complex data structure (like a multi-byte counter or a linked list) when that interrupt fires, and your signal handler tries to access that same data, you encounter a "race condition." The signal handler might read a half-written, corrupted value, leading to unpredictable crashes or logical errors.

The Solution: Atomic Variables

To prevent this, we use the concept of atomic operations. An atomic operation is one that completes entirely in a single, uninterruptible step from the perspective of the rest of the system. In the context of signals, we need to ensure that the variables shared between our main code and our signal handler cannot be "torn" or partially updated.

In C, we typically use the sig_atomic_t type. This type guarantees that reads and writes to the variable are atomic, meaning no signal handler can catch the variable in an inconsistent, "in-progress" state.

#include <signal.h>

/* Use volatile to prevent compiler optimization 
   that might cache this value in a register */
volatile sig_atomic_t event_flag = 0;

void handler(int sig) {
    // This is safe because assignment to sig_atomic_t is atomic
    event_flag = 1;
}

int main() {
    signal(SIGINT, handler);
    while (1) {
        if (event_flag) {
            // Process the signal event safely
            event_flag = 0;
        }
    }
}

Why this works

By declaring event_flag as volatile sig_atomic_t, we instruct the compiler two things:

  1. volatile: Do not optimize this variable by storing it in a CPU register; always read it from memory, as it may change outside the program's normal flow.
  2. sig_atomic_t: Guarantee that the CPU updates the memory location in one single cycle that cannot be interrupted by the delivery of a signal.

This ensures that your signal handler and your main loop remain perfectly synchronized, even under high-frequency signal bursts.

Key Takeaway: Use volatile sig_atomic_t for shared flags between handlers and main threads to ensure data consistency during asynchronous interruptions.