正文
4412 Linux定时器
小程序:扫一扫查出行
【扫一扫了解最新限行尾号】
复制小程序
【扫一扫了解最新限行尾号】
复制小程序
一、Linux定时器基础知识1.1 定时器的使用范围
延后执行某个操作,定时查询某个状态;前提是对时间要求不高的地方
1.2 内核时间概念- Hz:(系统时钟通过CONFIG_HZ来设置,范围是100-1000;HZ决定使用中断发生的频率)
- 1/200 = 5ms,说明4412中是5ms产生一次时钟中断。如果就没有定义的话,默认是100
- 内核的全局变量jiffies:(记录内核自启动来的节拍数,内核之启动以来,产生的中断数)时钟中断,每产生一个中断,jiffies就加1。
- jiffies/HZ:jiffies除以Hz得到内核自启动以来的秒数
2.1 内核定时器的例程
结构体timer_list,函数setup_timer,add_timer,del_timer,mod_timer
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
int start_pid;
void *start_site;
char start_comm[];
#endif
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
timer_list
timer_list参数
- struct list_head entry双向链表
- unsigned long expires:超时时间,记录什么时候产生时钟中断
- struct tvec_base *base:管理时钟的结构体
- void *(function)(unsigned long):时钟中断产生之后的动作
- unsigned long data:传递的参数
#define setup_timer(timer, fn, data) \
do { \
static struct lock_class_key __key; \
setup_timer_key((timer), #timer, &__key, (fn), (data));\
} while ()void add_timer(struct timer_list *timer);
int del_timer(struct timer_list * timer);
int mod_timer(struct timer_list *timer, unsigned long expires);
2.2 双向链表
platform_driver_register→driver_register
→bus_add_driver
→struct bus_type *bus
→struct subsys_private *p
→struct kset subsys→struct list_head list;
2.3 mod_timer相当于
mod_timer = del_timer(time);timer->expires = expires;add_timer(timer);
3 内核定时器实现的分析
从内核定时器初始化到定时器例程
3.1 add_timer如何添加定时器
add_timer→mod_timer
→__mod_timer(内核函数有下划线,表示“局部函数”)
→internal_add_timer
3.2 struct tvec_base *base结构体分析--管理内核时钟的结构体 struct tvec_base {
spinlock_t lock; //自旋锁
struct timer_list *running_timer; //内核中正在处理的定时器
unsigned long timer_jiffies; //内核目前正在处理的定时器时间
unsigned long next_timer;
struct tvec_root tv1;
{
struct list_head vec[TVR_SIZE];//256长度数组
TVR_SIZE→#define TVR_SIZE (1 << TVR_BITS)
TVR_BITS=;
宏定义CONFIG_BASE_SMALL=
TVR_SIZE =
}
struct tvec tv2; //64长度数组
struct tvec tv3;
struct tvec tv4;
struct tvec tv5;
}
struct tvec_base {
spinlock_t lock; //自旋锁
struct timer_list *running_timer; //内核中正在处理的定时器
unsigned long timer_jiffies; //内核目前正在处理的定时器时间
unsigned long next_timer;
struct tvec_root tv1;
{
struct list_head vec[TVR_SIZE];//256长度数组
TVR_SIZE→#define TVR_SIZE (1 << TVR_BITS)
TVR_BITS=;
宏定义CONFIG_BASE_SMALL=
TVR_SIZE =
}
struct tvec tv2; //64长度数组
struct tvec tv3;
struct tvec tv4;
struct tvec tv5;
}
per_cpu 与CPU核多少有关
DEFINE_PER_CPU看到这样的变量,就表明这个变量是和CPU核相关的。
有一些宏定义是在内核目录的config文件配置的
3.3 internal_add_timer
分析idx参数
如果idx<256,则将time_list添加到TV1
如果idx<256*64,则将time_list添加到TV2
如果idx<256*64*64,则将time_list添加到TV3
如果idx<256*64*64*64,则将time_list添加到TV4
如果idx > 0xffffffffUL,则将time_list添加到TV5
3.4 list_add_tail
双向链表操作函数都在include/linux/list.h文件中