内核时间管理

Linux内核计时、延时函数与内核定时器

内核通过定时器(timer)中断来跟踪时间流
硬件定时器以周期性的间隔产生时间中断,这个间隔(即频率)由内核根据HZ来确定,HZ是一个与体系结构无关的常数。这个时间间隔通常取1ms到10ms.

jiffies计算器

2.1每次当定时器中断发生时,内核内部通过一个64位的变量jiffies_64做加一计数。

2.2驱动程序开发者通常访问的是jiffies变量,它是jiffes_64的低32位。

定时器与时间管理:

1、节拍率——HZ:在alpha体系结构上1024,而在其它平台上,都为10数量级倍。在嵌入式ARM上为100(2.6内核)。这个值的意义是什么呢,也就是在ARM平台上时钟中断100次,为一秒。一般的情况下编程者不要改变这个值,因为内核编很多代码都是有时间要求的,而且内核编写都在很多地方都做了相应的优化与折衷处理,改变HZ的值会对系统的性能有很大的影响。

2、jiffies:这个值是用来记录系统自系统启动以来产生的节拍的总数,启动时,内核将这个变量初始化为0;在每次的时钟中断处理程序都会增加该变量的值,jiffies一秒内增加的值就是HZ,系统运行时间以秒为单位计算,则为系统运行了jiffies/HZ秒。

在如下定义(2.6内核):

extern u64 __jiffy_data jiffies_64;
extern unsigned long volatile __jiffy_data jiffies;

在2.6的内核中它的变量类型从无符号长整型变为了u64,也就是说,即使是32位的机器,也使用无符号的64位整型表示jiffies。大多数的代码只涉及jiffies的低32位,访问jiffies的代码中会读到jiffies_64的低32位,可以通过get_ jiffies_64()函数读取整个64位。在64位的体系结构中jiffies_64和jiffies指的同一个变量,代码可以既可以通过jiffies也可以通过get_ jiffies_64()读取。

内核超时处理

jiffies 计数器
定时器中断由系统定时硬件以规律地间隔产生; 这个间隔在启动时由内核根据 HZ 值来编程, HZ 是一个体系依赖的值, 每次发生一个时钟中断, 一个内核计数器的值递增. 这个计数器在系统启动时初始化为 0, 因此它代表从最后一次启动以来的时钟嘀哒的数目.
这个计数器和来读取它的实用函数位于 , 尽管你会常常只是包含 ,

#include
unsigned long j, stamp_1, stamp_half, stamp_n;

j = jiffies; /* read the current value /
stamp_1 = j + HZ; /
1 second in the future /
stamp_half = j + HZ/2; /
half a second /
stamp_n = j + n * HZ / 1000; /
n milliseconds */

忙等待
如果你想延时执行多个时钟嘀哒, 允许在值中某些疏忽, 最容易的( 尽管不推荐 ) 的实现是一个监视 jiffy 计数器的循环. 这种忙等待实现常常看来象下面的代码, 这里 j1 是 jiffies 的在延时超时的值:

while (time_before(jiffies, j1)){}

超时
到目前为止所展示的次优化的延时循环通过查看 jiffy 计数器而不告诉任何人来工作. 但是最好的实现一个延时的方法, 如你可能猜想的, 常常是请求内核为你做. 有 2 种方法来建立一个基于 jiffy 的超时, 依赖于是否你的驱动在等待其他的事件.
如果你的驱动使用一个等待队列来等待某些其他事件, 但是你也想确保它在一个确定时间段内运行, 可以使用 wait_event_timeout 或者wait_event_interruptible_timeout:

#include
long wait_event_timeout(wait_queue_head_t q, condition, long timeout);
long wait_event_interruptible_timeout(wait_queue_head_t q, condition, long timeout);
这些函数在给定队列上睡眠, 但是它们在超时(以 jiffies 表示)到后返回. 因此, 它们实现一个限定的睡眠不会一直睡下去. 注意超时值表示要等待的jiffies 数, 不是一个绝对时间值. 这个值由一个有符号的数表示, 因为它有时是一个相减运算的结果, 尽管这些函数如果提供的超时值是负值通过一个printk 语句抱怨. 如果超时到, 这些函数返回 0; 如果这个进程被其他事件唤醒, 它返回以 jiffies 表示的剩余超时值. 返回值从不会是负值, 甚至如果延时由于系统负载而比期望的值大.

