del_timer_sync不能睡眠的原因

这个函数不能在中断中被调用的原因就是防止删除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的回调函数也在等待中断赶快完成然而中断又退不出来,两者互不相让,这就是死锁。

原文链接: https://blog.csdn.net/dog250/article/details/5303558

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    del_timer_sync不能睡眠的原因

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/410119

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月26日 上午11:49
下一篇 2023年4月26日 上午11:50

相关推荐