关于linux下时间延迟函数精度问题

Linux内核时钟中断设施
linux的时钟中断需要两个全局变量,分别是xtime与jiffies。

1、xtime
一个timeval结构类型变量,是从cmos电路中取得的时间,一般是从某一历史时刻开始到现在的时间,也就是为了取得我们操作系统上显示的日期。这个就是“实时时钟”,它的精确度是微秒。获取方式是通过sys/time.h头文件里面的gettimeofday函数获取。

2、HZ
Linux核心每隔固定周期会发出timer interrupt (IRQ 0),HZ是用来保存每一秒有几次timer interrupts。如HZ为1000,代表每秒有1000次timer interrupts。 HZ可在编译核心时设定,可设定100、250、300或1000。核心版本预设值为250。
HZ这个值可以理解为操作系统的时钟频率,这个时钟的精度远低于硬件的时钟频率。如HZ设置为250,那么为一秒钟250次,每次为4ms,因此操作系统的时钟精度只能达到4ms。

3、Tick
Tick是HZ的倒数,意即timer interrupt每发生一次中断的时间。如HZ为250时,tick为4毫秒(millisecond)。

4、Jiffies
在<linux/jiffies.h>,定义了Jiffies(unsigned long),在linux内核中jiffies远比xtime重要。每发生一次timer interrupt,Jiffies变数会被加一。一秒内时钟中断的次数等于HZ,所以jiffies一秒内增加的值也就是HZ。
在 Linux 2.6 中,系统时钟每 1 毫秒中断一次(时钟频率,用 HZ 宏表示,定义为 1000,即每秒中断 1000 次,2.4 中定义为 100,很多应用程序也仍然沿用 100 的时钟频率),这个时间单位称为一个 jiffie。jiffies 与绝对时间之间的转换, 用两个宏来完成两种时间单位的互换:JIFFIES_TO_NS()、NS_TO_JIFFIES()。
jiffies是记录着从电脑开机到现在总共的时钟中断次数。在linux内核中jiffies远比xtime重要,那么他取决于系统的频率,单位是Hz。
这是硬件给内核提供一个系统定时器用以计算和管理时间,连续累加一年四个多月后就会溢出(假定HZ=100,1个jiffies等于1/100秒,jiffies可记录的最大秒数为 (2^32 -1)/100=42949672.95秒,约合497天或1.38年),即当取值到达最大值时继续加1,就变为了0。
因此为防止溢出需要使用这些宏函数进行:time_before()、 time_after()、time_after_eq()、time_before_eq()。因为jiffies随时钟滴答变化,不能用编译器优化 它,应取volatile值。
另外,80x86架构定义一个与jiffies相关的变数jiffies_64 ,w为64位,要等到此变数溢位可能要好几百万年。jiffies被对应至jiffies_64最低的32位元。因此,经由jiffies_64可以完全不理会溢位的问题便能取得jiffies。

5、RTC
除了系统定时器外,还有一个与时间有关的时钟:实时时钟(RTC),这是一个硬件时钟,用来持久存放系统时间,系统关闭后靠主板上的微型电池保持计时。系统启动时,内核通过读取RTC来初始化Wall Time,并存放在xtime变量中,这是RTC最主要的作用。

延迟函数问题
1、sleep

Linux下的精度为秒,是精确的,实现原理如下:
1. 注册一个信号signal(SIGALRM,handler)。接收内核给出的一个信号。
2. 调用alarm()函数。
3. pause()挂起进程。

alarm是当前进程的私有定时闹钟,pause函数挂起当前进程,内核切换到其他进程运行。当alarm设置的时间到时,内核通过SIGALRM信号去处理,pause函数返回后进程继续执行。

2、usleep
精度为微妙,但实际并不能精确到微妙。通过前面给出的操作系统时钟基础设置,系统精度由HZ系统时钟频率决定,一般设置为250时精度只能到4ms,因此usleep的微妙延迟只能是至少延迟的时间,时间上操作系统进行内核到用户的切换就会花10-30ms级别的时间,因此短时间的延迟一般都会大于设定值。
另外usleep有以下的问题
1. 在一些平台下不是线程安全,如HP-UX以及Linux usleep()会影响信号;
2. 在很多平台,如HP-UX以及某些Linux下,参数的值必须小于1 * 1000 * 1000也就是1秒,否则该函数会报错,并且立即返回。
3. 大部分平台的帮助文档已经明确说了,该函数是已经被舍弃的函数。

3、高精度延迟nanosleep、select
针对Linux平台下的高精度延迟,Linux2.0.x新增了nanosleep系统调用。精度虽然可以设置到纳秒,但是依然没有精确到纳秒,不过对于毫秒级别的延迟的精度已经足够高了,可以完全满足usleep无法达到的精度。但使用nanosleep应注意判断返回值和错误代码,否则容易造成cpu占用率100%。另外,nanosleep()没有usleep函数的缺点,在Solaris的多线程环境下编译器会自动把usleep()连接成nanosleep()。
select系统调用也可以实现延迟,精度可以达到微妙,而且是精确的,因此是首选。

struct timeval delay;
delay.tv_sec = 0;
delay.tv_usec = 20 * 1000; // 20 ms
select(0, NULL, NULL, NULL, &delay);


原文链接:https://blog.csdn.net/u010487568/article/details/52043136

猜你喜欢

转载自blog.csdn.net/z1026544682/article/details/105650719
今日推荐