linux定时器和Jiffies

合集下载

HZ和Jiffies

HZ和Jiffies

HZ和JiffiesHZ和Jiffies2011-06-04 15:17:49分类: C/C++2.4 内核定时器内核中许多部分的工作都高度依赖于时间信息。

Linux内核利用硬件提供的不同的定时器以支持忙等待或睡眠等待等时间相关的服务。

忙等待时,CPU 会不断运转。

但是睡眠等待时,进程将放弃CPU。

因此,只有在后者不可行的情况下,才考虑使用前者。

内核也提供了某些便利,可以在特定的时间之后调度某函数运行。

我们首先来讨论一些重要的内核定时器变量(jiffies、HZ和xtime)的含义。

接下来,我们会使用Pentium时间戳计数器(TSC)测量基于Pentium的系统的运行次数。

之后,我们也分析一下Linux 怎么使用实时钟(RTC)。

2.4.1 HZ和Jiffies系统定时器能以可编程的频率中断处理器。

此频率即为每秒的定时器节拍数,对应着内核变量HZ。

选择合适的HZ值需要权衡。

HZ 值大,定时器间隔时间就小,因此进程调度的准确性会更高。

但是,HZ值越大也会导致开销和电源消耗更多,因为更多的处理器周期将被耗费在定时器中断上下文中。

HZ的值取决于体系架构。

在x86系统上,在2.4内核中,该值默认设置为100;在2.6内核中,该值变为1000;而在2.6.13中,它又被降低到了250。

在基于ARM的平台上,2.6内核将HZ设置为100。

在目前的内核中,可以在编译内核时通过配置菜单选择一个HZ值。

该选项的默认值取决于体系架构的版本。

2.6.21内核支持无节拍的内核(CONFIG_NO_HZ),它会根据系统的负载动态触发定时器中断。

无节拍系统的实现超出了本章的讨论范围,不再详述。

jiffies变量记录了系统启动以来,系统定时器已经触发的次数。

内核每秒钟将jiffies变量增加HZ次。

因此,对于HZ值为100的系统,1个jiffy等于10ms,而对于HZ为1000的系统,1个jiffy仅为1ms。

为了更好地理解HZ和jiffies变量,请看下面的取自IDE驱动程序(drivers/ide/ide.c)的代码片段。

你需要了解Linux设备驱动之定时与延时的区别

你需要了解Linux设备驱动之定时与延时的区别

你需要了解Linux设备驱动之定时与延时的区别Linux通过系统硬件定时器以规律的间隔(由HZ度量)产生定时器中断,每次中断使得一个内核计数器的值jiffies累加,因此这个jiffies就记录了系统启动开始的时间流逝,然后内核据此实现软件定时器和延时。

Demo for jiffies and HZ#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 */内核定时器硬件时钟中断处理程序会唤起TIMER_SOFTIRQ 软中断,运行当前处理器上到期的所有内核定时器。

定时器定义/初始化在Linux内核中,TImer_list结构体的一个实例对应一个定时器:/* 当expires指定的定时器到期时间期满后,将执行funcTIon(data) */ struct TImer_list { unsigned long expires; /*定时器到期时间*/ void (*function)(unsigned long); /* 定时器处理函数*/ unsigned long data; /* function的参数*/ ... }; /* 定义*/ struct timer_list my_timer; /* 初始化函数*/ void init_timer(struct timer_list * timer); /* 初始化宏*/ TIMER_INITIALIZER(_function, _expires, _data) /* 定义并初始化宏*/ DEFINE_TIMER(_name, _function, _expires, _data)定时器添加/移除/* 注册内核定时器,将定时器加入到内核动态定时器链表中*/ void add_timer(struct timer_list * timer); /* del_timer_sync()是del_timer()的同步版,在删除一个定时器时需等待其被处理完,因此该函数的调用不能发生在中断上下文*/ void del_timer(struct timer_list * timer); void del_timer_sync(struct timer_list * timer);定时时间修改int mod_timer(struct timer_list *timer, unsigned long expires);。

linux中定时器的使用方法

linux中定时器的使用方法

linux中定时器的使用方法Linux是一个功能强大的操作系统,其中提供了许多工具来帮助用户管理和计划任务。

其中一个重要的工具是定时器,它可以在指定的时间间隔内执行某些操作。

本文将介绍Linux中定时器的使用方法。

1. 了解定时器的基本概念在Linux中,定时器是一种可重复执行的指令。

它们被设置在特定的时间段内,并在该时间段内自动执行。

