導航:首頁 > 操作系統 > linux中斷irq

linux中斷irq

發布時間:2022-12-17 23:27:48

linux 系統中的中斷是不是沒有中斷優先順序

關於中斷嵌套:在linux內核里,如果驅動在申請注冊中斷的時候沒有特別的指定,do_irq在做中斷響應的時候,是開啟中斷的,如果在驅動的中斷處理函數正在執行的過程中,出現同一設備的中斷或者不同設備的中斷,這時候新的中斷會被立即處理,還是被pending,等當前中斷處理完成後,再做處理。在2.4和2.6內核里,關於這一塊是否有什麼不同。 一般申請中斷的時候都允許開中斷,即不使用SA_INTERRUPT標志。如果允許共享則加上 SA_SHIRQ,如果可以為內核熵池提供熵值(譬如你寫的驅動是ide之類的驅動),則再加上 SA_SAMPLE_RANDOM標志。這是普通的中斷請求過程。對於這種一般情況,只要發生中斷,就可以搶占內核,即使內核正在執行其他中斷函數。這里有兩點說明:一是因為linux不支持 中斷優先順序,因此任何中斷都可以搶占其他中斷,但是同種類型的中斷(即定義使用同一個 中斷線的中斷)不會發生搶占,他們會在執行本類型中斷的時候依次被調用執行。二是所謂 只要發生中斷,就可以搶占內核這句是有一定限制的,因為當中斷發生的時候系統由中斷門 進入時自動關中斷(對於x86平台就是將eflags寄存器的if位置為0),只有當中斷函數被執行 (handle_IRQ_event)的過程中開中斷之後才能有搶占。 對於同種類型的中斷,由於其使用同樣的idt表項,通過其狀態標志(IRQ_PENDING和 IRQ_INPROGRESS)可以防止同種類型的中斷函數執行(注意:是防止handle_IRQ_event被重入, 而不是防止do_IRQ函數被重入),對於不同的中斷,則可以自由的嵌套。因此,所謂中斷嵌套, 對於不同的中斷是可以自由嵌套的,而對於同種類型的中斷,是不可以嵌套執行的。以下簡單解釋一下如何利用狀態標志來防止同種類型中斷的重入:當某種類型的中斷第一次發生時,首先其idt表項的狀態位上被賦予IRQ_PENDING標志,表示有待處理。 然後將中斷處理函數action置為null,然後由於其狀態沒有IRQ_INPROGRESS標志(第一次),故將其狀態置上IRQ_INPROGRESS並去處IRQ_PENDING標志,同時將action賦予相應的中斷處理函數指針(這里是一個重點,linux很巧妙的用法,隨後說明)。這樣,後面就可以順利執行handle_IRQ_event進行中斷處理,當在handle_IRQ_event中開中斷後,如果有同種類型的中斷發生,則再次進入do_IRQ函數,然後其狀態位上加上IRQ_PENDING標志,但是由於前一次中斷處理中加上的IRQ_INPROGRESS沒有被清除,因此這里無法清除IRQ_PENDING標志,因此action還是為null,這樣就無法再次執行handle_IRQ_event函數。從而退出本次中斷處理,返回上一次的中斷處理函數中,即繼續執行handle_IRQ_event函數。當handle_IRQ_event返回時檢查IRQ_PENDING標志,發現存在這個標志,說明handle_IRQ_event執行過程中被中斷過,存在未處理的同類中斷,因此再次循環執行handle_IRQ_event函數。直到不存在IRQ_PENDING標志為止。2.4和2.6的差別,就我來看,主要是在2.6中一進入do_IRQ,多了一個關閉內核搶占的動作,同時在處理中多了一種對IRQ_PER_CPU類型的中斷的處理,其他沒有什麼太大的改變。這類IRQ_PER_CPU的中斷主要用在smp環境下將中斷綁定在某一個指定的cpu上。例如arch/ppc/syslib/open_pic.c中的openpic_init中初始化ipi中斷的時候。 其實簡單的說,中斷可以嵌套,但是同種類型的中斷是不可以嵌套的,因為在IRQ上發生中斷,在中斷響應的過程中,這個IRQ是屏蔽的,也就是這個IRQ的中斷是不能被發現的。 同時在內核的臨界區內,中斷是被禁止的 關於do_IRQ可能會丟失中斷請求:do_IRQ函數是通過在執行完handle_IRQ_event函數之後判斷status是否被設置了IRQ_PENDING標志來判斷是否還有沒有被處理的同一通道的中斷請求。 但是這種方法只能判斷是否有,而不能知道有多少個未處理的統一通道中斷請求。也就是說,假如在第一個中斷請求執行handle_IRQ_event函數的過程中來了同一通道的兩個或更多中斷請求,而這些中斷不會再來,那麼僅僅通過判斷status是否設置了IRQ_PENDING標志不知道到底有多少個未處理的中斷,handle_IRQ_event只會被再執行一次。這算不算是個bug呢? 不算,只要知道有中斷沒有處理就OK了,知道1個和知道N個,本質上都是一樣的。作為外設,應當能夠處理自己中斷未被處理的情況。不可能丟失的,在每一個中斷描述符的結構體內,都有一個鏈表,鏈表中存放著服務常式序關於中斷中使用的幾個重要概念和關系: 一、基本概念 1. 產生的位置 發生的時刻 時序 中斷 CPU外部 隨機 非同步 異常 CPU正在執行的程序 一條指令終止執行後 同步 2.由中斷或異常執行的代碼不是一個進程,而是一個內核控制路徑,代表中斷發生時正在運行的進程的執行 中斷處理程序與正在運行的程序無關 引起異常處理程序的進程正是異常處理程序運行時的當前進程 二、特點 (2)能以嵌套的方式執行,但是同種類型的中斷不可以嵌套 (3)盡可能地限制臨界區,因為在臨界區中,中斷被禁止 2.大部分異常發生在用戶態,缺頁異常是唯一發生於內核態能觸發的異常 缺頁異常意味著進程切換,因此中斷處理程序從不執行可以導致缺頁的操作 3.中斷處理程序運行於內核態 中斷發生於用戶態時,要把進程的用戶空間堆棧切換到進程的系統空間堆棧,剛切換時,內核堆棧是空的 中斷發生於內核態時, 不需要堆棧空間的切換 三、分類 1.中斷的分類:可屏蔽中斷、不可屏蔽中斷 2.異常的分類: 分類 解決異常的方法 舉例 故障 那條指令會被重新執行 缺頁異常處理程序 陷阱 會從下一條指令開始執行 調試程序

