1、软中断由内核线程ksoftirqd处理,下面说一下它的创建过程。
start_kernel()创建init线程,init()调用do_pre_smp_initcalls()->spawn_ksoftirqd(
),spawn_ksoftirqd()分两次调用cpu_callback(),
分别使用参数CPU_UP_PREPARE和CPU_ONLINE。使用CPU_UP_PREPARE调用cpu_callback()
时,创建了ksoftirqd线程,并把task_struct指针存于per_cpu变量per_cpu(ksoftirqd,
hotcpu)中;使用CPU_ONLINE调用该函数对ksoftirqd线程进行唤醒。
start_kernel()创建init线程,init()调用do_pre_smp_initcalls()->spawn_ksoftirqd(
),spawn_ksoftirqd()分两次调用cpu_callback(),
分别使用参数CPU_UP_PREPARE和CPU_ONLINE。使用CPU_UP_PREPARE调用cpu_callback()
时,创建了ksoftirqd线程,并把task_struct指针存于per_cpu变量per_cpu(ksoftirqd,
hotcpu)中;使用CPU_ONLINE调用该函数对ksoftirqd线程进行唤醒。
--------------linux/init/main.c---------------------
static void do_pre_smp_initcalls(void)
{
extern int spawn_ksoftirqd(void);
#ifdef CONFIG_SMP
extern int migration_init(void);
migration_init();
#endif
spawn_ksoftirqd();
}
----------------linux/kernel/softirq.c-----------------
/*
主cpu(即引导cpu)通过spawn_ksoftirqd()->cpu_callback()创建该cpu上的ksoftirqd
内核线程,并调用register_cpu_notifier()
将cpu_nfb注册到cpu通知链)
*/
__init int spawn_ksoftirqd(void)
{
void *cpu = (void *)(long)smp_processor_id();
cpu_callback(&cpu_nfb, CPU_UP_PREPARE, cpu);
cpu_callback(&cpu_nfb, CPU_ONLINE, cpu);
register_cpu_notifier(&cpu_nfb);
return 0;
}
----------------linux/kernel/softirq.c---------------------
//cpu_callback()建立了ksoftirqd内核线程,并把task_struct指针存于per_cpu变量per_cpu(ksoftirqd, hotcpu)中
static int __devinit cpu_callback(struct notifier_block *nfb,
unsigned long action,
void *hcpu)
{
int hotcpu = (unsigned long)hcpu;
struct task_struct *p;
switch (action) {
case CPU_UP_PREPARE:
BUG_ON(per_cpu(tasklet_vec, hotcpu).list);
BUG_ON(per_cpu(tasklet_hi_vec, hotcpu).list);
p = kthread_create(ksoftirqd, hcpu, "ksoftirqd/%d", hotcpu);
if (IS_ERR(p)) {
printk("ksoftirqd for %i failed\n", hotcpu);
return NOTIFY_BAD;
}
kthread_bind(p, hotcpu);
per_cpu(ksoftirqd, hotcpu) = p;
break;
case CPU_ONLINE:
wake_up_process(per_cpu(ksoftirqd, hotcpu));
break;
#ifdef CONFIG_HOTPLUG_CPU
case CPU_UP_CANCELED:
/* Unbind so it can run. Fall thru. */
kthread_bind(per_cpu(ksoftirqd, hotcpu), smp_processor_id());
case CPU_DEAD:
p = per_cpu(ksoftirqd, hotcpu);
per_cpu(ksoftirqd, hotcpu) = NULL;
kthread_stop(p);
takeover_tasklets(hotcpu);
break;
#endif /* CONFIG_HOTPLUG_CPU */
}
return NOTIFY_OK;
}
2、从cpu的ksoftirqd线程的创建。从cpu调用start_kernel()->init()->smp_init()->cpu_up(),cpu_up()在各cpu上线前后分别调用notifier_call_chain()。
static void __init smp_init(void)
{
unsigned int i;
/* FIXME: This should be done in userspace --RR */
for_each_present_cpu(i) {
if (num_online_cpus() >= max_cpus)
break;
if (!cpu_online(i))
cpu_up(i);
}
……
}
int __devinit cpu_up(unsigned int cpu)
{
……
//上线前调用
ret = notifier_call_chain(&cpu_chain, CPU_UP_PREPARE, hcpu);
……
//等cpu上线:cpu_isset(cpu, cpu_online_map)
ret = __cpu_up(cpu);
……
/* Now call notifier in preparation. */
//上线后调用
notifier_call_chain(&cpu_chain, CPU_ONLINE, hcpu);
}
//函数notifier_call_chain()以cpu标号为参数运行cpu通知链上的各函数,其中包含上述spawn_ksoftirqd()中注册的cpu_nfb。
static struct notifier_block __devinitdata cpu_nfb = {
.notifier_call = cpu_callback
};
//通知链元素cpu_nb上的函数cpu_callback即用来创建各非引导cpu上的ksoftirqd线程。
int notifier_call_chain(struct notifier_block **n, unsigned long val, void *v)
{
int ret=NOTIFY_DONE;
struct notifier_block *nb = *n;
while(nb)
{
ret=nb->notifier_call(nb,val,v);
if(ret&NOTIFY_STOP_MASK)
{
return ret;
}
nb=nb->next;
}
return ret;
}