使用定时器的目的无非是为了周期性的执行某一任务,或者是到了一个指定时间去执行某一个任务。要达到这一目的,一般有两个常见的比较有效的方法。一个是用 Linux
内部的三个定时器;另一个是用 sleep
或 usleep
函数让进程睡眠一段时间;其实,还有一个方法,那就是用 gettimeofday
、difftime
等自己来计算时间间隔,然后时间到了就执行某一任务,但是这种方法效率低,所以不常用。
alarm
如果不要求很精确的话,用 alarm()
和 signal()
就够了
unsigned int alarm(unsigned int seconds)
专门为SIGALRM
信号而设,在指定的时间seconds
秒后,将向进程本身发送SIGALRM
信号,又称为闹钟时间。进程调用alarm
后,任何以前的alarm()
调用都将无效。如果参数seconds
为零,那么进程内将不再包含任何闹钟时间。如果调用alarm()
前,进程中已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0
。
示例:
#include
#include
#include
void sigalrm_fn(int sig)
{
printf("alarm!\n");
alarm(2);
return;
}
int main(void)
{
signal(SIGALRM, sigalrm_fn);
alarm(2);
while(1) pause();
}
setitimer
int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue));
int getitimer(int which, struct itimerval *value);
strcut timeval
{
long tv_sec; /*秒*/
long tv_usec; /*微秒*/
};
struct itimerval
{
struct timeval it_interval; /*时间间隔*/
struct timeval it_value; /*当前时间计数*/
};
setitimer()
比 alarm()
功能强大,支持3
种类型的定时器:
ITIMER_REAL
:给一个指定的时间间隔,按照实际的时间来减少这个计数,当时间间隔为0
的时候发出SIGALRM
信号。
ITIMER_VIRTUAL
:给定一个时间间隔,当进程执行的时候才减少计数,时间间隔为0
的时候发出SIGVTALRM
信号。
ITIMER_PROF
:给定一个时间间隔,当进程执行或者是系统为进程调度的时候,减少计数,时间到了,发出SIGPROF
信号。
setitimer()
第一个参数 which
指定定时器类型(上面三种之一);第二个参数是结构 itimerval
的一个实例;第三个参数可不做处理。
下面是关于setitimer
调用的一个简单示范,在该例子中,每隔一秒发出一个SIGALRM
,每隔0.5
秒发出一个SIGVTALRM
信号::
#include
#include
#include
#include
#include
#include
int sec;
void sigroutine(int signo){
switch (signo){
case SIGALRM:
printf("Catch a signal -- SIGALRM \n");
signal(SIGALRM, sigroutine);
break;
case SIGVTALRM:
printf("Catch a signal -- SIGVTALRM \n");
signal(SIGVTALRM, sigroutine);
break;
}
return;
}
int main()
{
struct itimerval value, ovalue, value2;
sec = 5;
printf("process id is %d ", getpid());
signal(SIGALRM, sigroutine);
signal(SIGVTALRM, sigroutine);
value.it_value.tv_sec = 1;
value.it_value.tv_usec = 0;
value.it_interval.tv_sec = 1;
value.it_interval.tv_usec = 0;
setitimer(ITIMER_REAL, &value, &ovalue);
value2.it_value.tv_sec = 0;
value2.it_value.tv_usec = 500000;
value2.it_interval.tv_sec = 0;
value2.it_interval.tv_usec = 500000;
setitimer(ITIMER_VIRTUAL, &value2, &ovalue);
for(;;);
}
该例子的执行结果如下:
localhost:~$ ./timer_test
process id is 579
Catch a signal – SIGVTALRM
Catch a signal – SIGALRM
Catch a signal – SIGVTALRM
Catch a signal – SIGVTALRM
Catch a signal – SIGALRM
Catch a signal –GVTALRM
注意:Linux
信号机制基本上是从Unix
系统中继承过来的。早期Unix
系统中的信号机制比较简单和原始,后来在实践中暴露出一些问题,因此,把那些建立在早期机制上的信号叫做”不可靠信号”,信号值小于SIGRTMIN
(Red hat 7.2
中,SIGRTMIN=32
,SIGRTMAX=63
)的信号都是不可靠信号。这就是”不可靠信号”的来源。它的主要问题是:进程每次处理信号后,就将对信号的响应设置为默认动作。在某些情况下,将导致对信号的错误处理;因此,用户如果不希望这样的操作,那么就要在信号处理函数结尾再一次调用 signal()
,重新安装该信号。
sleep
以及 usleep
实现定时执行任务select
来提供精确定时和休眠hrtimer
需要在 kernel
中打开 “high resolution Timer support”
,驱动程序中 hrtimer
的初始化如下:
hrtimer_init(&m_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
m_timer.function = vibrator_timer_func;
hrtimer_start(&m_timer, ktime_set(0, 62500), HRTIMER_MODE_REL_PINNED);
定时函数 vibrator_timer_func
如下:
static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer)
{
gpio_set_value(gpio_test, 1);
gpio_set_value(gpio_test, 0);
hrtimer_forward_now(&m_timer,ktime_set(0, 62500));
return HRTIMER_RESTART;
}
其中 gpio_test
为输出引脚,为了方便输出查看。但是用示波器查看引脚波形时,发现虽然设定的周期为62.5us
,但是输出总是为72us
左右,而且偶尔会有两个波形靠的很近(也就是说周期突然变为10us
以下)。我将周期设到40us
的话,就会出现72us
和10us
经常交替出现,无法实现精确的40us
的波形,如果设置到100us
时,则波形就是100us
了,而且貌似没有看到有10us
以下的周期出现。
posix_timer