linux的定时器串行执行原因
背景:
- 按键多次抖动激活的未运行定时器会覆盖
- 当按键中断触发时激活定时器,此时定时器正在运行且持有锁,若原先的定时器不能运行,则会造成死锁。
问题1:
- 定时器运行中被中断抢占,中断又重新激活该定时器,原先的定时器程序是否还会继续运行?
结论1:
- 定时器运行中被中断抢占,中断又重新激活该定时器,原先的定时器程序会继续运行。
问题2:
- 定时器运行中被中断抢占,中断又重新激活该定时器,原先的定时器程序正常运行还没结束,这时新的定时器又来了会怎么样?
结论2:
- 定时器运行中被中断抢占,中断又重新激活该定时器,原先的定时器程序正常运行还没结束,这时新的定时器会等待原先的定时器运行完再运行。
- 可以理解为相同的定时器串行执行。
原因:
基本知识:
- 原理:定时器是一种软中断,对应TIMER_SOFTIRQ进入软中断就会运行定时器。
进入软中断方式:( Linux版本3.10.0)
- 中断退出时:irq_exit - > invoke_softirq - > __do_softirq
- 调用raise_softirq_irqoff:raise_softirq_irqoff - > wakeup_softirqd - > wake_up_process(ksoftirqd) - > try_to_wake_up - > run_ksoftirqd - > __do_softirq
- 开启下半部时:local_bh_enable - > _local_bh_enable_ip - > do_softirq
- 其他调用do_softirq的地方:比如netif_rx_ni - > do_softirq
定时器的主要运行时机:以上方法都可以进入定时器,不过根据定时器的延时特性,其运行主要是依靠时钟中断的处理程序即:update_process_times - > run_local_timers - > raise_softirq - > 2
原因1:同一CPU同一时间只能运行一个__do_softirq同一CPU软中断串行执行。
- __do_softirq增加preempt_count的软中断部分标记的值
- __local_bh_disable(略,SOFTIRQ_OFFSET) - > add_preempt_count()
- 在上文红色标记处均有调用函数和in_interrupt(),该函数如下,用于判断是否在硬中断,软中断,NMI中断中。
-
若和in_interrupt()为真,则不执行__do_softirq函数。
/* * PREEMPT_MASK: 0x000000ff * SOFTIRQ_MASK: 0x0000ff00 * HARDIRQ_MASK: 0x03ff0000 * NMI_MASK: 0x04000000 */ #define in_interrupt() (irq_count()) #define irq_count() (preempt_count()&(HARDIRQ_MASK| SOFTIRQ_MASK | NMI_MASK))
-
- 因此,原先的定时器运行之前设置了软中断标记,在中断抢占激活该定时器之后,返回原先的定时器运行,直到运行完该__do_softirq。同一CPU才能运行新的__do_softirq。
原因2:中断抢占后,激活该定时器不影响其原先运行的参数。
- 定时器运行前参数会保存到局部变量并且删除链表中的该定时器。
- run_timer_softirq - > __run_timers
原因3:每个定时器在一个jiffies的内至多运行一次。
- __do_softirq先清除所有__softirq_pending,到下一个jiffies时将不再goto restart。
- __softirq_pending的TIMER_SOFTIRQ标志再次被设置是下一个jiffies的时钟中断