Ch20 – Signals: Fundamental Concepts

 

Ch20 – Signals: Fundamental Concepts
Chapter Summary & Complete Index Β· EmbeddedPathashala
πŸ“š 19 Topics
πŸ’» 40+ Code Examples
❓ 50+ Interview Q&A
🎯 Linux System Programming
Chapter Overview

Chapter 20 covers the foundational concepts of Linux signals β€” the software interrupt mechanism that lets the kernel, other processes, and a process itself notify a running program that an event has occurred. From understanding what a signal is, through the complete API for sending, catching, blocking, and waiting for signals, this chapter is the essential foundation for reliable systems programming.

Signals appear heavily in Linux interview questions for embedded, systems, and kernel roles. This index links to all 19 dedicated topic pages, each with code examples and interview Q&A.

πŸ“Š Quick Reference: Key API Summary
Function Purpose Returns
kill(pid, sig) Send signal to process(es) 0 or -1
raise(sig) Send signal to self 0 or nonzero
killpg(pgid, sig) Send signal to process group 0 or -1
signal(sig, handler) Set signal disposition (legacy) old handler or SIG_ERR
sigaction(sig, act, old) Set/get signal disposition (POSIX) 0 or -1
sigprocmask(how, set, old) Examine/change signal mask 0 or -1
sigpending(set) Get set of pending signals 0 or -1
pause() Wait until any signal arrives -1 (EINTR)
sigemptyset(set) Initialize set to empty 0 or -1
sigfillset(set) Initialize set to all signals 0 or -1
sigaddset(set, sig) Add signal to set 0 or -1
sigdelset(set, sig) Remove signal from set 0 or -1
sigismember(set, sig) Test if signal is in set 1 (yes), 0 (no), -1 (error)
strsignal(sig) Return signal description string char* (not thread-safe)
❓ Comprehensive Interview Q&A β€” Chapter 20
Q1. What is a signal and how is it different from a hardware interrupt?
A signal is a software notification to a process that an event has occurred. Hardware interrupts are handled by the kernel’s interrupt handlers at the CPU level; signals are a kernel-to-process notification mechanism. Signals can be generated by hardware faults (SIGSEGV, SIGBUS), user keystrokes (SIGINT), timers (SIGALRM), or explicitly by another process via kill(). Signals interrupt the normal flow of execution, much like hardware interrupts, but at the process level.
Q2. What are the three possible dispositions of a signal?
1) SIG_DFL β€” perform the default action (terminate, core dump, stop, continue, or ignore depending on the signal). 2) SIG_IGN β€” discard the signal. 3) Custom handler β€” call a programmer-defined function when the signal arrives. Note: SIGKILL and SIGSTOP cannot have their disposition changed.
Q3. What are the five default actions a signal can have?
Term (terminate), Core (terminate + core dump), Ignore, Stop (suspend execution), and Cont (resume if stopped).
Q4. Which signals cannot be caught, blocked, or ignored?
SIGKILL and SIGSTOP. These are the two unconditional control signals β€” SIGKILL always terminates, SIGSTOP always stops. Any attempt to block them via sigprocmask() or install a handler via sigaction() is silently ignored.
Q5. Explain the difference between signal generation and signal delivery.
Generation is the event that causes a signal to be raised (e.g., kill() called, hardware fault, timer expiry). Delivery is the actual action taken by the process in response (handler called, default action performed, etc.). A signal that has been generated but not yet delivered is pending. Signals remain pending while they are blocked in the process’s signal mask.
Q6. What does kill() do when pid is 0? When pid is -1?
pid = 0: sends to every process in the same process group as the caller. pid = -1: sends to every process the caller has permission to signal (except process 1, init). pid > 0: sends to that specific process. pid < -1: sends to every process in process group |pid|.
Q7. How do you test if a process exists without actually sending a signal?
Use kill(pid, 0) β€” the null signal. If it returns 0, the process exists and you have permission to send signals. If it returns -1 with errno=ESRCH, the process does not exist. If errno=EPERM, the process exists but you lack permission. Watch out for zombie processes and PID recycling.
Q8. How many times is a standard signal delivered if sent 100 times while it is blocked?
Once. Standard signals (1–31) are represented in the pending set as a bitmask β€” one bit per signal. No matter how many times it is sent, only one bit is set. When unblocked, it is delivered exactly once. Realtime signals (SIGRTMIN–SIGRTMAX) are queued and delivered once per send.
Q9. What is sigprocmask() and what are SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK?
sigprocmask() modifies the calling process’s signal mask (the set of currently blocked signals). SIG_BLOCK: add signals in set to the mask. SIG_UNBLOCK: remove signals in set from the mask. SIG_SETMASK: replace the mask entirely with set. The previous mask can be saved in oldset for later restoration.
Q10. Why use sigaction() instead of signal()?
signal() behaviour varies across UNIX implementations β€” on some, the handler is reset to SIG_DFL after delivery (unreliable signals); SA_RESTART behaviour is also inconsistent. sigaction() has well-defined POSIX semantics, lets you specify sa_mask (signals blocked during handler), sa_flags (SA_RESTART, SA_NODEFER, SA_RESETHAND, SA_SIGINFO), and retrieve existing disposition atomically. Always prefer sigaction() in portable, production code.
Q11. What does SA_RESTART do and when would you not want it?
SA_RESTART causes the kernel to automatically restart slow system calls (read, write, accept, etc.) that were interrupted by the signal handler, rather than returning -1/EINTR. You would NOT want it if your program explicitly needs to notice that a system call was interrupted β€” for example, to implement a timeout loop or to check a flag after each possible interruption point.
Q12. What is the race condition with while (!flag) pause(); and how is it fixed?
If the signal fires between the flag check and the pause() call, flag is set but pause() has not been entered yet. When pause() eventually runs, no signal is pending, so the process sleeps forever. The fix is sigsuspend(&empty_mask), which atomically unblocks signals and suspends β€” eliminating the window between checking the flag and sleeping.
Q13. Why must you use volatile sig_atomic_t for a flag shared between a handler and main code?
volatile prevents the compiler from caching the variable in a register and optimizing away re-reads (a while loop may otherwise become an infinite loop because the compiler assumes the variable never changes). sig_atomic_t is the only integer type guaranteed by the C standard to be read and written atomically on the target architecture, avoiding partial-update corruption.
Q14. What information does /proc/PID/status provide about signals?
Five bitmask fields in hexadecimal: SigPnd (per-thread pending), ShdPnd (process-wide pending, Linux 2.6+), SigBlk (blocked), SigIgn (ignored), SigCgt (caught/handled). Bit N-1 set means signal N is in that set. Useful for debugging signal state without attaching a debugger.
Q15. What is the purpose of the sa_mask field in struct sigaction?
sa_mask specifies additional signals to block for the duration of the signal handler’s execution (on top of the automatically blocked triggering signal itself). This ensures that a related signal cannot interrupt critical logic inside the handler.
πŸ”‘ Key Signals Cheat Sheet
Signal Default Action Common Cause
SIGINT (2) Terminate Ctrl-C from terminal
SIGTERM (15) Terminate kill PID (default signal)
SIGKILL (9) Terminate (uncatchable) kill -9 PID
SIGSEGV (11) Core dump Invalid memory access
SIGCHLD (17) Ignore Child terminates/stops
SIGALRM (14) Terminate alarm() or setitimer() expiry
SIGUSR1 (10) Terminate User-defined use
SIGUSR2 (12) Terminate User-defined use
SIGSTOP (19) Stop (uncatchable) kill -STOP PID
SIGCONT (18) Continue kill -CONT PID
SIGHUP (1) Terminate Terminal closed / daemon reload
SIGABRT (6) Core dump abort() called

Leave a Reply

Your email address will not be published. Required fields are marked *