1. 程式人生 > 其它 >Linux中斷原理、上半部和下半部、硬中斷和軟中斷

Linux中斷原理、上半部和下半部、硬中斷和軟中斷

目錄

1、中斷簡介

1.1 作用

1.2 物理實現

1.3 中斷請求線IRQ

1.4 異常

2、中斷處理程式

2.1 作用

2.2 上半部和下半部

2.3 中斷上下文

3、中斷系統

3.1 中斷機制的實現

3.2 中斷控制

4、下半部和軟中斷

4.1 下半部簡介

4.2 軟中斷

4.3 tasklet

4.4 工作佇列
1、中斷簡介
1.1 作用

中斷機制,是作業系統用來實現處理器和外部裝置協同工作的方案,讓硬體在需要的時間主動向核心發出訊號。
1.2 物理實現

中斷是一種特殊的電訊號,由硬體生成。中斷生成和處理的主要流程:

硬體生成中斷電訊號,並直接送入中斷控制器的輸入引腳;
中斷控制器接收到一箇中斷訊號後,向處理器傳送一個電訊號;
處理器檢測到這個電訊號後,會立即中斷自己當前的工作,並處理這個中斷(向作業系統反映此訊號的到來);
作業系統處理這個中斷請求

硬體裝置在產生中斷的時候,並不會考慮與處理器的時鐘同步問題,也就是說,中斷會隨時產生,核心隨時可能因為新到來的中斷而被打斷。
1.3 中斷請求線IRQ

中斷請求線指的是中斷的唯一數字標誌。不同裝置對應的中斷不同,作業系統通過IRQ,來區分中斷的來源是什麼硬體裝置,以提供相應的中斷處理程式。

IRQ的值是和硬體相關的。在經典的PC機上,IRQ 0是時鐘中斷,IRQ 1是鍵盤中斷;而在PCI總線上的裝置,中斷的IRQ則是動態分配的。其他非PC的體系結構裡,也有動態分配可用動態的特性。
1.4 異常

異常也被稱為同步中斷,因為異常產生的時候,必須考慮和處理器時鐘同步的問題。

在處理器執行到程式設計失誤導致的錯誤之類(如被0除),或者執行期間出現特殊情況(如缺頁),必須靠核心來處理的時候,處理器就會產生一個異常。所以,很多處理器體系結構處理異常和處理中斷的方式非常類似。
2、中斷處理程式
2.1 作用

在響應一個特定中斷的時候,核心會執行一個函式:中斷處理程式,或者叫中斷服務例程(ISR,Interrupt Service Routine)。中斷處理程式,是一個裝置的驅動的一部分,一方面負責通知硬體裝置中斷已經被接收,另外還有其他大量和裝置相關的工作。例如對網絡卡的中斷,還需要把來自網路的資料包拷貝到記憶體,並處理然後再交給合適的協議棧和應用程式。
2.2 上半部和下半部

由於中斷隨時可能發生,所以必須保證中斷處理程式可以快速執行;但是中斷處理程式可能又會處理大量的任務,兩者之間存在矛盾,所以一般會把中斷處理的過程分成兩部分:上半部和下半部。

上半部也叫硬中斷,是通常意義上的中斷處理程式,用來接收中斷,和簡單的、有時限的處理工作,例如對中斷接收後進行應答或者復位硬體等需要在所有中斷被禁止的情況下完成的工作。而其他的允許稍後完成的工作,則會推遲到下半部在合適的時間完成。Linux有多種機制來實現下半部,其中一種就是軟中斷。

對於網絡卡的中斷處理,上半部會執行 通知硬體、拷貝網路資料報到記憶體並繼續讀取新資料包,這些重要、緊急且與硬體相關的工作,因為網絡卡接收的網路資料包的快取大小通常是固定的、有限的,一旦被延遲可能造成快取溢位。而資料包的處理等操作,則由下半部來完成。
2.3 中斷上下文

中斷處理程式執行在中斷上下文中。中斷上下文也被稱為原子上下文,因為該上下文中執行的程式碼是不可阻塞的。

中斷上下文不可以睡眠,所以中斷上下文中的程式碼中,不允許使用睡眠函式。

中斷上下文的執行,是打斷了其他程式碼,甚至是其他中斷線上的中斷處理程式的執行,所以是有嚴格的時間限制的,所以中斷上下文中的程式碼必須簡潔迅速。儘量把工作分離出來放到下半部執行,因為下半部可以在更合適的實現來執行。

在Linux 2.6版本中,核心為每個中斷處理程式分部增加了一箇中斷棧,大小為1頁(32位系統是4KB,64位系統是8KB)。
3、中斷系統
3.1 中斷機制的實現

