Since printf
is not reentrant, it's not supposed to be safe to use it in a signal handler. But I've seen lots of example codes that uses printf
this way.
So my question is: when do we need to avoid using printf
in a signal handler, and is there a recommended replacement?
The primary problem is that if the signal interrupts
malloc()
or some similar function, the internal state may be temporarily inconsistent while it is moving blocks of memory between the free and used list, or other similar operations. If the code in the signal handler calls a function that then invokesmalloc()
, this may completely wreck the memory management.The C standard takes a very conservative view of what you can do in a signal handler:
POSIX is a lot more generous about what you can do in a signal handler.
Signal Concepts in the POSIX 2008 edition says:
However, the
printf()
family of functions is notably absent from that list and may not be called safely from a signal handler.The POSIX 2016 update extends the list of safe functions to include, in particular, a large number of the functions from
<string.h>
, which is a particularly valuable addition (or was a particularly frustrating oversight). The list is now:As a result, you either end up using
write()
without the formatting support provided byprintf()
et al, or you end up setting a flag that you test (periodically) in appropriate places in your code. This technique is ably demonstrated in the answer by Grijesh Chauhan.Standard C functions and signal safety
chqrlie asks an interesting question, to which I have no more than a partial answer:
For many of the functions in
<string.h>
, it is hard to see why they were not declared async-signal safe, and I'd agree thestrlen()
is a prime example, along withstrchr()
,strstr()
, etc. On the other hand, other functions such asstrtok()
,strcoll()
andstrxfrm()
are rather complex and are not likely to be async-signal safe. Becausestrtok()
retains state between calls, and the signal handler could not easily tell whether some part of the code that is usingstrtok()
would be messed up. Thestrcoll()
andstrxfrm()
functions work with locale-sensitive data, and loading the locale involves all sorts of state setting.The functions (macros) from
<ctype.h>
are all locale-sensitive, and therefore could run into the same issues asstrcoll()
andstrxfrm()
.I find it hard to see why the mathematical functions from
<math.h>
are not async-signal safe, unless it is because they could be affected by a SIGFPE (floating point exception), though about the only time I see one of those these days is for integer division by zero. Similar uncertainty arises from<complex.h>
,<fenv.h>
and<tgmath.h>
.Some of the functions in
<stdlib.h>
could be exempted —abs()
for example. Others are specifically problematic:malloc()
and family are prime examples.A similar assessment could be made for the other headers in Standard C (2011) used in a POSIX environment. (Standard C is so restrictive there's no interest in analyzing them in a pure Standard C environment.) Those marked 'locale-dependent' are unsafe because manipulating locales might require memory allocation, etc.
<assert.h>
— Probably not safe<complex.h>
— Possibly safe<ctype.h>
— Not safe<errno.h>
— Safe<fenv.h>
— Probably not safe<float.h>
— No functions<inttypes.h>
— Locale-sensitive functions (unsafe)<iso646.h>
— No functions<limits.h>
— No functions<locale.h>
— Locale-sensitive functions (unsafe)<math.h>
— Possibly safe<setjmp.h>
— Not safe<signal.h>
— Allowed<stdalign.h>
— No functions<stdarg.h>
— No functions<stdatomic.h>
— Possibly safe, probably not safe<stdbool.h>
— No functions<stddef.h>
— No functions<stdint.h>
— No functions<stdio.h>
— Not safe<stdlib.h>
— Not all safe (some are allowed; others are not)<stdnoreturn.h>
— No functions<string.h>
— Not all safe<tgmath.h>
— Possibly safe<threads.h>
— Probably not safe<time.h>
— Locale-dependent (buttime()
is explicitly allowed)<uchar.h>
— Locale-dependent<wchar.h>
— Locale-dependent<wctype.h>
— Locale-dependentAnalyzing the POSIX headers would be … harder in that there are a lot of them, and some functions might be safe but many won't be … but also simpler because POSIX says which functions are async-signal safe (not many of them). Note that a header like
<pthread.h>
has three safe functions and many unsafe functions.NB: Almost all of the assessment of C functions and headers in a POSIX environment is semi-educated guesswork. It is no sense a definitive statement from a standards body.
One technique which is especially useful in programs which have a select loop is to write a single byte down a pipe on receipt of a signal and then handle the signal in the select loop. Something along these lines (error handling and other details omitted for brevity):
If you care which signal it was, then the byte down the pipe can be the signal number.
You can use printf in signal handlers if you are using the pthread library. unix/posix specifies that printf is atomic for threads cf Dave Butenhof reply here : https://groups.google.com/forum/#!topic/comp.programming.threads/1-bU71nYgqw Note that in order to get a clearer picture of printf output, you should run your application in a console (on linux use ctl+alt+f1 to start console 1), rather than a pseudo-tty created by the GUI.
You can use some flag variable, set that flag inside signal handler, and based on that flag call
printf()
function in main() or other part of program during normal operation.Notice in example below, signal handler ding() set a flag
alarm_fired
to 1 as SIGALRM caught and in main functionalarm_fired
value is examined to conditionally call printf correctly.Reference: Beginning Linux Programming, 4th Edition, In this book exactly your code is explained (what you want), Chapter 11: Processes and Signals, page 484
Additionally, you need to take special care in writing handler functions because they can be called asynchronously. That is, a handler might be called at any point in the program, unpredictably. If two signals arrive during a very short interval, one handler can run within another. And It is considered better practice to declare
volatile sigatomic_t
, this type are always accessed atomically, avoid uncertainty about interrupting access to a variable. (read: Atomic Data Access and Signal Handling for detail expiation).Read Defining Signal Handlers :to learn how to write a signal handler function that can be established with the
signal()
orsigaction()
functions.List of authorized functions in manual page, calling this function inside signal handler is safe.
Always avoid it, will say: Just don't use
printf()
in signal handlers.At least on POSIX conforming systems, you can use
write(STDOUT_FILENO, ...)
instead ofprintf()
. Formatting may not be easy however: Print int from signal handler using write or async-safe functionsFor debugging purposes, I wrote a tool which verifies that you are in fact only calling functions on the
async-signal-safe
list, and prints a warning message for each unsafe function called within a signal context. While it doesn't solve the problem of wanting to call non async-safe functions from a signal context, it at least helps you find cases where you have done so accidentally.The source code is on GitHub. It works by overloading
signal/sigaction
, then temporarily hijacking thePLT
entries of unsafe functions; this causes calls to unsafe functions to be redirected to a wrapper.