Search code examples
csignalsscopesignal-handling

Signal handler won't see global variable


Here's the problem: this program should receive input from stdin and count the bytes inserted; the SIGUSR1 signal whill stop the main program and will print on file standard error how many bytes have been copied when I send the SIGUSR1.

That's how my teacher wants me to do this: in one terminal run

cat /dev/zero | ./cpinout | cat >/dev/null

while from a second terminal send signals with

kill -USR1 xxxx

where xxxx is the pid of cpinout.

I updated my previous code:

/* cpinout.c */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>

#define BUF_SIZE 1024   

volatile sig_atomic_t countbyte = 0;
volatile sig_atomic_t sigcount = 0;

/* my_handler: gestore di signal */
static void sighandler(int signum) {
    if(sigcount != 0)
        fprintf(stderr, "Interrupted after %d byte.\n", sigcount);
    sigcount = countbyte;    
}

int main(void) {
  
    int c;
    char buffer[BUF_SIZE];
    struct sigaction action;

    sigemptyset(&action.sa_mask);
    action.sa_flags = 0;
    action.sa_handler = sighandler;
    if(sigaction(SIGUSR1, &action, NULL) == -1) {
        fprintf(stderr, "sigusr: sigaction\n");
        exit(1);
    }
    while( c=getc(stdin) != EOF ) {
        countbyte++;
        fputc(c, stdout);
    }
    return(0);
}

Solution


  • EDIT

    In the comments you mentioned that you are running the command as:

    cat /dev/zero | ./namefile | cat >/dev/null

    The behaviour is actually fine. /dev/zero is an endless stream of zeros, which are being sent to the program. So it's counting them up very quickly. When you interrupt, it stops and you're left with a large number.


    The problem may be related to the fact that the signal handler may be called while the global variable is being updated (if this takes more than one instruction). However, the GNU documentation states that it's safe to assume that an int is always atomic on a POSIX system.

    The only other possibility I can think of is that you're calling fputc in the loop, with printf in the handler (it should however be safe to call printf in a handler if it's not being called by the program). Try removing fputc from the loop to see if it resolves the problem.

    EDIT:

    This appears to explain the problem. This relates to the kind of functions that are safe to call from within a signal handler:

    Functions can also be nonreentrant if they use static data structures for their internal bookkeeping. The most obvious examples of such functions are the members of the stdio library (printf(), scanf(), and so on), which update internal data structures for buffered I/O. Thus, when using printf() from within a signal handler, we may sometimes see strange output—or even a program crash or data corruption— if the handler interrupts the main program in the middle of executing a call to printf() or another stdio function. (The Linux Programming Interface)

    Your program is interrupting a stdio function, which seems to fit this perfectly.


    Here's an alternative approach:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <signal.h>
    
    int countbyte = 0;  // for main program
    int sigcount = 0;   // for signal handler
    
    /* my_handler: signal handler */
    static void sighandler(int signum)
    {
       sigcount = countbyte;
    }
    
    int main(void)
    { 
       int c;
       struct sigaction sigact;
    
       sigemptyset(&sigact.sa_mask);
       sigact.sa_flags = 0;
       sigact.sa_handler = sighandler;
       sigaction(SIGUSR1, &sigact, NULL);
       while ((c = getc(stdin)) != EOF) {
          countbyte++;
          fputc(c, stdout);
       }
       if (sigcount != 0) {
          printf("Interrupted after %d bytes\n", sigcount);
       }
    
       return 0;
    }