内核定时器
内核定时器
内核定时器依赖于内核软中断,如下代码
void __init init_timers(void)
{
open_softirq(TIMER_SOFTIRQ, run_timer_softirq)
}
//当TIMER_SOFTIRQ触发时会执行run_timer_softirq函数
void run_timer_softirq(struct softirq_action *h)
{
struct tvec_base *base = this_cpu_ptr(&tvec_bases);
struct tvec_base是个Per-CPU变量,只在本地CPU有效,
void __run_timers(struct tvec_base *base)
{
spin_lock_irq(&base->lock)
//找到超时的timer
base->running_timer = timer
detach_expired_timer(timer, base)
//将timer从base链表中删除
spin_unlock_irq(&base->lock)
call_timer_fn(timer, fn, timer->data)
spin_lock_irq(&base->lock)
base->running_timer = NULL
spin_unlock_irq(&base->lock)
}
return __run_timers(base);
}
程序在执行timer的会掉函数时会设置当前base的running_timer为当前执行函数的timer,但是此timer已经在链表中删除
定义定时器
- 方法1
DEFINE_TIMER(timer, cb, expires, data)
- 方法2
struct timer_list timer;
setup_timer(&timer, (*cb)(data), data)
timer.expires = jiffies + 5*HZ
- 方法3
struct timer_list timer
init_timer(&timer)
timer.timer.expires = jiffies + 5*HZ
timer.timer.data = data
timer.timer.function = cb
添加定时器
void add_timer(struct timer_list *timer)
调用如上函数实现将一个定时器添加到内核定时器链表中,也可以通过mod_timer重新注册定时器,
删除定时器
int del_timer(struct timer_list *timer)
int del_timer_sync(struct timer_list *timer)
通过del_timer函数注销一个定时器,如果定时器存在返回1,否则返回0,del_timer_sync是用在SMP系统中的,在单核系统中和del_timer是一样的,del_timer_sync会判断当前定时器是否在其他CPU上运行,如果运行等待其运行完成后删除此定时器。
判断定时器是否挂起
int timer_pending(struct timer_list *timer)
用于判断一个定时器是否存在在链表中,但是此定时器有可能正在运行
函数分析
void add_timer(struct timer_list *timer)
{
__mod_timer(timer, timer->expires)
}
int __mod_timer(struct timer_list *timer)
{
base = lock_timer_base(timer)
//获取tvec_base变量,我们知道base为PerCPU变量,这个函数也顺便给base加锁。
if (timer_pending(timer)) {
deteach_timer(timer, 0)
ret = 1
}
//如果timer已经存在,则删除,此处的操作是在加锁状态先的,因此和softirq里面不会重入
new_base = __get_cpu_var(tvec_base)
if (base != new_base) {
//我们不能修改一个正在运行的timer的base,否则del_timer_sync会出问题,后面我们再说这个。
if (base->running_timer != timer) {
timer->base =NULL;
//由于本地中断已经在76行处关闭,因此不开本地中断
spin_unlock(&base->lock)
base = new_base;
//由于前面已经禁用了本地中断,因此这只需加锁
spin_lock(&base->lock)
timer->base = base
}
}
//如上代码如果当前timer base不是timer->base则更新timer->base
internal_add_timer(base, timer)
//将timer加入本地base中
spin_unlock_irqrestore(&base->lock, flag)
//1,释放锁
//2,恢复中断
//3,启用内核抢占
//由以上代码我们可以知道,同一个timer只能被提交一次,
//但是同一个timer是有可能在不同的cpu上同时运行的,我们可以试想这个一个场景,
//CPU0注册一个timer,并已经在CPU0上运行了,
//CPU1也注册了这个timer,因为运行的timer已经在链表上删除了且没有获取base->lock,
//因此lock_timer_base和timer_pending都可以向下执行并注册成功
//假设此timer的时间非常短,CPU1的timer在注册完成后由于系统的一次中断立刻执行,
//此时同一个timer在CPU0和CPU1并发
//以上是个人的一个理解,如果错误,还请指出
}
//由以上可以知道,在哪个CPU运行增加timer的代码,则timer的回掉函哪个CPU上运行。
del_timer(struct timer_list *timer)
{
if (timer_pending(timer)) {
base = lock_timer_base(timer, &flag);
ret = detach_if_pending(timer, base, true);
spin_unlock_irqrestore(&base->lock, flag);
}
return ret
//由此函数我们可以知道,此函数只判断timer是否在链表中,然后就直接删除,但是删除时此函数的回掉有可能正在运行,
//在判断timer_pending(timer)时,理论上来说应该加锁保护,但是此处没有加锁保护,是因为在detach_if_pending中会再次判断是否pending如果pending则函数直接退出,
}
del_timer_sync(struct timer_list *timer)
{
for(;;) {
int try_to_del_timer_sync(timer)
{
base = lock_timer_base(timer, &flag);
//判断当前timer是否在运行,如果在运行则等待
if (base->running_timer != timer) {
//detach_if_pending如果timer pending返回0
//如果timer删除成功返回1
ret = detach_if_pending(timer, base, true);
}
spin_unlock_irqrestore(&base->lock, flags)
return ret;
}
//如果timer删除成功返回0或者1,负责等待删除
int ret = try_to_del_timer_sync(timer);
if (ret >= 0)
return ret;
cpu_relax();
}
}