Sunday, May 27, 2012

Low level interrupts

[Disclaimer: I don't make my living from writing C so the following should all be taken with a pinch of salt. I am a lowly Java programmer who is interested in what is going on in the low-level Linux world].

In my last post, I showed that Java interrupts are not achieved by Linux kernel calls even though Linux does allow such an "interrupt()".

Take this C code (SignalHandling.c):

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <signal.h>



void sig_handler(int signum)
{
    fprintf(stdout, "\n=============\nReceived signal %d\n==============\n", signum);
}


void addSigAction() {
    struct sigaction sa;
    sa.sa_handler = sig_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_INTERRUPT; /* Restart functions if
                                 interrupted by handler */
    if (sigaction(SIGINT, &sa, NULL) == -1) {
        fprintf(stderr, "Could not install signal handler\n");
    } else {
        fprintf(stdout, "addSigAction: registered handler\n");
    }
}



void* runSleep(void* object)
{
    addSigAction();
    printf("runSleep: about to sleep\n");
    sleep(100);
    printf("runSleep: finished sleeping\n");
}




int main(int argc, char *argv[])
{
    pid_t pid = getpid();
  fprintf(stdout, "Main: Started. PID = %d\n", pid);


  pthread_t otherThread; 


  int error = pthread_create(&otherThread, NULL, runSleep, NULL);


  printf("Hit return...\n");
  getchar();
  pthread_kill(otherThread, SIGINT);


  pthread_join(otherThread, NULL);
  fprintf(stdout, "Finished\n");
}

Forget rogue includes for now - we'll be needing them later.

Compile it with:


gcc -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"src/SignalHandling.d" -MT"src/SignalHandling.d" -o"src/SignalHandling.o" "../src/SignalHandling.c"


gcc  -o"SignalHandling"  ./src/SignalHandling.o   -lpthread -lrt


(Note the -lpthread switch to use the thread library).

This code starts a new thread (pthread_create) - the equivalent of instantiating a new java.lang.Thread with a Runnable that is actually the runSleep method.

(The first NULL is the thread attributes and the second is the argument passed to runSleep - that is: nothing as runSleep doesn't take an argument).

This new thread registers a signal handler (sig_handler method) and then sleeps for 100s.

Meanwhile, the main thread that waits for the user to hit return before sending a message to the first thread (pthread_kill).

Don't be fooled by this method name. The documentation in the GLIBC sigthread.h file says it will:
Send signal SIGNO to the given thread.
Indeed, using strace to see what kernel calls are being made shows that this method delegates to tgkill. The man docs say:
tgkill() sends the signal sig to the thread with the thread ID tid in the thread group tgid. (By contrast, kill(2) can only be used to send a signal to a process (i.e., thread group) as a whole, and the signal will be delivered to an arbitrary thread within that process.)
The use of the word kill is historic. The Linux Programming Interface says:
The term kill was chosen because the default action of most of the signals that were available on early UNIX implementations was to terminate the process.
Anyway, the point is that a kernel call is made to tell the other thread when an interrupt signal (SIGINT in the GLIBC signum.h) is sent. This does not appear to be the case in the Linux Java implementation that I am using (1.7.0-ea-b125 on Linux 2.6.32.9-70.fc12.i686).

My next post should (hopefully) be about what happens if a thread is waiting on a stream.

No comments:

Post a Comment