一、内核时间的相关概念
1. 墙上时钟:也就是实际时间。2. 系统时间:自系统启动开始所经过的时间。
3. 时钟中断:内核会周期性的产生时钟中断,在中断处理函数中执行一些与时间相关的操作,如更新时间,进程调度,检查时间片等。
4. 节拍率:在linux内核中,通过编程定义节拍率,也就是HZ。每1/HZ秒发生一次时钟中断。在ARM中,节拍率被定义为100,节拍率越大,系统进入时钟中断就越频繁,时间和进程调度等操作就越准确,但对系统的负担也就越大。
HZ在Linux内核源码目录下的arch/arm/include/asm/param.h定义:
#ifdef __KERNEL__
#define HZ CONFIG_HZ /* Internal kernel timer frequency */
#define USER_HZ 100 /* User interfaces are in "ticks" */
#define CLOCKS_PER_SEC (USER_HZ) /* like times() */
#else
#define HZ 100
#endif
可见,内核空间的HZ由__KERNEL__和CONFIG_HZ决定,而CONFIG_HZ在.config(选择编译选项后产生的配置文件)中定义,如在顶层目录下的.config有如下定义:CONFIG_HZ=256。
HZ在内核的源代码中是一个全局常量可以修改,但是一旦内核编译成镜像文件时,HZ就是一个常量了。HZ的值一般在10~1000之间。HZ越大,系统计时越精确,系统的实时性越好,但是系统的负担就会重。一般CPU的主频越高,HZ越的值越大。
5. jiffies:32位的全局变量,用来记录自系统启动以来产生的节拍的总数。系统启动时清零,每次时钟中断加一。所以,一秒内的时钟中断次数(即jiffies一秒内增加的值)也就等于HZ。如果系统时间以秒来表示,那就等于jiffies/HZ秒。
6. 实时时钟(RTC):体系结构中用于维持系统时间的设备,就像电脑的BIOS,需要在关机状态时通过电池供电。系统启动时通过读取RTC 来初始化墙上时钟
二、Linux内核定时器
2.1 定义及初始化一个定时器结构体timer_list
timer_list在Linux内核源码目录下include/linux/timer.h下定义:struct timer_list {
/*
* All fields that change during normal runtime grouped to the
* same cacheline
*/
struct list_head entry;
unsigned long expires;
struct tvec_base *base;
void (*function)(unsigned long);
unsigned long data;
int slack;
#ifdef CONFIG_TIMER_STATS
void *start_site;
char start_comm[16];
int start_pid;
#endif
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
关键成员说明:
.expires:定时器超时处理函数的超时时间
.function:定时器超时处理函数
.data:定时器超时处理函数的参数
这三个成员是由用户设置,其余一般由内核处理
此外,还有一个关于定时器结构体的宏定义,在Linux内核源码目录下include/linux/timer.h下定义:
#define DEFINE_TIMER(_name, _function, _expires, _data) \
struct timer_list _name = \
TIMER_INITIALIZER(_function, _expires, _data)
2.1.1 直接定义一个定时器结构体,如:
struct timer_list timer;
2.1.2 使用宏定义一个定时器结构体,如:
DEFINE_TIMER(timer, timer_func,100);
2.2 设置超时时间,定义定时器处理函数和传参
指定timer的expires、function和data。2.2.1 给结构体成员分别赋值或,如:
void timer_func(unsigned long data)
{
}
static int __init timer_dev_init(void)
{
timer.funciton = timer_func;
timer.expires = jiffies+5*HZ;
timer.data = 100;
}
2.3 使用init_timer()完成全部初始化;
函数原型:int init_timer(struct timer_list *timer);参数说明:
timer: 定时器结构体变量地址
返回值:
成功返回0,失败返回一个负的错误码。
如:init_timer(&timer);
2.4 激活定时器
激活定时器使用add_timer();函数原型:int add_timer(struct timer_list *timer);
参数说明:
timer: 定时器结构体变量地址
返回值:
成功返回0,失败返回一个负的错误码。
如:add_timer(&timer);
当调用add_timer后,就会将定时器交由内核来管理,当时间一到,内核调用进程/*swapper*/来执行处理函数,因此,若设置了重复执行,当rmmod驱动(若没有删除定时器)后,定时器超时处理函数会继续执行,定时器激活后,它只会在指定时间执行一次处理函数,执行后会将定时器在内核中移除。
2.5 修改定时器
如果要实现隔指定时间又重复执行,可使用如下方法:2.5.1 在定时器处理函数中添加如下代码,即再次激活定时器。
timer.expires = jiffies+5*HZ;
add_timer(&timer);
2.5.2 在定时器处理函数中使用mod_timer();
这是改变定时器超时时间的函数,如果在指定的定时器(timer)没超时前调用,超时时间会更新为新的新的超时时间(expires)。
如果在定时器超时后调用,那就相当于重新指定超时时间并再次激活定时器。
函数原型:int mod_timer(struct timer_list *timer, unsigned long expires);
参数说明:
timer: 定时器结构体变量地址
expires:定时时间
返回值:
成功返回0,失败返回一个负的错误码。
如:mod_timer(&timer, jiffies+5*HZ);
2.6 删除定时器
若想在定时器没有超时前取消定时器,则使用del_timer();函数原型:void del_timer(struct timer_list *timer);
参数说明:
timer: 定时器结构体变量地址
返回值:
无返回值。
如:del_timer(&timer);
三、Linux内核定时器实例
驱动程序(timer_dev.c):
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/sched.h> //current->comm
//1. 定义一个定时器结构体
static struct timer_list timer;
void timer_func(unsigned long data)
{
printk("data:%lu\n", data);
printk("jiffies:%lu\n", jiffies);
printk("current.comm:%s\n", current->comm);
//修改定时器
mod_timer(&timer, jiffies+2*HZ);
}
static int __init timer_dev_init(void)
{
//2. 设置超时时间,定义定时器处理函数和传参
timer.expires = jiffies+5*HZ;
timer.function = timer_func;
timer.data = 100;
//3. 初始化定时器结构体
init_timer(&timer);
//4. 激活定时器
add_timer(&timer);
printk("timer_dev init\n");
printk("current.comm:%s\n", current->comm);
return 0;
}
static void __exit timer_dev_exit(void)
{
//删除定时器
del_timer(&timer);
printk("timer_dev exit\n");
}
module_init(timer_dev_init);
module_exit(timer_dev_exit);
MODULE_LICENSE("GPL");
编译文件(Makefile):
obj-m += timer_dev.o
KERN_DIR=/home/gec/linux-2.6.35.7-gec-v3.0-gt110
modules:
$(MAKE) -C $(KERN_DIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KERN_DIR) M=$(PWD) modules clean