② Linux中斷補充

在系統結構中,CPU工作的模式有兩種,一種是中斷,由各種設備發起;一種是輪詢,由CPU主動發起。
中斷IRQ:
中斷允許讓設備(如鍵盤,串口卡,並口等設備)表明它們需要CPU。一旦CPU接收了中斷請求,CPU就會暫時停止執行正在運行的程序,並且調用一個稱為中斷處理器或中斷服務程序(interrupt service routine)的特定程序。CPU處理完中斷後,就會恢復執行之前被中斷的程序。
中斷分類:
硬中斷+軟中斷
硬中斷:
①非屏蔽中斷:不能被屏蔽,硬體發生的錯誤:內存錯誤,風扇故障,溫度感測器故障等。
②可屏蔽中斷:可被CPU忽略或延遲處理。當緩存控制器的外部針腳被觸發的時候就會產生這種類型的中斷,而中斷屏蔽寄存器就會將這樣的中斷屏蔽掉。我們可以將一個比特位設置為0,來禁用在此針腳觸發的中斷。
軟中斷:
是軟體實現的中斷,也就是程序運行時其他程序對它的中斷;而硬中斷是硬體實現的中斷,是程序運行時設備對它的中斷。

CPU之間的中斷處理(IPI)
處理器間中斷允許一個CPU向系統其他的CPU發送中斷信號,處理器間中斷(IPI)不是通過IRQ線傳輸的,而是作為信號直接放在連接所有CPU本地APIC的匯流排上。
CALL_FUNCTION_VECTOR (向量0xfb)

發往所有的CPU,但不包括發送者,強制這些CPU運行發送者傳遞過來的函數,相應的中斷處理程序叫做call_function_interrupt(),例如,地址存放在群居變數call_data中來傳遞的函數,可能強制其他所有的CPU都停止,也可能強制它們設置內存類型範圍寄存器的內容。通常,這種中斷發往所有的CPU,但通過smp_call_function()執行調用函數的CPU除外。

RESCHEDULE_VECTOR (向量0xfc)

