HZ和Jiffies

系统定时器timer能够以可编程的方式设定频率,来中断cpu处理器。此频率即hz,为每秒的定时器节拍(tick)数,
对应着内核变量HZ。选择合适的HZ值需要权衡。
tick为两个连续中断的时间间隔。
//查看我们系统为 7.9centos,3.1内核
[root@k3master ~]# cat /boot/config-`uname -r` | grep 'CONFIG_HZ='
CONFIG_HZ=1000 //这个就是内核hz
而在2.6.13中,它又被降低到了250。 2.5之前是100,3.10是10000,
在目前的内核中,可以在编译内核时通过配置菜单选择一个HZ值。该选项的默认值取决于体系架构的版本。
jiffies变量记录了系统启动以来,系统定时器已经触发的次数。内核每秒钟将jiffies变量增加HZ数。
因此,对于HZ值为100的系统,1个jiffy等于10ms,而对于HZ为1000的系统,1个jiffy仅为1ms。
jiffies既反应了次数指标,又反映的是精度的指标,HZ值大,定时器间隔时间就小,因此进程调度的准确性会更高。
但是,HZ值越大也会导致开销和电源消耗更多,因为更多的处理器将被耗费在系统定时器中断上下文中。

HZ的值取决于体系架构。在x86系统上,在2.4内核中,该值默认设置为100;在2.6内核中,该值变为1000;
系统的运行时间(jiffies/Hz), 将jiffies转化为以秒为单位的时间,计算公式为 jiffies/hz=?s 
HZ为1秒钟的时钟中断次数,jiffies类型 unsigned long 表示无整形都是正整数 
1、HZ=1000时 1jiffies = 1ms(1毫秒) 
2、HZ=100时 1jiffies=10ms
3、HZ=10时 1jiffies=100ms

这里实际基本都是100或1000,只是理解jiffies的计算方法.

注: 1 秒=1000 毫秒

//E:\linux内核\linux-2.6.38.5\linux-2.6.38.5\arch\x86\include\asm\param.h
//E:\linux内核\linux-2.6.38.5\linux-2.6.38.5\include\asm-generic\param.h

linux内核定义了两者的值:

#ifndef __ASM_GENERIC_PARAM_H
#define __ASM_GENERIC_PARAM_H #ifdef __KERNEL__
# define HZ CONFIG_HZ /* Internal kernel timer frequency */
# define USER_HZ 100 /* some user interfaces are */
# define CLOCKS_PER_SEC (USER_HZ) /* in "ticks" like times() */
#endif

其中CONFIG_HZ为通过make menuconfig配置的HZ值,一般为1000,
[root@k3master ~]# cat /boot/config-`uname -r` | grep 'CONFIG_HZ='
CONFIG_HZ=1000
即表示每秒钟jiffies增加1000个计数。
系统时钟每1ms(毫秒)中断一次, 时钟频率,用HZ宏表示,设定为1000,即每秒运行了时钟中断1000次.
实际值可参见代码中include/generated/autoconf.h文件。USER_HZ固定为100,用户层调用 times 系统调用,
返回的是按照USER_HZ计算的jiffies值。
#ifndef HZ
#define HZ 100
#endif
#ifndef EXEC_PAGESIZE
#define EXEC_PAGESIZE 4096
#endif #ifndef NOGROUP
#define NOGROUP (-1)
#endif #define MAXHOSTNAMELEN 64 /* max length of hostname */ #endif /* __ASM_GENERIC_PARAM_H */

用户层与内核之间相关交互

E:\linux内核\linux-2.6.0\linux-2.6.0\include\linux\times.h 

//内核定义了USER_HZ来代表用户空间看到的HZ值
//在x86体系结构上,由于HZ值原来一直是2.5之前是100 ,所以USER_HZ值就定义为100。2.6之后就是1000
//内核可以使用宏 jiffies_to_clock_t()将一个有HZ表示的节拍计数转换为一个由USER_HZ表示的节拍计数
//HZ的值就是CONFIG_HZ值.
//取模运算(%)就是两个整数相除的余数,那么这里就是1000%100=0

