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

Signal Masks and Critical Sections

Explain how to prevent race conditions by blocking signals during time-sensitive operations.

In this lesson, you will learn how to protect sensitive blocks of code from being interrupted by asynchronous signals using signal masks.

The Race Condition Problem

In previous lessons, we discussed how signals act like asynchronous interrupts. When a signal arrives, the kernel pauses your process’s current execution to run a signal handler. If your code is in the middle of updating a shared data structure—for example, adding a node to a linked list—and a signal handler fires that tries to read or modify that same structure, your program will likely crash or suffer from data corruption. This scenario is a classic race condition.

The Solution: Signal Masks

To prevent this, we use a signal mask. Think of the signal mask as a "Do Not Disturb" sign for your process. When you add a specific signal to the mask, the kernel holds that signal in a pending state. The signal is not delivered to your process until you explicitly remove it from the mask.

A "Critical Section" is the block of code that must run from start to finish without interruption. By blocking signals before entering this block and unblocking them after, you guarantee that no signal handler will interfere with your sensitive operations.

Conceptual Workflow

While the specific system calls (sigprocmask) are the tools of the trade, the logic follows this simple pattern:

// 1. Define a mask that includes the signals we want to block
sigset_t new_mask, old_mask;
sigemptyset(&new_mask);
sigaddset(&new_mask, SIGINT);

// 2. Block the signal and save the previous state
sigprocmask(SIG_BLOCK, &new_mask, &old_mask);

// 3. START CRITICAL SECTION
// Modify shared data structure safely here
// 4. END CRITICAL SECTION

// 5. Restore the original mask to allow signals again
sigprocmask(SIG_SETMASK, &old_mask, NULL);

By wrapping your logic this way, you create an "atomic" operation relative to the signals you’ve masked. The kernel ensures that if a SIGINT occurs during your critical section, the signal remains pending and won't trigger the handler until you restore the previous mask at the end of the block.

Key Takeaway: Use signal masks to create critical sections, ensuring that time-sensitive operations remain protected from asynchronous interruption and data corruption.