1. 程式人生 > >linux核心中斷分析

linux核心中斷分析

知識要點
一、struct irq_chip、struct irq_desc[]、struct irqaction三者之間的關係
二、Linux核心中中斷的初始化流程、中斷的註冊流程、中斷的執行流程
三、多核cpu的中斷親和力和中斷負載均衡
四、中斷的上半部和下半部

一、struct irq_chip、struct irq_desc[]、struct irqaction三者之間的關
include /linux/irq.h
主要的三個資料結構
struct irq_chip:中斷控制器描述符, CPU所對應的一個具體的中斷控制器,如早期intel對應的中斷控制器為8259a,ioapic_chip。 一個cpu可以有多個irq_chip,即多箇中斷控制器
struct irq_desc : 中斷描述符陣列,每一個IRQ對應自己的struct irq_desc物件,共同組成一個
struct irqaction : 中斷服務程式描述符,該IRQ對應的一系列中斷程式

  





      如圖所示為該三個結構體關係,一箇中斷控制器(irq_chip)對應著一箇中斷描述符陣列(irq_desc),每一個成員都是一箇中斷號,每一箇中斷號下面都有具體的中斷服務程式(irqaction)連結串列

1、/*中斷描述符*/------>一個IRQ對應自己的struct irq_desc物件,多個irq_desc組成irq_desc[ ]陣列 
struct irq_desc {/*中斷描述符*/

unsigned intirq;/* 該irq_desc具體的中斷號 */

irq_flow_handler_thandle_irq;/*該irq線公共中斷服務程式*/
struct irq_chip*chip;/*該中斷線所屬的中斷控制器*/

struct msi_desc*msi_desc;
void*handler_data;
void*chip_data;
struct irqaction*action;/*該中斷服務程式,區別於公共中斷服務程式,這裡指向的是中斷服務程式的佇列頭*/
unsigned intstatus;/* 中斷線狀態*/
unsigned intdepth;/* nested irq disables */
unsigned intwake_depth;/* nested wake enables */
unsigned intirq_count;/* For detecting broken IRQs */    
unsigned longlast_unhandled;/* Aging timer for unhandled count */
unsigned intirqs_unhandled;
const char*name;

} ____cacheline_internodealigned_in_smp;

2、/*中斷控制器描述符*/--->CPU所對應的一個具體的中斷控制器,如早期intel對應的中斷控制器為8259a etc..
struct irq_desc {
struct irq_chip*chip;/*該中斷線所屬的中斷控制器*/
}

struct irq_chip {
const char*name;/*中斷控制器名稱*/
unsigned int(*startup)(unsigned int irq);
void(*shutdown)(unsigned int irq);
void(*enable)(unsigned int irq);
void(*disable)(unsigned int irq);

void(*ack)(unsigned int irq);
void(*mask)(unsigned int irq);
void(*mask_ack)(unsigned int irq);
void(*unmask)(unsigned int irq);
void(*eoi)(unsigned int irq);

void(*end)(unsigned int irq);
int(*set_affinity)(unsigned int irq,
const struct cpumask *dest);
int(*retrigger)(unsigned int irq);
int(*set_type)(unsigned int irq, unsigned int flow_type);
int(*set_wake)(unsigned int irq, unsigned int on);

void(*bus_lock)(unsigned int irq);
void(*bus_sync_unlock)(unsigned int irq);

/* Currently used only by UML, might disappear one day.*/
#ifdef CONFIG_IRQ_RELEASE_METHOD
void(*release)(unsigned int irq, void *dev_id);
#endif
/*
* For compatibility, ->typename is copied into ->name.
* Will disappear.
*/
const char*typename;
};

