我在互联网上找到了下面的代码,我试图理解Linux计时器是如何工作的,无论如何,正如你在下面看到的那样,counter1是全局var,如果while正在处理它并且计时器会发生什么会发生什么关闭并更改了counter1的值,我需要锁定吗?
// 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;
}
}
答案 0 :(得分: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;
}
答案 1 :(得分:1)
信号处理程序很危险。操作系统将打断你的程序,无论它做什么,并运行处理程序;当处理程序返回时,操作系统会将程序恢复到正在执行的操作。
假设counter1++
被编译为加载,增量和存储。如果中断在加载和存储之间触发,则指令序列将是加载,加载,增量,增量,存储,存储。变量将增加一个,而不是两个;灾害!你是对的,这看起来像是一个经典的多线程问题。
如果我们添加锁会怎么样?现在代替加载,增量,存储,我们得到锁定,加载,增量,存储,解锁。但是如果信号在我们处于锁定和解锁之间时触发,则OS会将我们直接跳入处理程序 - 它不会让我们的代码先解锁。当处理程序试图锁定时,main()仍然持有锁,所以我们死锁。灾难!
安全的做法是编写信号处理程序,以便记下必须完成的工作,并编写代码以便在其他地方实际执行此操作。但即使这样也不一定简单 - 如果您的信号处理程序本身被中断了怎么办?在这种特殊情况下不是问题,但值得考虑一个兔子洞有多深的例子。编写正确的信号处理程序很难。有一些技巧,例如你可以block signal handlers。但它仍然很难。