〇、背景
??在linux C嵌入式开发编程中,经常会遇到精度要求比较高的周期性的定时事件,对于一般性要求 1ms 级别的精度,linux下可以使用 select() 函数实现,但是 select() 有一个弱点就是其 定时的误差会累积,运行到一定程度的时候,实际误差可能已经超出了允许的范围,那么在这种情况,setitimer() 函数可以解决误差累积这样的问题。
一、简介
??setitimer() 函数是一个比较常用的函数,可以用来作为定时器,或者实现延时功能,其精确度为 ms 级别,相比于 select() 或者 sleep() 族函数功能,setitimer() 在运行期间将尽量保持平均时间误差为 1ms,这取决于系统计时器分辨率和系统负载,定时时间到期后,将生成一个信号(signal)并重置计时器。如果在进程处于活动状态时计时器到期(对于 ITIMER_VIRTUAL 始终为真),则信号将在生成时立即传递。否则,信号发送将被抵消一小段时间,具体取决于系统加载。
??setitimer() 工作机制是,先对 it_value 进行倒计时,当 it_value 为的数值减为 0 时触发信号。然后再将为 it_value 重置 it_interval,之后继续对 it_value 倒计时,如此往复,当 it_value 为 0 或 计时器到期且 it_interval 为 0 时计时器停止运行。
??所以基于此机制,setitimer() 既可以用来作为 延时器 使用,也可以作为 定时器 使用。
二、函数说明
??最重要的需要说明就是,在一个进程中只能有一个 setitimer() 函数的调用,下一个函数的调用会覆盖前一个的计时,是不支持在同一进程中同时使用多次以支持多个定时器的功能的。/font>
2.1 函数原型
??在ubuntu 16.04环境下,setitimer() 和 getitimer() 函数原型如下所示。
#include <sys/time.h>
int getitimer(int which, struct itimerval *curr_value);
int setitimer(int which, const struct itimerval *new_value,
struct itimerval *old_value);
2.1.1 setitimer函数说明
函数名称: ??setitimer 函数功能: ??将指定的计时器设置为 new_value 中的值 ??如果 old_value 不为 NULL,则计时器的旧值存储在那里 函数参数: ??which:计时器的类型,可选的类型如下描述 ??new_value:要设置的计时器的新的值 ??old_value:计时器的下一次到期的剩余时间,与 getitimer() 返回的信息相同 ????????一般情况此参数用处不大,设置为 NULL 即可 函数返回: ????成功,返回 0 ????出错,返回 -1,并适当设置 errno 函数说明: ??计时器从 it_value 递减到零,生成一个信号,然后重置为 it_interval。设置为零的计时器(it_value 为零或计时器到期且 it_interval 为零)停止。
2.1.2 getitimer函数说明
函数名称: ??getitimer 函数功能: ??获取 which 指定的计时器的下一次到期的剩余时间 函数参数: ??which:计时器的类型,与函数 setitimer() 参数相同 ??curr_value:获取到的下一次到期的剩余时间的输出指针 函数返回: ????成功,返回 0 ????出错,返回 -1,并适当设置 errno 函数说明: ??参数 curr_value 指向的结构的字段 it_value 的子字段设置为计时器上剩余的时间量,如果计时器被禁用,则设置为零。 it_interval 字段设置为定时器间隔(周期);在该字段(的两个子字段)中返回的值为零表示这是一个单次计时器。
2.1.3 计时器类型说明
??作为 setitimer 与 getitimer 函数通用的计数器的类型,其可选的值有如下三个。
-
ITIMER_REAL:以系统真实的时间进行递减计算,定时时间到期时发送 SIGALRM 信号 -
ITIMER_VIRTUAL:仅在该进程进程执行时的时间来递减计算,定时时间到期时发送 SIGVTALRM 信号 -
ITIMER_PROF:以该进程在用户态下和内核态下执行的时间来递减计算。与 ITIMER_VIRTUAL 结合使用,此计时器通常用于分析应用程序在用户和内核空间中花费的时间,定时器到期时发送 SIGPROF 信号。
2.1.4 函数错误说明
??函数 setitimer 与 getitimer 可能在调用时发生错误情况,其主要的错误有如下两种情况。
2.2 参数itimerval结构说明
??函数 setitimer 与 getitimer 的时间参数均为 struct itimerval 类型,其结构体定义如下所示。
struct itimerval {
struct timeval it_interval;
struct timeval it_value;
};
struct timeval {
time_t tv_sec;
suseconds_t tv_usec;
};
??由上面的代码可以,itimeval 结构体又是由两个 timeval 结构体组成,timeval 包括 tv_sec 和 tv_usec 两部分。其中 tv_sec 为秒,tv_usec 为微秒(即 1/1000000 秒)。tv_sec 和 tv_usec 在确定计时器的持续时间时都很重要。
四、源码测试
??基于上面的描述,我们简单的编写一个用于定时器的测试程序,主要实现的功能是,每隔100ms在终端输出信息,包括当前的时间。示例代码入下所示。
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
void signalHandler(int signo)
{
struct timeval tm;
gettimeofday(&tm, NULL);
switch (signo)
{
case SIGALRM:
printf("Get the SIGALRM signal! time = %ld.%03ld\n",
tm.tv_sec, tm.tv_usec / 1000);
break;
case SIGVTALRM:
printf("Get the SIGVTALRM signal! time = %ld.%03ld\n",
tm.tv_sec, tm.tv_usec / 1000);
break;
case SIGPROF:
printf("Get the SIGPROF signal! time = %ld.%03ld\n",
tm.tv_sec, tm.tv_usec / 1000);
break;
}
}
int main(int argc, char *argv[])
{
struct itimerval new_value = {0};
signal(SIGALRM, signalHandler);
new_value.it_value.tv_sec = 0;
new_value.it_value.tv_usec = 100000;
new_value.it_interval.tv_sec = 0;
new_value.it_interval.tv_usec = 100000;
if (setitimer(ITIMER_REAL, &new_value, NULL) < 0)
{
printf("Setitimer Failed : %s\n", strerror(errno));
_exit(EXIT_FAILURE);
}
while (1)
{
pause();
}
return 0;
}
五、测试效果图
??编译上述程序并运行,程序执行的效果如下图5.1所示。
图5.1 程序执行效果图
??好啦,如果对本文的内容有任何疑问或者建议,请随时联系并欢迎讨论,由于本人学识与精力有限,难免会有一些错误,欢迎大佬指出并斧正。如果你觉得此文章对你有帮助,烦请帮忙点个赞鼓励一下,你的肯定将是我继续女里坚持的动力。
|