撰寫過 Linux driver 的程式設計師基本上都知道在實作一個硬體的 interrupt service routine (HW ISR) 時,有數個要遵守的規則,其中最重要的一點就是:不可在 HW ISR 中呼叫任何可能引起阻擋的function。
但是為什麼有這條規則呢?原因很簡單,因為 HW interrupt 何時觸發的時間點是未定的,當觸發時會在當前的 kernel stack 上執行函式,所以當 context switch 發生時,就有可能造成回不去了的悲劇,如下圖所示:
但是為什麼有這條規則呢?原因很簡單,因為 HW interrupt 何時觸發的時間點是未定的,當觸發時會在當前的 kernel stack 上執行函式,所以當 context switch 發生時,就有可能造成回不去了的悲劇,如下圖所示:
如果要從在 task Y 時發生的 ISR j 回去 ISR i ,勢必得獲得 task X 的 kernel stack ,但除非在 IRQ j 發生時額外紀錄是從哪個 task 過來的才行,也就是說得在執行 iret 的同時完成一次 context switch,這件事即使可能在硬體平台上以奇技淫巧作到,也是有問題的。因為 CPU 賦與 HW ISR 隨時可岔斷其他控制路徑的權利,其實也同時希望 HW ISR 盡可能地能夠快速返回以免耽誤原本的執行,所以若允許在 nested IRQ 發生時還進行 context switch ,那麼 HW IRQ 幾乎不可能即時完成任何任務並返回最原先的工作。
但是為何一樣是觸發 ISR,page fault 與 software interrupt 就允許可被 context switch呢?因為觸發這兩件事的時機點非常明確:只會在特定的 task 執行時發生,並且在利用到該 task 的 kernel stack 時,絕不會再次發生(若再次發生,要麼是 kernel 有 bug ,不然就是 user 傳遞了有問題的參數所引起的,後者可以修正,請參考 Understanding the Linux Kernel 的 10.4.3)。所以就可以在回復 task context 後,循著同一個 stack 的路徑返回 user mode。
結論就是,千萬不要在 HW ISR 中呼叫任何可能造成阻擋的function。 :-)
同理,在所有的interrupt context(tasklet, softirq...),其實都有這項限制,理由也都與此篇文章一樣。
回覆刪除