How to avoid using printf in a signal handler?
Asked Answered
C

8

99

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?

Calendula answered 3/6, 2013 at 6:23 Comment(4)
A simple and not very useful answer to the question in your title: See that printf call in that signal hander? Delete it.Downturn
Hello Yu Hao! I think you will find link very interesting to read. "Use reentrant functions for safer signal handling" I have read it after so long time, I would like to share the artificial here with you. Hope you enjoy.Chronicle
Also see Print int from signal handler using write or async-safe functions.Omni
As the nice article posted above is gone - get the 2021 capture of this still very relevant and good 2005 article from web.archive.org/web/20210121185226/http://www.ibm.com/… (of course, we all store the errno directly after the function which set it...)Amberly
C
62

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.

Chronicle answered 3/6, 2013 at 6:27 Comment(7)
It is considered better practice to declare volatile sigatomic_t alarm_fired;Stater
Adding a link "What happens during this signal handling program?"Chronicle
@GrijeshChauhan: if we are working in a code of product , then we can not call pause function , flow can be anywhere when signal occur , so in that case we really dont know where to keep "if (alarm_fired) printf("Ding!\n");" in code.Berti
@pankajkushwaha yes, your are correct, it is suffering from race conditionChronicle
@GrijeshChauhan, There are two things which i could not understand. 1. How do you know when to check the flag? So will there be multiple check points in the code at almost every point to print. 2.There will definitely be race conditions where in the signal might be called before the signal registration or the signal may occur after the check point. I think this will only help the print in some conditions but does not completely solve the problem.Traitorous
@Darshanb (1) Yes, you are correct, signals are asynchronous they can happen any time in your system. But understand the signal-handlers are also called asynchronously when the signal trigger to your process you are not supposed to call signal handlers explicitly,- Secondly, there is a list of authorized functions those can be called from a signal handler. ...for unauthorized function, you have to check the alarm_fired repeadtly ...it largely depends on need and design of your applicationChronicle
@Darshanb (2) yes, to prevent from race conditions we declrated atomic flagChronicle
R
73

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 invokes malloc(), this may completely wreck the memory management.

The C standard takes a very conservative view of what you can do in a signal handler:

ISO/IEC 9899:2011 §7.14.1.1 The signal function

¶5 If the signal occurs other than as the result of calling the abort or raise function, the behavior is undefined if the signal handler refers to any object with static or thread storage duration that is not a lock-free atomic object other than by assigning a value to an object declared as volatile sig_atomic_t, or the signal handler calls any function in the standard library other than the abort function, the _Exit function, the quick_exit function, or the signal function with the first argument equal to the signal number corresponding to the signal that caused the invocation of the handler. Furthermore, if such a call to the signal function results in a SIG_ERR return, the value of errno is indeterminate.252)

252) If any signal is generated by an asynchronous signal handler, the behavior is undefined.

POSIX is a lot more generous about what you can do in a signal handler.

Signal Concepts in the POSIX 2008 edition says:

If the process is multi-threaded, or if the process is single-threaded and a signal handler is executed other than as the result of:

  • The process calling abort(), raise(), kill(), pthread_kill(), or sigqueue() to generate a signal that is not blocked

  • A pending signal being unblocked and being delivered before the call that unblocked it returns

the behavior is undefined if the signal handler refers to any object other than errno with static storage duration other than by assigning a value to an object declared as volatile sig_atomic_t, or if the signal handler calls any function defined in this standard other than one of the functions listed in the following table.

The following table defines a set of functions that shall be async-signal-safe. Therefore, applications can invoke them, without restriction, from signal-catching functions:

_Exit()             fexecve()           posix_trace_event() sigprocmask()
_exit()             fork()              pselect()           sigqueue()
…
fcntl()             pipe()              sigpause()          write()
fdatasync()         poll()              sigpending()

All functions not in the above table are considered to be unsafe with respect to signals. In the presence of signals, all functions defined by this volume of POSIX.1-2008 shall behave as defined when called from or interrupted by a signal-catching function, with a single exception: when a signal interrupts an unsafe function and the signal-catching function calls an unsafe function, the behavior is undefined.

Operations which obtain the value of errno and operations which assign a value to errno shall be async-signal-safe.

When a signal is delivered to a thread, if the action of that signal specifies termination, stop, or continue, the entire process shall be terminated, stopped, or continued, respectively.

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:

_Exit()              getppid()            sendmsg()            tcgetpgrp()
_exit()              getsockname()        sendto()             tcsendbreak()
abort()              getsockopt()         setgid()             tcsetattr()
accept()             getuid()             setpgid()            tcsetpgrp()
access()             htonl()              setsid()             time()
aio_error()          htons()              setsockopt()         timer_getoverrun()
aio_return()         kill()               setuid()             timer_gettime()
aio_suspend()        link()               shutdown()           timer_settime()
alarm()              linkat()             sigaction()          times()
bind()               listen()             sigaddset()          umask()
cfgetispeed()        longjmp()            sigdelset()          uname()
cfgetospeed()        lseek()              sigemptyset()        unlink()
cfsetispeed()        lstat()              sigfillset()         unlinkat()
cfsetospeed()        memccpy()            sigismember()        utime()
chdir()              memchr()             siglongjmp()         utimensat()
chmod()              memcmp()             signal()             utimes()
chown()              memcpy()             sigpause()           wait()
clock_gettime()      memmove()            sigpending()         waitpid()
close()              memset()             sigprocmask()        wcpcpy()
connect()            mkdir()              sigqueue()           wcpncpy()
creat()              mkdirat()            sigset()             wcscat()
dup()                mkfifo()             sigsuspend()         wcschr()
dup2()               mkfifoat()           sleep()              wcscmp()
execl()              mknod()              sockatmark()         wcscpy()
execle()             mknodat()            socket()             wcscspn()
execv()              ntohl()              socketpair()         wcslen()
execve()             ntohs()              stat()               wcsncat()
faccessat()          open()               stpcpy()             wcsncmp()
fchdir()             openat()             stpncpy()            wcsncpy()
fchmod()             pause()              strcat()             wcsnlen()
fchmodat()           pipe()               strchr()             wcspbrk()
fchown()             poll()               strcmp()             wcsrchr()
fchownat()           posix_trace_event()  strcpy()             wcsspn()
fcntl()              pselect()            strcspn()            wcsstr()
fdatasync()          pthread_kill()       strlen()             wcstok()
fexecve()            pthread_self()       strncat()            wmemchr()
ffs()                pthread_sigmask()    strncmp()            wmemcmp()
fork()               raise()              strncpy()            wmemcpy()
fstat()              read()               strnlen()            wmemmove()
fstatat()            readlink()           strpbrk()            wmemset()
fsync()              readlinkat()         strrchr()            write()
ftruncate()          recv()               strspn()
futimens()           recvfrom()           strstr()
getegid()            recvmsg()            strtok_r()
geteuid()            rename()             symlink()
getgid()             renameat()           symlinkat()
getgroups()          rmdir()              tcdrain()
getpeername()        select()             tcflow()
getpgrp()            sem_post()           tcflush()
getpid()             send()               tcgetattr()

As a result, you either end up using write() without the formatting support provided by printf() 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:

How come most string functions from <string.h> or the character class functions from <ctype.h> and many more C standard library functions are not in the list above? An implementation would need to be purposely evil to make strlen() unsafe to call from a signal handler.

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 the strlen() is a prime example, along with strchr(), strstr(), etc. On the other hand, other functions such as strtok(), strcoll() and strxfrm() are rather complex and are not likely to be async-signal safe. Because strtok() retains state between calls, and the signal handler could not easily tell whether some part of the code that is using strtok() would be messed up. The strcoll() and strxfrm() 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 as strcoll() and strxfrm().

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 (but time() is explicitly allowed)
  • <uchar.h> — Locale-dependent
  • <wchar.h> — Locale-dependent
  • <wctype.h> — Locale-dependent

Analyzing 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.

Raisin answered 3/6, 2013 at 7:20 Comment(6)
How come most string functions from <string.h> or the character class functions from <ctype.h> and many more C standard library functions are not in the list above? An implementation would need to be purposely evil to make strlen() unsafe to call from a signal handler.Crossway
@chqrlie: interesting question — see the update (there was no way to fit that much into comments sensibly).Raisin
Thank you for your in depth analysis. Regarding the <ctype.h> stuff, it is locale specific and could cause problems if the signal interrupts a locale setting function, but once the locale is loaded, using them should be safe. I guess, in some complex situations, loading the locale data could be done incrementally, thus making the functions from <ctype.h> unsafe. The conclusion remains: When in doubt, abstain.Crossway
@chqrlie: I agree that the moral of the story should be When in doubt, abstain. That's a nice summary.Raisin
What about trapping signals from a shell such as bash? is this the same as using signal handlers? Because with trap it's usually to define code, e.g. printf, rm, mkdir, etc. I've not seen anything about using shell code when trapping signals so I suppose is safeUnfleshly
If you're asking about whether to worry about what you do in the action for trap and catching signals in a shell script, the answer is "you don't have to worry". The shell deals with it all for you. You're expected to execute whole commands (e.g. rm to remove temporary files) in the action for a trap — and that's working at a very different level than catching signals in C code. Granted, the shell is in C, but it detects that the signals arrive carefully and then executes the trap action outside the signal handler.Raisin
C
62

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.