裝置產生中斷,通過匯流排將電訊號傳送給中斷控制器;
如果中斷線是啟用的(中斷線允許被遮蔽),中斷控制器將中斷訊號傳送給處理器。在大多數體系結構裡,這一步是通過電訊號給處理器的特定管腳傳送一個訊號;
處理器如果沒有禁止這個中斷,則立即停止正在進行的事情,關閉中斷系統,並跳轉到記憶體中預定義的位置,執行那裡的程式碼。這個預定義的位置,是核心設定的,中斷程式的入口。對於每條中斷線,核心都會跳轉到一個唯一的位置,核心以此獲取中斷的IRQ號;
核心首先在棧中儲存中斷的IRQ號,並儲存暫存器的值,這些值是被中斷任務的;
核心呼叫do_IRQ()方法響應中斷:
對接收的中斷進行應答,並禁止這條線的中斷傳遞;
do_IRQ()檢查這條中斷線是否有一個有效的處理程式,而且這條程式已經啟動,但是當前沒有執行;
如果符合條件,do_IRQ()呼叫handle_IRQ_event()執行這條中斷線的中斷處理程式;
如果不符合條件,或者handle_IRQ_event()執行完成後,do_IRQ()會呼叫ret_from_intr()方法,結束中斷過程,返回中斷前執行的使用者地址空間。

3.2 中斷控制

Linux核心提供了一套用來操作中斷的系統呼叫,可以用來遮蔽處理器的中斷,或者遮蔽掉一部分、甚至某一條中斷線的中斷。這些可以在<asm/system.h>和<asm/irq.h>中找到。

Linux之所以要實現中斷控制系統,主要是因為處理器需要同步。通過禁止中斷,可以防止某個或者某些中斷處理程式不會搶佔當前核心執行的程式碼;此外,禁止中斷還可以禁止核心搶佔。但是禁止中斷和禁止核心搶佔,並不能防止其他處理器併發訪問。想要防止其他處理器的併發訪問,需要Linux的鎖保護機制來實現。

1、禁止和啟用全部中斷

linux核心提供了介面,用於禁止或啟用全部中斷線的中斷請求:

local_irq_disable():禁止全部中斷線的中斷請求;
local_irq_enable():啟用全部中斷線的中斷請求。

這兩個介面會有一些問題:它們會無條件的禁止和啟用所有的中斷線。如果有部分中斷線本來就是被禁止的,local_irq_enable()也會將它們恢復。在很多場景啟用所有的中斷線可能會帶來一些問題。

2、禁止中斷和恢復

由於禁止和啟用全部中斷會帶來一些問題,Linux核心提供了禁止中斷並恢復禁止前狀態的呼叫:

unsigned long flags:值傳遞的一些引數
local_irq_save(flags):儲存本地中斷的當前狀態,然後禁止這些中斷線的中斷請求;
local_irq_restore(flags):恢復本地中斷控制狀態到儲存的狀態。

3、禁止指定中斷線的中斷

一些情況下,作業系統只需要禁止某條中斷線的中斷,即遮蔽這條中斷線。例如在執行某條中斷線的中斷處理程式時,一般會遮蔽這條中斷線。

Linux提供了相應的系統呼叫:

disable_irq(unsigned long irq):在當前中斷線的中斷處理程式執行完成後,遮蔽這條中斷線;
disable_irq_nosync(unsigned long irq):立即遮蔽這條中斷線;
enable_irq(unsigned long irq):啟用某條中斷線;
synchronize_irq(unsigned long irq):等待某條中斷線的中斷處理程式退出,如果該處理程式正在執行,則處理程式退出後方法才會返回。

4、下半部和軟中斷

根據上面的內容可知:

中斷處理過程的上半部就是硬中斷,主要負責處理中斷過程中和硬體相關的、對時間敏感的操作;
下半部主要用來處理一些比較耗時的操作,linux有很多中實現方式,包括tasklet、軟中斷、工作佇列。即軟中斷是下半部的一種實現方式。

4.1 下半部簡介

1、為什麼要使用下半部

如果所有任務都放到上半部執行,會導致處理器較長時間不能處理一部分甚至全部中斷:當一箇中斷正在處理的時候,這個中斷線在所有處理器上都會被遮蔽;甚至如果一個處理程式是IRQF_DISABLED型別的,它在執行的過程中會遮蔽所有中斷
中斷處理器不在程序上下文中執行,所以它們不能被阻塞,這在很多場景會有限制;
中斷處理程式是非同步執行的。

