关于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表示的节拍计数。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类