wait_event_timeout 和 wait_event_interruptible_timeout 被设计为有硬件驱动存在, 这里可以用任何一种方法来恢复执行: 或者有人调用 wake_up 在等待队列上, 或者超时到. 这不适用于 jitqueue, 因为没人在等待队列上调用 wake_up ( 毕竟, 没有其他代码知道它 ), 因此这个进程当超时到时一直唤醒. 为适应这个特别的情况, 这里你想延后执行不等待特定事件, 内核提供了 schedule_timeout 函数, 因此你可以避免声明和使用一个多余的等待队列头:

#include
signed long schedule_timeout(signed long timeout);
这里, timeout 是要延时的 jiffies 数. 返回值是 0 除非这个函数在给定的 timeout 流失前返回(响应一个信号). schedule_timeout 请求调用者首先设置当前的进程状态, 因此一个典型调用看来如此:

set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout (delay);
第一行调用 set_current_state 来设定一些东西以便调度器不会再次运行当前进程, 直到超时将它置回 TASK_RUNNING 状态. 为获得一个不可中断的延时, 使用 TASK_UNINTERRUPTIBLE 代替.

(1)内核中的时间概念

时间管理在linux内核中占有非常重要的作用。
相对于事件驱动而言,内核中有大量函数是基于时间驱动的。
有些函数是周期执行的,比如每10毫秒刷新一次屏幕;
有些函数是推后一定时间执行的,比如内核在500毫秒后执行某项任务。
要区分:
*绝对时间和相对时间
*周期性产生的事件和推迟执行的事件
周期性事件是由系统系统定时器驱动的

(2)HZ值
内核必须在硬件定时器的帮助下才能计算和管理时间。
定时器产生中断的频率称为节拍率(tick rate)。
在内核中指定了一个变量HZ,内核初始化的时候会根据这个值确定定时器的节拍率。
HZ定义在<asm/param.h>,在i386平台上,目前采用的HZ值是1000。
也就是时钟中断每秒发生1000次,周期为1毫秒。即:
#define HZ 1000

注意!HZ不是个固定不变的值,它是可以更改的,可以在内核源代码配置的时候输入。
不同的体系结构其HZ值是不一样的,比如arm就采用100。
如果在驱动中要使用系统的中断频率,直接使用HZ,而不要用100或1000 a.理想的HZ值 i386的HZ值一直采用100,直到2.5版后才改为1000。 提高节拍率意味着时钟中断产生的更加频繁,中断处理程序也会更频繁地执行。 带来的好处有: *内核定时器能够以更高的频率和更高的准确度运行 *依赖定时器执行的系统调用,比如poll()和select(),运行的精度更高 *提高进程抢占的准确度 (缩短了调度延时,如果进程还剩2ms时间片,在10ms的调度周期下,进程会多运行8ms。 由于耽误了抢占,对于一些对时间要求严格的任务会产生影响) 坏处有: *节拍率要高,系统负担越重。 中断处理程序将占用更多的处理器时间。

(3)jiffies
全局变量jiffies用于记录系统启动以来产生的节拍的总数。
启动时,jiffies初始化为0,此后每次时钟中断处理程序都会增加该变量的值。
这样,系统启动后的运行时间就是jiffies/HZ秒

jiffies定义于<linux/jiffies.h>中:
extern unsigned long volatile jiffies; jiffies变量总是为unsigned long型。
因此在32位体系结构上是32位,而在64位体系上是64位。
对于32位的jiffies,如果HZ为1000,49.7天后会溢出。
虽然溢出的情况不常见,但程序在检测超时时仍然可能因为回绕而导致错误。
linux提供了4个宏来比较节拍计数,它们能正确地处理节拍计数回绕。

