Linux 中断子系统,软中断实现原理解析,softirq:
首先是二级中断向量,接着是
一级中断的内容:
/* PLEASE, avoid to allocate new softirqs, if you need not _really_ high
frequency threaded job scheduling. For almost all the purposes
tasklets are more than enough. F.e. all serial device BHs et
al. should be converted to tasklets, not to softirqs.
*/
enum
{
HI_SOFTIRQ=0, //高优先级软中断,高优先级的tasklet
tasklet_hi_action
NET_TX_SOFTIRQ, //网络数据包发送
NET_RX_SOFTIRQ, //网络数据包接受
TASKLET_SOFTIRQ //一般行的软中断,一般性的tasklet
bh_action
};
底层触发方式:
static inline void raise_softirq(int nr) //内联函数为最高封装,属于原子操作。
{
unsigned long flags;
local_irq_save(flags); //现场的保存
__cpu_raise_softirq(smp_processor_id(), nr);
local_irq_restore(flags); //位置①
}
static inline void __cpu_raise_softirq(int cpu, int nr)
{
softirq_active(cpu) |= (1<<nr);
}
也就是对某个个位置1,就可以实现产生一个中断,并且回到位置①处,显然单片机的定时器很容易实现该功能,比如对某个寄存器进行点评的修改,长生一个中断给中断控制器,进而执行相应的中断处理函数,然后返回。例子:红外接收。
高层中断触发方式:
static inline void tasklet_hi_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
int cpu = smp_processor_id();
unsigned long flags;
local_irq_save(flags); //现场保存寄存器,内存,代码等
t->next = tasklet_hi_vec[cpu].list;
tasklet_hi_vec[cpu].list = t;
__cpu_raise_softirq(cpu, HI_SOFTIRQ);
local_irq_restore(flags); //现场的恢复。
}
}
以下同理:
static inline void tasklet_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
int cpu = smp_processor_id();
unsigned long flags;
local_irq_save(flags);
t->next = tasklet_vec[cpu].list;
tasklet_vec[cpu].list = t;
__cpu_raise_softirq(cpu, TASKLET_SOFTIRQ);
local_irq_restore(flags);
}
}
二级中断系统:
中断类型:
num {
TIMER_BH = 0, //定时器或者是时钟中断
TQUEUE_BH,
DIGI_BH,
SERIAL_BH,
RISCOM8_BH,
SPECIALIX_BH,
AURORA_BH,
ESP_BH,
SCSI_BH,
IMMEDIATE_BH,
CYCLADES_BH,
CM206_BH,
JS_BH,
MACSERIAL_BH,
ISICOM_BH
};
软中断初始化:
void __init softirq_init()
{
int i;
for (i=0; i<32; i++)
tasklet_init(bh_task_vec+i, bh_action, i);
open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
绑定一级中断的两个中断服务程序
open_softirq(HI_SOFTIRQ,
tasklet_hi_action, NULL); 绑定一级中断的两个中断服务程序
}
中断服务程序:
static void tasklet_action(struct softirq_action *a)
{
int cpu = smp_processor_id();
struct tasklet_struct *list;
local_irq_disable();
list = tasklet_vec[cpu].list;
tasklet_vec[cpu].list = NULL;
local_irq_enable();
while (list != NULL) {
struct tasklet_struct *t = list;
list = list->next;
if (tasklet_trylock(t)) {
if (atomic_read(&t->count) == 0) {
clear_bit(TASKLET_STATE_SCHED, &t->state);
t->func(t->data); //执行tasklet_struct里面的函数,以及tasklet_struct里面保存的参数具体见绿色的初始化。
/*
* talklet_trylock() uses test_and_set_bit that imply
* an mb when it returns zero, thus we need the explicit
* mb only here: while closing the critical section.
*/
#ifdef CONFIG_SMP
smp_mb__before_clear_bit();
#endif
tasklet_unlock(t);
continue;
}
tasklet_unlock(t);
}
local_irq_disable();
t->next = tasklet_vec[cpu].list;
tasklet_vec[cpu].list = t;
__cpu_raise_softirq(cpu, TASKLET_SOFTIRQ);
local_irq_enable();
}
}
绿色:
void __init softirq_init()
{
……
for (i=0; i<32; i++)
tasklet_init(bh_task_vec+i, bh_action, i);
……
}
void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data)
{
t->func = func;
t->data = data;
t->state = 0;
atomic_set(&t->count, 0);
}
一起使用bh_action函数,但使用不同的i参数,所以:
static void bh_action(unsigned long nr)
{
int cpu = smp_processor_id();
if (!spin_trylock(&global_bh_lock))
goto resched;
if (!hardirq_trylock(cpu))
goto resched_unlock;
if (bh_base[nr])
bh_base[nr](); //根据不同的参数,调用bh_base数组的不同的函数。而bh_base数组是32个对应于二级中断的32中类型。(中断底半部)
hardirq_endlock(cpu);
spin_unlock(&global_bh_lock);
return;
resched_unlock:
spin_unlock(&global_bh_lock);
resched:
mark_bh(nr);
}
Tasklet的状态:
(1)state成员所表示的运行状态;(2)count成员决定的使能/禁止状态。 Enable disabled
num
{
TASKLET_STATE_SCHED, /* Tasklet is scheduled for execution */ 已经准备好了,只要被选择就可以执行。
TASKLET_STATE_RUN /* Tasklet is running (SMP only) */
对于SMP系统表示已经在其它cpu上运行了。
};