这个函数不能在中断中被调用的原因就是防止删除timer的时候忙等,怎么忙等呢?在try_to_del_timer_sync出现之前,如果中断打断了正在执行的timer,那么中断中执行del_timer_sync的时候就会永远忙等下去,2.6.9中具体就是:
static inline void __run_timers(tvec_base_t *base)
{
...
timer = list_entry(head->next,struct timer_list,entry);
fn = timer->function;
data = timer->data;
list_del(&timer->entry); //这里删除这个timer。
set_running_timer(base, timer); //设置base->running_timer为timer
smp_wmb();
timer->base = NULL;
spin_unlock_irq(&base->lock); //放开自旋锁
fn(data); //执行回调函数,恰在此时被中断
spin_lock_irq(&base->lock); //锁上base
goto repeat;
...
}
timer 有两种删除方式,一个是自动删除,就是在执行完timer的回调函数以后自己删除,就是上面的代码中的 list_del(&timer->entry);这一句,另外一种删除方式就是手工删除,就是下面的这种方式,这个函数会等待timer 的base变为NULL,也就是等待timer被彻底删除完毕,如果在非中断中调用,那么没有问题,因为在下面这个函数的本意就是删除一个在任意的cpu 上的还没有被执行回调函数的timer的,如果删除正在执行的timer,那么大多是在不同的cpu上,同一个cpu上不可能有这种情况,因为除非是在回 调函数内部删除timer,如此一来就会在下面的函数中忙等。如果在中断中调用,那么分析一下:假设在执行timer回调函数的时候被中断。
int del_timer_sync(struct timer_list *timer)
{
tvec_base_t *base;
int i, ret = 0;
check_timer(timer);
del_again:
ret += del_timer(timer); //这里显然会返回0,因为timer->base已经为NULL了
for_each_online_cpu(i) {
base = &per_cpu(tvec_bases, i);
if (base->running_timer == timer) {
while (base->running_timer == timer) { //这里将忙等下去
cpu_relax();
preempt_check_resched(); //无法切换,因为中断中会增加preempt计数
}
break;
}
}
smp_rmb();
if (timer_pending(timer)) //如果timer的base不为NULL,那么就要重新开始,说明没有删除干净
goto del_again;
return ret;
}
即 使是软中断中也不能那样的忙等,虽然不睡眠,但是忙等也不是被欢迎的,特别是在中断或者软中断上下文中。在2.6.13内核以后,出现了一个新的函数,叫 做try_to_del_timer_sync,从它的名字可以看出,就是尝试一次,如果不成功不忙等而是返回,即使如此却还是不能在中断中调用。忙等的 条件就是中断正好发生在一个timer被执行的cpu上,并且中断中要删除这个timer
int del_timer_sync(struct timer_list *timer)
{
for (;;) {
int ret = try_to_del_timer_sync(timer); //这个函数不忙等,可是它外面的for循环却忙等,因为这个try每次都不会成功。
if (ret >= 0)
return ret;
cpu_relax();
}
}
这个现象就是一个广义的死锁,虽然并没有什么锁,可是想象一下,中断等待当前的cpu的base的当前timer不再是这个要被删除的timer,而该timer的回调函数也在等待中断赶快完成然而中断又退不出来,两者互不相让,这就是死锁。
del_timer_sync不能睡眠的原因
最新推荐文章于 2024-07-27 12:13:32 发布