3、/*中斷服務程式描述符*/-------> 一箇中斷服務程式結構體宣告
struct irq_desc {
struct irqaction*action;/*中斷服務程式,區別於公共中斷服務程式,這裡指向的是中斷服務程式的佇列頭*/
}
struct irqaction {
irq_handler_t handler;/*中斷服務程式*/
unsigned long flags;/*IRQ 中斷處理標誌*/
const char *name;/*裝置名*/
void *dev_id;
struct irqaction *next;/*指向該IRQ向中斷請求佇列下一個irqaction物件*/
int irq;
struct proc_dir_entry *dir;
irq_handler_t thread_fn;
struct task_struct *thread;
unsigned long thread_flags;
};

共享同一個IRQ先的多個irqaction物件組成的佇列,即所謂的中斷請求佇列。當該IRQ線上產生中斷時,請求佇列中的中斷服務程式將被依次執行

二、Linux核心中中斷的初始化流程、中斷的註冊流程、中斷的執行流程
1、中斷子系統的初始化核心程式碼linux 2.6.30.4
start_kernel
{
trap_init/* arm 為空函式 */
early_irq_init/* irq_desc[NR_IRQS]陣列基本初始化 */
init_IRQ/* irq_desc[NR_IRQS] 中斷描述符陣列初始化 */
{
   for (irq = 0; irq < NR_IRQS; irq++)
irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE;

 init_arch_irq();/* 在setup_arch中init_arch_irq = mdesc->init_irq;*/
--> s3c24xx_init_irq  以s3c24400為例
{
set_irq_chip(irqno, &s3c_irq_chip);/* 根據 irqno 關聯對應的irq_desc[irqno]和irq_chip */
set_irq_handler(irqno, handle_edge_irq);/* 設定irq_dest[irqno]公共中斷函式 */
set_irq_flags(irqno, IRQF_VALID);/* 設定irq_dest[irqno] flags */
set_irq_chained_handler(IRQ_UART0, s3c_irq_demux_uart0);/* 設定irq_dest[irqno]公共中斷函式 */
}
}
}
void __init setup_arch(char **cmdline_p)
{
mdesc = setup_machine(machine_arch_type);/*從machine_arch_type 段中獲取 machine_desc結構體 */
init_arch_irq = mdesc->init_irq;/* 其中 mdesc結構體在具體的架構中定義 */
}

#define MACHINE_START(_type,_name)\---->  arch/arm/include/asm/mach/arch.h
static const struct machine_desc __mach_desc_##_type\
 __used \
 __attribute__((__section__(".arch.info.init"))) = {\
.nr= MACH_TYPE_##_type,\
.name= _name,
#define MACHINE_END\

};

