3

How signal handler takes the signal number without passing arguments to signalhandler() function in main()?

For example in the below source code inside main() passing 2nd argument of signal system call as signalhandler without any argument in signal handler but while coming to signalhandler definition it is collecting an argument with name sig_num ...

How actually it is possible?

According ANSI C if we don't pass any arguments then function definition will should not collect any argument.

Please help me regarding this.

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

void signalhandler(int sig_num)
{
  printf("caught signal number: %d\n", sig_num);
}

int main(void)
{
  while(1)
  {
    printf("hello world\n");
    sleep(1);
    signal(SIGINT, signalhandler);
  }
}
too honest for this site
  • 12,050
  • 4
  • 30
  • 52
raj
  • 33
  • 5

3 Answers3

2

Your function signalhandler is not called when you give it as an argument of signal(2). The prototype is :

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

So you can see that the second argument is a function pointer. Your function signalhandler will be called later (when a signal is delivered) and the code that will call it will give it an int argument.

Take a look at some other function pointer examples to understand better this callback mechanism.

blatinox
  • 833
  • 6
  • 18
2

The signal API registers the provided function pointer to be called upon receipt of the specified signal. It is possible to register the same function pointer against multiple signals, so the function when called will be provided the signal value that was received.

In Linux (and all operating systems that I am aware of) signal delivery is asynchronous to the process's execution, and the notification is immediate. The OS behaves as if it preempts the program and injects the signal handler function call on top of whatever the program is currently doing. The OS is aware which signal was delivered, and passes it as the parameter to the function call.

Note: It is possible to use raise to generate a synchronous signal to the program. But, it will typically use the OS service to deliver it to the process rather than directly invoke the signal handler.

If you use a debugger and set a breakpoint within your signal handler, and deliver the appropriate signal, you might see that the backtrace will show that the signal handler is being injected by the OS.

For example, consider the program:

void signalhandler (int sig)
{
    write(2, "signal!\n", 8);
}

void foo (void)
{
    for (;;) {}
}

int main (void)
{
    signal(SIGINT, signalhandler);
    foo();
}

When running the program in gdb, you can deliver the signal with the signal command. The resulting backtrace would look like:

(gdb) signal SIGINT 
Continuing with signal SIGINT.

Breakpoint 1, signalhandler (sig=2) at s.c:5
5       write(2, "signal!\n", 8);
(gdb) bt
#0  signalhandler (sig=2) at s.c:5
#1  <signal handler called>
#2  foo () at s.c:10
#3  0x08048498 in main () at s.c:16

Note: It is important to realize that the signal handler function call should not be treated like a regular function call. Since the OS injects the call, it has limitations that are discussed below.

The signal call can be injected at some arbitrary place in your code execution. For this reason, POSIX mandates certain functions to be safe to be called from signal handlers. Functions that are not designed to be re-entrant run the risk of being in an inconsistent state if it is interrupted by a injected call to the signal handler function, which then in turn calls the interrupted function.

As an example of a problem that could happen, suppose you are writing code that is manipulating a data structure, such as removing a node from a linked list. However, if the pointers of the elements have not been completely fixed up when the signal is delivered, then the signal handler may see a corrupted linked list. This situation can happen more often than you might assume, especially if you call a function that requires heap allocation.

Thus, it is often safest to make the signal handler dead simple. For example, the signal handler may simply set a flag, and your application code would then need code to detect whether or not the flag was set.

jxh
  • 69,070
  • 8
  • 110
  • 193
1

I will explain in simple terms.

Signal are way of delivering messages to a program. For example when the program is running in a terminal window and you press Ctrl+C then the terminal window sends SIGINT to the program.

Now, for each process, the kernel maintains a table which maps from each signal to what is to be called when the signal is received. By default, on SIGINT, the behavior is set to terminate. But, a program can change the default behavior for some signals by a call to the function signal.

Eg. signal(SIGINT, funcHandler) changes the behavior on receiving SIGINT from termination to calling funcHandler. But it never calls funcHandler. funcHandler will be called when the program receives a SIGINT.

Now, when the program receives SIGINT then the kernel looks up the table to decide which function to call(say handler), then the kernel sets up handler's stack with the received signal as the argument and then returns to the program. As effect funcHandler is called with signal_number as the argument in the program's context.

alk
  • 69,737
  • 10
  • 105
  • 255
Nishant
  • 2,571
  • 1
  • 17
  • 29