當一個CPU接收這種類型的中斷時,相應的處理程序限定自己來應答中斷,當從中斷返回時,所有的重新調度都自動運行。

INVALIDATE_TLB_VECTOR (向量0xfd)

發往所有的CPU,但不包括發送者,強制它們的轉換後援緩沖器TLB變為無效。相應的處理程序刷新處理器的某些TLB表項。

③ 關於linux 軟中斷對網卡性能的影響以及優化

首先,要對軟中斷有一個認識,程序運行後,操作系統會發送程序需要的一些cpu指令到某個cpu,扔給CPU的這個過程是非同步的,cpu獲得指令後操作完成會觸發一個硬中斷,並且把操作的結果保存在寄存器,之後linux內核會啟動ksofttrip進程去,來獲取操作結果,這個動作就叫做軟中斷。
linux默認會起n個ksofttrip進程,n等於cpu的個數,ksofttrip是死循環,只要有軟中斷,它就會一直去獲取,n個ksoftrip獲取源是一樣的,為什麼要起n個進程呢?就是為了 ,當某個cpu空閑,哪個就去跑。通常操作系統里它的進程名是 ksoftrip/n ,n是對應的cpu的編號,ksoft進程跟cpu是一對一綁定的。
現在來說說網卡的性能問題,要想優化,首先你的網卡必須是多通道隊列的。那如何知道你的網卡是否是多隊列的呢? 通過cat /proc/interrept |grep eth0|wc -l 可以看到網卡通道隊列的數量.
現在來來說說優化方案,為什麼要優化,因為linux默認情況所有的網卡的軟中斷都是的cpu0,所以加入你的ksoftrip/0總是跑滿,就說明可能是網卡問題了。

方案1 ,SMP IRQ affinity技術
說白了,就是信號量分布技術,把特定信號量的處理放到固定的cpu上,每個網卡的通道隊列都有一個自己的信號量。
首先查看所有網卡通道隊列的信號量,方法 cat/proc/interrept |grep eth0
每行最開頭的數字「n:」就是信號量,在/proc/irq/下面可以找到對應的以信號量命名的目錄
找完了之後,可以進行信號量綁定了,在/proc/irq/n/下面有兩個文件,分別是smp_affinity跟smp_affinity_list, 這兩個是文件的內容是對應的,smp_affinity里是通過bitmask演算法綁定cpu,smp_affinity_list是通過數字指定cpu編號的方法,例如 cpu0,文件里就是「0」,如果是cpu1跟2就是「1,2」
!!重點來了,雖然默認裡面填寫的是多個,但是!!!但是它只跑在綁定cpu中的第一個!!!坑啊!!!
所以,你要做的就是單獨綁定每一個網卡的通道隊列。
直接echo "1" >/proc/irq/ (cpu1的信號量)/snmp_affinity_list
echo "3" >/proc/irq/$(cpu2的信號量)/snmp_affinity_list

這個是最快速的解決方案,提升效率顯著啊!!!

升級方案2,在方案1基礎之上,RPS/RFS技術
此技術大家可以查網上,文章很多,優化效果是,單個網卡通道隊列的軟中斷會平均到所有cpu上,並且會優化為,中斷落在發出中斷的程序所在的那個cpu上,這樣節省了cpu cache。

壞消息是對單隊列網卡而言,「smp_affinity」和「smp_affinity_list」配置多CPU無效。

好消息是Linux支持RPS,通俗點來說就是在軟體層面模擬實現硬體的多隊列網卡功能。

首先看看如何配置RPS,如果CPU個數是 8 個的話,可以設置成 ff:

shell> echo ff > /sys/class/net/eth0/queues/rx-0/rps_cpus

接著配置內核參數rps_sock_flow_entries(官方文檔推薦設置: 32768):

shell> sysctl net.core.rps_sock_flow_entries=32768

最後配置rps_flow_cnt,單隊列網卡的話設置成rps_sock_flow_entries即可:

echo 32768 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt

說明:如果是多隊列網卡,那麼就按照隊列數量設置成 rps_sock_flow_entries / N 。

④ Linux下通過哪個命令怎麼查看中斷

與Linux設備驅動中中斷處理相關的首先是申請與釋放IRQ的API request_irq()和free_irq()。

C++是一種面向對象的計算機程序設計語言,由美國AT&T貝爾實驗室的本賈尼·斯特勞斯特盧普博士在20世紀80年代初期發明並實現,最初它被稱作「C with Classes」(包含類的C語言)。