Chronicle answered 3/6, 2013 at 6:27 Comment(7)
It is considered better practice to declare volatile sigatomic_t alarm_fired;Stater
Adding a link "What happens during this signal handling program?"Chronicle
@GrijeshChauhan: if we are working in a code of product , then we can not call pause function , flow can be anywhere when signal occur , so in that case we really dont know where to keep "if (alarm_fired) printf("Ding!\n");" in code.Berti
@pankajkushwaha yes, your are correct, it is suffering from race conditionChronicle
@GrijeshChauhan, There are two things which i could not understand. 1. How do you know when to check the flag? So will there be multiple check points in the code at almost every point to print. 2.There will definitely be race conditions where in the signal might be called before the signal registration or the signal may occur after the check point. I think this will only help the print in some conditions but does not completely solve the problem.Traitorous
@Darshanb (1) Yes, you are correct, signals are asynchronous they can happen any time in your system. But understand the signal-handlers are also called asynchronously when the signal trigger to your process you are not supposed to call signal handlers explicitly,- Secondly, there is a list of authorized functions those can be called from a signal handler. ...for unauthorized function, you have to check the alarm_fired repeadtly ...it largely depends on need and design of your applicationChronicle
@Darshanb (2) yes, to prevent from race conditions we declrated atomic flagChronicle
P
14

How to avoid using printf in a signal handler?

  1. Always avoid it, will say: Just don't use printf() in signal handlers.

  2. At least on POSIX conforming systems, you can use write(STDOUT_FILENO, ...) instead of printf(). Formatting may not be easy however: Print int from signal handler using write or async-safe functions

Partook answered 3/6, 2013 at 8:44 Comment(3)
Alk Always avoid it. means? Avoid printf()?Chronicle
@GrijeshChauhan: Yes, as the OP was asking when to avoid using printf() in signal handlers.Partook
Alk +1 for 2 point, check OP asking for How to avoid using printf() in signal handlers?Chronicle
B
7

For 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 the PLT entries of unsafe functions; this causes calls to unsafe functions to be redirected to a wrapper.

Butterfingers answered 27/3, 2016 at 0:0 Comment(1)
GCC feature request: gcc.gnu.org/ml/gcc-help/2012-03/msg00210.htmlRattigan
R
3

