天天看點

對問題“為什麼執行softirq時不能被搶占?”的解答

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後的話,更加可以的啊.

 本文轉自 dog250 51CTO部落格,原文連結:http://blog.51cto.com/dog250/1273331

繼續閱讀