Linux/Glibc. How to Use Fprintf in Signal Handler

linux/glibc. Can I use fprintf in signal handler?

No you cannot. Check the manpage signal(7) for a list of async-signal-safe functions. fprintf is not included in that list.

If you don't need formatting then you can use write(STDERR_FILENO, <buf>, <buflen>) to write to stderr.

Is it safe to call fprintf() on a signal handler?

fprintf is not safe to call in a signal handler, due in part to the buffering capabilities for FILE objects.

What you should do is set a global flag in the signal handler, then check that flag elsewhere in your code and act accordingly.

How to avoid using printf in a signal handler?

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.

It is not safe to call all functions, such as printf, from within a signal handler.
A useful technique is to use a signal handler to set a flag and then check that flag
from the main program and print a message if required.

Notice in example below, signal handler ding() set a flag alarm_fired to 1 as SIGALRM caught and in main function alarm_fired value is examined to conditionally call printf correctly.

static int alarm_fired = 0;
void ding(int sig) // can be called asynchronously
{
alarm_fired = 1; // set flag
}
int main()
{
pid_t pid;
printf("alarm application starting\n");
pid = fork();
switch(pid) {
case -1:
/* Failure */
perror("fork failed");
exit(1);
case 0:
/* child */
sleep(5);
kill(getppid(), SIGALRM);
exit(0);
}
/* if we get here we are the parent process */
printf("waiting for alarm to go off\n");
(void) signal(SIGALRM, ding);
pause();
if (alarm_fired) // check flag to call printf
printf("Ding!\n");
printf("done\n");
exit(0);
}

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() or sigaction() functions.

List of authorized functions in manual page, calling this function inside signal handler is safe.

Print int from signal handler using write or async-safe functions

If you really insist on doing the printing from a signal handler, you basically have 2 options:

  1. Block the signal except in a dedicated thread you create for handling the signal. This special thread can simply perform for (;;) pause(); and since pause is async-signal-safe, the signal handler is allowed to use any functions it wants; it's not restricted to only async-signal-safe functions. On the other hand, it does have to access shared resources in a thread-safe way, since you're now dealing with threads.

  2. Write your own code for converting integers to decimal strings. It's just a simple loop of using %10 and /10 to peel off the last digit and storing them to a short array.

However, I would highly recommend getting this operation out of the signal handler, using the self-pipe trick or similar.

snprintf in signal handler creates segmentation fault if started with valgrind

This is a valgrind bug. It calls your signal handler with a stack that is not 16-byte aligned as required by the ABI. On x86_64, floating point arguments are passed in XMM registers which can only be stored at addresses that are 16-byte aligned. You can work around the problem by compiling for 32-bit (gcc -m32).

malloc inside linux signal handler cause deadlock

malloc() is not a function that can be safely called from a signal handler. It's not a async-signal-safe function.
So, you should never call malloc() from a signal handler. You are only allowed to call a limited set of functons from a signal handler.
See the man signal-safety for the list of functions you can safely call from a signal handler.

Looking at your GDB output, it appears that while malloc() is holding a lock, you are calling malloc() again which results in a deadlock.

How to make POSIX/Linux signal handling safe?

To focus on the stated question, I shall limit my suggestions to the signal handling aspect.

Consider using a realtime signal (SIGRTMIN+0 to SIGRTMAX-0, inclusive) instead of SIGUSR1. Standard signals such as SIGUSR1 are not queued, so you may lose them (if you have one already pending when another same signal triggers), but realtime signals are queued, and much more reliable. See Real-time signals section in man 7 signal for details.

Also consider saving errno at the beginning of your signal handler, and restoring it before returning. Otherwise, it is possible that in some corner cases the signal delivery "corrupts" errno (because your signal handler modifies it implicitly), which is very hard to debug -- simply put, in some cases the errno you think was assigned due to a failed syscall, was actually reset by your signal handler.

(Language-lawyers might point out that accessing thread-local variables, errno typically being one, is non-async-signal safe, at least in theory. In practice it is safe, especially if the thread-local variables have been accessed by the thread prior to the signal. For further details regarding glibc, see this thread at the libc-alpha mailing list. Personally, I create my pthreads with smaller than default stacks (the default being way too large for typical worker threads), and ensure the thread function reads and writes thread-local variables as the first thing, avoiding any thread-local non-async-signal-safe issues in practice. This also applies to the main thread. In short, if the thread-local variables are known to be allocated and available prior to the signal delivery, their use is, in practice, async-signal-safe. Finally, async-signal-safe functions such as read() and write() do modify errno internally, without any special handling, so if they are async-signal-safe, restoring errno has to be too.)

As described in the man 7 signal man page, and mentioned by Andrew Henle in a comment to the original question, only async-signal-safe functions are safe to use in a signal handler. Neither aio_read() nor printf() are async-signal-safe.

Note that read(2) and write(2) are async-signal-safe, and can be used with eg. an anonymous socket pair to transfer an information package (describing the event) to an event-processing thread, or to print (debugging) information to standard output or standard error (STDOUT_FILENO and STDERR_FILENO descriptors, respectively).

If you absolutely need to use non-async-signal-safe functions, block those signals, and create a helper thread that uses sigwaitinfo() to handle the signals. This won't necessarily work for thread-targeted signals on Linux, and I personally would use a signal handler, GCC atomic builtins (they're supported by most C compilers, fortunately) to maintain a queue of events, and e.g. sem_post() to wake up the event-processing thread. There are several design options here, and thus far even the oddball problems I've come across have always been solvable using a relatively straightforward approach.

As described in the man 2 sigaction man page, you can examine si->code to find out the reason for the signal; it will be SI_ASYNCIO for AIO completions, POLL_IN/POLL_OUT/POLL_MSG/POLL_ERR/POLL_PRI/POLL_HUP for SIGIO signals, SI_KERNEL for other kernel-sent signals, and so on. If si->code is SI_USER or SI_QUEUE, you can examine si->pid to find out which process sent the signal.

It is also recommended to clear the entire struct sigaction via e.g. memset(&sa, 0, sizeof sa); prior to setting any of the fields. (This is because some of the fields may or may not be unions; clearing the entire structure to all zeros ensures "safe" values for the unused fields.)

Hmm, did I forget something? If you notice something I missed, please let me know in the comments, so I can fix this answer.



Related Topics



Leave a reply



Submit