NAPI(中斷後輪詢混合機制)
/ NAH-pee /
一張快速網路卡每秒可遞送數百萬個封包。若驅動程式每個封包接受一個中斷,機器會把全部時間都花在進出中斷處理常式上,並可能在負載下停擺——這是個真實的失敗模式,稱為接收活結(receive livelock)。NAPI 是解決這個問題的巧妙折衷:當封包開始到達時,接受一個中斷,然後關掉網卡的中斷並改用輪詢成批撈起封包,等洪流退去才重新啟用中斷。
具體而言,當第一個封包到達時網卡發出中斷。NAPI 驅動程式在那個處理常式中只做最少的事:它停用網卡上後續的接收中斷,並排定一次輪詢。核心接著呼叫驅動程式的 poll 函式,一口氣從接收環取出至多一個預算量(比如 64 個)的封包,並把它們往上推進堆疊。若正好還有那麼多或更多在等,核心就繼續輪詢;若環被清空到預算以下,驅動程式重新啟用中斷並回去睡。所以在輕度流量下它表現得像中斷驅動式 I/O(一個封包、一個中斷、低延遲),而在重度流量下它表現得像輪詢(沒有中斷、高吞吐、封包以高效批次處理)。
NAPI 之所以重要,是因為它正是 Linux 在高速率網路下存活的方式——閒置時給低延遲、忙碌時給高吞吐,自動切換,不必驅動程式作者永遠選定一種模式。誠實的框架:NAPI 不是魔法,它是兩種經典策略刻意的混合,依負載在它們之間自適應地切換。同樣的「中斷後輪詢」想法如今也出現在網路之外(例如在某些儲存與區塊路徑中),凡是事件到達速度可能快過每事件中斷所能負擔之處皆然。
// 在中斷中:停用 IRQ、排定一次輪詢 disable_rx_irq(dev); napi_schedule(&dev->napi); // poll 函式,由核心呼叫: int my_poll(struct napi_struct *n, int budget) { int done = drain_rx_ring(n, budget); // 取至多 'budget' 個 if (done < budget) { // 環已空 napi_complete(n); enable_rx_irq(dev); // 切回中斷 } return done; }
NAPI:一個中斷停用後續中斷並排定輪詢;輪詢成批處理封包,僅在環清空時才重新武裝中斷。
NAPI 不是一個獨立的魔法模式——它是中斷(閒置時低延遲)與輪詢(忙碌時高吞吐)的自適應混合。它存在的主因是避免封包洪流下的接收活結。