定时器可以执行任何命令,如运行程序、创建文件、编辑文件、重启服务等。

2. 创建定时器要创建定时器,可以使用定时器脚本。

定时器脚本是一个简单的文件,包含定时器的指令和设置。

例如,可以使用以下命令来创建一个名为“crontab”的定时器脚本:```crontab -e```这将打开一个新的编辑器窗口,其中包含一个名为“crontab”的选项。

在这个窗口中,可以添加、编辑和删除定时器。

3. 编辑定时器要编辑定时器,需要使用“crontab”命令。

例如,可以使用以下命令来编辑一个已经存在的定时器:```crontab -e```在编辑定时器时,可以选择要使用的定时器、设置时间和日期,以及要自动执行的指令。

例如,要创建一个在每天下午3点定时执行“ls -l”命令的定时器,可以使用以下命令:```*/3 * * * * ls -l```这将在每天的下午3点自动执行“ls -l”命令。

请注意,“*/3 * * * *”是一个固定的指令,将在每个下午3点自动执行。

4. 删除定时器要删除定时器,可以使用“crontab”命令。

例如,可以使用以下命令来删除一个已经存在的定时器:```crontab -r```这将删除当前文件中的所有定时器。

5. 了解定时器的优点和限制定时器是一种非常有用的工具,可以帮助用户在特定时间执行某些操作。

虽然定时器可以提高效率,但也存在一些限制。

首先,定时器的设置是固定的,无法更改。

这意味着,如果希望在特定时间执行不同的操作,需要使用多个定时器。

其次,定时器不会在周末或节假日期间运行。

Linux驱动技术关键之一:内核定时器与延迟工作

Linux驱动技术关键之一:内核定时器与延迟工作

Linux驱动技术关键之一:内核定时器与延迟工作内核定时器软件上的定时器最终要依靠硬件时钟来实现,简单的说,内核会在时钟中断发生后检测各个注册到内核的定时器是否到期,如果到期,就回调相应的注册函数,将其作为中断底半部来执行。

实际上,时钟中断处理程序会触发TIMER_SOFTIRQ软中断,运行当前处理器上到期的所有定时器。

设备驱动程序如要获得时间信息以及需要定时服务,都可以使用内核定时器。

jiffies要说内核定时器,首先就得说说内核中关于时间的一个重要的概念:jiffies变量,作为内核时钟的基础,jiffies每隔一个固定的时间就会增加1,称为增加一个节拍,这个固定间隔由定时器中断来实现,每秒中产生多少个定时器中断,由在中定义的HZ宏来确定,如此,可以通过jiffies获取一段时间,比如jiffies/HZ表示自系统启动的秒数。

下两秒就是(jiffies/HZ+2),内核中用jiffies来计时,秒转换成的jiffies:seconds*HZ,所以以jiffiy为单位,以当前时刻为基准计时2秒:(jiffies/HZ+2)*HZ=jiffies+2*HZ如果要获取当前时间,可以使用do_getTImeofday(),该函数填充一个struct TImeval结构,有着接近微妙的分辨率。

//kernel/TIme/timekeeping.c 473 /** 474 * do_gettimeofday - Returns the time of day in a timeval 475 * @tv: pointer to the timeval to be set 476 * 477 * NOTE: Users should be converted to using getnstimeofday() 478 */ 479 void do_gettimeofday(struct timeval *tv)驱动程序为了让硬件有足够的时间完成一些任务,常常需要将特定的代码延后一段时间来执行,根据延时的长短,内核开发中使用长延时和短延时两个概念。

linux内核定时器详解及实例

linux内核定时器详解及实例

Linux内核定时器详解80X86体系结构上,常用的定时器电路实时时钟(RTC)RTC内核通过IRQ8上发出周期性的中断,频率在2-8192HZ之间,掉电后依然工作,内核通过访问0x70和0x71 I/O端口访问RTC。

时间戳计时器(TSC)利用CLK输入引线,接收外部振荡器的时钟信号,该计算器是利用64位的时间戳计时器寄存器来实现额,与可编程间隔定时器传递来的时间测量相比,更为精确。

可编程间隔定时器(PIT)PIT的作用类似于微波炉的闹钟,PIT永远以内核确定的固定频率发出中断,但频率不算高。

CPU本地定时器利用PIC或者APIC总线的时钟计算。

高精度时间定时器(HPET)功能比较强大,家机很少用,也不去记了。

