reentrant functions in signal
When a signal that is being caught is handled by a
process, the normal sequence of instructions being executed by the process is
temporarily interrupted by the signal handler. The process then continues executing,
but the instructions in the signal handler are now executed. If the signal
handler returns (instead of calling exit or longjmp, for example), then the
normal sequence of instructions that the process was executing when the signal
was caught continues executing. (This is similar to what happens when a
hardware interrupt occurs.) But in the signal handler, we can't tell where the process
was executing when the signal was caught. What if the process was in the middle
of allocating additional memory on its heap using malloc, and we call malloc from
the signal handler ?
The Single UNIX Specification specifies the functions
that are guaranteed to be reentrant. Figure below lists these reentrant
functions.
Reentrant functions that may be called from a signal
handler
Most functions that are not in Figure above are missing
because (a) they are known to use static data structures, (b) they call malloc or free, or (c) they are part of the standard I/O
library. Most implementations of the standard I/O library use global
data structures in a nonreentrant way. Note that even though we call printf from signal handlers in
some of our examples, it is not guaranteed to produce the expected results, since the signal hander can
interrupt a call to printf from our main program.
Be aware that even if we call a function listed in Figure
above from a signal handler, there is only one errno variable per thread, and we might modify its value. Consider a
signal handler that is invoked right after main has set errno. If the signal handler
calls read, for example, this call can change the value of errno, wiping out
the value that was just stored in main. Therefore, as a general rule, when
calling the functions listed in Figure above from a signal handler, we should
save and restore errno. (Be aware that a commonly caught signal is SIGCHLD, and
its signal handler usually calls one of the wait functions. All the wait functions
can change errno.)
Note that longjmp and siglongjmp are missing from Figure above, because
the signal may have occurred while the main routine was updating a data
structure in a nonreentrant way. This data structure could be left half updated
if we call siglongjmp instead of returning from the signal handler. If it is
going to do such things as update global data structures, as we describe here, while catching signals that
cause sigsetjmp to be executed, an application needs to block the signals while
updating the data structures.
Example
Example Below shows a program that calls the nonreentrant
function getpwnam from a signal handler that is called every second.
When this program was run, the results were
random. Usually, the program would be terminated by a SIGSEGV signal when the
signal handler returned after several iterations. An examination of the core file
showed that the main function had called getpwnam, but that some internal
pointers had been corrupted when the signal handler called the same function.
Occasionally, the program would run for several seconds before crashing with a SIGSEGV
error. When the main function did run correctly after the signal had been
caught, the return value was sometimes corrupted and sometimes fine. Once (on Mac
OS X), messages were printed from the malloc library routine warning about
freeing pointers not allocated through malloc.
As shown by this example, if we call a nonreentrant
function from a signal handler, the results are unpredictable.
example code : Call a nonreentrant function from a signal handler
example code : Call a nonreentrant function from a signal handler
No comments:
Post a Comment