#include <linux/jiffies.h>
#define time_after(unknown, known) // unknow > known
#define time_before(unknown, known) // unknow < known
#define time_after_eq(unknown, known) // unknow >= known
#define time_before_eq(unknown, known) // unknow <= known
unknown通常是指jiffies,known是需要对比的值(常常是一个jiffies加减后计算出的相对值)

例:

unsigned long timeout = jiffies + HZ/2; /* 0.5秒后超时 /

if(time_before(jiffies, timeout)){
/
没有超时,很好 /
}else{
/
超时了,发生错误 */

time_before可以理解为如果在超时(timeout)之前(before)完成 *系统中还声明了一个64位的值jiffies_64,在64位系统中jiffies_64和jiffies是一个值。
可以通过get_jiffies_64()获得这个值。 *使用

1
2
u64 j2;
j2 = get_jiffies_64();

(4)获得当前时间
驱动程序中一般不需要知道墙钟时间(也就是年月日的时间)。但驱动可能需要处理绝对时间。
为此,内核提供了两个结构体,都定义在<linux/time.h>:

a.

struct timeval {
time_t tv_sec; /* seconds /
suseconds_t tv_usec; /
microseconds */
};
较老,但很流行。采用秒和毫秒值,保存了1970年1月1日0点以来的秒数

b.

struct timespec {
time_t tv_sec; /* seconds /
long tv_nsec; /
nanoseconds */
};
较新,采用秒和纳秒值保存时间。

c.do_gettimeofday() 该函数用通常的秒或微秒来填充一个指向struct timeval的指针变量,原型如下:

#include <linux/time.h>
void do_gettimeofday(struct timeval *tv);
d.current_kernel_time()
该函数可用于获得timespec

#include <linux/time.h>
struct timespec current_kernel_time(void);

确定时间的延迟执行
设备驱动程序经常需要将某些特定代码延迟一段时间后执行,通常是为了让硬件能完成某些任务。
长于定时器周期(也称为时钟嘀嗒)的延迟可以通过使用系统时钟完成,而非常短的延时则通过软件循环的方式完成

(1)短延时
对于那些最多几十个毫秒的延迟,无法借助系统定时器。
系统通过软件循环提供了下面的延迟函数:
内核时间管理
(1)内核中的时间概念
时间管理在linux内核中占有非常重要的作用。
相对于事件驱动而言,内核中有大量函数是基于时间驱动的。
有些函数是周期执行的,比如每10毫秒刷新一次屏幕;
有些函数是推后一定时间执行的,比如内核在500毫秒后执行某项任务。
要区分:
*绝对时间和相对时间
*周期性产生的事件和推迟执行的事件
周期性事件是由系统系统定时器驱动的

(2)HZ值
内核必须在硬件定时器的帮助下才能计算和管理时间。
定时器产生中断的频率称为节拍率(tick rate)。
在内核中指定了一个变量HZ,内核初始化的时候会根据这个值确定定时器的节拍率。
HZ定义在<asm/param.h>,在i386平台上,目前采用的HZ值是1000。
也就是时钟中断每秒发生1000次,周期为1毫秒。即:
#define HZ 1000

注意!HZ不是个固定不变的值,它是可以更改的,可以在内核源代码配置的时候输入。
不同的体系结构其HZ值是不一样的,比如arm就采用100。
如果在驱动中要使用系统的中断频率,直接使用HZ,而不要用100或1000 a.理想的HZ值 i386的HZ值一直采用100,直到2.5版后才改为1000。 提高节拍率意味着时钟中断产生的更加频繁,中断处理程序也会更频繁地执行。 带来的好处有: *内核定时器能够以更高的频率和更高的准确度运行 *依赖定时器执行的系统调用,比如poll()和select(),运行的精度更高 *提高进程抢占的准确度 (缩短了调度延时,如果进程还剩2ms时间片,在10ms的调度周期下,进程会多运行8ms。 由于耽误了抢占,对于一些对时间要求严格的任务会产生影响) 坏处有: *节拍率要高,系统负担越重。 中断处理程序将占用更多的处理器时间。

