Signals and Nonlocal jump
8.5.4 Blocking and Unblocking Signals
Linux provides implicit and explicit mechanisms for blocking signals:
- Implicit blocking mechanism
- By default, the kernel blocks any pending signals of the type currently being processed by a handler.
- For example, in Figure 8.31, suppose the program has caught signal s and is currently running handler S. If another signals is sent to the process, then s will become pending but will not be received until after handler S returns.
- Explicit blocking mechanism
- Applications can explicitly block and unblock selected signals using the
sigprocmaskfunction and its helpers.
- Applications can explicitly block and unblock selected signals using the
1 |
|
The sigprocmask function changes the set of currently blocked signals (a bit vector )
The specific behavior depends on the value of how:
- SIG_BLOCK. Add the signals in set to blocked
(blocked = blocked | set). - SIG_UNBLOCK. Remove the signals in set from blocked
(blocked = blocked & ~set) - SIG_SETMASK.
blocked = set.
If oldset is non-NULL, the previous value of the blocked bit vector is stored in oldset
- The
sigemptysetinitializes set to the empty set. - The
sigfillsetfunction adds every signal to set. - The
sigaddsetfunction adds signum to set,sigdelsetdeletes signum from set, andsigismemberreturns 1 if signum is a member of set, and 0 if not.
1 | sigset_t mask, prev_mask; |
8.5.5 Writing Signal Handlers
Handlers have several attributes that make them difficult to reason about
- Handlers run concurrently with the main program and share the same global variables, and thus can interfere with the main program and with other handlers
- The rules for how and when signals are received is often counterintuitive
- Different systems can have different signal-handling semantics
In this section, we address these issues and give you some basic guidelines for writing safe, correct, and portable signal handlers.
Safe Signal Handling
Keep handlers as simple as possible.
For example, the handler might simply set a global flag and return immediately, and then all processing associated with the receipt of the signal is performed by the main program, which periodically checks (and resets) the flag.
Call only async-signal-safe functions in your handlers.
These functions can be safely called from a signal handler, either because it is reentrant (e.g., accesses only local variables), or because it cannot be interrupted by a signal handler.
Notice that many popular functions, such as
printf, sprintf, malloc,andexit, are not safeThe only safe way to generate output from a signal handler is to use the
writefunction

Save and restore
errno- save
errnoto a local variable on entry to the handler and restore it before the handler returns to prevent other handler from interfering it. - It is not necessary if the handler terminates the process by calling
_exit.
- save
Protect accesses to shared global data structures by blocking all signals.
Declare global variables with
volatileYou can tell the compiler neither to cache a variable or put it in register by declaring it with the
volatiletype qualifierThe
volatilequalifier forces the compiler to read the value from memory each time the value is referenced in the code
Declare flags with
sig_atomic_t- C provides an integer data type,
sig_atomic_t, for which reads and writes are guaranteed to be atomic (uninterruptible) because they can be implemented with a single instruction
- C provides an integer data type,
Correct Signal Handling
Since pending signals are implemented with a bit vector, it can only record 1 signal each type, so signals cannot be used to count the occurrence of events in other processes
Practice Problem 8.8
What is the output of the following program?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 volatile long counter = 2;
void handler1(int sig){
sigset_t mask, prev_mask;
sigfillset(&mask);
sigprocmask(SIG_BLOCK, &mask, &prev_mask); /* Block sigs */
sio_putl(--counter);
sigprocmask(SIG_SETMASK, &prev_mask, NULL); /* Restore sigs */
_exit(0);
}
int main(){
pid_t pid;
sigset_t mask, prev_mask;
printf("%ld", counter);
fflush(stdout);
signal(SIGUSR1, handler1);
if ((pid = Fork()) == 0){
while(1) {};
}
kill(pid, SIGUSR1);
waitpid(-1, NULL, 0);
sigfillset(&mask);
sigprocmask(SIG_BLOCK, &mask, &prev_mask); /* Block sigs */
printf("%ld", ++counter);
sigprocmask(SIG_SETMASK, &prev_mask, NULL); /* Restore sigs */
exit(0);
}
My solution: :white_check_mark:
1 | 213 |
Portable Signal Handling
Another ugly aspect of Unix signal handling is that different systems have different signal-handling semantics
The Posix standard defines the sigaction function, which allows users to clearly specify the signal-handling semantics they want when they install a handler.
1 |
|
8.5.6 Synchronizing Flows to Avoid Nasty Concurrency Bugs
8.5.7 Explicitly Waiting for Signals
Sometimes a main program needs to explicitly wait for a certain signal handler to run
1 |
|
The
sigsuspendfunction temporarily replaces the current blocked set with mask and then suspends the process until the receipt of a signal whose action is either to run a handler or to terminate the process.If the action is to run a handler, then
sigsuspendreturns after the handler returns, restoring the blocked set to its state when sigsuspend was called.The sigsuspend function is equivalent to an atomic (uninterruptible) version of the following:
1
2
3sigprocmask(SIG_BLOCK, &mask, &prev);
pause();
sigprocmask(SIG_SETMASK, &prev, NULL);8.6 Nonlocal Jumps
C provides a form of user-level exceptional control flow, called a nonlocal jump, that transfers control directly from one function to another
1 |
|
- The
setjmpfunction saves the current calling environment in theenvbuffer, for later use bylongjmp, and returns 0 - The calling environment includes the program counter, stack pointer, and general-purpose registers.
- For subtle reasons return value should not be assigned to a variable
1 |
|
- The longjmp function restores the calling environment from the env buffer and then triggers a return from the most recent
setjmpcall that initialized env. - The
setjmpfunction is called once but returns multiple times
An important application of nonlocal jumps is to permit an immediate return from a deeply nested function call, usually as a result of detecting some error condition.
1 |
|
Another important application of nonlocal jumps is to branch out of a signal handler to a specific code location, rather than returning to the instruction that was interrupted by the arrival of the signal
1 |
|
- The
sigsetjmpandsiglongjmpfunctions are versions ofsetjmpandlongjmpthat can be used by signal handlers. - To avoid a race, we must install the handler after we call
sigsetjmp.
The exception mechanisms provided by C++ and Java are higher-level, more structured versions of the C setjmp and longjmp functions. You can think of a catch clause inside a try statement as being akin to a setjmp function. Similarly, a throw statement is similar to a longjmp function.
8.7 Tools for Manipulating Processes
strace- Prints a trace of each system call invoked by a running program and its children.
- Compile your program with
-staticto get a cleaner trace without a lot of output related to shared libraries.
ps: Lists processes (including zombies) currently in the system.topPrints information about the resource usage of current processes.pmapDisplays the memory map of a process./proc. A virtual filesystem that exports the contents of numerous kernel data structures in an ASCII text form that can be read by user programs. For example, type cat/proc/loadavgto see the current load average on your Linux system.
8.8 Summary

Signals
- A signal only change some states in the context of the destination process.
- Signals are always sent by kernal, user program can only ask the kernal to send signals for them.
Why printf is not safe for signal?
Potential dead-lock
1 | void handler(int signum){ |
- When
printfdo the printing,stdoutwill be locked untilprintfis finished. - However, it is possible that a signal recevied when executing the
printf - Then the
printfin thehandlerwant to usestdout, it wait forstdoutto be unlocked - The waiting will never end since the interrupted
printfin main has no way to finish.
Tips
when using wait(&childExitStat) to get the exit value of child, it will be multiplication of 256.
1 | if (pid == 0){ |