MACHINE_START(S3C2440, "SMDK2440")-----> mach-smdk2440.c 
/* Maintainer: Ben Dooks <[email protected]> */
.phys_io= S3C2410_PA_UART,
.io_pg_offst= (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params= S3C2410_SDRAM_PA + 0x100,

.init_irq= s3c24xx_init_irq,
.map_io= smdk2440_map_io,
.init_machine= smdk2440_machine_init,
.timer= &s3c24xx_timer,


2、中斷服務的註冊核心程式碼linux 2.6.30.4
request_irq
-->request_threaded_irq
{
desc = irq_to_desc(irq);/* 根據irq號取出其對應的irq_desc */
__setup_irq(irq, desc, action);/* 將struct irqaction *action 新增到desc->action連結串列中*/
}

3、核心中中斷的執行過程核心程式碼linux 2.6.30.4
entry_armv.S 
[email protected]  0  (USR_26 / USR_32)使用者模式下中斷
[email protected]  1  (FIQ_26 / FIQ_32)
[email protected]  2  (IRQ_26 / IRQ_32)
[email protected]  3  (SVC_26 / SVC_32)核心模式下中斷
[email protected]  4
[email protected]  5
[email protected]  6
[email protected]  7
[email protected]  8
[email protected]  9
[email protected]  a
[email protected]  b
[email protected]  c
[email protected]  d
[email protected]  e
[email protected]  f

__irq_svc:entry_armv.S 
irq_handler
ENDPROC(__irq_svc)

.macroirq_handlerentry_armv.S 
bneasm_do_IRQ

asm_do_IRQ----> irq.c 
--->generic_handle_irq(irq);
----->generic_handle_irq_desc
------->__do_IRQ(irq);
{
struct irq_desc *desc = irq_to_desc(irq);
action_ret = handle_IRQ_event(irq, desc->action);/* 依次執行 irq對應的desc->action的handler服務例程 */
}

handle_IRQ_event(unsigned int irq, struct irqaction *action)/* 依次執行 irq對應的desc->action的handler服務例程 */
{
do {
ret = action->handler(irq, action->dev_id);
action = action->next;
} while (action);
}

又比如hisi晶片的IRQ執行流程

vector_stub irq, IRQ_MODE, 4----> entry-armv.S
[email protected]  0  (USR_26 / USR_32)
[email protected]  1  (FIQ_26 / FIQ_32)
[email protected]  2  (IRQ_26 / IRQ_32)
[email protected]  3  (SVC_26 / SVC_32)
[email protected]  4
[email protected]  5
[email protected]  6
[email protected]  7
[email protected]  8
[email protected]  9
[email protected]  a
[email protected]  b
[email protected]  c
[email protected]  d
[email protected]  e
[email protected]  f

__irq_svc:----> entry-armv.S
irq_handler
ENDPROC(__irq_svc)
.macro irq_handler
#ifdef CONFIG_MULTI_IRQ_HANDLER     ----> entry-armv.S

ldrr1, =handle_arch_irq/* 在 init_IRQ中賦值, handle_arch_irq*/
movr0, sp
adrlr, BSYM(9997f)
ldrpc, [r1]
#else
arch_irq_handler_default

#endif

==========handle_arch_irq賦值流程,handle_arch_irq=gic_handle_irq=============

void __init init_IRQ(void)
{
if (IS_ENABLED(CONFIG_OF) && !machine_desc->init_irq)
irqchip_init();
else
machine_desc->init_irq();
}

hi3536_gic_init_irq
gic_init_bases
-->set_handle_irq(gic_handle_irq);
{
if (handle_arch_irq)
return;
handle_arch_irq = handle_irq;
}

MACHINE_START(HI3536, "hi3536")----> arch/arm/mach-hi3536/core.c
.atag_offset  = 0x100,
.map_io       = hi3536_map_io,
.init_early   = hi3536_init_early,
.init_irq     = hi3536_gic_init_irq,
#ifdef CONFIG_HI3536_SYSCNT
.init_time    = arch_timer_init,
#else
.init_time    = hi3536_timer_init,
#endif
.init_machine = hi3536_init,
.smp          = smp_ops(hi3536_smp_ops),
.reserve      = hi3536_reserve,
.restart      = hi3536_restart,
MACHINE_END

==================================================== 所以handle_arch_irq=gic_handle_irq

gic_handle_irq
--->handle_IRQ
----->generic_handle_irq
{
      struct irq_desc *desc = irq_to_desc(irq);
      generic_handle_irq_desc(irq, desc);
      {
desc->handle_irq(irq, desc);/*這裡根據具體的handle_irq來執行,若irq<32,則使用 handle_percpu_devid_irq,否則使用handle_fasteoi_irq*/
 handle_fasteoi_irq/* 以irq大於32的handle_fasteoi_irq為例 */
{
handle_irq_event(desc);
--->handle_irq_event_percpu/* 依次執行irq_desc上action上的服務例程 */
{
do {
ret = action->handler(irq, action->dev_id);
action = action->next;
} while (action);
}
}
      }
}

static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
{
if (hw < 32) /*中斷號小於32 */
irq_set_chip_and_handler(irq, &gic_chip, handle_percpu_devid_irq);/*賦值desc->handle_irq*/
        else /* 中斷號大於32 */
irq_set_chip_and_handler(irq, &gic_chip,handle_fasteoi_irq);/*賦值desc->handle_irq*/

return 0;
}


三、多核cpu的中斷親和力和中斷負載均衡
1、處理器間中斷
   在多核cpu中,中斷可以通過 處理器間中斷(Inter-Processor Interrupt) 傳遞到其他核上.
2、中斷親和力和中斷負載均衡
   利用中斷親和可以來做中斷的負載均衡,將負載繫結到負載較輕的cpu上,更好的優化效能.
四、中斷的上半部和下半部
linux中斷上半部(top half)------> 不可中斷
上半部的功能是"登記中斷",當一箇中斷髮生時,它進行相應地硬體讀寫後並把中斷例程的下半部掛到該裝置的下半部執行 工作佇列/tasklet中去
linux中斷的下半部(botttom half)----->  可中斷
具體的下半部幾種方式
幾種下半部方式 : 
1、軟中斷
2、tasklet
3、工作佇列

當前下半部看到使用使用工作隊和tasklet比較多

參考資料

Linux 2.6.30.4原始碼

linux 3.10.y原始碼

《linux核心修煉之道》






相關推薦

linux核心中斷分析

知識要點 一、struct irq_chip、struct irq_desc[]、struct irqaction三者之間的關係 二、Linux核心中中斷的初始化流程、中斷的註冊流程、中斷的執行流程 三、多核cpu的中斷親和力和中斷負載均衡 四、中斷的上半部和下半部 一、s

LINUX-核心-中斷分析-中斷向量表(2)-mips

mips中斷概念 在《MIPS體系結構透視》的第5章說到,在MIPS中,中斷、陷阱、系統呼叫和任何可以中斷程式正常執行流的情況全被都被稱為異常。 以上這種統一到“異常”的概念及其邏輯當然會體現在MIPS的異常入口點的設計中,特別如MIPS中斷入口點的引出。

Linux核心中斷和異常分析(上)

中斷,通常被定義為一個事件。打個比方,你燒熱水,水沸騰了,這時候你要去關掉燒熱水的電磁爐,然後再去辦之前手中停不下來的事情。那麼熱水沸騰就是打斷你正常工作的一個訊號機制。當然,還有其它的情況,我們以後

Linux核心排程分析(轉,侵刪)

多工 併發和並行 Linux作為一個多工作業系統,必須支援程式的併發執行。 分類 非搶佔式多工    除非任務自己結束,否則將會一直執行。 搶佔式多工(Linux) 這種情況下,由排程程式來決定什麼時候停止一個程序的執行,這個強制的掛起動作即為**“搶佔”**。採用搶佔式多工

Linux核心Crash分析

  在工作中經常會遇到一些核心crash的情況,本文就是根據核心出現crash後的列印資訊,對其進行了分析,使用的核心版本為:Linux2.6.32。        每一個程序的生命週期內,其生命週期的範圍為幾毫秒到幾個月。一般都是和核心有互動,

linux 核心程式碼分析1 TI am335x

1.     TI AM335x  核心原始碼分析 1.1 Board-am335xevm.c Board-am335xevm.c(./arch/arm/mach-omap2)中開始執行入口: MACHINE_START(A

linux核心原始碼分析-夥伴系統

之前的文章已經介紹了夥伴系統,這篇我們主要看看原始碼中是如何初始化夥伴系統、從夥伴系統中分配頁框,返回頁框於夥伴系統中的。   我們知道,每個管理區都有自己的夥伴系統管理屬於這個管理區的頁框,這也說明了,在夥伴系統初始化時,管理區必須要已經存在(初始化完成)

Linux核心中斷引入使用者空間(非同步通知機制)

當linux核心空間發生中斷後怎麼使使用者空間的應用程式執行相應的函式呢,當晶片有資料到來時核心會產生一箇中斷,但是怎樣通知應用程式來取資料,以前這個問題一直困擾我很長時間,後來發現linux中有非同步通知機制,在使用者程式中用signal註冊一個響應SIGIO訊號的回撥函式,然後在驅動程式中向該程

Linux核心原始碼分析--zImage出生實錄(Linux-3.0 ARMv7)

此文為兩年前為好友劉慶敏的書《嵌入式Linux開發詳解--基於AT91RM9200和Linux 2.6》中幫忙寫的章節的重新整理。如有雷同,純屬必然。經作者同意,將我寫的部分重新整理後放入blog中。 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Linux核心情景分析》經典解說--程序

get_current 使用巨集的原因 ==================== include/asmi386/ current.h 6 13 ==================== 6 static inline struct task_struct * get_current

ioctl配置IP地址 Linux核心實現分析

1 執行flow 本文以Linux kernel3.10版本描述 上圖是《Understanding LINUX NETWORK INTERNALS》一書中對socket的ioctl呼叫的整體flow,本文只對其中SIOCSIFADDR這一個command進行flow

深入理解 Linux 核心---中斷和異常

中斷或異常會改變處理器執行的指令順序。 異常: 來源:CPU 控制單元, 時機:只有在一條指令終止執行後 CPU 才會發出中斷。 原因:程式產生錯誤,或核心必須處理的異常條件。 中斷: 來源:間隔定時器或 I/O 裝置。 時機:隨機產生。 原因:依照 CP

linux核心原始碼分析

本文基於3.18.3核心的分析,nvme裝置為pcie介面的ssd,其驅動名稱為nvme.ko,驅動程式碼在drivers/block/nvme-core.c. 驅動的載入   驅動載入實際就是module的載入,而module載入時會對整個module進行初始化,nvme驅動的module初始化函式

s3c2410 RTC驅動框架linux核心原始碼分析

實在無聊中就將原來的一些東西整理了一下,自己是個記性不好的人,隔斷時間整理自己,同時也希望可以方便他人。 -------------------------------------------------------------------------------------

linux核心-中斷處理程式

中斷共享  中斷共享是指多個裝置共享一根中斷線的情況  中斷共享的使用方法:  (1).在申請中斷時,使用IRQF_SHARED標識  (2).在中斷到來時,會遍歷共享此中斷的所有中斷處理程式,直到某一個函式返回  IRQ_HANDLED,在中斷處理程式頂半部中,應迅速根據硬體暫存器中的資訊參照dev_id引

Linux核心原始碼分析——Linux核心的入口

Jack:hi,淫龍,在Linux核心的原始碼裡,有幾段彙編程式碼,那幾段程式碼是負責Linux核心引導的。 我:是的。早期的Linux核心引導程式碼只有bootsect.s、setup.s、head.s這3個檔案,這三個檔案都是Linus在1991年左右親手寫的。後來的程

Linux核心原始碼分析--記憶體管理(一、分頁機制)

        Linux系統中分為幾大模組:程序排程、記憶體管理、程序通訊、檔案系統、網路模組;各個模組之間都有一定的聯絡,就像蜘蛛網一樣,所以這也是為什麼Linux核心那麼難理解,因為不知道從哪裡開始著手去學習。很多人會跟著系統上電啟動 BIOS-->bootse

linux 核心中斷trace機制使用

如果要開啟Linux核心的中斷trace機制,需要在config裡面,開啟如下選項: CONFIG_IRQSOFF_TRACER=y 並且需要重新編譯核心。 然後,依次輸入以下命令: echo 0 > /sys/kernel/debug/tracing/trac

Linux核心原始碼分析--檔案系統(五、Inode.c)

_bmap()         1、_bmap()函式用於把一個檔案資料塊對映到盤塊的處理操作                  因為一個i節點對應一個檔案,所以上面的i節點對映的邏輯塊號就是檔案資料存放的邏輯塊號;i_zone[0]到i_zone[6]是直接邏輯塊號,i

linux核心目錄分析

kernel目錄分析 一 分析源目錄下資料夾: 1.arch arch是architecture的縮寫,意思是架構。arch目錄下是好多個不同架構的CPU的子目錄,譬如arm這種cpu的所有檔案都在arch/arm目錄下,X86的CPU的所有