Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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);
}
like image 315
elmazzun Avatar asked Mar 19 '13 13:03

elmazzun


People also ask

How do you fix a global variable?

The simplest way to avoid globals all together is to simply pass your variables using function arguments. As you can see, the $productData array from the controller (via HTTP request) goes through different layer: The controller receives the HTTP request. The parameters are passed to the model.

Can global variables be accessed anywhere?

Variables that are created outside of a function are known as Global Variables . A global variable is one that can be accessed anywhere . This means, global variable can be accessed inside or outside of the function.


2 Answers

Signals may only write volatile sig_atomic_t variables according to the C89 and POSIX 7 standards:

the behavior is undefined if the signal handler refers to any object [CX] [Option Start] other than errno [Option End] with static storage duration other than by assigning a value to an object declared as volatile sig_atomic_t

Implementations often offer more, but I doubt that using non-volatile global variables or printf is something provided by yours.

like image 199
AProgrammer Avatar answered Oct 06 '22 02:10

AProgrammer



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;
}
like image 26
teppic Avatar answered Oct 06 '22 02:10

teppic