概述:上一个基于Kernel 定时器,编写简单的demo,接下来看看定时器相关的数据结构和API。
kernel版本:4.0
1、timer_list 结构体
PATH: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
int start_pid;
void *start_site;
char start_comm[16];
#endif
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
此处没有定义“CONFIG_TIMER_STATS” 和 “CONFIG_LOCKDEP”,所以只有最基本的
struct list_head entry; /*内核中timer 链表挂接点,初始化时添加到kernel timer 链表末尾,并将下一个指向NULL */
unsigned long expires; /* timer_list 启动时间间隔,初始化传入一个值,从初始化结束开始计算,如果到达这个时间点,则执行 下面的 function函数。
struct tvec_base *base; */记录该软件时钟所在的 struct tvec_base 变量 */
void (*function)(unsigned long); /* 时间到了的时候的回调函数 */
unsigned long data; /* 回调函数参数 */
int slack; /* 初始化的时候设置为了 -1, 后续的代码暂时未见使用 */
此时有一个新的结构体出现:
struct tvec_base {
spinlock_t lock; // 同步用的锁
struct timer_list *running_timer; // 该base 指向的 timer_list
unsigned long timer_jiffies; //下述几个都是timer_lsit 的时钟周期
unsigned long next_timer;
unsigned long active_timers;
unsigned long all_timers;
int cpu;
struct tvec_root tv1; //这几个是软件时钟的记录链表
struct tvec tv2;
struct tvec tv3;
struct tvec tv4;
struct tvec tv5;
} ____cacheline_aligned;
对于 struct tvec 结构体具体怎么使用,暂时还不是特别清楚,后续深入到这部分再进行补充。
2. 相关的接口函数
(1).初始化
/*初始化 timer */
setup_timer(&test_timer, timer_test_callback, 0);
/*调用关系如下*/
//1.宏定义
#define setup_timer(timer, fn, data) \
__setup_timer((timer), (fn), (data), 0)
//2. 展开 __setup_timer
#define __setup_timer(_timer, _fn, _data, _flags) \
do { \
__init_timer((_timer), (_flags)); \ /*继续宏定义,做初始化动作*/
(_timer)->function = (_fn); \ /*挂接回调函数*/
(_timer)->data = (_data); \ /*赋值回调函数参数*/
} while (0)
//3.展开 __init_timer
#ifdef #ifdef CONFIG_LOCKDEP
#define __init_timer(_timer, _flags) \
do { \
static struct lock_class_key __key; \
init_timer_key((_timer), (_flags), #_timer, &__key); \
} while (0)
#define __init_timer_on_stack(_timer, _flags) \
do { \
static struct lock_class_key __key; \
init_timer_on_stack_key((_timer), (_flags), #_timer, &__key); \
} while (0)
#else
#define __init_timer(_timer, _flags) \
init_timer_key((_timer), (_flags), NULL, NULL)
#define __init_timer_on_stack(_timer, _flags) \
init_timer_on_stack_key((_timer), (_flags), NULL, NULL)
#endif
#define __init_timer(_timer, _flags) \
do { \
static struct lock_class_key __key; \
init_timer_key((_timer), (_flags), #_timer, &__key); \
} while (0)
#define __init_timer_on_stack(_timer, _flags) \
do { \
static struct lock_class_key __key; \
init_timer_on_stack_key((_timer), (_flags), #_timer, &__key); \
} while (0)
#else //CONFIG_LOCKDEP 未定义,应该定走else
#define __init_timer(_timer, _flags) \
init_timer_key((_timer), (_flags), NULL, NULL)
#define __init_timer_on_stack(_timer, _flags) \
init_timer_on_stack_key((_timer), (_flags), NULL, NULL)
#endif
//4. 展开后,调用的是 init_timer_key() 函数
void init_timer_key(struct timer_list *timer, unsigned int flags,
const char *name, struct lock_class_key *key)
{
debug_init(timer);
do_init_timer(timer, flags, name, key);
}
//5. debug_init() 方法是没有实现的,因为没有配置对应的config
//6. do_init_timer()函数做了真正的初始动作,代码如下
static void do_init_timer(struct timer_list *timer, unsigned int flags,
const char *name, struct lock_class_key *key)
{
struct tvec_base *base = raw_cpu_read(tvec_bases);
timer->entry.next = NULL;
timer->base = (void *)((unsigned long)base | flags);
timer->slack = -1;
#ifdef CONFIG_TIMER_STATS
timer->start_site = NULL;
timer->start_pid = -1;
memset(timer->start_comm, 0, TASK_COMM_LEN);
#endif
lockdep_init_map(&timer->lockdep_map, name, key, 0);
}
(2).设置超时时间, 超时后调用回调函数
/*更改时间*/
ret = mod_timer(&test_timer, jiffies + msecs_to_jiffies(3000));
//1. 函数原型
int mod_timer(struct timer_list *timer, unsigned long expires)
{
expires = apply_slack(timer, expires);
if (timer_pending(timer) && timer->expires == expires)
return 1;
return __mod_timer(timer, expires, false, TIMER_NOT_PINNED);
}
(3).删除定时器
del_timer(&test_timer);
//函数原型
int del_timer(struct timer_list *timer)
{
struct tvec_base *base;
unsigned long flags;
int ret = 0;
debug_assert_init(timer);
timer_stats_timer_clear_start_info(timer);
if (timer_pending(timer)) {
base = lock_timer_base(timer, &flags);
ret = detach_if_pending(timer, base, true);
spin_unlock_irqrestore(&base->lock, flags);
}
return ret;
}
关于 timer 相关的API,也不算多,在 kernle/linux/timer.h中,有如下几个常用的
static inline int timer_pending(const struct timer_list * timer)
{
return timer->entry.next != NULL;
}
extern void add_timer_on(struct timer_list *timer, int cpu);
extern int del_timer(struct timer_list * timer);
extern int mod_timer(struct timer_list *timer, unsigned long expires);
extern int mod_timer_pending(struct timer_list *timer, unsigned long expires);
extern int mod_timer_pinned(struct timer_list *timer, unsigned long expires);
extern void set_timer_slack(struct timer_list *time, int slack_hz);
struct timer_list 中的“slack” 成员,是对延时进行微调整,但是不能起到完全控制的时间的作用,看如下对比验证:
验证一: slack 做全部延时时间设置,超时函数中的时间设置为0
===》 时间对不上
验证二:slack不设置,超时时长全部从超时函数设置
===> 基本可以对上,和之前的验证一致。
验证三: 两个都设置
==> 准确度偏高