user2635911
user2635911

Reputation: 502

Not sure how signal handler is being called

Code:

#include <signal.h>

static void sigHandler(int sig) {
    printf("Ouch!\n");
}

int main(int argc, char *argv[])
{
    int j;

    if (signal(SIGINT, sigHandler) == SIG_ERR)
        perror("signal");

    for (j = 0; ; j++) {
        printf("%d\n", j);
        sleep(3);
    }
}

I am not sure how the code works. sigHandler clearly takes in one argument that is an integer. However, in the if statement we write (signal(SIGINT, sigHandler) == SIG_ERR). Clearly when we call sigHandler, we are NOT passing in an integer value so why doesn't the program tell us it's an error? Why does the program still work as intended? Also I am not sure how the "signal" function works either.

According to the Kerris textbook on linux the syntax for signal functions is as follows void ( *signal(int sig, void (*handler)(int)) ) (int); We can clearly see that the handler function in the syntax is a pointer but in the actual code, we are NOT using a pointer to a function. Why is that?

Upvotes: 1

Views: 4465

Answers (4)

wholerabbit
wholerabbit

Reputation: 11566

Clearly when we call sigHandler, we are NOT passing in an integer value so why doesn't the program tell us it's an error?

That line doesn't call sigHandler at all; it passes it to signal() as the function pointer. That might be clearer written this way:

if (signal(SIGINT, &sigHandler) == SIG_ERR)

Notice the "address of" operator, &. However, in this case it is actually optional -- just the name of the function will serve as a function pointer.

To explain a bit, the compiler checks that the signature of the function pointed to matches the parameter to signal(). If this passes, at runtime the system can call it with an int argument as required. The signal() function is just a means of telling the system about the handler.

Upvotes: 0

user208139
user208139

Reputation:

Signal handler types are fairly tricky. I think it works like this: when the compiler encounters this line:

static void sigHandler(int sig) {

It creates a symbol named "sigHandler". The type is void (*)(int). Your example declaration of signal() (the system call that sets a signal handling function) has a prototype argument void (*handler)(int). The "handler" token is just a dummy: you could put any valid C language identifier in for it, or nothing.

When the compiler gets to the actual invocation of signal(SIGINT, sigHandler) it checks to see if a symbol sigHandler of the proper type exists, and it does as you defined just such a symbol above the invocation of signal(). Sometime during linking (static and/or dynamic), the actual physical address of sigHandler gets into the code.

The actual mechanics of signal handling are also tricky.

Once the program starts running, the physical address of sigHandler gets passed as an argument to the system call signal(), along with the integer 2, which is the value of SIGINT manifest constant. That's where the function pointer exists - it's the physical address of sigHandler() at run-time. The Unix/Linux kernel will keep that address in some data structure associated with the process that matches the number SIGINT (2 by convention or standard) to that physical addres.

If and when a SIGINT arrives for your process, the Unix/Linux kernel arranges for your process to stop, and then does something like put together a stack frame for sigHandler() which contains a value 2 (SIGINT) for the int sigint formal argument of sigHandler(), change the stack pointer register to match the new stack frame, change the instruction pointer register to the physical address it stored when signal() got called. The next instruction executed is for sigHandler(). Lots goes on under the covers of signal handling.

Upvotes: 0

countermode
countermode

Reputation: 322

Clearly when we call sigHandler, we are NOT passing in an integer value so why doesn't the program tell us it's an error? Why does the program still work as intended? Also I am not sure how the "signal" function works either.

Surely, "we" don't call the signal handler—the system does upon receipt of the signal. The signal() system call just installs the handler: "signal() sets the disposition of the signal signum to handler, which is either SIG_IGN, SIG_DFL, or the address of a programmer-defined function (a "signal handler")." There you go.

Btw. to quote once again from the manual page: "The behavior of signal() varies across UNIX versions, and has also varied historically across different versions of Linux. Avoid its use: use sigaction(2) instead."

We can clearly see that the handler function in the syntax is a pointer but in the actual code, we are NOT using a pointer to a function. Why is that?

The compiler makes the necessary cast automatically.

Upvotes: 3

derobert
derobert

Reputation: 51177

You need to brush up on C...

However, in the if statement we write (signal(SIGINT, sigHandler) == SIG_ERR). Clearly when we call sigHandler, we are NOT passing in an integer value

That statement doesn't call sigHandler. It calls signal. The parenthesis are a required part of the C function call syntax, so sigHandler there isn't a function call.

... the syntax for signal functions is as follows void ( *signal(int sig, void (*handler)(int)) ) (int); We can clearly see that the handler function in the syntax is a pointer but in the actual code, we are NOT using a pointer to a function. Why is that?

When you use a function name without the parenthesis, what you get is a pointer to that function. So that is using a function pointer.

Upvotes: 2

Related Questions