struct softirq_action{ void (*action)(struct softirq_action *); void *data;};static struct softirq_action softirq_vec[32] __cacheline_aligned; 这个softirq_vec[]仅比bh_base[]增加了action()函数的参数,在执行上,softirq比bottom half的限制更少。 和bottom half类似,系统也预定义了几个softirq_vec[]结构的用途,通过以下枚举表示: enum{ HI_SOFTIRQ=0, NET_TX_SOFTIRQ, NET_RX_SOFTIRQ, TASKLET_SOFTIRQ}; HI_SOFTIRQ被用于实现bottom half,TASKLET_SOFTIRQ用于公共的tasklet使用,NET_TX_SOFTIRQ和NET_RX_SOFTIRQ用于网络子系统的报文收发。在软中断子系统初始化(softirq_init())时,调用了open_softirq()对HI_SOFTIRQ和TASKLET_SOFTIRQ做了初始化: void open_softirq(int nr, void (*action)(struct softirq_action*), void *data) open_softirq()会填充softirq_vec[nr],将action和data设为传入的参数。TASKLET_SOFTIRQ填充为tasklet_action(NULL),HI_SOFTIRQ填充为tasklet_hi_action(NULL),在do_softirq()函数中,这两个函数会被调用,分别启动tasklet_vec[cpu]和tasklet_hi_vec[cpu]链上的tasklet运行。 static inline void __cpu_raise_softirq(int cpu, int nr) 这个函数用来激活软中断,实际上就是第cpu号CPU的第nr号软中断的active位置1。在do_softirq()中将判断这个active位。tasklet_schedule()和tasklet_hi_schedule()都会调用这个函数。 do_softirq()有4个执行时机,分别是:从系统调用中返回(arch/i386/kernel/entry.S::ENTRY(ret_from_sys_call))、从异常中返回(arch/i386/kernel/entry.S::ret_from_exception标号)、调度程序中(kernel/sched.c::schedule()),以及处理完硬件中断之后(kernel/irq.c::do_IRQ())。它将遍历所有的softirq_vec,依次启动其中的action()。需要注意的是,软中断服务程序,不允许在硬中断服务程序中执行,也不允许在软中断服务程序中嵌套执行,但允许多个软中断服务程序同时在多个CPU上并发。 使用示例 softirq作为一种底层机制,很少由内核程序员直接使用,因此,这里的使用范例仅对其余几种软中断机制。 1.bottom half 原有的bottom half用法在drivers/char/serial.c中还能看到,包括三个步骤: init_bh(SERIAL_BH,do_serial_bh); //在串口设备的初始化函数rs_init()中,do_serial_bh()是处理函数mark_bh(SERIAL_BH); //在rs_sched_event()中,这个函数由中断处理例程调用remove_bh(SERIAL_BH); //在串口设备的结束函数rs_fini()中调用 尽管逻辑上还是这么三步,但在do_serial_bh()函数中的动作却是启动一个task queue:run_task_queue(&tq_serial),而在rs_sched_event()中,mark_bh()之前调用的则是queue_task(...,&tq_serial),也就是说串口bottom half已经结合task queue使用了。而那些更通用一些的bottom half,比如IMMEDIATE_BH,更是必须要与task queue结合使用,而且一般情况下,task queue也很少独立使用,而是与bottom half结合,这在下一节task queue使用示例中可以清楚地看到。 2.task queue 一般来说,程序员很少自己定义task queue,而是结合bottom half,直接使用系统预定义的tq_immediate等,尤以tq_immediate使用最频繁。看以下代码段,节选自drivers/block/floppy.c: static struct tq_struct floppy_tq; //定义一个tq_struct结构变量floppy_tq,不需要作其他初始化动作static void schedule_bh( void (*handler)(void*) ){ floppy_tq.routine = (void *)(void *) handler; //指定floppy_tq的调用函数为handler,不需要考虑floppy_tq中的其他域 queue_task(&floppy_tq, &tq_immediate); //将floppy_tq加入到tq_immediate中 mark_bh(IMMEDIATE_BH); //激活IMMEDIATE_BH,由上所述可知, 这实际上将引发一个软中断来执行tq_immediate中挂接的各个函数} | | | | | |