全球变量的Linux计时器

我在互联网上找到了下面的代码,我试图理解Linux计时器是如何工作的,无论如何,正如你在下面看到的那样,counter1是全局var,如果while正在处理它并且计时器关闭并改变会发生什么counter1的值,我需要锁吗?

// timertst1.c: Simple timer demo program. Use cross-gcc // Vers. 1.00 - 21.Oct. 2002 // kdwalter@t-online.de #include  #include  #include  #include  // 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; } } 

信号处理程序很危险。 操作系统将打断你的程序,无论它做什么,并运行处理程序; 当处理程序返回时,操作系统会将程序恢复到正在执行的操作。

假设counter1++被编译为一个加载,一个增量和一个商店。 如果中断在加载和存储之间触发,则指令序列将是加载,加载,增量,增量,存储,存储。 变量将增加一个,而不是两个; 灾害! 你是对的,这看起来像是一个经典的multithreading问题。

如果我们添加一个锁会怎么样? 现在我们得到锁定,加载,增量,存储,解锁,而不是加载,增量,存储。 但是如果信号在我们处于锁定和解锁之间时触发,则操作系统会将我们直接跳入处理程序 – 它不会让我们的代码先解锁。 当处理程序试图锁定时,main()仍然持有锁,所以我们死锁。 灾害!

安全的做法是编写信号处理程序,以便记下必须完成的工作,并编写代码以便在其他地方实际执行此操作。 但即使这样也不一定简单 – 如果您的信号处理程序本身被中断了怎么办? 在这种特殊情况下不是问题,但值得考虑一个兔子洞有多深的例子。 编写正确的信号处理程序很难。 有一些技巧,例如你可以阻止信号处理程序 。 但它仍然很难。

您还可以考虑使用sig_atomic_t从信号处理程序与程序的其余部分进行安全通信。 但是,这在您发布的示例中不起作用,因为读取,递增和存储值不是primefaces操作。

在这里您可以阅读GLIBC手册解释sig_atomic_t的摘录:

为避免中断对变量的访问的不确定性,您可以使用访问始终为primefaces的特定数据类型:sig_atomic_t。 读取和写入此数据类型保证在单个指令中发生,因此处理程序无法在访问的“中间”运行。

类型sig_atomic_t始终是整数数据类型,但它是哪一个,以及它包含多少位,可能因机器而异。

– 数据类型:sig_atomic_t这是一个整数数据类型。 始终以primefaces方式访问此类对象。

实际上,您可以假设int是primefaces的。 您还可以假设指针类型是primefaces的; 这很方便。 这两个假设都适用于GNU C库支持的所有机器以及我们所知道的所有POSIX系统。

这是一个用法示例:

  #include  #include  #include  /* 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; }