它是一種靜態數據類型檢查的、支持多重編程範式的通用程序設計語言,支持過程化程序設計、數據抽象、面向對象程序設計、泛型程序設計等多種程序設計風格。

在C基礎上,一九八三年又由貝爾實驗室的Bjarne Strou-strup推出了C++,C++進一步擴充和完善了C語言,成為一種面向 對象的程序設計語言。

C++目前流行的編譯器最新版本是Borland C++ 4.5,Symantec C++ 6.1,和Microsoft Visual C++ 2012。

⑤ linux進入臨界去開關中斷的幾種方式

進入中斷時候關閉全局的中斷是為了避免程序處理中斷過程中,再進入另一個中斷打亂執行的順序,也就是為了防止中斷嵌套的情況發生。
比如在irq_handler函數中首先就應該關閉中斷。
或者,在某些操作順序中是不允許中斷發生打斷的情況。例如在驅動中常用的方式:

unsigned int flag;
local_irq_save(&flag);
... ... ... ...
local_irq_restore(&flag);

spin_loc_irqsave 禁止中斷(只在本地處理器)在獲得自旋鎖之前;
之前的中斷狀態保存在 flags 里. 如果你絕對確定在你的處理器上沒有禁止中斷的(或者, 換句話說, 你確信你應當在你釋放你的自旋鎖時打開中斷),你可以使用 spin_lock_irq 代替, 並且不必保持跟蹤 flags.
最後, spin_lock_bh 在獲取鎖之前禁止軟體中斷, 但是硬體中斷留作打開的。

⑥ Linux如何及時響應外部中斷

FPGA每隔100us給運行linux的ARM一個中斷,要求在20us內響應中斷,並讀走2000*16bit的數據。
目前主要的問題是,當系統同時發生多個中斷時,會嚴重影響linux對FPGA中斷的響應時間。如何解決?

1、首先想到了ARM的FIQ,它可以打斷IRQ中斷服務程序,保證對外部FIQ的及時響應。但是發現linux只實現了IRQ,沒有顯示FIQ。
linux是從devicetree讀取中斷號,加入中斷向量表的。

interrupts = <0x0 0x32 0x0>;中的第一個欄位0表示非共享中斷,非零表示共享中斷,SDK產生的dts統一為0,此時第二欄位的值比XPS中的小32;如果第一欄位非零,則第二欄位比XPS小16.
最後欄位表示中斷的觸發方式。
IRQ_TYPE_EDGE_RISING =0x00000001,
IRQ_TYPE_EDGE_FALLING =0x00000002,
IRQ_TYPE_LEVEL_HIGH =0x00000004,
IRQ_TYPE_LEVEL_LOW =0x00000008,
很明顯,devicetree根本沒有提供通知linux有FIQ的渠道。
2、再來看linux的IRQ
linux的中斷分為上半部和下半部,上半部運行在IRQ模式,會屏蔽所有中斷,下半部運行在SVC模式,會重新打開中斷。
也就是說,當一個中斷的上半部正在運行時(不能再次響應中斷),FPGA的中斷是不能被linux響應的;
反過來,當FPGA中斷的上半部正在運行時(不能再次響應中斷),其他的中斷也不能被linux響應;
unsigned long flags;
...
local_irq_save(flags);
....

local_irq_restore(flags);