ACPI电源管理定时器它的时钟信号拥有大约为3.58MHZ的固定频率,该设备实际上是一个简单的计数器,为了读取计算器的值,内核需要访问某个I/O端口,需要初始化定时器的数据结构利用timer_opts描述定时器Timer_opts的数据结构Name :标志定时器员的一个字符串Mark_offset :记录上一个节拍开始所经过的时间,由时钟中断处理程序调用Get_offset 返回自上一个节拍开始所经过的时间Monotonic_clock :返回自内核初始化开始所经过的纳秒数Delay:等待制定数目的“循环”定时插补就好像我们要为1小时35分34秒进行定时,我们不可能用秒表去统计,肯定先使用计算时的表,再用计算分的,最后才用秒表,在80x86架构的定时器也会使用各种定时器去进行定时插补,我们可以通过cur_timer指针来实现。

单处理器系统上的计时体系结构所有与定时有关的活动都是由IRQ线0上的可编程间隔定时器的中断触发。

初始化阶段1. 初始化间,time_init()函数被调用来建立计时体系结构2. 初始化xtime变量(xtime变量存放当前时间和日期,它是一个timespec 类型的数据结构)3. 初始化wall_to_monotonic变量,它跟xtime是同一类型的,但它存放将加在xtime上的描述和纳秒数,这样即使突发改变xtime也不会受到影响。

Linux内核定时器--原版PPT优秀课件

Linux内核定时器--原版PPT优秀课件
变的值,大多数体系结构的HZ值是可调的
7
2.1.1 理想的HZ值
自Linux问世以来,i386体系结构中时 钟中断频率就设定为100HZ,但是在2.5开发 版本内核中,中断频率被提高到1000HZ。
提高节拍率意味着时钟中断产生得更 加频繁,所以中断处理程序也会更频繁的 执行,带来以下几点好处: • 更高的时钟中断解析度可提高时间驱动事 件的解析度。 • 提高了时间驱动事件的准确度。
13
2.2.1 jiffies的回绕
• Jiffies变量总是无符号长整数,因此,在32 位体系结构上时32位,在64位体系结构上 时64位。32位的jiffies变量,如果HZ = 100, 497天后会溢出,如果HZ = 1000,49.7天后 会溢出。而64位则别指望能看到溢出。
• 访问jiffies的代码仅会读取jiffies_64的低32位, 通过get_jiffies_64()函数,可以读取整个64 位,多数代码只需要访问低32位就够了。
/*还没有超时,继续执行任务*/ }
/*超时了,发生错误*/
15
• 正常的情况下,上面的代码没有问题。但是当 jiffies接近最大值的时候,就会出现回绕问题, 如下图所示:
• 1. 循环中第一次比较时,jiffies = J1,没有超时 • 2. 循环中第二次比较时,jiffies = J2,实际已经
执行中断处理程序,占用CPU时间过多。 • 减少了处理器对其他工作的时间,更频繁
的打乱处理器高速缓存并增加耗电。
11
2.2 jiffies
• 全局变量jiffies用来记录自系统启动以来产 生得节拍的总数。例如:系统启动了N秒, 那么jiffies就为N x HZ。
• Jiffies的类型为无符号长整型(unsigned long),用其它任何类型存放都不正确。

Linux定时器使用

Linux定时器使用

Linux定时器的使用内核定时器是内核用来控制在未来某个时间点(基于jiffies)调度执行某个函数的一种机制,其实现位于<linux/timer.h> 和kernel/timer.c 文件中。

被调度的函数肯定是异步执行的,它类似于一种“软件中断”,而且是处于非进程的上下文中,所以调度函数必须遵守以下规则:1) 没有current 指针、不允许访问用户空间。

因为没有进程上下文,相关代码和被中断的进程没有任何联系。

2) 不能执行休眠(或可能引起休眠的函数)和调度。

3) 任何被访问的数据结构都应该针对并发访问进行保护,以防止竞争条件。

内核定时器的调度函数运行过一次后就不会再被运行了(相当于自动注销),但可以通过在被调度的函数中重新调度自己来周期运行。

在SMP系统中,调度函数总是在注册它的同一CPU上运行,以尽可能获得缓存的局域性。

内核定时器的数据结构struct timer_list {struct list_head entry;unsigned long expires;void (*function)(unsigned long);unsigned long data;struct tvec_base *base;/* ... */};其中expires 字段表示期望定时器执行的jiffies 值,到达该jiffies 值时,将调用function 函数,并传递data 作为参数。