所以應該儘量縮短中斷處理程式的執行時間。這樣,我們會將和硬體不相干的、對時間不太敏感的操作,儘量放到下半部執行

2、什麼任務會放到下半部

Linux作業系統目前沒有辦法強制要求哪些任務放到下半部,目前完全取決於開發者自己去判斷。但是有一些規則提供借鑑:

如果一個任務,對時間非常敏感,則應該放到上半部;
如果一個任務,和硬體相關,則應該放到上半部;
如果一個任務,要求不被其他任務打斷,尤其是不被同類型的中斷打斷,則應該放到上半部;
其他的任務都應該放到下半部。

3、下半部的實現方式

對於現在的Linux 2.6來說,主要有三種方式實現下半部:

軟中斷:有些地方會混淆軟中斷和下半部的概念,實際上軟中斷是下半部的實現方式之一。
tasklet:tasklet實際上是基於軟中斷實現的,是在效能和易用性之間尋求平衡的產物。對於大部分軟中斷,使用tasklet即可。
工作佇列:和上面兩種不同,工作佇列可以把任務退後,交給核心執行緒去執行。工作佇列是工作在程序上下文中的,用於程序的各種特性,比如睡眠等。

4.2 軟中斷

軟中斷是編譯期間動態分配的,不像tasklet一樣可以動態的註冊和銷燬。軟中斷最多隻有32個軟中斷,大部分下半部都是通過tasklet來實現的,目前只用了9個左右。

軟中斷執行在軟中斷上下文中,軟中斷的執行會搶佔程序上下文。軟中斷執行過程中,不允許重新排程和睡眠。

1、資源搶佔

軟中斷不會被另一個軟中斷打斷,只有硬體的中斷處理程式才能打斷軟中斷;
另一個軟中斷可以在其他處理器上執行;
同一種軟中斷,也可以在另一個處理器上執行

2、什麼時候應該使用軟中斷實現下半部

軟中斷應該保留給系統中對時間要求最嚴格的下半部使用。目前只有兩個子系統直接使用軟中斷:網路和SCSI。此外,核心定時器和tasklet也是基於軟中斷實現的。由於同一個軟中斷可以在不同處理器上同時執行,所以軟中斷要求對共享資源的處理要非常嚴格,鎖保護的要求很高。

3、軟中斷的觸發和執行

一個註冊的軟中斷被標記後才會被執行,這被稱為軟中斷的觸發。通常中斷處理程式會在返回前標記相應的軟中斷,使其稍後會被執行。

待處理的軟中斷,被檢查和執行的時機包括:

從一個硬體中斷程式碼處返回;
在ksoftirqd核心執行緒中;
在顯式檢查和執行待處理的軟中斷的程式碼中,如網路子系統中。

4.3 tasklet

tasklet是基於軟中斷實現的一種下半部機制,所以也是執行在軟中斷上下文的。

相比於直接使用軟中斷,tasklet的介面更簡單,鎖保護的要求也更低。tasklet可以靜態定義,也可以動態初始化。

tasklet由兩類軟中斷代表:HI_SOFTIRQ和TASKLET_SOFTIRQ。這兩者唯一的區別在於,HI_SOFTIRQ型別的軟中斷先於TASKLET_SOFTIRQ型別的軟中斷執行。

1、資源搶佔

tasklet不會被其他tasklet打斷,只有硬體的中斷處理程式才能打斷tasklet;
如果一個tasklet正在一個處理器上執行,則這個tasklet不允許在本處理器和其他處理器上再次觸發;
不同的tasklet可以同時在不同的處理器上執行。

4.4 工作佇列

工作佇列可以把任務推後,交由一個核心執行緒來完成。工作佇列的任務是執行在程序上下文中的,這一點和軟中斷、tasklet不同,工作佇列可以使用程序的特性,最重要的是可以重新排程和睡眠。

一般來說,如果任務需要睡眠或者重新排程,就需要使用工作佇列;但是如果不需要,一般使用tasklet來實現。使用工作佇列的場景一般包括:需要獲取大量記憶體的場景、需要獲取訊號量或者鎖的場景、需要阻塞式的執行IO的場景等。

理論上可以用建立核心執行緒的方式來代替工作佇列,但是由於隨便建立核心執行緒會帶來其他問題,所以實際上並不建議直接建立核心執行緒,而應使用工作佇列的形式。

原文連結:https://blog.csdn.net/weixin_38569499/article/details/113919471

作者:柒月 出處:https://www.cnblogs.com/Ph-one/ 開源:https://github.com/iqiy/ 站點:https://qiy.net/ Q群 :2122210(嵌入式/機器學習)