3.
ARM有七種模式,我們這里只討論SVC、IRQ和FIQ模式。
我們可以假設ARM核心有兩根中斷引腳(實際上是看不見的),一根叫 irq pin, 一根叫fiq pin.
在ARM的cpsr中,有一個I位和一個F位,分別用來禁止IRQ和FIQ的。
先不說中斷控制器,只說ARM核心。正常情況下,ARM核都只是機械地隨著pc的指示去做事情,當CPSR中的I和F位為1的時候,IRQ和FIQ全部處於禁止狀態。無論你在irq
pin和fiq pin上面發什麼樣的中斷信號,ARM是不會理你的,你根本不能打斷他,因為他耳聾了,眼也瞎了。
在I位和F位為0的時候,當irq
pin上有中斷信號過來的時候,就會打斷arm的當前工作,並且切換到IRQ模式下,並且跳到相應的異常向量表(vector)位置去執行代碼。這個過程是自動的,但是返回到被中斷打斷的地方就得您親自動手了。當你跳到異常向量表,處於IRQ的模式的時候,這個時候如果irq
pin上面又來中斷信號了,這個時候ARM不會理你的,irq
pin就跟秘書一樣,ARM核心就像老闆,老闆本來在做事,結果來了一個客戶,秘書打斷它,讓客戶進去了。而這個時候再來一個客戶,要麼秘書不斷去敲門問,要麼客戶走人。老闆第一個客戶沒有會見完,是不會理你的。
但是有一種情況例外,當ARM處在IRQ模式,這個時候fiq pin來了一個中斷信號,fiq
pin是什麼?是快速中斷呀,比如是公安局的來查刑事案件,那才不管你老闆是不是在會見客戶,直接打斷,進入到fiq模式下,並且跳到相應的fiq的異常向量表處去執行代碼。那如果當ARM處理FIQ模式,fiq
pin又來中斷信號,又就是又一批公安來了,那沒戲,都是執法人員,你打不斷我。那如果這個時候irq
pin來了呢?來了也不理呀,正在辦案,還敢來妨礙公務。
所以得出一個結論: IRQ模式只能被FIQ模式打斷,FIQ模式下誰也打不斷。
在打不斷的情況下,irq pin 或 fiq pin隨便你怎麼發中斷信號,都是白發。
所以除了fiq能打斷irq以外,根本沒有所謂中斷嵌套的情況。
Linux不用FIQ,只用到了IRQ。但是我們有時候一個中斷需要處理很長時間,那我們就需要佔用IRQ模式那麼長的時間嗎?沒有,linux在IRQ模式下只是簡單的記錄是什麼中斷,馬上就切換回了SVC模式,換句話說,Linux的中斷處理都是在SVC模式下處理的。
只不過SVC模式下的ISR上半部關閉了當前中斷線,下半部才重新打開

⑦ Linux內核中斷之獲取中斷號

Linux內核中可使用 platform_get_irq() 函數獲取 dts 文件中設置的中斷號。

函數原型: int platform_get_irq(struct platform_device *dev, unsigned int num)

定義文件: driversaseplatform.c

中斷號獲取函數 platform_get_irq() 調用流程如下:

rk3399 使用的是 GICv3 ,對應 irq_domain->name 。

文件: drivers/irqchip/irq-gic-v3.c 。

translate() 函數實現如下:

以 RockPI 4A 單板 Debian 系統Linux 4.4內核中的獲取 HDMI 中斷號為例。

1、查找中斷號

從手冊「Rockchip RK3399 TRM V1.3 Part1.pdf」中,可以查到 HDMI_IRQ 中斷號,即55。

2、 dts 配置

文件: arch/arm64/boot/dts/rockchip/rk3399.dtsi

hdmi 使用的是 GIC_SPI 中斷,按照 gic_irq_domain_translate() 函數中處理,需要將中斷號55減去32,得到 dts 中的中斷號23。

註: interrupts = <中斷類型 中斷號 中斷觸發類型 中斷分區(對應哪個CPU cluster,PPI類型中斷特有)>

3、驅動函數

文件: driversgpudrm ockchipdw_hdmi-rockchip.c

此時, irq 返回值為55。

後續會介紹 GIC 和中斷注冊等實現函數。

⑧ 《Linux設備驅動程序》(十六)-中斷處理

設備與處理器之間的工作通常來說是非同步,設備數據要傳遞給處理器通常來說有以下幾種方法:輪詢、等待和中斷。

讓CPU進行輪詢等待總是不能讓人滿意,所以通常都採用中斷的形式,讓設備來通知CPU讀取數據。

2.6內核的函數參數與現在的參數有所區別,這里都主要介紹概念,具體實現方法需要結合具體的內核版本。

request_irq函數申請中斷,返回0表示申請成功,其他返回值表示申請失敗,其具體參數解釋如下:

flags 掩碼可以使用以下幾個:

快速和慢速處理常式 :現代內核中基本沒有這兩個概念了,使用SA_INTERRUPT位後,當中斷被執行時,當前處理器的其他中斷都將被禁止。通常不要使用SA_INTERRUPT標志位,除非自己明確知道會發生什麼。