(3)jiffies
全局变量jiffies用于记录系统启动以来产生的节拍的总数。
启动时,jiffies初始化为0,此后每次时钟中断处理程序都会增加该变量的值。
这样,系统启动后的运行时间就是jiffies/HZ秒

jiffies定义于<linux/jiffies.h>中:
extern unsigned long volatile jiffies; jiffies变量总是为unsigned long型。
因此在32位体系结构上是32位,而在64位体系上是64位。
对于32位的jiffies,如果HZ为1000,49.7天后会溢出。
虽然溢出的情况不常见,但程序在检测超时时仍然可能因为回绕而导致错误。
linux提供了4个宏来比较节拍计数,它们能正确地处理节拍计数回绕。

#include <linux/jiffies.h>
#define time_after(unknown, known) // unknow > known
#define time_before(unknown, known) // unknow < known
#define time_after_eq(unknown, known) // unknow >= known
#define time_before_eq(unknown, known) // unknow <= known
unknown通常是指jiffies,known是需要对比的值(常常是一个jiffies加减后计算出的相对值)

例:

unsigned long timeout = jiffies + HZ/2; /* 0.5秒后超时 /

if(time_before(jiffies, timeout)){
/
没有超时,很好 /
}else{
/
超时了,发生错误 */

time_before可以理解为如果在超时(timeout)之前(before)完成 *系统中还声明了一个64位的值jiffies_64,在64位系统中jiffies_64和jiffies是一个值。
可以通过get_jiffies_64()获得这个值。 *使用

1
2
u64 j2;
j2 = get_jiffies_64();

(4)获得当前时间
驱动程序中一般不需要知道墙钟时间(也就是年月日的时间)。但驱动可能需要处理绝对时间。
为此,内核提供了两个结构体,都定义在<linux/time.h>:

a.

struct timeval {
time_t tv_sec; /* seconds /
suseconds_t tv_usec; /
microseconds */
};
较老,但很流行。采用秒和毫秒值,保存了1970年1月1日0点以来的秒数

b.

struct timespec {
time_t tv_sec; /* seconds /
long tv_nsec; /
nanoseconds */
};
较新,采用秒和纳秒值保存时间。

c.do_gettimeofday() 该函数用通常的秒或微秒来填充一个指向struct timeval的指针变量,原型如下:

#include <linux/time.h>
void do_gettimeofday(struct timeval *tv);
d.current_kernel_time()
该函数可用于获得timespec

#include <linux/time.h>
struct timespec current_kernel_time(void);

确定时间的延迟执行
设备驱动程序经常需要将某些特定代码延迟一段时间后执行,通常是为了让硬件能完成某些任务。
长于定时器周期(也称为时钟嘀嗒)的延迟可以通过使用系统时钟完成,而非常短的延时则通过软件循环的方式完成

(1)短延时
对于那些最多几十个毫秒的延迟,无法借助系统定时器。
系统通过软件循环提供了下面的延迟函数:

Linux时间、定时器、时间中断超时处理相关推荐

  1. linux上点时间延时,Linux上时间和定时器

    Linux下时间和定时器 http://blog.chinaunix.net/u1/35065/showart_1870601.html重点读了第三种方法.文章写得很好,加了一点点注释可参考http: ...

  2. Linux内核——定时器和时间管理

    定时器和时间管理 系统定时器是一种可编程硬件芯片.它能以固定频率产生中断.该中断就是所谓的定时器中断.它所相应的中断处理程序负责更新系统时间,还负责执行须要周期性执行的任务. 系统定时器和时钟中断处理 ...

  3. linux 时间与定时器编程原理,浅析 Linux 中的时间编程和实现原理-嵌入式-火龙果软件工程...

    引子 我们都生活在时间中,但却无法去思考它.什么是时间呢?似乎这是一个永远也不能被回答的问题.然而作为一个程序员,在工作中,总有那么几次我必须思考什么是时间.比如,需要知道一段代码运行了多久:要在 l ...

  4. linux ubuntu 18.04设置锁屏时间和用户登录超时时间

    设置锁屏时间 设置用户登录超时时间 方法一: 1.针对所有用户 sudo vim /etc/profile //编辑环境变量文件 --------------- export TMOUT=0 //修改 ...

  5. linux驱动开发5 按键中断实验(定时器和中断)

    led:IO的输出 :key:IO的输入 法一:直接读写IO 使用while(1)无限读取,但CPU占用达到了99.6%,所以不行 #include <linux/types.h>#inc ...

  6. Linux内核之时间系统

    Linux内核之时间系统 1.Linux时间系统 (1)CMOS时钟 (2)系统时钟 (3)节拍数(jiffies) (4)墙上时间(xtime) 2.重要数据结构 (1)struct tk_read ...

  7. linux 多核 系统时钟,Linux中的时间

    1. Linux中time相关概念 1.1 real time 指的是实际流逝的时间,又称为Wall Clock Time(墙上时间). 比如,time命令统计出的real time指的是该进程从开始 ...

  8. 高性能定时器--时间轮/多级时间轮

    运行原理 指针指向轮子上的一个槽,轮子以恒定的速度顺时针转动,每转动一步就指向下一个槽(虚线指针指向的槽),每次转动称为一个tick,一个tick的时间称为时间轮的槽间隔slot interval,即 ...

  9. linux 不同用户时间,Linux时间子系统之(一):时间的基本概念

    Linux时间子系统之(一):时间的基本概念 作者:linuxer 发布于:2014-12-23 12:22 分类:时间子系统 本文使用Q & A的方式来和大家以前探讨一下时间的基本概念 一. ...

最新文章

  1. 1120. Friend Numbers (20)
  2. sql随机查询数据语句(NewID(),Rnd,Rand(),random())
  3. Windows Server AppFabric Caching
  4. 【转载】Jsoup设置代理ip访问
  5. matlab闭式网络潮流计算,闭式网络潮流计算.ppt
  6. matlab软件编程求解方程实验报告,数学实验“线性方程组高斯消去法”实验报告内含matlab程序.doc...
  7. php之使用curl对百度orc进行文字识别(二维码识别同理)--base64编码方式(解决image format error)
  8. cornerstone 使用
  9. Java判断拼音的工具类
  10. 边缘检测——Roberts算子
  11. 谷歌浏览器不能用_正在用 Chrome 或 Edge 浏览器的你,不能错过这亿个好用插件...
  12. NTC功率型热敏电阻
  13. 本人想了解CPU原理,大家能否推荐几本关于学习CPU原理的书?
  14. win10系统蓝牙服务器,如何打开win10系统的蓝牙并进行设备添加
  15. java 状态模式 同步_多人联机射击游戏中的设计模式应用(二):观察者模式,单例模式,状态模式,适配器模式...
  16. 戴尔微型计算机3048,戴尔5460一体机拆解,戴尔3048一体机
  17. pythondjango教程_【秒懂】号称最为简明实用的Django上手教程
  18. 电影“防火墙” 引发的黑客攻击迅雷(转)
  19. 【ceph】ceph osd blacklist cep黑名单|MDS问题分析
  20. 阿里云下载镜像失败:ERROR: certificate common name “img.ucdl.pp.uc.cn” doesn’t match requested host name “mirr

热门文章

  1. 易经玄学诠释人的一生
  2. Unhandled Rejection (ChunkLoadError): Loading chunk mf-dep_vendors-node_modules_core-js_index_j
  3. 记笔记最好用的超高颜值软件之一!Typora 你值得拥有!
  4. 【MySQL】单行函数
  5. 知识付费内容靠什么“变现”
  6. awtk开发实践——学习篇27: guage_pointer(仪表指针控件)
  7. JAVA获取当前时间并作比较
  8. 苹果触控笔有必要买吗?好用不贵主动式电容笔推荐
  9. 阅读1.Mobility pattern recognition based prediction for the subway station related bike-sharing trips
  10. day03-svg标签