We had a lecture last week that talked about how the OS (in this case Linux, and in this particular case, our school server uses SUSE Linux 11) handles interrupts. It should be noted that for most signals, you can catch an interrupt and define your own signal handler to trigger instead of the default value. We used an example to illustrate this, and I found what seemed like interesting behavior at first. Here is the code:
#include <stdio.h> #include <signal.h> #define INPUTLEN 100 main(int ac, char *av[]) { void inthandler (int); void quithandler (int); char input[INPUTLEN]; int nchars; signal(SIGINT, inthandler); signal(SIGQUIT, quithandler); do { printf("\nType a message\n"); nchars = read(0, input, (INPUTLEN - 1)); if ( nchars == -1) perror("read returned an error"); else { input[nchars] = '\0'; printf("You typed: %s", input); } } while(strncmp(input, "quit" , 4) != 0); } void inthandler(int s) { printf(" Received Signal %d ....waiting\n", s); int i = 0; for(int i; i<3; ++i){ sleep(1); printf("inth=%d\n",i); } printf(" Leaving inthandler \n"); } void quithandler(int s) { printf(" Received Signal %d ....waiting\n", s); for(int i; i<7; ++i){ sleep(1); printf("quith=%d\n",i); } printf(" Leaving quithandler \n"); }
So, when running this code, I expected something like this:
- Running code .... ^ C
- Enter inthandler, running a loop, press ^ \
- Exit inthandler, go to quithandler, execute the quithandler loop
- ^ C back to inthandler. If I execute ^ C again while I enter the inthandler, ignore consecutive inthandler signals until the current inthandler is processed.
I found something that, based on observation, looks like nested “planning” of two-line “signals” of signals. If, for example, I quickly introduce the following interrupts:
- ^ C, ^ \, ^ C, ^ \, ^ \, ^ C
I get the following behavior / output from code:
^CReceived signal 2 ....waiting ^\Received Signal 3 ....waiting ^C^\^\^C quith=0 quith=1 quith=2 quith=3 quith=4 quith=5 quith=6 quith=7 Leaving quithandler Received Signal 3 ....waiting quith=1 quith=2 quith=3 quith=4 quith=5 quith=6 quith=7 Leaving quithandler inth=0 inth=1 inth=2 inth=3 Leaving inthandler Received Signal 2 ....waiting inth=0 inth=1 inth=2 inth=3 Leaving inthandler
In other words, it looks like this:
- Get the first signal ^ C
- Get ^ \ signal, "hold" inthandler and go to quithandler
- Get the next ^ C signal, but since we are already "nested" in the inthandler, put it at the end of the inthandler "queue"
- Gets the quithandler, a place at the end of the quithandler queue.
- Execute quithandler until the queue is empty. Ignore the third quithandler because it only has a queue depth of 2.
- Leave the quithandler and complete the 2 remaining inthandslers. Ignore the final inthandler because the queue depth is 2.
I showed the behavior to my professor, and he seems to agree that the behavior of the "nested 2nd stage" is what happens, but we are not 100% sure why (it comes from the hardware background and just started teaching this class). I wanted to post to SO to find out if anyone can shed some light on why / how Linux processes these signals, since we did not expect any behavior, i.e. nesting.
I think that the test script I wrote should be sufficient to illustrate what is happening, but here are a bunch of screenshots from additional test cases:
http://imgur.com/Vya7JeY,fjfmrjd.30YRQfk,uHHXFu5,Pj35NbF
I wanted to leave additional test cases as a reference, as they are great screenshots.
Thanks!