天天看點

關于linux中斷的一些記述

1. linux不支援中斷優先級

2. linux預設不支援中斷嵌套,老版本支援嵌套(不同類型中斷可以嵌套,但是同類型中斷需要依次處理),參考 Linux的中斷可以嵌套嗎?

老版本中斷處理流程:

中斷發生------> (此時中斷關閉)中斷門 ------> ... ------>handle_IRQ_event (中斷打開(可選))------> irqaction(執行驅動注冊的中斷處理函數)

新版本中去除了handle_IRQ_event後打開中斷的操作,可以防止嵌套過多導緻棧溢出.

3. 中斷産生到處理(不同架構上可能有所不同x86和mips上應該是這樣吧)

  • 裝置産生中斷,設定中斷标志位(pending位),并向中斷控制器發送中斷請求;
  • 中斷控制器(可以全局屏蔽中斷請求,也可以單獨屏蔽某一請求)将通過中斷控制器到cpu的中斷線通知cpu有中斷,然後通過總線告知cpu此次中斷的中斷号;
  • cpu硬體修改CPSR(Current Program Status Register)寄存器,切換工作模式為IRQ模式;儲存CPSR值(上一步修改模式之前的值)和PC值 ;通過寫寄存器位"CPSR.I = 1",禁用本地cpu中斷;設定PC值為IRQ exception vector,ARM處理器就會跳轉到IRQ的exception vector位址.(參考 Linux kernel的中斷子系統之(六):ARM中斷處理過程)
  • 之後軟體處理(balabala...),根據中斷号跳轉到相應的中斷服務程式(這邊核心要做一些工作,balabala...);
  • 對于幾個裝置共享的中斷,依次執行irqaction連結清單上驅動注冊的中斷處理函數
  • 由于是共享中斷不清楚到底是哪個裝置産生的,是以每個中斷處理函數需要判斷是不是自己裝置産生的中斷,方法是查詢裝置的中斷标志位(pending位)
  • 如果是自己裝置産生的中斷,那麼就清除中斷标志位(目的是使裝置能夠繼續工作産生下一次中斷),接着執行服務.

4. HW interrupt id到irq的映射

(圖檔出處 Linux kernel的中斷子系統之(一):綜述)

關于linux中斷的一些記述

從硬體層面上講,中斷由三個部分參與:

  • 中斷的制造者(中斷源):能夠産生中斷的裝置
  • 中斷的管理者:中斷控制器(interrupt controller)
  • 中斷的處理者:cpu

能夠産生中斷的裝置的interrupt request line連接配接到某一interrupt controller上;interrupt controller對于它管理的裝置中斷請求進行屏蔽,優先級等的管理,然後将中斷請求發送給上級interrupt controller或者cpu.

為了能夠處理盡可能多的外部中斷,可以增加interrupt controllers的數量來實作,Interrupt controllers通過級聯的方式實作擴充,其中root interrupt controller負責轉發其它interrupt controller的interrupt requset給cpu.

linux系統中為了友善管理中斷,采用虛拟的irq(軟體中斷号)來标示某一中斷,每個interrupt controller管理的中斷對應這一個irq domain.irq通過某一關系map到HW interrupt id上(譬如線性映射).大體流程(參考 Linux kernel的中斷子系統之(二):IRQ Domain介紹):

  1. 初始化interrupt controllers和irq-domain,每個interrupt controller對應一個irq domain
  2. 裝置樹初始化過程中建立irq <-----> HW interrupt id的map關系;沒有裝置樹,采用BSP(闆級支援包)的話就在BSP中建立map關系
  3. 裝置驅動通過"int platform_get_irq(struct platform_device *, unsigned int);"來擷取irq
    int platform_get_irq(struct platform_device *, unsigned int);
               
    因為我們的裝置樹中描述了整個中斷子系統的層級關系,是以裝置樹初始化過程中可以建構出irq <-----> HW interrupt id的map關系,而且目前裝置的裝置樹節點中描述了它所引用的中斷屬性(HW interrupt id,interrupt controller,trigger level等),是以當使用platform_get_irq時系統會根據目前裝置節點的描述結合map表得到裝置樹節點中描述的HW interrupt id對應的irq;對于BSP,由BSP工程師建構map表,并裝置驅動對應的BSP裝置檔案的platform_device結構的resorce字段中指定irq(這裡直接指定irq不是HW interrupt id).

5. 常用接口

注冊釋放接口:

static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
	    const char *name, void *dev);

void free_irq(unsigned int irq, void *dev_id);
           
開關接口(屏蔽非屏蔽):
           
/*本地 CPU 全局中斷的開關*/
/*local_irq_save/restore可以儲存和回複中斷的開關狀态*/
/*local_irq_enable/disable則忽視了本地cpu之前的狀态*/
#define local_irq_enable()	do { raw_local_irq_enable(); } while (0)
#define local_irq_disable()	do { raw_local_irq_disable(); } while (0)
#define local_irq_save(flags)	do { raw_local_irq_save(flags); } while (0)
#define local_irq_restore(flags) do { raw_local_irq_restore(flags); } while (0)

/*單個中斷源中斷的開關*/
void enable_irq(unsigned int irq);
void disable_irq(unsigned int irq);
           

6. 中斷線程化:

一般的可以通過request_irq()來注冊中斷,通過request_threaded_irq()來注冊線程化中斷.線程化主要目的是把中斷上下文的任務遷移到線程中,減少系統關中斷的時間(linux預設中斷不可嵌套,中斷發生後到中斷處理函數執行完畢過程中,中斷是一直關閉的,上面有提到),增強系統響應中斷的實時性.

中斷對應的線程命名規則為:

t = kthread_create(irq_thread, new, "irq/%d-%s", irq, new->name);
           

我們可以看到形如下圖中的線程名:

root@:/ # ps | grep "irq/"
root      171   2     0      0     irq_thread 0000000000 S irq/389-charger
root      239   2     0      0     irq_thread 0000000000 S irq/296-PS_int-
root      247   2     0      0     irq_thread 0000000000 S irq/297-1124000
root      1415  2     0      0     irq_thread 0000000000 S irq/293-goodix_
[email protected]:/ #

           

7.軟中斷,tasklet,workqueue(轉自)

linux kernel的中斷子系統之(八):softirq

linux kernel的中斷子系統之(九):tasklet

參考文章:

Linux Interrupt

linux kernel的中斷子系統之(七):GIC代碼分析

中斷子系統  (強烈推薦看一下)

繼續閱讀