Ch20.18 โ Waiting for a Signal: pause()
Linux System Programming ยท EmbeddedPathashala
๐ก Topic 18 of 19
๐ฏ Beginner
๐ป 3 Code Examples
โ Interview Q&A
๐ Key Terms
pause() EINTR signal-driven wait busy wait sigsuspend() sleep() event loop
What does pause() do?
pause() suspends the calling process until any signal is delivered. If the signal has a handler, the handler runs first; then pause() returns. If the signal terminates the process, pause() never returns.
It is the simplest way to make a process sleep and wait for work driven by signals โ consuming zero CPU while waiting, unlike a busy-wait loop.
๐ Function Prototype
#include <unistd.h>
int pause(void);
/* Always returns -1 with errno set to EINTR */
pause() always returns -1 and always sets errno to EINTR. This is not an error โ it simply means the call was interrupted by a signal, which is the only way it ever returns.๐ pause() Execution Flow
| Step | What Happens |
|---|---|
| 1 | Process calls pause() โ goes to sleep (blocked state) |
| 2 | Signal arrives (from kernel, another process, or itself) |
| 3a (if handled) | Signal handler executes. After it returns, pause() returns -1 / EINTR |
| 3b (if ignored) | Signal is discarded. pause() does NOT return โ continues sleeping |
| 3c (if default=terminate) | Process is terminated. pause() never returns. |
Ignored signals do not wake pause(). Only a signal that is caught (via a handler) or that terminates the process will cause pause() to return or exit.
๐ป Code Example 1 โ Simple signal-driven event loop
/* Wait for signals using pause() in a loop
Compile: gcc -o pause_demo pause_demo.c
Run: ./pause_demo then send: kill -USR1 <PID> or press Ctrl-C */
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
static volatile sig_atomic_t quit = 0;
static void usr1_handler(int sig) {
write(STDOUT_FILENO, "SIGUSR1 received\n", 17);
}
static void sigint_handler(int sig) {
quit = 1;
}
int main(void)
{
struct sigaction sa;
/* Install SIGUSR1 handler */
sa.sa_handler = usr1_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sigaction(SIGUSR1, &sa, NULL);
/* Install SIGINT handler */
sa.sa_handler = sigint_handler;
sigaction(SIGINT, &sa, NULL);
printf("PID %ld โ waiting for SIGUSR1. Ctrl-C to quit.\n", (long)getpid());
while (!quit) {
pause(); /* sleep until any signal is caught */
/* errno is EINTR here โ that is normal and expected */
}
printf("Exiting on SIGINT.\n");
return 0;
}
๐ป Code Example 2 โ Busy wait vs. pause() comparison
/* --- WRONG: Busy wait burns 100% CPU --- */
static volatile sig_atomic_t flag = 0;
void handler(int s) { flag = 1; }
/* BAD */
while (!flag)
; /* spins, wastes CPU */
/* --- CORRECT: use pause() --- */
while (!flag)
pause(); /* sleeps until a signal wakes it */
/* Note: there is a race condition above โ if the signal arrives
between checking flag and calling pause(), the process will
sleep forever. The proper fix is sigsuspend() (Chapter 22). */
Race condition warning:
while (!flag) pause(); has a tiny window where the signal can arrive after checking flag but before entering pause(). In that case the process sleeps indefinitely. The correct solution for production code is sigsuspend(), covered in Section 22.9.๐ป Code Example 3 โ Using pause() as a simple sleep-until-signal
/* A daemon-style loop: do periodic work on SIGALRM
Compile: gcc -o alarm_loop alarm_loop.c
Run: ./alarm_loop */
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
static volatile sig_atomic_t do_work = 0;
static void alarm_handler(int sig)
{
do_work = 1;
}
int main(void)
{
struct sigaction sa;
sa.sa_handler = alarm_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGALRM, &sa, NULL);
printf("Periodic tick every 2 seconds. Ctrl-C to stop.\n");
alarm(2); /* schedule first SIGALRM */
for (;;) {
pause(); /* sleep until SIGALRM (or any other signal) */
if (do_work) {
do_work = 0;
printf("Tick! Doing periodic work...\n");
alarm(2); /* reschedule */
}
}
return 0;
}
๐ pause() vs. Other Waiting Methods
| Method | Wakes on | Mask control? | Notes |
|---|---|---|---|
| pause() | Any caught signal | No | Simple; has race condition risk |
| sigsuspend() | Any caught signal | Yes (atomic) | Race-free; preferred for production |
| sleep() | Timer or caught signal | No | Has timeout; interrupted by SIGALRM |
| select()/poll() | I/O or timeout | No (pselect adds it) | For I/O multiplexing, not signal waiting |
โ Interview Questions
Q1. What does pause() return and why?
pause() always returns -1 with errno set to EINTR. It has no other return path. EINTR here is informational โ it means “a signal was caught and handled; you may retry or continue.”
Q2. Does pause() return if a signal is ignored?
No. Only a signal that is caught (a handler runs) or that terminates the process will cause pause() to return or exit. An ignored signal is discarded without waking the process.
Q3. What is the race condition in
while (!flag) pause();?If the signal arrives between the check of
flag and the call to pause(), the flag is set but pause() has not yet been entered. The program then calls pause() and sleeps indefinitely because there is no pending signal left to wake it. The fix is sigsuspend(), which atomically unmasks signals and sleeps.Q4. When is pause() appropriate vs. sigsuspend()?
pause() is appropriate for simple programs where the race condition is acceptable (e.g., demos, test code, or where SIGALRM is the only signal and the window is negligible). sigsuspend() is the correct choice for production code that needs to atomically wait for a specific signal while blocking others.
Continue Learning
