Request irq和setup irq的區別

2021-08-25 17:46:21 字數 3595 閱讀 7889

linux 核心提供了兩個註冊中斷處理函式的介面:setup_irq和request_irq。這兩個函式都定義在kernel/irq/manage.c裡。

/** internal function to register an irqaction - typically used to

* allocate special interrupts that are part of the architecture.

*/int setup_irq(unsigned int irq, struct irqaction *new);

/** request_irq - allocate an interrupt line

* this call allocates interrupt resources and enables the

* interrupt line and irq handling.

*/int request_irq(unsigned int irq,

irqreturn_t (*handler)(int, void *, struct pt_regs *),

unsigned long irqflags, const char *devname, void *dev_id)

這兩個函式有什麼樣的區別呢?

先看看setup_irq

setup_irq通常用在系統時鐘(gp timer)驅動裡,註冊系統時鐘驅動的中斷處理函式。

下面舉個列子, 如s3c2410 timer驅動:

/* arch/arm/mach-s3c2410/time.c */

static struct irqaction s3c2410_timer_irq = ;

static void __init s3c2410_timer_init (void)

struct sys_timer s3c24xx_timer = ;

struct sys_timer s3c24xx_timer = ;

可以看到,setup_irq的使用流程很簡單。首先定義s3c2410 timer驅動的irqaction結構體,該結構體用於描述timer中斷的基本屬性包括中斷名、類別以及該中斷handler等。然後通過setup_irq函式將timer的irqaction註冊進核心。其中,irq_timer4為s3c2410 timer的中斷號。

再看看request_irq

request_irq原始碼如下:

/* kernel/irq/manage.c */

int request_irq(unsigned int irq,

irqreturn_t (*handler)(int, void *, struct pt_regs *),

unsigned long irqflags, const char *devname, void *dev_id)

由上可以看出,request_irq的大致流程為先對申請的中斷線進行安全檢測,然後根據request_irq傳進來的引數,動態建立該中斷對應的irqaction結構體,最後通過setup_irq函式將該irqaction註冊進核心適當的位置。

這兩個函式的使用流程搞清楚了,那麼兩者之間的聯絡也就清楚了: 1)

request_irq的註冊過程包含setup_irq,最終是呼叫setup_irq。 2)

request_irq比setup_irq多一套錯誤檢測機制,即kmalloc前面3行if語句。

而setup_irq通常是直接註冊irqaction,並沒針對相應中斷線進行錯誤檢測,如該irq 線是否已經被占用等。因此setup_irq通常只用在特定的中斷線上,如system timer。除系統時鐘驅動外,大部份驅動還是通過request_irq註冊中斷。

這裡有個小問題:

既然request_irq實際上就是包含了setup_irq的註冊過程,那系統時鐘驅動(gp timer driver)中斷可以用request_irq來註冊嗎?

做個小試驗, 將s3c2410 timer驅動的setup_irq那行去掉,改為用request_irq註冊。

修改後**如下:

static void __init s3c2410_timer_init (void)

編譯執行。

結果:核心掛掉

為什麼呢?很明顯,系統時鐘驅動中斷不能用request_irq註冊,大致搜了一下原始碼也發現,看到其他平台相關的時鐘驅動中斷部分都是用的setup_irq註冊的。

我們來分析一下原因。

看看request_irq和setup_irq 還有哪些細節不一樣?

仔細觀察後注意到request_irq內有這麼一行**:

action = kmalloc(sizeof(struct irqaction), gfp_atomic);

作用為動態建立乙個irqaction。

kmalloc實際上也是使用的slab機制進行分配的。原始碼如下:

/* include/linux/slab.h */

static inline void *kmalloc(size_t size, gfp_t flags)

found:

return kmem_cache_alloc((flags & gfp_dma) ?

malloc_sizes[i].cs_dmacachep :

malloc_sizes[i].cs_cachep, flags);

}return __kmalloc(size, flags);

}使用slab機制分配記憶體必須先對slab進行初始化,包括mem_init和kmem_cache_init。

看看kernel的初始化流程:

/* init/main.c */

asmlinkage void __init start_kernel(void)

time_init函式在mem_init和kmem_cache_init之前被呼叫,而time_init會呼叫體系結構相關部分系統時鐘驅動的初始化函式。拿s3c2410的例子來說,time_init最終會呼叫s3c2410_timer_init函式,進行s3c2410時鐘驅動的初始化和註冊中斷處理函式。

具體過程如下:

time_init函式定義在arch/arm/kernel/time.c內:

void __init time_init(void)

system_timer在setup_arch(arch/arm/kernel/setup.c)內通過map_desc機制被初始化為s3c24xx_timer. 如上面s3c2410時鐘驅動**所示,s3c24xx_timer的init成員即指向s3c2410_timer_init函式。

現在我們搞清楚了,我們大概的估計是系統時鐘驅動(gp timer driver)的中斷處理函式不能用request_irq註冊是因為request_irq內會呼叫kmalloc動態分配記憶體建立timer的irqaction結構體。而kmalloc也是使用的slab記憶體分配機制,使用kmalloc前必須先對kernel的slab以及mem data structure進行初始化。而這部分初始化工作是在系統時鐘驅動初始化之後才進行的,所以造成kmalloc失敗,從而造成系統時鐘驅動的中斷未註冊成功,進而核心掛掉。

Request irq和setup irq的區別

linux 核心提供了兩個註冊中斷處理函式的介面 setup irq和request irq。這兩個函式都定義在kernel irq manage.c裡。internal function to register an irqaction typically used to allocate spe...

Request irq和setup irq的區別

linux 核心提供了兩個註冊中斷處理函式的介面 setup irq和request irq。這兩個函式都定義在kernel irq manage.c裡。這兩個函式有什麼樣的區別呢?1 setup irq,setup irq通常用在系統時鐘 gp timer 驅動裡,註冊系統時鐘驅動的中斷處理函式。...

Request irq和setup irq的區別

linux 核心提供了兩個註冊中斷處理函式的介面 setup irq和request irq。這兩個函式都定義在kernel irq manage.c裡。internal function to register an irqaction typically used to allocate spe...