对全局变量的Linux小时(Linux timers on global vars)

2019-09-18 04:40发布

我发现下面的代码在互联网上,我想了解Linux的定时器工作,反正你可以看到C1的下面全局变量,会发生什么,如果同时正在研究它和定时器熄灭,改变计数器1的值,我需要在那里锁?

// 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;
   }
}

Answer 1:

信号处理是很危险的。 操作系统是要打断你的程序,不管它是干什么的,运行处理程序; 当处理程序返回时,OS将你的程序返回到它在做什么。

比方说, counter1++编译到负载,增量和存储。 如果负载和存储之间的中断触发,然后将指令序列将负载,负载,增量,增量,商店,商店。 变量就上去了又一个,不是两个; 灾害! 你说的没错,这看起来像一个典型的多线程问题。

如果我们增加一个锁,会发生什么? 现在,而不是负担,增量,存储,我们得到了锁,加载,增量,存储,解锁。 但是,如果我们锁定和解锁之间,而信号触发时,OS跳到我们对进入处理程序 - 它不会让我们的代码解锁第一。 当处理程序试图锁定,主()仍然持有锁,所以我们死锁。 灾害!

安全的事情就是写你的信号处理程序,以便它只是使一个音符的什么必须做,写代码,以实际这样做,其他地方的照顾。 但是,即使不一定是简单的 - 如果你的信号处理程序本身中断什么? 在这种特殊情况下不是问题,但值得考虑的兔子洞有多深的例子。 编写正确的信号处理是很难的。 有一些技巧,比如,你可以阻止信号处理 。 但它仍然很难。



Answer 2:

您也可以考虑使用sig_atomic_t从信号处理程序安全地沟通与程序的其余部分。 然而,这不会在你阅读以来,递增和存储的值不是一个原子操作张贴的例子工作。

在这里,你必须从GLIBC手册,解释摘录sig_atomic_t

为了避免对中断的变量访问的不确定性,可以使用针对访问总是原子的特定数据类型:sig_atomic_t。 读取和写入这些数据类型是保证在一个指令的情况发生,所以没有办法为处理程序为“中间”的访问中运行。

类型sig_atomic_t总是整数数据类型,但它是其中的一个,它多少位包含,可能会发生变化,从机器到机器。

- 数据类型:sig_atomic_t这是一个整数数据类型。 这种类型的对象总是原子访问。

在实践中,你可以假设int是原子。 您也可以假设指针类型的原子; 这是非常方便的。 这两种假设都是对所有的GNU C库支持,并在所有POSIX系统,我们知道的机器也是如此。

这里使用示例:

 #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;
 }


文章来源: Linux timers on global vars
标签: c timer locking