sleep函数是我们编程中非常常见的,它可以使得进程睡眠指定时间之后再执行
它的参数分别为秒级(sleep)和微秒级(usleep 1000000us为1s)
头文件
#include
函数原型
unsigned int sleep(unsigned int seconds);
int usleep(useconds_t usec);
值得注意的是,sleep和usleep都是可以中断的,即当进程在睡眠状态如果被中断,程序会从sleep或usleep之后的第一行代码开始运行,直接结束睡眠状态
返回值
通过函数原型我们可以观察到,sleep的返回值是无符号整型数据,其返回值分为两种情况:
我们可以使用Ctrl+C方便的实现一个中断,不过此中断的默认处理是退出程序,我们需要使用signal函数自定义信号处理
示例程序
#include
#include
#include
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的参数虽然是微妙级别的,但是其实际使用情况确实不准确的,使用以下示例程序可以观测到其误差率
示例程序
#include
#include
#include
#include
/*
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函数可以设置定时器,在指定秒数之后内核将向该进程发送SIGALRM信号,定时精度为秒级
首先需要明确的一点是,一个进程只能设置一个定时器,如果在调用alarm之前已经存在了定时器,则定时器被刷新(启动一个新的时间的定时器)
注意:当alarm的参数为0时,进程取消定时器,并且也不会发送SIGALRM信号
与sleep不同的是,alarm可以在调用之后继续执行其他程序,而非挂起进程
头文件
#include
函数原型
unsigned int alarm(unsigned int seconds);
返回值
alarm的返回值如下:
示例程序
#include
#include
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
#include
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的时间精度可以达到1us,是目前精确定时的主要方案
头文件
//使用man 2 select的结果
/* 根据POSIX.1-2001*/
#include
/* 根据先前的标准*/
#include
#include
#include
函数原型
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
参数介绍
示例代码
#include
#include
#include
#include
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处于同一水平???这就令人十分疑惑,本人能力有限,希望有高人看到之后可以指点一二,传道授业解惑