简体   繁体   中英

Signal Handler Example in The Linux Programming Interface

The following example from The Programming Linux Programming Interface by Michael Kerrisk

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

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

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

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

is supposed print "Ouch!" to the terminal whenever the user types Control-C (CTRL+C); in the author's own example, he types it twice before finally quitting the terminal with Control-\\ (CTRL+\\ ).

When I do this, the program works as expected on only the first execution of CTRL+C. If I type it in for the second time, like the author does in his example, my program quits the terminal- it does not print "Ouch!" nor does it continue to run (looping).

I used the exact same code as given here, on the book's website:

Ouch.c

Typically signal needs the signal handler to be re-installed. Otherwise, it defautlts to SIG_DFL (default action corresponding to the signal). The default action for SIGINT is to terminate the program.

Note that printf(3) is not one of the async-safe functions. So you could write(2) to to do the same. See the POSIX list of Async-signal-safe functions.

Re-installing it should make work as expected:

static void sigHandler(int sig){
    signal(SIGINT, sigHandler);
    write(STDOUT_FILENO, "Ouch!\n", 6);
}

This is one of the reasons why you should avoid signal and use sigaction instead. The above mentioned behaviour is not universal across platforms. So perhaps, the platform you are running is not what author tested his code on or you are using a different Linux kernal.

The behaviour where you need to re-install the signal handler upon receiving a signal is System V behaviour. But BSD semantics doesn't need re-installing. Until recently, Linux showed System V behaviour but it seems to have been fixed in the recent kernel and I do not see this on my 3.19 kernel but can see 2.6.32 kernel (considerably old).

Signal's documentation states:

  The situation on Linux is as follows: * The kernel's signal() system call provides System V semantics. * By default, in glibc 2 and later, the signal() wrapper function does not invoke the kernel system call. Instead, it calls sigaction(2) using flags that supply BSD semantics. This default behavior is provided as long as the _BSD_SOURCE feature test macro is defined. By default, _BSD_SOURCE is defined; it is also implicitly defined if one defines _GNU_SOURCE, and can of course be explicitly defined. * On glibc 2 and later, if the _BSD_SOURCE feature test macro is not defined, then signal() provides System V semantics. (The default implicit definition of _BSD_SOURCE is not provided if one invokes gcc(1) in one of its standard modes (-std=xxx or -ansi) or defines various other feature test macros such as _POSIX_SOURCE, _XOPEN_SOURCE, or _SVID_SOURCE; see feature_test_macros(7).) 

So you could work around by defining _BSD_SOURCE to get BSD semantics. So behaviour you observe is mostly likely because the signal on your system follows System V semantics and recent Linux (and probably what Kerrisk tested it on) follows BSD semantics.

you should not use printf() in signal and exception handlers because they are not reentrant. printf also buffer data in memory before putting this to console so using fflush() will help to print but not recommended. for test purpose use counter(flag) in handler and use printf outside handler. do not use signal() to register handler as every flavor of unix(BSD,Linux) does not provide same implementation. Instead use sigaction.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM