0

I have a signal handling snippet but it is somehow malfunctioning on my Mac and virtual Linux box at koding.com but on my office Linux PC it is working..Can someone please tell me why..

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void my_isr(int n){
   printf("Hello World");
   signal(SIGINT, SIG_DFL);

}


int main(){
   signal(SIGINT, my_isr);
   printf("pid = %d\n", getpid());
   while(1);

   return 0;
 }

When I am pressing Ctrl+C it is not printing Hello World on the first time but it is re-modifying the SIGINT signal action & hence it is exiting the program when I press Ctrl+C second time. Can someone explain me why?

alk
  • 69,737
  • 10
  • 105
  • 255
Mayukh Sarkar
  • 2,289
  • 1
  • 14
  • 38
  • 4
    Using printf in a signal handler is *undefined behavior* in ISO C. But it is allowed to change a file scope variable of type sig_atomic_t in a signal handler. You should test this variable in your while loop. – Jens Jul 23 '15 at 13:36
  • 1
    `printf` is not re-entrant, since it holds a global resource stdout. in general, all functions reserve global states and fail to manipulate all involved ones atomically are not re-entrant. – Jason Hu Jul 23 '15 at 13:56
  • @Jens: It's actually not undefined behaviour in general. The restriction is (very roughly) that you can't call any (unsafe) library functions from the signal handler *if they might be running* at the time the signal is delivered. So assuming the user only presses Ctrl+C after the `pid=` message has been displayed, there is no UB. – psmears Jul 23 '15 at 14:20
  • @psmears: From the C11Draft 7.14.1.1/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 calls any function in the standard library other than the `abort` function, the `_Exit` function, the `quick_exit function` [...]*" – alk Jul 23 '15 at 16:04
  • 1
    @alk: Right (and if you need to run on a non-POSIX C system you'll have to abide by that) but in general that restriction is relaxed by POSIX - see IEEE Std 1003.1-2013 Vol.2 Ch. 2.4: *"In the presence of signals, all functions defined by this volume of POSIX.1-2008(sic) 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."* (`printf` is such an unsafe function). – psmears Jul 23 '15 at 17:27

2 Answers2

5

You are not allowed to call every function in a signal handler.

Read signal(7). Only async signal safe functions can be called (directly or indirectly) from a signal handler, and printf is not such a function. If you really want to reliably "print" something from inside a signal handler (which I don't recommend), you can only use the low-level write(2) syscall (it is async signal safe).

So you've got undefined behavior. This explains why it is so bad.

The recommended way is to set a volatile sigatomic_t flag in your signal handler, and to test it outside of it (e.g. in your while loop...). And you forgot to call fflush(3). You might be more lucky by ending your printf format string with \n since stdout is line-buffered!

Of course, changing your printf inside your signal handler is still UB, even with a \n, but very often it would appear to work.

Here is a conforming version of your program....

#include <signal.h>
#include <unistd.h>
#include <stdio.h>

volatile sig_atomic_t got_signal;

void my_sigint_handler (int signum) {
  if (signum == SIGINT) // this is always true!
    got_signal = 1;
#define INTERRUPT_MESSAGE "Interrupted!\n"
  write(STDOUT_FILENO, INTERRUPT_MESSAGE, strlen(INTERRUPT_MESSAGE));
};

int main(int argc, char**argv) {
  struct sigaction act_int;
  memset (&act_int, 0, sizeof(act_int));
  act_int.sa_handler = my_sigint_handler;
  if (sigaction(SIGINT, &act_int, NULL)) {
     perror("sigaction"); exit(EXIT_FAILURE);
  };
  printf ("start %s pid %d\n", argv[0], (int)getpid());
  while (!got_signal) {
  };
  printf ("ended %s after signal\n", argv[0]);
  return 0;
}

A useful (and permissible) trick could be to write(2) a single byte -inside your signal handler- on a pipe(7) to self (you set up that pipe using pipe(2) early at program initialization), and in your event loop poll(2) the read end of that pipe.

Community
  • 1
  • 1
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
0

printf is the culprit just use counter in handler and print outside handler its value it will work.

use sigaction instead of signal

incompetent
  • 1,715
  • 18
  • 29
  • can you please elaborate? – Mayukh Sarkar Jul 23 '15 at 13:32
  • using signal() is not recommended as it is not compitible with all versions of unix systems. every flavor implement its own version so avoid it. never use printf() in exception handlers and signal handlers – incompetent Jul 23 '15 at 13:35
  • 2
    check this http://stackoverflow.com/questions/16891019/how-to-avoid-using-printf-in-a-signal-handler – incompetent Jul 23 '15 at 13:36
  • this is simple explanation of your question http://stackoverflow.com/questions/12139609/why-no-output-on-console-on-signal-handling?rq=1 – incompetent Jul 23 '15 at 13:47
  • Your explanation of the issue's root-cause does not really exist. – alk Jul 23 '15 at 16:12
  • 1
    trust on my answer your accepted answer is also true and more ellaborative follow that it will solve your problem :) – incompetent Jul 23 '15 at 16:16