【整理】--【KERNEL】内核定时器

时间:2023-03-09 13:19:30
【整理】--【KERNEL】内核定时器

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

被调度的函数肯定是异步执行的,它类似于一种“软件中断”,而且是处于非进程的上下文中,所以调度函数必须遵守以下规则:

1) 没有 current 指针、不允许访问用户空间。因为没有进程上下文,相关代码和被中断的进程没有任何联系。

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

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

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

二、函数使用

下面是关于timer的API函数:

(0)初始化定时器:

void init_timer(struct timer_list * timer);

(1)增加定时器:

void add_timer(struct timer_list * timer);

注册定时器,将定时器放到内核定时器链表中

(2)删除定时器:

int del_timer(struct timer_list * timer);

(3)修改定时器的expire:

int mod_timer(struct timer_list *timer, unsigned long expires);

三、使用流程

使用定时器的一般流程为:

(1)timer、编写function;

(2)为timer的expires、data、function赋值;

(3)调用add_timer将timer加入列表;

(4)在定时器到期时,function被执行;

(5)在程序中涉及timer控制的地方适当地调用del_timer、mod_timer删除timer或修改timer的expires。

注意:

mod_timer   内核通过函数mod_timer来实现已经激活的定时器超时时间。

mod_timer函数也可以操作那些已经初始化,但还没有被激活的定时器,如果定时器没有激活,mod_timer会激活它。如果调用时定时器未被激活,该函数返回0,否则返回1。一旦从mod_timer函数返回,定时器都将被激活而且设置了新的定时值。如果需要在定时器超时前停止定时器,可以使用del_timer函数。

被激活或未被激活的定时器都可以使用该函数,如果定时器还未被激活,该函数返回0;否则返回1。当删除定时器,必须小心一个潜在的竞争条件。当del_timer返回后,可以保证的只是:定时器不会被再激活,但是多处理器上定时器中断可能已经在其他处理上运行了,所以需要等待可能在其他处理器上运行的定时器处理程序都退出,这时需要使用del_timer_sync函数执行删除工。和del_timer函数不同,del_timer_sync数不能在中断上下文中使用。

四、HZ和Jiffies

系统定时器能以可编程的频率中断处理器。此频率即为每秒的定时器节拍数,对应着内核变量HZ。选择合适的HZ值需要权衡。HZ值大,定时器间隔时间 就小,因此进程调度的准确性会更高。但是,HZ值越大也会导致开销和电源消耗更多,因为更多的处理器周期将被耗费在定时器中断上下文中。

HZ的值取决于体系架构。在x86系统上,在2.4内核中,该值默认设置为100;在2.6内核中,该值变为1000;而在2.6.13中,它又被 降低到了250。在基于ARM的平台上,2.6内核将HZ设置为100。在目前的内核中,可以在编译内核时通过配置菜单选择一个HZ值。该选项的默认值取 决于体系架构的版本。

jiffies变量记录了系统启动以来,系统定时器已经触发的次数。内核每秒钟将jiffies变量增加HZ次。因此,对于HZ值为100的系统,1个jiffy等于10ms,而对于HZ为1000的系统,1个jiffy仅为1ms。

六、实例

#include <linux/timer.h>

static struct timer_list key_autorepeat_timer =

{

function: key_callback

};

static void

kbd_processkeycode(unsigned char keycode, char up_flag, int autorepeat)

{

char raw_mode = (kbd->kbdmode == VC_RAW);

if (up_flag) {

rep = 0;

if(!test_and_clear_bit(keycode, key_down))

up_flag = kbd_unexpected_up(keycode);

} else {

rep = test_and_set_bit(keycode, key_down);

/* If the keyboard autorepeated for us, ignore it.

* We do our own autorepeat processing.

*/

if (rep && !autorepeat)

return;

}

if (kbd_repeatkeycode == keycode || !up_flag || raw_mode) {

kbd_repeatkeycode = -1;

del_timer(&key_autorepeat_timer);

}

/*

* Calculate the next time when we have to do some autorepeat

* processing. Note that we do not do autorepeat processing

* while in raw mode but we do do autorepeat processing in

* medium raw mode.

*/

if (!up_flag && !raw_mode) {

kbd_repeatkeycode = keycode;

if (vc_kbd_mode(kbd, VC_REPEAT)) {

if (rep)

key_autorepeat_timer.expires = jiffies + kbd_repeatinterval;

else

key_autorepeat_timer.expires = jiffies + kbd_repeattimeout;

add_timer(&key_autorepeat_timer);

}

}

}