共享中斷 :使用共享中斷時,一方面要使用SA_SHIRQ位,另一個是request_irq中的dev_id必須是唯一的,不能為NULL。這個限制的原因是:內核為每個中斷維護了一個共享處理常式的列表,常式中的dev_id各不相同,就像設備簽名。如果dev_id相同,在卸載的時候引起混淆(卸載了另一個中斷),當中斷到達時會產生內核OOP消息。

共享中斷需要滿足以下一個條件才能申請成功:

當不需要使用該中斷時,需要使用free_irq釋放中斷。

通常我們會在模塊載入的時候申請安裝中斷處理常式,但書中建議:在設備第一次打開的時候安裝,在設備最後一次關閉的時候卸載。

如果要查看中斷觸發的次數,可以查看 /proc/interrupts 和 /proc/stat。

書中講述了如何自動檢測中斷號,在嵌入式開發中通常都是查看原理圖和datasheet來直接確定。

自動檢測的原理如下:驅動程序通知設備產生中斷,然後查看哪些中斷信號線被觸發了。Linux提供了以下方法來進行探測:

探測工作耗時較長,建議在模塊載入的時候做。

中斷處理函數和普通函數其實差不多,唯一的區別是其運行的中斷上下文中,在這個上下文中有以下注意事項:

中斷處理函數典型用法如下:

中斷處理函數的參數和返回值含義如下:

返回值主要有兩個:IRQ_NONE和IRQ_HANDLED。

對於中斷我們是可以進行開啟和關閉的,Linux中提供了以下函數操作單個中斷的開關:

該方法可以在所有處理器上禁止或啟用中斷。

需要注意的是:

如果要關閉當前處理器上所有的中斷,則可以調用以下方法:

local_irq_save 會將中斷狀態保持到flags中,然後禁用處理器上的中斷;如果明確知道中斷沒有在其他地方被禁用,則可以使用local_irq_disable,否則請使用local_irq_save。

locat_irq_restore 會根據上面獲取到flags來恢復中斷;local_irq_enable 會無條件打開所有中斷。

在中斷中需要做一些工作,如果工作內容太多,必然導致中斷處理所需的時間過長;而中斷處理又要求能夠盡快完成,這樣才不會影響正常的系統調度,這兩個之間就產生了矛盾。

現在很多操作系統將中斷分為兩個部分來處理上面的矛盾:頂半部和底半部。

頂半部就是我們用request_irq來注冊的中斷處理函數,這個函數要求能夠盡快結束,同時在其中調度底半部,讓底半部在之後來進行後續的耗時工作。

頂半部就不再說明了,就是上面的中斷處理函數,只是要求能夠盡快處理完成並返回,不要處理耗時工作。

底半部通常使用tasklet或者工作隊列來實現。

tasklet的特點和注意事項:

工作隊列的特點和注意事項:

⑨ 5.2 Linux中斷注冊

注冊中斷最常用的函數是request_irq

第 1個參數 irq 為中斷號
第 2 個參數 handler 為要中斷服務函數
第 3 個參數 flags為中斷標志位包含觸發方式,是否共享,是否支持嵌套等
第 4 個參數 name,通常是 設備驅動程序的名稱。該值用在 /proc/interrupt 系統文件上
第 5 個參數 dev 中斷名稱 可作為共享中斷時的中斷區別參數,也可以用來指定中斷服務函數需要參考的數據地址。建議將 設備結構指針作為 dev參數

flags參數定義

注冊中斷的另一個函數是request_threaded_irq
request_threaded_irq是將中斷處理函數線程化執行的介面,其實request_irq也是直接調用的request_threaded_irq,只不過線程化回調thread_fn設置為NULL,不進行中斷處理程序線程化處理。

和request_irq的參數有少許差異
handler:表示中斷服務常式,指向primary handler 和request_irq的中斷處理函數handler類似。中斷發生時優先執行primary handler;
如果primary handler 為NULL,且thread_fn不為NULL,那麼執行默認primary handler = irq_default_primary_handler。
thread_fn:中斷線程化,NULL表示沒有中斷線程化。thread_fn如果該參數不為NULL,內核會為該irq創建一個內核線程,
當中斷發生時,如果handler回調返回值是IRQ_WAKE_THREAD,內核將會激活中斷線程,
在中斷線程中,該回調函數將被調用,所以,該回調函數運行在進程上下文中,允許進行阻塞操作。

其中

其中

⑩ linux驅動中斷,程序運行幾個小時後系統崩潰

