本来没想到写这个的,但在研究linux的动态定时器时,发现动态定时器是用到软中断机制的,所以对软中断做一个浅显的了解:
在分析过程中,愈加的觉得kernel是一个整体,想单单搞懂驱动是有难度的,因为kernel是个整体。你要懂驱动,就要对驱动模型,就要对文件系统、内存管理和进程调度等等有一定了解。
下面来分析soft irq:
在kernel/softirq.c中:
staticstruct softirq_action softirq_vec[NR_SOFTIRQS]__cacheline_aligned_in_smp;
这就是软中断的中断向量表。
还有一个枚举变量:
enum
{
HI_SOFTIRQ=0,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
BLOCK_IOPOLL_SOFTIRQ,
TASKLET_SOFTIRQ,
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ,
RCU_SOFTIRQ, /*Preferable RCU should always be the last softirq */
NR_SOFTIRQS
};
TIMER_SOFTIRQ的索引值是1,优先值是很高的。
软中断的注册:
open_softirq(TIMER_SOFTIRQ, run_timer_softirq);
void open_softirq(int nr, void (*action)(struct softirq_action *))
{
softirq_vec[nr].action = action;
}
这里将TIMER_SOFTIRQ软中断索引号与run_timer_softirq中断处理程序联系起来。
软中断的触发:
内核在硬件时钟中断发生后执行定时器,定时器作为软中断在下半部上下文中执行。具体的情况是:时钟中断处理程序会执行update_process_times()函数,该函数调用run_local_timers()函数:
void run_local_timers(void)
{
hrtimer_run_queues();
raise_softirq(TIMER_SOFTIRQ);
softlockup_tick();
}
inline void raise_softirq_irqoff(unsigned int nr)
{
__raise_softirq_irqoff(nr);
/*
* If we're in an interrupt or softirq, we're done
* (this also catches softirq-disabled code). We will
* actually run the softirq once we return from
* the irq or softirq.
*
* Otherwise we wake up ksoftirqd to make sure we
* schedule the softirq soon.
*/
if (!in_interrupt())
wakeup_softirqd();
}
void raise_softirq(unsigned int nr)
{
unsigned long flags;
local_irq_save(flags);
raise_softirq_irqoff(nr);
local_irq_restore(flags);
}
在注释中有这么一句:Otherwise we wake up ksoftirqd to make sure we schedule the softirq soon
唤醒ksoftirqd内核线程来保证调度立即执行softirq
软中断的执行:
在kernel/softirq.c中:
staticint __cpuinit cpu_callback(struct notifier_block *nfb,
unsigned long action,
void *hcpu)
中,建立一个ksoftirqd内核线程来执行软中断:
kthread_create(run_ksoftirqd,hcpu, "ksoftirqd/%d", hotcpu);
在run_ksoftirqd当中,
run_ksoftirqd-->do_softirq-->__do_softirq
__do_softirq函数的核心部分如下:
h = softirq_vec;
do {
if (pending & 1) {
int prev_count = preempt_count();
kstat_incr_softirqs_this_cpu(h - softirq_vec);
trace_softirq_entry(h, softirq_vec);
h->action(h);
trace_softirq_exit(h, softirq_vec);
if (unlikely(prev_count != preempt_count())) {
printk(KERN_ERR "huh, entered softirq %td %s %p"
"with preempt_count %08x,"
" exited with %08x?\n", h - softirq_vec,
softirq_to_name[h - softirq_vec],
h->action, prev_count, preempt_count());
preempt_count() = prev_count;
}
rcu_bh_qs(cpu);
}
h++;
pending >>= 1;
} while (pending);
大致看出这个过程是循环遍历每一个待处理的软中断,调用它们的中断处理程序。
这样,利用了软中断,我们的run_timer_softirq这个中断处理程序得以运行。
对这个linux软中断的分析比较简单,仅仅为linux的动态定时器做了铺垫。后面还需要再仔细分析linux的软中断。