现在的位置: 首页 > 综合 > 正文

对问题“为什么执行softirq时不能被抢占?”的解答

2013年09月29日 ⁄ 综合 ⁄ 共 2708字 ⁄ 字号 评论关闭

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后的话,更加可以的啊.  为什么进入do_softirq时要禁止呢?
>> 5.注意,softirq的执行函数不能主动睡眠,但是在softirqd中却是可以被抢占的,睡眠和抢占是两个概念,前者主动后者被动。而且不能睡眠是在softirq的处理函数中,可以抢占是在softirq外,在执行过程中还是不能抢占的。
用softirqd内核线程处理softirq时,完全是进程的上下文了.  为什么还不能主动睡眠,以及在执行过程中就抢占呢?

我的回答:

这里在do_softirq之前都可以被抢占了..在进入do_softirq后的话,更加可以的啊.  为什么进入do_softirq时要禁止呢?
答: 进入do_softirq之前是内核的控制机制,有确定的行为,但是一旦进入你自己的softirq处理函数,那里面怎么执行就只有你自己知道了,比如你 抢了一把锁,然后做事,内核此时根本不知道你做了些什么,为了不使事情混乱不可收拾,比如你在占有锁时被抢占了就会有问题,于是内核约定不允许抢占 softirq。
用softirqd内核线程处理softirq时,完全是进程的上下文了.  为什么还不能主动睡眠,以及在执行过程中就抢占呢?
答:是进程上下文了,但是softirq并不是都在拥有进程上下文的softirqd中执行,更多的要在硬件中断后紧接着执行,那时它就是任意上下文了,引入softirqd的目的仅仅是不想让softirq过多的占据任意上下文,这其实是最大约束原则。
其实不要把softirq想得太神秘,它其实和硬件中断机制在代码上是统一的。

然后又问:

谢谢...
第二问题是基本明白了..也猜到了.呵呵.
第一个问题,在加锁时,spin_lock()其实会禁止抢占的.
我不想深究了.就理解成内核的约定了.为了尽快的返回被中断的进程.

我的回答:

呵呵,深究起来确实会没有完的,比如加锁就一定禁用抢占吗,要加的不是自旋锁呢?呵呵,不说了。其实理解linux的最好方法就是调试而不是深入 代码细节,我以前初涉内核的时候就总进入一些死胡同,比如在一个地方spin_lock,然后我就为找不到unspin_lock而茶饭不思,后来才发现 是别的文件里面unspin_lock了,呵呵。在调试内核前一定要先了解内核的架构,其实linux内核设计得相当好的。我在张江,有时间一定交流交流,呵呵...

抱歉!评论已关闭.