Linux实现定时的方法
文章目录Linux环境下实现定时的方法sleep和usleepLinux环境下实现定时的方法sleep和usleepsleep函数是我们编程中非常常见的,它可以使得进程睡眠指定时间之后再执行它的参数分别为秒级(sleep)和微秒级(usleep 1000000us为1s)sleep和usleep的头文件和函数原型如下所示:#include <unistd.h>unsigned int
Linux环境下实现定时的方法
sleep(精准)和usleep(不精准)
sleep函数是我们编程中非常常见的,它可以使得进程睡眠指定时间之后再执行
它的参数分别为秒级(sleep)和微秒级(usleep 1000000us为1s)
头文件
#include <unistd.h>
函数原型
unsigned int sleep(unsigned int seconds);
int usleep(useconds_t usec);
值得注意的是,sleep和usleep都是可以中断的,即当进程在睡眠状态如果被中断,程序会从sleep或usleep之后的第一行代码开始运行,直接结束睡眠状态
返回值
通过函数原型我们可以观察到,sleep的返回值是无符号整型数据,其返回值分为两种情况:
- 休眠过程中发生中断,sleep返回剩余的秒数
- 休眠结束,sleep返回值为0
我们可以使用Ctrl+C方便的实现一个中断,不过此中断的默认处理是退出程序,我们需要使用signal函数自定义信号处理
示例程序
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void Sig_handler(int num)
{
printf("The signal val is:%d\n", num);
}
int main()
{
signal(SIGINT, Sig_handler);
int num = sleep(10);
printf("This is sleep return val: %d!!!\n",num);
return 0;
}
程序编译运行之后,终端选择性输入Ctrl+C,通过查看函数执行过程中打印语句,我们可以观察到两者的区别
- 发生中断
- 未发生中断
usleep返回值
usleep和sleep不同的是,如果usleep休眠期间没有中断,则返回0;否则返回-1
usleep的误差问题
usleep的参数虽然是微妙级别的,但是其实际使用情况确实不准确的,使用以下示例程序可以观测到其误差率
示例程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
/*
timeval结构体
struct timeval
{
__time_t tv_sec; // Seconds. 秒
__suseconds_t tv_usec; // Microseconds. 微妙
};
timezone结构体
//具体内容含义本人并不清楚,只是下方的gettimeforday函数需要该结构体,感兴趣的朋友可以自行了解
struct timezone
{
int tz_minuteswest; // Minutes west of GMT. 和Greewich时间差了多少分钟
int tz_dsttime; // Nonzero if DST is ever in effect. 日光节约时间的状态
};
gettimeofday获取当前时间
*/
int main()
{
struct timeval nowTime;
struct timezone Tz;
int i;
for(i = 0; i < 10; i++)
{
usleep(1);
gettimeofday(&nowTime, &Tz);
printf("The nowTime.sec is: %ld, nowTime.usec is: %ld\n",
nowTime.tv_sec, nowTime.tv_usec);
}
return 0;
}
程序运行效果
由上方程序运行效果可以观察到,usleep函数的最大误差可达到1022us,远远超过设定的1us的限制
其实这是由于Linux内核定时器HZ引起的,查看Linux系统的定时器频率可以使用以下命令:
getconf CLK_TCK
# 本人频率为100
当定时器频率为100HZ时,也就意味着Linux核心每秒钟会产生100次timer interrupt,则系统的时钟精度就为1/100s=10ms(本人系统定时误差为1000us=1ms左右,不知为何不是10ms)
如果需要使用usleep尽可能的提高函数精度,可以开启高精度定时器,在编译Linux内核时打开Kernel Features ----》[High Resolution Timer Support] 选项
alarm函数(精准)
alarm函数可以设置定时器,在指定秒数之后内核将向该进程发送SIGALRM信号,定时精度为秒级
首先需要明确的一点是,一个进程只能设置一个定时器,如果在调用alarm之前已经存在了定时器,则定时器被刷新(启动一个新的时间的定时器)
注意:当alarm的参数为0时,进程取消定时器,并且也不会发送SIGALRM信号
与sleep不同的是,alarm可以在调用之后继续执行其他程序,而非挂起进程
头文件
#include <unistd.h>
函数原型
unsigned int alarm(unsigned int seconds);
返回值
alarm的返回值如下:
- 如果在调用alarm之前已经存在了定时器,则alarm的返回值为上个定时器的剩余时间
- 如果进程中没有定时器,则alarm返回0
- 如果alarm调用失败,则函数返回-1
示例程序
#include <signal.h>
#include <unistd.h>
void timeout_Sig_handler(int num)
{
printf("The timeout signal val is:%d\n", num);
}
int main()
{
signal(SIGALRM, timeout_Sig_handler);
alarm(10);
sleep(1);
printf("This is alarm timeVal : %d!!!\n", alarm(3));
pause(); //挂起进程直到捕捉到信号
return 0;
}
分析上述程序可知,程序会输出9,并在3s之后将SIGALRM的值14输出
程序运行结果如下:
固定间隔触发定时器示例程序
如果我们需要每隔一定时间就出发一次定时器,可以在SIGALRM的处理函数中添加alarm,再添加一个死循环,让进程持续发送定时信号
示例程序如下:
#include <signal.h>
#include <unistd.h>
int sleepVal = 0;
void timeout_Sig_handler()
{
printf("The timeout val is: %d\n", sleepVal++);
alarm(1);
}
int main()
{
signal(SIGALRM, timeout_Sig_handler);
alarm(1);
while(1);
return 0;
}
程序运行效果如下所示:
select函数(精准)
和之前几个函数相比,select的时间精度可以达到1us,是目前精确定时的主要方案
头文件
//使用man 2 select的结果
/* 根据POSIX.1-2001*/
#include <sys/select.h>
/* 根据先前的标准*/
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
函数原型
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
参数介绍
- nfds:fd_set集合中最大的描述符值加1
- readfds:用于检查可读性
- writefds:用于检查可写性
- exceptfds:用于检查错误事件(带外数据)
- timeout:执行timeval结构的指针,用于决定select等待I/O的最长时间,如果为空将一直等待
示例代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
void setTime(int sec, int usec)
{
struct timezone tz;
struct timeval tVal;
tVal.tv_sec = sec;
tVal.tv_usec = usec;
select(0, NULL, NULL, NULL, &tVal);
gettimeofday(&tVal, &tz);
printf("This tVal.sec is: %ld, tVal.usec is: %ld\n", tVal.tv_sec, tVal.tv_usec);
}
int main()
{
int i = 0;
for(i = 0; i < 10; i++)
{
setTime(0, 1);
}
return 0;
}
运行效果
根据程序运行效果来看,虽然select号称是精准到1us的精确度,但实际也是受到内核频率限制的,误差和usleep处于同一水平???这就令人十分疑惑,本人能力有限,希望有高人看到之后可以指点一二,传道授业解惑
更多推荐
所有评论(0)