中斷與定時器:
中斷的概念:指CPU在執行過程中,出現某些突發事件急待處理,CPU暫停執行當前程序,轉去處理突發事件
,處理完後CPU又返回原程序被中斷的位置繼續執行
中斷的分類:內部中斷和外部中斷
內部中斷:中斷源來自CPU內部(軟體中斷指令、溢出、觸發錯誤等)
外部中斷:中斷源來自CPU外部,由外設提出請求

屏蔽中斷和不可屏蔽中斷:
可屏蔽中斷:可以通過屏蔽字被屏蔽,屏蔽後,該中斷不再得到響應
不可平布中斷:不能被屏蔽

向量中斷和非向量中斷:
向量中斷:CPU通常為不同的中斷分配不同的中斷號,當檢測到某中斷號的中斷到來後,就自動跳轉到與該中斷號對應的地址執行
非向量中斷:多個中斷共享一個入口地址。進入該入口地址後再通過軟體判斷中斷標志來識別具體哪個是中斷
也就是說向量中斷由軟體提供中斷服務程序入口地址,非向量中斷由軟體提供中斷入口地址

/*典型的非向量中斷首先會判斷中斷源,然後調用不同中斷源的中斷處理程序*/
irq_handler()
{
...
int int_src = read_int_status();/*讀硬體的中斷相關寄存器*/
switch(int_src){//判斷中斷標志
case DEV_A:
dev_a_handler();
break;
case DEV_B:
dev_b_handler();
break;
...
default:
break;
}
...
}

定時器中斷原理:
定時器在硬體上也以來中斷,PIT(可編程間隔定時器)接收一個時鍾輸入,
當時鍾脈沖到來時,將目前計數值增1並與已經設置的計數值比較,若相等,證明計數周期滿,產生定時器中斷,並
復位計數值。

如下圖所示:

Linux中斷處理程序架構:
Linux將中斷分為:頂半部(top half)和底半部(bottom half)
頂板部:完成盡可能少的比較緊急的功能,它往往只是簡單的讀取寄存器中的中斷狀態並清除中斷標志後就進行
「登記中斷」(也就是將底半部處理程序掛在到設備的底半部執行隊列中)的工作
特點:響應速度快

底半部:中斷處理的大部分工作都在底半部,它幾乎做了中斷處理程序的所有事情。
特點:處理相對來說不是非常緊急的事件

小知識:Linux中查看/proc/interrupts文件可以獲得系統中斷的統計信息。

如下圖所示:

第一列是中斷號 第二列是向CPU產生該中斷的次數

介紹完相關基礎概念後,讓我們一起來探討一下Linux中斷編程

Linux中斷編程:
1.申請和釋放中斷
申請中斷:
int request_irq(unsigned int irq,irq_handler_t handler,
unsigned long irqflags,const char *devname,void *dev_id)
參數介紹:irq是要申請的硬體中斷號
handler是向系統登記的中斷處理程序(頂半部),是一個回調函數,中斷發生時,系統調用它,將
dev_id參數傳遞給它
irqflags:是中斷處理的屬性,可以指定中斷的觸發方式和處理方式:
觸發方式:IRQF_TRIGGER_RISING、IRQF_TRIGGER_FALLING、IRQF_TRIGGER_HIGH、IRQF_TRIGGER_LOW
處理方式:IRQF_DISABLE表明中斷處理程序是快速處理程序,快速處理程序被調用時屏蔽所有中斷
IRQF_SHARED表示多個設備共享中斷,dev_id在中斷共享時會用到,一般設置為NULL

返回值:為0表示成功,返回-EINVAL表示中斷號無效,返回-EBUSY表示中斷已經被佔用,且不能共享
頂半部的handler的類型irq_handler_t定義為
typedef irqreturn_t (*irq_handler_t)(int,void*);
typedef int irqreturn_t;

2.釋放IRQ
有請求當然就有釋放了
void free_irq(unsigned int irq,void *dev_id);
參數定義與request_irq類似

3.使能和屏蔽中斷
void disable_irq(int irq);//等待目前中斷處理完成(最好別在頂板部使用,你懂得)
void disable_irq_nosync(int irq);//立即返回
void enable_irq(int irq);//

4.屏蔽本CPU內所有中斷:
#define local_irq_save(flags)...//禁止中斷並保存狀態
void local_irq_disable(void);//禁止中斷,不保存狀態