当一个定时器被注册到内核之后,entry 字段用来连接该定时器到一个内核链表中。

base 字段是内核内部实现所用的。

需要注意的是expires 的值是32位的,因为内核定时器并不适用于长的未来时间点。

初始化在使用struct timer_list 之前,需要初始化该数据结构,确保所有的字段都被正确地设置。

初始化有两种方法。

方法一:DEFINE_TIMER(timer_name, function_name, expires_value, data);该宏会定义一个名叫timer_name 内核定时器,并初始化其function, expires, name 和base 字段。

Linux时间子系统之一:定时器的应用

Linux时间子系统之一:定时器的应用

Linux时间子系统之一:定时器的应用我们知道低分辨率定时器和高精度定时器的实现原理,内核为了方便其它子系统,在时间子系统中提供了一些用于延时或调度的API,例如msleep,hrtimer_nanosleep 等等,这些API基于低分辨率定时器或高精度定时器来实现,本章的内容就是讨论这些方便、好用的API是如何利用定时器系统来完成所需的功能的。

1. msleepmsleep相信大家都用过,它可能是内核用使用最广泛的延时函数之一,它会使当前进程被调度并让出cpu一段时间,因为这一特性,它不能用于中断上下文,只能用于进程上下文中。

要想在中断上下文中使用延时函数,请使用会阻塞cpu的无调度版本mdelay。

msleep 的函数原型如下:[cpp] view plain copyvoid msleep(unsigned int msecs)延时的时间由参数msecs指定,单位是毫秒,事实上,msleep的实现基于低分辨率定时器,所以msleep的实际精度只能也是1/HZ级别。

内核还提供了另一个比较类似的延时函数msleep_interrupTIble:[cpp] view plain copyunsigned long msleep_interrupTIble(unsigned int msecs)延时的单位同样毫秒数,它们的区别如下:函数延时单位返回值是否可被信号中断msleep毫秒无否msleep_interrupTIble毫秒未完成的毫秒数是最主要的区别就是msleep会保证所需的延时一定会被执行完,而msleep_interrupTIble则可以在延时进行到一半时被信号打断而退出延时,剩余的延时数则通过返回值返回。

两个函数最终的代码都会到达schedule_timeout函数,它们的调用序列如下图所示:图1.1 两个延时函数的调用序列下面我们看看schedule_timeout函数的实现,函数首先处理两种特殊情况,一种是传入的。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

1.linux HZLinux核心几个重要跟时间有关的名词或变数,以下将介绍HZ、tick与jiffies。

HZLinux核心每隔固定周期会发出timer interrupt (IRQ 0),HZ是用来定义每一秒有几次timer interrupts。

举例来说,HZ为1000,代表每秒有1000次timer interrupts。

HZ可在编译核心时设定,如下所示(以核心版本2.6.20-15为例):adrian@adrian-desktop:~$ cd /usr/src/linuxadrian@adrian-desktop:/usr/src/linux$ make menuconfigProcessor type and features ---> Timer frequency (250 HZ) --->其中HZ可设定100、250、300或1000。

小实验观察/proc/interrupt的timer中断次数,并于一秒后再次观察其值。

理论上,两者应该相差250左右。

adrian@adrian-desktop:~$ cat /proc/interrupts | grep timer && sleep 1 && cat /proc/interrupts | grep timer0: 9309306 IO-APIC-edge timer0: 9309562 IO-APIC-edge timer上面四个栏位分别为中断号码、CPU中断次数、PIC与装置名称。

要检查系统上HZ的值是什么,就执行命令cat kernel/.config | grep '^CONFIG_HZ='2.TickTick是HZ的倒数,意即timer interrupt每发生一次中断的时间。

如HZ为250时,tick为4毫秒(millisecond)。

3.JiffiesJiffies为Linux核心变数(unsigned long),它被用来记录系统自开机以来,已经过了多少tick。

每发生一次timer interrupt,Jiffies变数会被加一。

值得注意的是,Jiffies于系统开机时,并非初始化成零,而是被设为-300*HZ (arch/i386/kernel/time.c),即代表系统于开机五分钟后,jiffies 便会溢位。

那溢位怎么办?事实上,Linux核心定义几个macro(timer_after、time_after_eq、time_before与time_before_eq),即便是溢位,也能借由这几个macro正确地取得jiffies的内容。

另外,80x86架构定义一个与jiffies相关的变数jiffies_64 ,此变数64位元,要等到此变数溢位可能要好几百万年。

