tasklet schedule
(2011-10-06 10:57:53)
标签:
杂谈 |
分类: linux |
Linux的中断处理框架大体为:入口点-->driver安装的中断处理函数-->退出点
通常说Linux下的中断处理分为top和bottom两部分,top部分中断是关闭的,因此一般在这里的代码都会以迅雷不及掩耳盗铃之势做完该做的事,然后打开中断以防止可能的中断丢失。因为要迅雷不及掩耳盗铃,所以大部分耗时的任务交给了bottom部分去处理。bottom阶段中断是打开的,所以有可能被其他中断到来时所打断。
bottom的延后处理,其中之一遍是由tasklet来完成,相对于workqueue这种任务延后机制,tasklet实际上是处在中断上下文中,因为处在空灵世界(不属于任一进程),所以在tasklet中休眠是绝对应该禁止的。
其他的一些bottom方法:
enum
{
HI_SOFTIRQ=0,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
TASKLET_SOFTIRQ,
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ,
RCU_SOFTIRQ,
NR_SOFTIRQS
};
通常说Linux下的中断处理分为top和bottom两部分,top部分中断是关闭的,因此一般在这里的代码都会以迅雷不及掩耳盗铃之势做完该做的事,然后打开中断以防止可能的中断丢失。因为要迅雷不及掩耳盗铃,所以大部分耗时的任务交给了bottom部分去处理。bottom阶段中断是打开的,所以有可能被其他中断到来时所打断。
bottom的延后处理,其中之一遍是由tasklet来完成,相对于workqueue这种任务延后机制,tasklet实际上是处在中断上下文中,因为处在空灵世界(不属于任一进程),所以在tasklet中休眠是绝对应该禁止的。
其他的一些bottom方法:
enum
{
};
- static inline void tasklet_schedule(struct tasklet_struct
*t)
- {
-
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) -
__tasklet_schedule(t); - }
- void __tasklet_schedule(struct tasklet_struct *t)
- {
-
unsigned long flags; -
local_irq_save(flags); -
t->next = NULL; -
*__get_cpu_var(tasklet_vec).tail = t; -
__get_cpu_var(tasklet_vec).tail = &(t->next); -
raise_softirq_irqoff(TASKLET_SOFTIRQ); -
local_irq_restore(flags); - }
上诉代码依然处在top阶段,不过做了个标记,留待bottom阶段到来时去检查
invoke_softirq()的核心函数为__do_softirq,实现如下:
- #define MAX_SOFTIRQ_RESTART 10
- asmlinkage void __do_softirq(void)
- {
-
struct softirq_action *h; -
__u32 pending; -
int max_restart = MAX_SOFTIRQ_RESTART; -
int cpu; -
pending = local_softirq_pending(); -
account_system_vtime(current); -
__local_bh_disable((unsigned long)__builtin_return_address(0)); -
trace_softirq_enter(); -
cpu = smp_processor_id(); - restart:
-
-
set_softirq_pending(0); -
local_irq_enable(); -
h = softirq_vec; -
do { -
if (pending & 1) { -
int prev_count = preempt_count(); -
h->action(h); -
if (unlikely(prev_count != preempt_count())) { -
printk(KERN_ERR "huh, entered softirq %td %p" -
"with preempt_count x," -
" exited with x?\n", h - softirq_vec, -
h->action, prev_count, preempt_count()); -
preempt_count() = prev_count; -
} -
rcu_bh_qsctr_inc(cpu); -
} -
h++; -
pending >>= 1; -
} while (pending); -
local_irq_disable(); -
pending = local_softirq_pending(); -
if (pending && --max_restart) -
goto restart; -
if (pending) -
wakeup_softirqd(); -
trace_softirq_exit(); -
account_system_vtime(current); -
_local_bh_enable(); - }
在上述代码的21和45行打开了可中断的窗口,在该窗口期间执行的代码可以被新的硬件中断所打断。本来软中断的pending字段已经被set_softirq_pending(0)代码所清除,但是新产生的中断有可能重新挂载一些pending的软中断,所以在执行完上次软中断pending字段的snapshot之后,会再次check有没有新的pending的软中断,也就是上述代码的goto restart的目的。
1.
tasklet在中断上下文中执行,所以tasklet中的代码不应该导致调度点的产生(如果调度,因为当前代码不属于任何一进程,或者current此时无效,tasklet在一个边缘世界里).而工作线程是在一个内核进程中,所以工作队列的函数当然可以睡眠。
2.tasklet始终运行在被当初提交它的那个CPU上,但对于工作队列而言,这只是它的缺省行为,当然可以改变它。
3.内核代码可以请求工作队列函数的执行延迟给定的时间间隔。

加载中…