Linux timers on global vars

2019-05-19 05:40发布

I found the below code in the Internet, I'm trying to understand how Linux timer works, anyway, as you can see below the counter1 is global var, what will happen if the while is working on it and the timer goes off and changed the value of counter1, do I need a lock in there?

// timertst1.c: Simple timer demo program. Use cross-gcc 
// Vers. 1.00 - 21.Oct. 2002
// k.d.walter@t-online.de

#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>

// This is a timer handler.
int counter1 = 0;
void timerHandler (int signum)
{
   printf ("timerHandler: counter= %d\n", counter1++);
   fflush (stdout);
}

// This is the one and only main function.

int main (void)
{
   struct sigaction sa;
   struct itimerval timer;

   // Install the timer handler...

   memset (&sa, 0, sizeof (sa));
   sa.sa_handler= &timerHandler;
   sigaction (SIGALRM, &sa, NULL);

   // Configure the timer to expire every 100 msec...

   timer.it_value.tv_sec= 0;            // First timeout
   timer.it_value.tv_usec=  500000;
   timer.it_interval.tv_sec= 0;         // Interval
   timer.it_interval.tv_usec= 500000;

   // Start timer...

   setitimer (ITIMER_REAL, &timer, NULL);   setitimer (ITIMER_REAL, &timer, NULL);
   // Do noting...

   while (1) {
       printf("i'm here waiting to be interuppted = %d\n",counter1);
       //some work;
       counter1++;
       //some other work;
   }
}

标签: c timer locking
2条回答
The star\"
2楼-- · 2019-05-19 05:51

Signal handlers are dangerous. The OS is going to interrupt your program, whatever it was doing, and run the handler; when the handler returns, the OS will take your program back to what it was doing.

Let's say that counter1++ is compiled to a load, an increment and a store. If the interrupt fires between the load and the store, then the sequence of instructions will be load, load, increment, increment, store, store. The variable will go up by one, not two; disaster! And you're right, this looks like a classic multi-threading issue.

What happens if we add a lock? Now instead of load, increment, store, we get lock, load, increment, store, unlock. But if the signal fires while we're between lock and unlock, the OS jumps us right into the handler - it doesn't let our code unlock first. When the handler tries to lock, main() is still holding the lock, so we deadlock. Disaster!

The safe thing to do is to write your signal handler so it just makes a note of what has to be done, and write code to take care of actually doing that elsewhere. But even that is not necessarily simple - what if your signal handler is itself interrupted? Not a problem in this particular case, but worth considering an example of how deep the rabbit hole goes. Writing correct signal handlers is hard. There are some tricks, for instance you can block signal handlers. But it's still hard.

查看更多
等我变得足够好
3楼-- · 2019-05-19 05:51

You can also consider using sig_atomic_t for safely communicating from a signal handler with the rest of the program. This, however, would not work in the example that you posted since reading, incrementing and storing a value is not an atomic operation.

Here you have an excerpt from GLIBC manual explaining sig_atomic_t:

To avoid uncertainty about interrupting access to a variable, you can use a particular data type for which access is always atomic: sig_atomic_t. Reading and writing this data type is guaranteed to happen in a single instruction, so there's no way for a handler to run “in the middle” of an access.

The type sig_atomic_t is always an integer data type, but which one it is, and how many bits it contains, may vary from machine to machine.

— Data Type: sig_atomic_t This is an integer data type. Objects of this type are always accessed atomically.

In practice, you can assume that int is atomic. You can also assume that pointer types are atomic; that is very convenient. Both of these assumptions are true on all of the machines that the GNU C library supports and on all POSIX systems we know of.

And here a usage example:

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

 /* This flag controls termination of the main loop. */
 volatile sig_atomic_t keep_going = 1;

 /* The signal handler just clears the flag and re-enables itself. */
 void catch_alarm (int sig) {
   keep_going = 0;
   signal (sig, catch_alarm);
 }

 void do_stuff (void) {
   puts ("Doing stuff while waiting for alarm....");
 }

 int main (void) {
   /* Establish a handler for SIGALRM signals. */
   signal (SIGALRM, catch_alarm);

   /* Set an alarm to go off in a little while. */
   alarm (2);

   /* Check the flag once in a while to see when to quit. */
   while (keep_going)
     do_stuff ();

   return EXIT_SUCCESS;
 }
查看更多
登录 后发表回答