因此要等到溢位这刻发生应该很难吧。

3.1 jiffies及其溢出全局变量jiffies取值为自操作系统启动以来的时钟滴答的数目,在头文件<linux/sched.h>中定义,数据类型为unsigned long volatile (32位无符号长整型)。

jiffies转换为秒可采用公式:(jiffies/HZ)计算,将秒转换为jiffies可采用公式:(seconds*HZ)计算。

当时钟中断发生时,jiffies 值就加1。

因此连续累加一年又四个多月后就会溢出(假定HZ=100,1个jiffies等于1/100秒,jiffies可记录的最大秒数为 (2^32 -1)/100=42949672.95秒,约合497天或1.38年),即当取值到达最大值时继续加1,就变为了0。

3.4 Linux内核如何来防止jiffies溢出Linux内核中提供了以下四个宏,可有效解决由于jiffies溢出而造成程序逻辑出错的情况。

下面是从Linux Kernel 2.6.7版本中摘取出来的代码:/** 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.*/#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)在宏time_after中,首先确保两个输入参数a和b的数据类型为unsigned long,然后才执行实际的比较。

8. 结论系统中采用jiffies来计算时间,但由于jiffies溢出可能造成时间比较的错误,因而强烈建议在编码中使用 time_after等宏来比较时间先后关系,这些宏可以放心使用。

内核时钟:内核使用硬件提供的不同时钟来提供依赖于时间的服务,如busy-waiting(浪费CPU周期)和sleep-waiting(放弃CPU)5.HZ and Jiffiesjiffies记录了系统启动后的滴答数,常用的函数:time_before()、time_after()、time_after_eq()、time_before_eq()。

因为jiffies随时钟滴答变化,不能用编译器优化它,应取volatile值。

32位jiffies变量会在50天后溢出,太小,因此内核提供变量jiffies_64来hold 64位jiffies。

该64位的低32位即为jiffies,在32位机上需要两天指令来赋值64位数据,不是原子的,因此内核提供函数get_jiffies_64()。

6.Long Delaysbusy-wait:timebefore(),使CPU忙等待;sleep-wait:shedule_timeout(截至时间);无论在内核空间还是用户空间,都没有比HZ 更精确的控制了,因为时间片都是根据滴答更新的,而且即使定义了您的进程在超过指定时间后运行,调度器也可能根据优先级选择其他进程执行。

sleep-wait():wait_event_timeout()用于在满足某个条件或超时后重新执行,msleep()睡眠指定的ms后重新进入就绪队列,这些长延迟仅适用于进程上下文,在中断上下文中不能睡眠也不能长时间busy-waiting。

内核提供了timer API来在一定时间后执行某个函数:#include <linux/timer.h>struct timer_list my_timer;init_timer(&my_timer); /* Also see setup_timer() */my_timer.expire = jiffies + n*HZ; /* n is the timeout in number of seconds */my_timer.function = timer_func; /* Function to executeafter n seconds */my_timer.data = func_parameter; /* Parameter to be passed to timer_func */add_timer(&my_timer); /*Start the timer*/如果您想周期性执行上述代码,那么把它们加入timer_func()函数。

您使用mod_timer()来改变my_timer的超时值,del_timer()来删掉my_timer,用timer_pending()查看是否my_timer处于挂起状态。

用户空间函数clock_settime()和clock_gettime()用于获取内核时钟服务。

用户应用程序使用setitimer()和getitimer()来控制alarm信号的传递当指定超时发生后。

8.Real Time ClockRTC时钟track绝对时间。

RTC电池常超过computer生存期。

可以用RTC完成以下功能:(1)读或设置绝对时钟,并在clock updates时产生中断;(2)以2HZ到8192HZ来产生周期性中断;(3)设置alarms。

jiffies仅是相对于系统启动的相对时间,如果想获取absolute time或wall time,则需要使用RTC,内核用变量xtime来记录,当系统启动时,读取RTC并记录在xtime中,当系统halt时,则将wall time写回RTC,函数do_gettimeofday()来读取wall time。

#include <linux/time.h>static struct timeval curr_time;do_gettimeofday(&curr_time);my_timestamp = cpu_to_le32(curr__sec); /* Record timestamp */ 用户空间获取wall time的函数:time()返回calendar time或从00:00:00 on January 1,1970的秒数;(2)localtime():返回calendar time in broken-down format;(3)mktime():与localtime()相反;(4)gettimeofday()以microsecond 精确度返回calendar时间。

相关文档
最新文档