Chinaunix首页 | 论坛 | 博客
  • 博客访问: 834611
  • 博文数量: 143
  • 博客积分: 455
  • 博客等级: 一等列兵
  • 技术积分: 861
  • 用 户 组: 普通用户
  • 注册时间: 2012-08-03 00:11
文章分类

全部博文(143)

文章存档

2018年(10)

2017年(6)

2016年(28)

2015年(14)

2014年(67)

2013年(1)

2012年(17)

我的朋友

分类: LINUX

2014-01-19 19:49:02

    这个函数不能在中断中被调用的原因就是防止删除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需要访问的资源都正常,那么又怎么会崩溃呢?而你的测试代码不就没有牵涉资源释么~

阅读(2619) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~