下面來分別介紹一下頂半部和底半部的實現機制

底半部機制:
簡介:底半部機制主要有tasklet、工作隊列和軟中斷
1.底半部是想方法之一tasklet
(1)我們需要定義tasklet機器處理器並將兩者關聯
例如:
void my_tasklet_func(unsigned long);/*定義一個處理函數*/
DECLARE_TASKLET(my_tasklet,my_tasklet_func,data);
/*上述代碼定義了名為my_tasklet的tasklet並將其餘
my_tasklet_func()函數綁定,傳入的參數為data*/
(2)調度
tasklet_schele(&my_tasklet);
//使用此函數就能在是當的時候進行調度運行

tasklet使用模板:
/*定義tasklet和底半部函數並關聯*/
void xxx_do_tasklet(unsigned long);
DECLARE_TASKLET(xxx_tasklet,xxx_do_tasklet,0);

/*中斷處理底半部*/
void xxx_do_tasklet(unsigned long)
{
...
}

/*中斷處理頂半部*/
irqreturn_t xxx_interrupt(int irq,void *dev_id)
{
...
tasklet_schele(&xxx_tasklet);//調度地板部
...
}

/*設備驅動模塊載入函數*/
int __init xxx_init(void)
{
...
/*申請中斷*/
result = request_irq(xxx_irq,xxx_interrupt,
IRQF_DISABLED,"xxx",NULL);
...

return IRQ_HANDLED;
}

/*設備驅動模塊卸載函數*/
void __exit xxx_exit(void)
{
...
/*釋放中斷*/
free_irq(xxx_irq,xxx_interrupt);
...
}

2.底半部實現方法之二---工作隊列
使用方法和tasklet類似
相關操作:
struct work_struct my_wq;/*定義一個工作隊列*/
void my_wq_func(unsigned long);/*定義一個處理函數*/
通過INIT_WORK()可以初始化這個工作隊列並將工作隊列與處理函數綁定
INIT_WORK(&my_wq,(void (*)(void *))my_wq_func,NULL);
/*初始化工作隊列並將其與處理函數綁定*/
schele_work(&my_wq);/*調度工作隊列執行*/

/*工作隊列使用模板*/

/*定義工作隊列和關聯函數*/
struct work_struct(unsigned long);
void xxx_do_work(unsigned long);

/*中斷處理底半部*/
void xxx_do_work(unsigned long)
{
...
}

/*中斷處理頂半部*/
/*中斷處理頂半部*/
irqreturn_t xxx_interrupt(int irq,void *dev_id)
{
...
schele_work(&my_wq);//調度底半部
...
return IRQ_HANDLED;
}

/*設備驅動模塊載入函數*/
int xxx_init(void)
{
...
/*申請中斷*/
result = request_irq(xxx_irq,xxx_interrupt,
IRQF_DISABLED,"xxx",NULL);
...
/*初始化工作隊列*/
INIT_WORK(&my_wq,(void (*)(void *))xxx_do_work,NULL);
}

/*設備驅動模塊卸載函數*/
void xxx_exit(void)
{
...
/*釋放中斷*/
free_irq(xxx_irq,xxx_interrupt);
...
}

閱讀全文

與linux中斷irq相關的資料

熱點內容
扣扣加密技巧 瀏覽:720
蘋果如何創建伺服器錯誤 瀏覽:495
軟考初級程序員大題分值 瀏覽:473
js壓縮視頻文件 瀏覽:578
linux如何通過命令創建文件 瀏覽:989
應用加密app還能訪問應用嘛 瀏覽:433
安卓怎麼用支付寶交違章罰款 瀏覽:665
php面向對象的程序設計 瀏覽:504
數據挖掘演算法書籍推薦 瀏覽:894
投訴聯通用什麼app 瀏覽:150
web伺服器變更ip地址 瀏覽:954
java正則表達式驗證郵箱 瀏覽:360
成熟商務男裝下載什麼軟體app 瀏覽:609
加密2h代表長度是多少厘米 瀏覽:23
拍賣程序員 瀏覽:101
電腦的圖片放在哪個文件夾 瀏覽:276
unsignedintjava 瀏覽:217
編譯器下載地址 瀏覽:43
什麼是面對對象編程 瀏覽:708
b站伺服器什麼時候恢復 瀏覽:721