一、软中断注册和硬中断类似,软中断也有类似的中断向量表,只不过是用“软件”实现的。
struct softirq_action softirq_vec[32]是软中断向量表
(文件linux_2_6_24/kernel/softirq.c)
- struct softirq_action
-
{
-
void (*action)(struct softirq_action *); //钩子函数
-
void *data; //钩子函数的形参
-
};
内核用到的中断向量(其实就是数组下标)如下所示
(文件linux_2_6_24/include/linux/interrupt.h)
- enum{
-
HI_SOFTIRQ=0, //高优先级的tasklet
-
TIMER_SOFTIRQ, //内核定时器
-
NET_TX_SOFTIRQ, //网络发送
-
NET_RX_SOFTIRQ, //网络接收
-
BLOCK_SOFTIRQ, //???
-
TASKLET_SOFTIRQ, //普通的tasklet
-
SCHED_SOFTIRQ
-
}
内核注册一个软中断用函数,本质上就是就是初始化数组某一元素
- void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)
-
{ //nr 即软中断向量编号
-
softirq_vec[nr].data = data;
-
softirq_vec[nr].action = action;
-
}
内核注册软中断的地方有:
- start_kernel()
-
-->init_timers()
-
-->open_softirq(TIMER_SOFTIRQ,run_timer_softirq,NULL)
-
-->softirq_init()
-
-->open_softirq(TASKLET_SOFTIRQ, tasklet_action,NULL)
-
-->open_softirq(HI_SOFTIRQ,tasklet_hi_action,NULL)
-
-
-->do_initcall()
-
-->net_dev_init()
-
-->open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL);
-
-->open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL);
-
-->blk_dev_init()
-
-->open_softirq(BLOCK_SOFTIRQ, blk_done_softirq,NULL)
二、软中断触发前面注册完了,现在开始触发。
内核用一个数据结构来标记曾经有“软中断”发生过(或者说成软中断被触发过)
__softirq_pending 共32bit,即每个bit对应软中断的一个向量,实际使用了6个bit
第n个bit置1,即softirq_vec[n]有软中断发生。
- typedef struct {
-
unsigned int __softirq_pending; /* set_bit is used on this */
-
unsigned int __last_jiffy_stamp;
-
} ____cacheline_aligned irq_cpustat_t;
-
-
extern irq_cpustat_t irq_stat[]; /* defined in asm/hardirq.h */
-
#define __IRQ_STAT(cpu, member) (irq_stat[cpu].member)
-
-
#define local_softirq_pending() \
-
__IRQ_STAT(smp_processor_id(), __softirq_pending)
-
-
#define set_softirq_pending(x) (local_softirq_pending() = (x))
-
#define or_softirq_pending(x) (local_softirq_pending() |= (x))
-
-
#define __raise_softirq_irqoff(nr) do { or_softirq_pending(1UL << (nr)); } while (0)
常用的软中断触发函数
- void raise_softirq_irqoff(int nr){ //nr 即软中断向量编号
-
__raise_softirq_irqoff(nr);
-
}
但这只是“触发”软中断,软中断并不会立即被处理
三、软中断处理函数_ _do_softirq是
一次性按照向量表从高到低循环处理所有软中断(潜台词,软中断不可嵌套)
_ _do_softirq()的调用时机:
1.
irq_exit() 硬件中断处理完,返回时调用
- do_IRQ() -->irq_exit()
-
-->local_softirq_pending()
-
-->_ _do_softirq()
2.
ksoftirqd() 内核进程
- start_kernel() --> kernel_init() -->do_pre_smp_initcalls()
-
-->spawn_ksoftirqd() -->cpu_callback()
-
-->kthread_create(ksoftirqd, hcpu, "ksoftirqd/%d", hotcpu)
-
-->ksoftirqd()
-
-->local_softirq_pending()
-
-->_ _do_softirq()
3.
local_bh_enable()时,发现有待处理的软中断且当时没处在软硬中断上下文中
- local_bh_enable()
-
-->local_softirq_pending()
-
-->_ _do_softirq()
处理过程代码详解
- 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:
-
/* Reset the pending bitmask before enabling irqs
-
下面首先清除pending,以便系统可以激活其它软件中断,
-
然后使能外部中断
-
系统在下面的处理中,将使能外部中断以提高系统的响应,
-
注意,必须先清除pending,再使能外部中断,否则死锁*/
-
set_softirq_pending(0);
-
-
local_irq_enable();
-
-
h = softirq_vec;
-
-
/*下面按照从高到低调用open_softirq注册的句柄*/
-
do {
-
if (pending & 1) {
-
h->action(h); //关键的一句,tasklet、内核timer、网络中断都是在这里搞的
-
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();
-
}
阅读(883) | 评论(0) | 转发(0) |