#if (HZ % USER_HZ)==0
# define jiffies_to_clock_t(x) ((x) / (HZ / USER_HZ))
#else
# define jiffies_to_clock_t(x) ((clock_t) jiffies_64_to_clock_t((u64) x))
#endif
static inline unsigned long clock_t_to_jiffies(unsigned long x)
{
#if (HZ % USER_HZ)==0
if (x >= ~0UL / (HZ / USER_HZ))
return ~0UL;
return x * (HZ / USER_HZ);
#else
u64 jif; /* Don't worry about loss of precision here .. */
if (x >= ~0UL / HZ * USER_HZ)
return ~0UL; /* .. but do try to contain it here */
jif = x * (u64) HZ;
do_div(jif, USER_HZ);
return jif;
#endif
} static inline u64 jiffies_64_to_clock_t(u64 x)
{
#if (HZ % USER_HZ)==0
do_div(x, HZ / USER_HZ);
#else
/*
* There are better ways that don't overflow early,
* but even this doesn't overflow in hundreds of years
* in 64 bits, so..
*/
x *= USER_HZ;
do_div(x, HZ);
#endif
return x;
}
#endif
struct tms {
clock_t tms_utime;
clock_t tms_stime;
clock_t tms_cutime;
clock_t tms_cstime;
}; #endif

E:\linux内核\linux-2.6.38.5\linux-2.6.38.5\kernel\time.c

两者定义的差别导致用户层与内核交互时,需要进行转换。除了以上的64位转换函数 jiffies_64_to_clock_t。

内核还提供了另外两个互换函数,实现函数:

/*
* Convert jiffies/jiffies_64 to clock_t and back.
*/
clock_t jiffies_to_clock_t(long x)
{
//一个中断时间间隔叫做一个节拍(tick),它的长度以纳秒为单位存放在tick_nsec变量中
//NSEC_PER_SEC单位是每毫秒有多少纳秒
#if (TICK_NSEC % (NSEC_PER_SEC / USER_HZ)) == 0
# if HZ < USER_HZ
return x * (USER_HZ / HZ);
# else
return x / (HZ / USER_HZ);
# endif
#else
return div_u64((u64)x * TICK_NSEC, NSEC_PER_SEC / USER_HZ);
#endif
}
//EXPORT_SYMBOL标签内定义的函数或者符号对全部内核代码公开,不用修改内核代码就可以在您的内核模块中直接调用,
//即使用
EXPORT_SYMBOL可以将一个函数以符号的方式导出给其他模块使用
EXPORT_SYMBOL(jiffies_to_clock_t); unsigned long clock_t_to_jiffies(unsigned long x)
{
#if (HZ % USER_HZ)==0
if (x >= ~0UL / (HZ / USER_HZ))
return ~0UL;
return x * (HZ / USER_HZ);
#else
/* Don't worry about loss of precision here .. */
if (x >= ~0UL / HZ * USER_HZ)
return ~0UL; /* .. but do try to contain it here */
return div_u64((u64)x * HZ, USER_HZ);
#endif
}
EXPORT_SYMBOL(clock_t_to_jiffies); u64 jiffies_64_to_clock_t(u64 x)
{
#if (TICK_NSEC % (NSEC_PER_SEC / USER_HZ)) == 0
# if HZ < USER_HZ
x = div_u64(x * USER_HZ, HZ);
# elif HZ > USER_HZ
x = div_u64(x, HZ / USER_HZ);
# else
/* Nothing to do */
# endif
#else
/*
* There are better ways that don't overflow early,
* but even this doesn't overflow in hundreds of years
* in 64 bits, so..
*/
x = div_u64(x * TICK_NSEC, (NSEC_PER_SEC / USER_HZ));
#endif
return x;
}
EXPORT_SYMBOL(jiffies_64_to_clock_t);

Linux网桥的ioctl为例,设置和获取bridge表项的最大超时时间
 代码位于文件net/bridge/br_ioctl.c中

static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
unsigned char mac[ETH_ALEN];
struct net_bridge *br = netdev_priv(dev);
unsigned long args[4]; if (copy_from_user(args, rq->ifr_data, sizeof(args)))
return -EFAULT; switch (args[0]) { case BRCTL_GET_BRIDGE_INFO:
{
struct __bridge_info b;
b.max_age = jiffies_to_clock_t(br->max_age); //转化为USER_HZ表示的clock
return 0;
}
case BRCTL_SET_BRIDGE_MAX_AGE:
{
unsigned long t = clock_t_to_jiffies(args[1]); //转化为HZ表示的jiffies
br->bridge_max_age = t;
return 0;
}
}

获取运行系统HZ值(网络中邻居表的locktime参数,默认设置的是一个HZ)

通过proc文件可读取
[root@ht8 ens192]# cat /proc/sys/net/ipv4/neigh/ens192/locktime
100

