关于jiffies(/proc/stat)

我采样内核版本是linux-2.6.0

全局变量jiffies,这个变量非常重要

linux内核相关
E:\linux内核\linux-2.6.0\linux-2.6.0\kernel\itimer.c
E:\linux内核\linux-2.6.0\linux-2.6.0\include\linux\jiffies.h
E:\linux内核\linux-2.6.0\linux-2.6.0\include\linux\smp_lock.h
E:\linux内核\linux-2.6.0\linux-2.6.0\include\linux\time.h
E:\linux内核\linux-2.6.0\linux-2.6.0\kernel\timer.c
E:\linux内核\linux-2.6.0\linux-2.6.0\kernel\time.c
E:\linux内核\linux-2.6.0\linux-2.6.0\arch\x86_64\kernel\time.c

 

硬件给内核提供一个系统定时器用以计算和管理时间,内核通过编程预设系统定时器的频率(在内核代码中写死的)
系统定时器以固定的频率触发,这个频率称为tick rate(HZ),每个触发周期的时间叫做tick, 

它等于1/HZ秒。触发后系统会转到内核相应的handler去处理
Linux 2.5内核版本以上将tick从100改成1000,相当于系统时钟从10ms变成每1ms要中断一次.
我们可以查看系统设定的hz数,
 [root@k3master ~]# cat /boot/config-`uname -r` | grep 'CONFIG_HZ='
   CONFIG_HZ=1000

如果想计算系统运行了多长时间,就可以用 jiffies/hz 来计算
时间单位 jiffies 有多长?
在Linux 2.6中,系统时钟每1ms(毫秒)中断一次, 时钟频率,用HZ宏表示,设定为1000,即每秒中断1000次,
linux 2.4内核中设定为100,很多应用程序也仍然沿用100的时钟频率
 [root@k3master ~]# cat /boot/config-`uname -r` | grep 'CONFIG_HZ='
   CONFIG_HZ=1000
百科百科解释频率:

  频率,是指单位时间内完成周期性变化的次数,是描述周期运动频繁程度的量,
  常用符号f或ν表示,单位为秒分之一,符号为s 即 次数/s 

频率记为:  1000次/s=hz

 

 

hz,tick,jiffies值的查询和计算

1、hz的单位即 n次数/秒
2、tick单位是ms
3、jiffies单位是数字(即次数)

//
查看系统的HZ [root@k3master ~]# cat /boot/config-`uname -r` | grep 'CONFIG_HZ=' CONFIG_HZ=1000 计算方法:
tick=1/hz= ? ms.
所以当前的为 tick
=1/1000=0.001=10ms

系统运行时间以秒为单位,换算方法等于jiffies/Hz。

 

 

 

在linux内核中jiffies远比xtime重要
jiffies是记录着从电脑开机到现在总共的时钟中断次数(产生的tick的总数)。在linux内核中jiffies远比xtime重要,
那么他取决于系统的频率,单位是Hz,这里说一下频率的单位,1MHz=1000,000Hz(6个零),1KHz=1000Hz(3个零)
inux启动时,内核将jiffies变量初始化为0,此后,每次时钟中断处理程序都会增加该变量的值。
所以jiffies一秒内增加的值也就是Hz(频率)。 
注:一秒内时钟中断的次数等于Hz 频率是周期的倒数,一般是一秒钟中断产生的次数,所以,假如我们需要知道系统的精确的时间单位时,需要换算了,假如我们系统的频率是200Mhz,
那么一次中断的间隔是1秒
/200,000,000Hz=0.000 000 005秒看一下上面我们的时间单位,对照一下小数点后面是9个零, 所以理论上我们系统的精确度是5纳秒。LINUX系统时钟频率是一个常数HZ来决定的,通常HZ=100,那么他的精度度就是10ms(毫秒)。 也就是说每10ms一次中断。所以一般来说Linux的精确度是10毫秒

 

jiffies转化为以秒为单位的时间:jiffies /Hz
jiffies类型为无符号长整型(unsigned long),其他任何类型存放它都不正确。
将以秒为单位的时间转化为jiffies:seconds*Hz
将jiffies转化为以秒为单位的时间:jiffies /Hz
相比之下,内核中将秒转换为jiffies用的多些。

 在LINUX的时钟中断中涉及至二个全局变量一个是xtime,它是timeval数据结构变量,另一个则是jiffies。

xtime是从cmos电路中取得的时间,一般是从某一历史时刻开始到现在的时间,也就是为了取得我们操作系统上显示的日期。

这个就是所谓的“实时时钟”,它的精确度是微秒。

 

 

