1.首先,在irq_exit中调用do_softirq前已经退掉了preempt_count中的HARDIRQ_MASK,因此softirq此时如果不在preempt_count加上SOFTIRQ_MASK位还是会被抢占的,我们看到softirq是在硬件中断后执行的,因此如果硬件中断是任意上下文的话那么softirq也是任意上下文,我们知道任意上下文下的睡眠或者调度会使得被中断的进程不确定而且对被中断的进程不公平,因此 softirq也不能睡眠,这也是一个约定,但是此时在未进入softirq的处理前却是可以被抢占的,因为硬件中断处理已经完成,软件中断是相对不急的,因此仅在此时它可以被抢占,然而一旦进入do_softirq的话,那么softirq逻辑就又开始在这个被中断的进程的上下文大做文章了,因此不能被抢占,也不能调度,原因和上述硬件中断一样。

2.至于SOFTIRQ_MASK位,它是防止软中断被重入而设置的,看看do_softirq中有个local_bh_disable,就是它递增了preempt_count的SOFTIRQ_MASK位,使得在退出此次softirq处理前不能再进入,另外它也保证了在执行softirq的时候不被抢占。

3.如果就到我上面说的为止,一切显得很合理,但是我们知道softirq是不急的动作,如果没完没了的执行它的话,那么对被中断的进程仍然不公平,毕竟softirq一直在此进程上下文执行着,于是你说的softirqd就出来了,内核专门开了一个softirq服务线程来处理软中断,我们看到在do_softirq(void)中:

int max_restart = MAX_SOFTIRQ_RESTART; //最大的softirq执行次数

if (pending && --max_restart) //如果没有到最大次数,继续执行

goto restart;

...

if (pending) //如果在执行上次softirq途中又pending了新的softirq请求并且已经不能再继续执行softirq了,那么唤醒softirqd吧。

wakeup_softirqd();

就是这样的,上面的if(pending)判断以及唤醒softirqd的逻辑在于不能给鼻子上脸,给了softirq MAX_SOFTIRQ_RESTART的执行机会就够意思了,如果还有请求,抱歉,本次进程还忙着呢,不能再帮你了,你还是去请求softirqd吧,人家是专业的执行softirq的。做事不能太霸道了,softirq在硬件中断完成后获得执行机会,而且还递增了被中断进程的 preempt_count导致不能抢占,这不但给当前进程增加不确定性,还导致了别的就绪进程无法抢占当前被中断的进程,这样十分不好。

4.linux 巧妙的处理了中断和性能的关系,你不是说中断中不能睡眠并且不能执行太久吗?那么好我引入了软中断softirq,如果softirq中能睡眠并且能长期占用cpu那还不是和没有引入一样吗?那么好,linux又引入了softirqd,干脆给它一个进程上下文。

5.注意,softirq的执行函数不能主动睡眠,但是在softirqd中却是可以被抢占的,睡眠和抢占是两个概念,前者主动后者被动。而且不能睡眠是在softirq的处理函数中,可以抢占是在softirq外,在执行过程中还是不能抢占的。

aha,说了这么多有点罗嗦了,总结一下:

softirq不能被抢占是因为它有可能在任意进程上下文执行,所谓抢占必须在确定上下文中执行,不能睡眠也是因为它可能在任意上下文执行。至于softirqd只是为了不使softirq长期占用cpu而提出的一个手段而已,应该这么认为:以 softirq为主,以ksoftirqd为辅。

如此给你带来的问题也同样困扰了linux内核的开发者们,于是工作队列就出来了...下文太精彩,闪!

接下来又回邮件问:

>> 因此仅在此时它可以被抢占,然而一旦进入do_softirq的话,那么softirq逻辑就又开始在这个被中断的进程的上下文大做文章了,因此不能被抢占,也不能调度,原因和上述硬件中断一样。
这里在do_softirq之前都可以被抢占了..在进入do_softirq后的话,更加可以的啊.