我发现下面的代码在互联网上,我想了解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;
}
}
信号处理是很危险的。 操作系统是要打断你的程序,不管它是干什么的,运行处理程序; 当处理程序返回时,OS将你的程序返回到它在做什么。
比方说, counter1++
编译到负载,增量和存储。 如果负载和存储之间的中断触发,然后将指令序列将负载,负载,增量,增量,商店,商店。 变量就上去了又一个,不是两个; 灾害! 你说的没错,这看起来像一个典型的多线程问题。
如果我们增加一个锁,会发生什么? 现在,而不是负担,增量,存储,我们得到了锁,加载,增量,存储,解锁。 但是,如果我们锁定和解锁之间,而信号触发时,OS跳到我们对进入处理程序 - 它不会让我们的代码解锁第一。 当处理程序试图锁定,主()仍然持有锁,所以我们死锁。 灾害!
安全的事情就是写你的信号处理程序,以便它只是使一个音符的什么必须做,写代码,以实际这样做,其他地方的照顾。 但是,即使不一定是简单的 - 如果你的信号处理程序本身中断什么? 在这种特殊情况下不是问题,但值得考虑的兔子洞有多深的例子。 编写正确的信号处理是很难的。 有一些技巧,比如,你可以阻止信号处理 。 但它仍然很难。
您也可以考虑使用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;
}