jiffies的内部表示jiffies定义于文件<E:\linux内核\linux-2.6.0\linux-2.6.0\include\linux\jiffies.h>中:

/* * The 64-bit value is not atomic - you MUST NOT read it *
without sampling the sequence number in xtime_lock. * get_jiffies_64() 
will do this for you as appropriate. */extern u64 __jiffy_data jiffies_64;
extern unsigned long volatile __jiffy_data jiffies;
*/

 

ld(1)脚本用于连接主内核映像(在x86上位于arch/i386/kernel/vmlinux.lds.S中),
然后用jiffies_64变量的初值覆盖jiffies变量。因此jiffies取整个jiffies_64变量的低32位。
访问jiffies的代码只会读取jiffies_64的低32位,通过get_jiffies_64()函数就可以读取整个64位的值。
在64位体系结构上,jiffies_64和jiffies指的是同一个变量。

#ifndef _LINUX_JIFFIES_H
#define _LINUX_JIFFIES_H

#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/seqlock.h>
#include <asm/system.h>
#include <asm/param.h> /* for HZ */

/*
* The 64-bit value is not volatile - you MUST NOT read it
* without holding read_lock_irq(&xtime_lock).
* get_jiffies_64() will do this for you as appropriate.

   1、unsigned的作用就是将数字类型无符号化(无符号类型就是不表示负数,只表示正数的数据类型)
   2、在C语言中,修饰符extern用在变量或者函数的声明前,用来说明“此变量/函数是在别处定义的,要在此处引用
   3、u8、u16、u32、u64、 s8、s16、s32、s64是linux内核确定大小的类型
   4、 __u8等式linux用户态确定大小的类型
   5、BITS_PER_LONG定义用于unsigned long变量的比特位数目,因而也适用于指向虚拟地址空间的通用指针
   6、C语言中的inline关键字是C99标准的关键字,它的作用是将函数展开,把函数的代码复制到每一个调用该函数的地方。

     这样调用该函数的地方就可以直接执行函数代码,而不发生跳转、压栈等一般性函数操作。

     可以节省时间,也会提高程序的执行速度。使用inline关键字修饰的函数就是内联函数。

*/

extern u64 jiffies_64;  
extern unsigned long volatile jiffies;

#if (BITS_PER_LONG < 64)
u64 get_jiffies_64(void);
#else
static inline u64 get_jiffies_64(void)
{
return (u64)jiffies;
}
#endif

/*
* These inlines deal with timer wrapping correctly. You are
* strongly encouraged to use them
* 1. Because people otherwise forget
* 2. Because if the timer wrap changes in future you won't have to
* alter your driver code.
* time_after(a,b) returns true if the time a is after time b.
* Do this with "<0" and ">=0" to only test the sign of the result. A
* good compiler would generate better code (and a really good compiler
* wouldn't care). Gcc is currently neither.
*/

//内核提供了四个宏来比较节拍计数
//unsigned long是无符号的长整型类型

#define time_after(a,b) \
(typecheck(unsigned long, a) && \
typecheck(unsigned long, b) && \
((long)(b) - (long)(a) < 0))
#define time_before(a,b) time_after(b,a)

#define time_after_eq(a,b) \
(typecheck(unsigned long, a) && \
typecheck(unsigned long, b) && \
((long)(a) - (long)(b) >= 0))
#define time_before_eq(a,b) time_after_eq(b,a)

#endif

jiffies 的回绕wrap around

  当jiffies的值超过它的最大存放范围后就会发生溢出。对于32位无符号长整型,最大取值为(2^32)-1,即429496795。
无符号型64位整数,值域为:0 -》 18446744073709551615 。如果节拍计数达到了最大值后还要继续增加,它的值就会回绕到0。


linux内核提供了四个宏来帮助比较节拍计数,它们能正确的处理节拍计数回绕的问题:

  在2.6以前的内核中,如果改变内核中的HZ值会给用户空间中某些程序造成异常结果。
因为内核是以节拍数/秒的形式给用户空间导出这个值的,应用程序便依赖这个特定的HZ值。如果在内核中改变了HZ的定义值,
就打破了用户空间的常量关系---用户空间并不知道新的HZ值。

 

解决方法:

内核更改所有导出的jiffies值。内核定义了USER_HZ来代表用户空间看到的HZ值。
在x86体系结构上,由于HZ值原来一直是100,所以USER_HZ值就定义为100。
linux内核可以使用宏jiffies_to_clock_t()将一个有HZ表示的节拍计数转换为一个由USER_HZ表示的节拍计数。

 


 

posted @ 2022-03-02 20:10  jinzi  阅读(197)  评论(0编辑  收藏  举报