1、软中断类型是静态定义的,10种
2、软中断回调函数是在本地CPU开中断的情况下执行。因此能够被中断打断,但是软中断无法抢占软中断。因为在irq_exit的时候会判断是否在中断上下文,如果在中断上下文不会去执行软中断。即当一个软中断被中断打断,然后在中断上半部返回时,它不会去执行新的软中断,所以软中断在一个cpu上总是串行执行。
asmlinkage void __do_softirq(void)
{
...................
//进入软中断
local_irq_enable();();
............................
/* 循环处理所有的软中断.总共10个 */
do {
if (pending & 1) {
.................
h->action(h);
..................
}
h++;
pending >>= 1;
} while (pending);
local_irq_disable();
..........................
}
3、 同一类型的软中断可以在多个CPU上并行执行。感觉是raise_softirq并未对CPU限制,也并未对软中断触发的类型做出限制。多个CPU都能触发相同类型的软中断
4、软中断的回调函数不能睡眠,不能使用会导致睡眠的函数。软中断的回调函数在执行的时候,处于软中断上下文,即中断上下文。在硬中断上下文退出时,irq_exit会判断当前是否有待处理的软中断,以及是否处于硬中断上下文和软中断上下文。如果不是才会去处理软中断。虽然这个时候执行软中断,保证了不在软中断上下文。但是后面在处理软中断时,会调用__local_bh_disable(_RET_IP_, SOFTIRQ_OFFSET);将preempt_count的softirq域增加,此时又会进入软中断上下文(local_bh_disable,到local_bh_enab,屏蔽了软中断,这段临界区都属于软中断上下文。。。。不知道为什么,这种情况并未执行软中断,也算软中断上下文)
中断运行在中断上下文,没有一个所谓的中断描述符来描述它,它不是操作系统调度的单位。一旦在中断上下文中睡眠,首先无法切换上下文(因为没有中断描述符,当前上下文的状态得不到保存),其次,没有人来唤醒它,因为它不是操作系统的调度单位。从代码上怎么理解呢?是因为在进行进程上下文切换的时候,会出现问题嘛??中断上下文和进程上下文啥区别哦。我感觉还是在中断上下文切换的时候,linux没有定义数据结构去保存中断处理程序时的现场信息。如果在中断上下文进行切换会导致当前中断的信息丢失,无法返回这个现场继续处理中断
5、软中断执行时间点是在硬中断返回前,即退出硬中断上下文时,先检查是否有待处理的软中断,然后在检查是否需要抢占当前进程。软中断上下文总是抢占进程上下文。另外调用raise主动触发也可以zhi
irq_handler-->handle_arch_irq-->gic_handle_irq-->handle_IRQ...这些都是属于中断上半部。即硬中断处理过程,会有irq_enter进行硬中断上下文和irq_exit退出硬中断上下文
在irq_exit时会查看是否需要处理软中断。当这些处理完成之后,才能执行到后面的汇编代码,判断是否开启抢占功能,是否需要抢占当前进程。
软中断上下文执行在进程上下文前。因此前者总是抢占后者
__irq_svc:
.....................
@中断处理过程
irq_handler
@如果开启了抢占功能,则中断返回时会检查是否可以抢占发生中断时的进程
@检查thread_info->preempt_count是否为0
#ifdef CONFIG_PREEMPT
get_thread_info tsk
ldr r8, [tsk, #TI_PREEMPT] @ get preempt count
ldr r0, [tsk, #TI_FLAGS] @ get flags
teq r8, #0 @ if preempt count != 0
movne r0, #0 @ force flags to 0
tst r0, #_TIF_NEED_RESCHED
blne svc_preempt
#endif
svc_exit r5, irq = 1 @ return from exception
UNWIND(.fnend )
ENDPROC(__irq_svc)
tasklet是基于软中断的一种下半部机制
1、tasklet是串行执行的。即同一时刻,同一个tasklet只能在一个CPU上执行(因为从这点来看软中断是比tasklet要快的,同一个软中断同一个时刻可以在不同cpu上执行)。在tasklet_schedule时,会将该tasklet绑定到某个CPU的tasklet_vec链表上。它需要在该CPU执行完其回调函数时,才能和该CPU解绑。才能去其他CPU上执行。
主要是靠SCHED和RUN两个状态构成了串行。tasklet_schedule时,会将taskletA的状态设置为SCHED,如果设置成功,则会被加到本CP的tasklet_vec(反之失败,则表明已经被加入了其他CPU)。在软中断触发,处理tasklet时,又会尝试将taskletA设置为RUN状态(失败则说明已经有CPU在处理该tasklet回调函数)。
static inline void tasklet_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
__tasklet_schedule(t);
}
static void tasklet_action(struct softirq_action *a)
{
......................
/*
遍历整个链表处理tasklet
处理tasklet链表是在开中断的情况下进行的
*/
while (list) {
................
/*
设置tasklet状态为RUN.如果本身已经被RUN(说明该tasklet被其他CPU执行了),则返回false
本轮不执行该tasklet,这样保证了同一个tasklet只能在同一个CPU上运行
*/
if (tasklet_trylock(t)) {
if (!atomic_read(&t->count)) {/* count = 0,才表示激活状态*/
if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))//清除SCHED标志
BUG();
t->func(t->data);
tasklet_unlock(t);//清除RUN标志
continue;
}
tasklet_unlock(t);
}
........................
}
}
中断上下文包含硬中断上下文(hardirq context)和软中断上下文(softirq context)。硬件中断上下文表示硬件中断处理过程。
软中断上下文包含三个部分。
情形1、下半部执行的软中断包括tasklet。irq_exit-->invoke_softirq
情形2、ksoftirqd内核线程执行软中断。eg 系统使能了强制中断线程化。或者是软中断执行时机过长,早do_softirq中唤醒了内核线程ksoftirqd(wakeup_softirqd)
static inline void invoke_softirq(void)
{
if (!force_irqthreads) {//是否强制中断线程化
............................
} else {
wakeup_softirqd();
}
}
asmlinkage void __do_softirq(void)
{
.......................
if (pending) {
/*
当执行软中断的过程中再次触发软中断。重新执行软中断的条件
1、软中断处理时间没有超过2ms
2、当前进程没有调度需求
3、循环处理的次数不能超过max_restart(10)次
*/
if (time_before(jiffies, end) && !need_resched() &&
--max_restart)
goto restart;
wakeup_softirqd();
}
}
情形3、进程上下文中调用local_bh_enable也会去处理软中断。local_bh_enable-->do_softirq。情形1运行在中断下半部中。情形2和情形3运行与进程上下文中。linux内核使用以下几个宏进行判断这些情况。