用户USER_HZ与内核HZ的值的更多相关文章

  1. linux信号机制 - 用户堆栈和内核堆栈的变化【转】

    转自:http://itindex.net/detail/16418-linux-%E4%BF%A1%E5%8F%B7-%E5%A0%86%E6%A0%88 此文只简单分析发送信号给用户程序后,用户堆 ...

  2. Linux用户空间与内核空间(理解高端内存)

    Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数 ...

  3. Linux用户空间与内核空间

    源:http://blog.csdn.net/f22jay/article/details/7925531 Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针 ...

  4. 深入理解linux网络技术内幕读书笔记(三)--用户空间与内核的接口

    Table of Contents 1 概论 1.1 procfs (/proc 文件系统) 1.1.1 编程接口 1.2 sysctl (/proc/sys目录) 1.2.1 编程接口 1.3 sy ...

  5. Linux操作系统学习_用户态与内核态之切换过程

    因为操作系统的很多操作会消耗系统的物理资源,例如创建一个新进程时,要做很多底层的细致工作,如分配物理内存,从父进程拷贝相关信息,拷贝设置页目录.页表等,这些操作显然不能随便让任何程序都可以做,于是就产 ...

  6. linux 用户空间与内核空间——高端内存详解

    摘要:Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对 ...

  7. 用户态与内核态 & 文件流与文件描述符 简介【转】

    转自:https://www.cnblogs.com/Jimmy1988/p/7479856.html 用户态和内核态 程序代码的依赖和调用关系如下图所示: Lib:标准ASCI C函数,几乎所有的平 ...

  8. 用户态与内核态 & 文件流与文件描述符 简介

    用户态和内核态 程序代码的依赖和调用关系如下图所示: Lib:标准ASCI C函数,几乎所有的平台都支持该库函数,因此依赖该库的程序可移植性好: System Function:系统调用函数,与系统内 ...

  9. Linux用户抢占和内核抢占详解(概念, 实现和触发时机)--Linux进程的管理与调度(二十)

    1 非抢占式和可抢占式内核 为了简化问题,我使用嵌入式实时系统uC/OS作为例子 首先要指出的是,uC/OS只有内核态,没有用户态,这和Linux不一样 多任务系统中, 内核负责管理各个任务, 或者说 ...

随机推荐

  1. python 关于heapq模块的随笔

    heapq模块提供了很多高级功能可以通过help(heapq)查看详细文档: 要点: 1优先级队列让我们可以按照重要程度来处理元素,而不是先进先出 2使用heapq可以应对长列表,因为heap不是复杂 ...

  2. Java多线程【三种实现方法】

    java多线程 并发与并行 并发:在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行 并行:一组程 ...

  3. 如何用系统命令sc修改系统服务

    系统服务是系统的一项重要组成部分,sc命令在特定权限下,可以实现对指定服务的启动项.配置文件及状态进行修改,位置在c:\windows\system32\目录下面,下面就和大家分享一下如何用系统命令s ...

  4. 选择Key-Value Store

    [IT168 专稿]在之前的文章中,给大家介绍了<Redis快速入门:Key-Value存储系统简介>,今天进一步给大家介绍为什么选择Key-Value Store.Key-Value S ...

  5. Linux系统常用的命令

    1.查看本机IP地址:ifconfig 2.查看当前所在路径:pwd 3.查看指定名称线程:ps -ef | grep tomcat 4.查看当前目录结构:ll 或者 ls 5.杀死指定线程:kill ...

  6. 在idea中新建完springboot项目的时候遇到问题(右键没有class选择;控制台报错:Could not transfer artifact org.apache.tomcat.embed:tomcat-embed-core:jar:9.0.60 from/to central ....)

    一.在idea中新建完springboot项目的时候遇到问题 问题1:右键没有class选择 解决:之所以会如此,是因为项目还没完成创建完成,解决:只需等等即可,等到完全创建完成即可 问题2(报tom ...

  7. 为什么JVM要用到压缩指针?Java对象要求8字节的整数倍?

    前言 前两天在一个帖子中看到一道面试题: 堆内存超过32G时,为什么压缩指针失效? 之前没有了解过这方面的知识,于是开始google起来,但当我翻看了不下一页的帖子,我都仍然没有搞懂,因为好多答案给我 ...

  8. String -- char[]互转

    1.String --> char[] String str = "abc"; char[] chs = str.toCharArray(); 2.char[] --> ...

  9. 比较一下 Java 和 JavaSciprt?

    JavaScript 与 Java 是两个公司开发的不同的两个产品.Java 是原 Sun Microsystems 公司推出的面向对象的程序设计语言,特别适合于互联网应用程序 开发:而 JavaSc ...

  10. 简述 Mybatis 的插件运行原理,以及如何编写一个插件。

    Mybatis 仅可以编写针对 ParameterHandler.ResultSetHandler. StatementHandler.Executor 这 4 种接口的插件,Mybatis 使用 J ...