这个函数不能在中断中被调用的原因就是防止删除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的回调函数也在等待中断赶快完成然而中断又退不出来,两者互不相让,这就是死锁。
另附CSDN中关于del_timer产生bug的讨论!摘要如下:
根据LKD3上的说法,在允许睡眠的情况下,应该使用del_timer_sync函数来取消掉timer,因为del_timer在多核的系统上可能会导致core 1去del_timer时,core 0上正在运行timer,从而导致bug。
我觉得LZ的测试代码不造成内核崩溃是正常的。
del_timer要做的事情是把timer从待处理链表里面detach,并不是要销毁这个timer对象; 这件事情跟timer.function回调函数正在被执行本身毫无瓜葛,怎么可能会引起崩溃呢?
当然,这个地方确实是需要注意的,del_timer和timer.function是可以同时发生的。(del_timer和__run_timers
都会给tvec_base上锁,但是__run_timers在调用timer.function的时候会先把锁释放掉。)
考虑下面一种情况:调用del_timer以后,我们可能会kfree(timer)把对象销毁掉、把与这个timer相关的对象也销毁掉(这些对象可能会被timer.function访问)。而在timer.function里面,我们又可能会通过arg参数把timer传进去,然后调用mod_timer之类的;又或者会在
timer.function里面访问那些与timer相关的对象(这些对象我们在del_timer之后已经销毁了……)。如果是这样,就应该使用
del_timer_sync,必须等到timer.function完成了,才能做那些销毁工作。如果del_timer以后我们不对
timer.function里面需要访问的任何数据产生影响,那么也就不需要del_timer_sync了。
话说回来,如果del_timer在SMP环境下随便怎么用有问题,那么这么久了,有问题怎么还不修,还留着这个函数干什么……
关键不在于我们传了什么东西进去,而在于我们在timer.function里面引用的内存或者其他资源,可能已经被释放了。
为什么会被释放呢?因为我们错误的以为del_timer之后,这些“东西”就可以被释放。但是实际上, del_timer之后,timer.function可能还在运行,释放这些“东西”是可能会出问题的。
所以在释放这些“东西”之前,除了调用del_timer把timer从待处理队列里面detach之外,如果timer.function正在执行,还需要等它执行完。这就是del_timer_sync干的事情。
上面所说的是,del_timer(&t)之后,会调用X_Release_Resources( )来释放某些资源。而这些资源会被t.function使用。
由于del_timer调用之后,t.function可能正在其他CPU上运行,可能正在访问这些资源。而与此同时,这些资源又正在被X_Release_Resources释放……
在这样的情况下,才应该使用del_timer_sync,而不是del_timer。
如果你在del_timer之后不去释放资源,t.function需要访问的资源都正常,那么又怎么会崩溃呢?而你的测试代码不就没有牵涉资源释么~
阅读(2463) | 评论(0) | 转发(2) |