Implement your own async-signal-safe snprintf("%d and use write

It is not as bad as I thought, How to convert an int to string in C? has several implementations.

Since there are only two interesting types of data that signal handlers can access:

  • sig_atomic_t globals
  • int signal argument

this basically covers all interesting use cases.

The fact that strcpy is also signal safe makes things even better.

The POSIX program below prints to stdout the number of times it received SIGINT so far, which you can trigger with Ctrl + C, and the and signal ID.

You can exit the program with Ctrl + \ (SIGQUIT).

main.c:

#define _XOPEN_SOURCE 700
#include <assert.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>

/* Calculate the minimal buffer size for a given type.
 *
 * Here we overestimate and reserve 8 chars per byte.
 *
 * With this size we could even print a binary string.
 *
 * - +1 for NULL terminator
 * - +1 for '-' sign
 *
 * A tight limit for base 10 can be found at:
 * https://mcmap.net/q/56332/-how-can-i-convert-an-int-to-a-string-in-c/32871108#32871108
 *
 * TODO: get tight limits for all bases, possibly by looking into
 * glibc's atoi: https://mcmap.net/q/55313/-where-is-the-itoa-function-in-linux/52127877#52127877
 */
#define ITOA_SAFE_STRLEN(type) sizeof(type) * CHAR_BIT + 2

/* async-signal-safe implementation of integer to string conversion.
 *
 * Null terminates the output string.
 *
 * The input buffer size must be large enough to contain the output,
 * the caller must calculate it properly.
 *
 * @param[out] value  Input integer value to convert.
 * @param[out] result Buffer to output to.
 * @param[in]  base   Base to convert to.
 * @return     Pointer to the end of the written string.
 */
char *itoa_safe(intmax_t value, char *result, int base) {
    intmax_t tmp_value;
    char *ptr, *ptr2, tmp_char;
    if (base < 2 || base > 36) {
        return NULL;
    }

    ptr = result;
    do {
        tmp_value = value;
        value /= base;
        *ptr++ = "ZYXWVUTSRQPONMLKJIHGFEDCBA9876543210123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[35 + (tmp_value - value * base)];
    } while (value);
    if (tmp_value < 0)
        *ptr++ = '-';
    ptr2 = result;
    result = ptr;
    *ptr-- = '\0';
    while (ptr2 < ptr) {
        tmp_char = *ptr;
        *ptr--= *ptr2;
        *ptr2++ = tmp_char;
    }
    return result;
}

volatile sig_atomic_t global = 0;

void signal_handler(int sig) {
    char key_str[] = "count, sigid: ";
    /* This is exact:
     * - the null after the first int will contain the space
     * - the null after the second int will contain the newline
     */
    char buf[2 * ITOA_SAFE_STRLEN(sig_atomic_t) + sizeof(key_str)];
    enum { base = 10 };
    char *end;
    end = buf;
    strcpy(end, key_str);
    end += sizeof(key_str);
    end = itoa_safe(global, end, base);
    *end++ = ' ';
    end = itoa_safe(sig, end, base);
    *end++ = '\n';
    write(STDOUT_FILENO, buf, end - buf);
    global += 1;
    signal(sig, signal_handler);
}

int main(int argc, char **argv) {
    /* Unit test itoa_safe. */
    {
        typedef struct {
            intmax_t n;
            int base;
            char out[1024];
        } InOut;
        char result[1024];
        size_t i;
        InOut io;
        InOut ios[] = {
            /* Base 10. */
            {0, 10, "0"},
            {1, 10, "1"},
            {9, 10, "9"},
            {10, 10, "10"},
            {100, 10, "100"},
            {-1, 10, "-1"},
            {-9, 10, "-9"},
            {-10, 10, "-10"},
            {-100, 10, "-100"},

            /* Base 2. */
            {0, 2, "0"},
            {1, 2, "1"},
            {10, 2, "1010"},
            {100, 2, "1100100"},
            {-1, 2, "-1"},
            {-100, 2, "-1100100"},

            /* Base 35. */
            {0, 35, "0"},
            {1, 35, "1"},
            {34, 35, "Y"},
            {35, 35, "10"},
            {100, 35, "2U"},
            {-1, 35, "-1"},
            {-34, 35, "-Y"},
            {-35, 35, "-10"},
            {-100, 35, "-2U"},
        };
        for (i = 0; i < sizeof(ios)/sizeof(ios[0]); ++i) {
            io = ios[i];
            itoa_safe(io.n, result, io.base);
            if (strcmp(result, io.out)) {
                printf("%ju %d %s\n", io.n, io.base, io.out);
                assert(0);
            }
        }
    }

    /* Handle the signals. */
    if (argc > 1 && !strcmp(argv[1], "1")) {
        signal(SIGINT, signal_handler);
        while(1);
    }

    return EXIT_SUCCESS;
}

Compile and run:

gcc -std=c99 -Wall -Wextra -o main main.c
./main 1

After pressing Ctrl + C fifteen times, the terminal shows:

^Ccount, sigid: 0 2
^Ccount, sigid: 1 2
^Ccount, sigid: 2 2
^Ccount, sigid: 3 2
^Ccount, sigid: 4 2
^Ccount, sigid: 5 2
^Ccount, sigid: 6 2
^Ccount, sigid: 7 2
^Ccount, sigid: 8 2
^Ccount, sigid: 9 2
^Ccount, sigid: 10 2
^Ccount, sigid: 11 2
^Ccount, sigid: 12 2
^Ccount, sigid: 13 2
^Ccount, sigid: 14 2

where 2 is the signal number for SIGINT.

Tested on Ubuntu 18.04. GitHub upstream.

Rattigan answered 3/9, 2018 at 7:38 Comment(0)
S
2

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):

static int sigPipe[2];

static void gotSig ( int num ) { write(sigPipe[1], "!", 1); }

int main ( void ) {
    pipe(sigPipe);
    /* use sigaction to point signal(s) at gotSig() */

    FD_SET(sigPipe[0], &readFDs);

    for (;;) {
        n = select(nFDs, &readFDs, ...);
        if (FD_ISSET(sigPipe[0], &readFDs)) {
            read(sigPipe[0], ch, 1);
            /* do something about the signal here */
        }
        /* ... the rest of your select loop */
    }
}

If you care which signal it was, then the byte down the pipe can be the signal number.

Signac answered 25/5, 2017 at 14:40 Comment(1)
But what if write() in the signal handler fails :) ?Premonish
S
0

You can also use write() directly which is an async-signal-safe function.

#include <unistd.h> 
 
int main(void) { 
    write(1,"Hello World!", 12); 
    return 0; 
}
Supercargo answered 19/12, 2020 at 15:58 Comment(0)
R
-2

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.

Radiochemical answered 17/10, 2017 at 15:29 Comment(1)
Signal handlers do not run in some separate thread, they run in the context of the thread that was running when the signal interrupt occurred. This answer is completely wrong.Clung

© 2022 - 2024 — McMap. All rights reserved.