#一、中斷作用
Linux 內核需要對連接到計算機上的所有硬件設備進行管理。如果要管理這些設備,首先得和它們互相通信才行。 一般有兩種方案可實現這種功能:
- 輪詢(polling) 讓內核定期對設備的狀態進行查詢,然后做出相應的處理;
- 中斷(interrupt) 讓硬件在需要的時候向內核發出信號(變內核主動為硬件主動)。
使用輪詢的方式會占用CPU比較多的時間,效率極低。例如:要讀取一個按鍵有沒有被按下時,一個進程需要不斷地查詢按鍵有沒有被按下。這樣這個任務就占用CPU大量得時間,使得CPU做了大量的無用功。使用中斷提供這樣的一個機制。當按鍵沒有被按下的時候,掛起當前進程,將控制權轉交給其他進程。當按鍵按下的時候,操作系統把當前進程設為活動的,從而允許該進程繼續執行。
二、linux中斷管理
linux 內核將所有的中斷統一編號,使用一個
irq_desc
結構體數組描述中斷。一個數組項對用一個中斷(或者是一組中斷,它們共用中斷號)。struct irq_desc
結構體記錄了,中斷的名稱、中斷狀態,底層硬件訪問接口(使能中斷,屏蔽中斷,清除中斷),中斷處理函數的入口, 通過它可以調用用戶注冊的中斷處理函數。
1、struct irq_desc
struct irq_desc
在include\linux\irq.h
文件里面定義
struct irq_desc {
irq_flow_handler_t handle_irq; /* 當前中斷的處理函數入口 */
struct irq_chip *chip; /* 底層硬件訪問 */
...
struct irqaction *action; /* 用戶注冊的中斷處理函數鏈表 */
unsigned int status; /* IRQ狀態 */
...
const char *name; /* 中斷函數名 */
} ____cacheline_internodealigned_in_smp;
a. handle_irq
handle_irq
是這個或者是這組的中斷的處理函數入口。發生中斷時,會調用asm_do_IRQ
函數。在這個函數里面根據中斷號調用相應irq_desc
數組項的handle_irq
。 在handle_irq
里面會使用chip
成員的接口來使能、屏蔽、清除中斷。還會一一調用用戶注冊在action
鏈表里面的處理函數。
b. struct irq_chip
struct irq_chip 在
include\linux\irq.h
文件里面定義
struct irq_chip {
const char *name;
/* 啟動中斷,如果不設置則缺省為 "enable" */
unsigned int (*startup)(unsigned int irq);
/* 關閉中斷,如果不設置則缺省為 "disable" */
void (*shutdown)(unsigned int irq);
/* 使能中斷,如果不設置則缺省為"unmask" */
void (*enable)(unsigned int irq);
/* 禁止中斷,如果不設置則缺省為"mask" */
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);
....
};
c. struct irqaction
struct irqaction
結構體在include\linux\interrupt.h
文件里面定義。 用戶注冊的每個中斷處理函數都用一個irqaction
結構體來描述一個中斷(例如共享中斷)可以有多個處理函數。 它們的irqaction
結構以action
為表頭鏈成一個鏈表
struct irqaction {
/* 用戶注冊的中斷處理函數 */
irq_handler_t handler;
/* 中斷的標志,是否是共享中斷,中斷的觸發方式是電平觸發,還是邊沿觸發 */
unsigned long flags;
cpumask_t mask;
/* 用戶注冊時,給的中斷的名字 */
const char *name;
/* handler 中斷函數的參數,也可以用來區分共享中斷 */
void *dev_id;
/* 鏈表的指針 */
struct irqaction *next;
/* 中斷號 */
int irq;
struct proc_dir_entry *dir;
};
2. 小結
對於
struct irq_desc
數組 、struct irq_chip
結構體 和struct irqaction
三者之間的關系,如下圖:
三、中斷處理初始化
1、中斷處理初始化
在
init\Main.c
文件的start_kernel
函數里面調用的init_IRQ()
函數就是中斷體系結構的初始化
2、init_IRQ 函數
init_IRQ
用來初始化中斷處理體系結構, 在arch\arm\kernel\irq.c
文件里面
void __init init_IRQ(void)
{
int irq;
/* 初始化irq_desc[] 每一項的中斷狀態 */
for (irq = 0; irq < NR_IRQS; irq++)
irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE;
...
/* 架構相關的中斷初始化函數 */
init_arch_irq();
}
3、 init_arch_irq
init_arch_irq
是一個函數指針,arch\arm\kernel\setup.c
文件setup_arch()
函數被初始化
void __init setup_arch(char **cmdline_p)
{
...
init_arch_irq = mdesc->init_irq;
...
}
mdesc->init_irq
指向的是arch\arm\plat-s3c24xx\irq.c
文件的s3c24xx_init_irq()
函數。MACHINE_START(S3C2440, "SMDK2440")
是一個宏,用來定義struct machine_desc
結構體 結構體在arch\arm\mach-s3c2440\Mach-smdk2440.c
文件里面定義並且初始化init_irq
指向s3c24xx_init_irq()
函數
MACHINE_START(S3C2440, "SMDK2440")
/* Maintainer: Ben Dooks <ben@fluff.org> */
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
/* init_irq成員在這里初始化 */
.init_irq = s3c24xx_init_irq,
.map_io = smdk2440_map_io,
.init_machine = smdk2440_machine_init,
.timer = &s3c24xx_timer,
MACHINE_END
4、s3c24xx_init_irq函數
s3c24xx_init_irq()
函數在arch\arm\plat-s3c24xx\irq.c
定義(部分代碼如下面的代碼塊),他為所有的所有與芯片操作相關的數據結構(irq_desc[irq].chip),,並且初始化了處理函數入口(irq_desc[irq].handle_irq)。 以IRQ_EINT0
和IRQ_EINT3
為例,set_irq_chip
函數就是將irq_desc[irqno].chip = &s3c_irq_eint0t4
,以后就可以通過irq_desc[irqno].chip
結構的函數指針來設置觸方式,使能中斷、屏蔽中斷了set_irq_handler
函數就是設置中斷處理函數入口 將irq_desc[irqno].handle_irq = handle_edge_irq
發生中斷時,handle_edge_irq
會調用用戶具體注冊的處理函數irq_desc[irqno].falgs
設置為IRQF_VALID
void __init s3c24xx_init_irq(void)
{
/* 中斷號是組的初始化 */
set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint4t7);
set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8);
...
/* external interrupts */
for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {
irqdbf("registering irq %d (ext int)\n", irqno);
set_irq_chip(irqno, &s3c_irq_eint0t4);
set_irq_handler(irqno, handle_edge_irq);
set_irq_flags(irqno, IRQF_VALID);
}
for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) {
irqdbf("registering irq %d (extended s3c irq)\n", irqno);
set_irq_chip(irqno, &s3c_irqext_chip);
set_irq_handler(irqno, handle_edge_irq);
set_irq_flags(irqno, IRQF_VALID);
}
...
}
###5、set_irq_chip函數
int set_irq_chip(unsigned int irq, struct irq_chip *chip)
{
struct irq_desc *desc;
unsigned long flags;
/* 判斷是否超過最大的中斷號 */
if (irq >= NR_IRQS) {
printk(KERN_ERR "Trying to install chip for IRQ%d\n", irq);
WARN_ON(1);
return -EINVAL;
}
if (!chip)
chip = &no_irq_chip;
/* 通過中斷號找到irq_desc數組對應的數組項 */
desc = irq_desc + irq;
spin_lock_irqsave(&desc->lock, flags);
/* 判斷 chip 的成員即&s3c_irq_eint0t4的成員是否為空,如果為空就設置為默認的操作函數 */
irq_chip_set_defaults(chip);
/* 設置irq_desc[].chip成員 */
desc->chip = chip;
spin_unlock_irqrestore(&desc->lock, flags);
return 0;
###5、set_irq_handler函數
set_irq_handler(unsigned int irq, irq_flow_handler_t handle)
{
__set_irq_handler(irq, handle, 0, NULL);
}
/***************************************************************************************/
__set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
const char *name)
{
struct irq_desc *desc;
unsigned long flags;
/* 通過中斷號找到irq_desc數組對應的數組項 */
desc = irq_desc + irq;
...
/* 中間還會做一些判斷 */
...
/* 設置中斷處理函數,名字*/
desc->handle_irq = handle;
desc->name = name;
/* 設置中斷的狀態,開啟中斷 */
if (handle != handle_bad_irq && is_chained) {
desc->status &= ~IRQ_DISABLED;
desc->status |= IRQ_NOREQUEST | IRQ_NOPROBE;
desc->depth = 0;
desc->chip->unmask(irq);
}
}
###6、set_irq_flags函數
void set_irq_flags(unsigned int irq, unsigned int iflags)
{
struct irq_desc *desc;
unsigned long flags;
if (irq >= NR_IRQS) {
printk(KERN_ERR "Trying to set irq flags for IRQ%d\n", irq);
return;
}
/* 找到數組項 */
desc = irq_desc + irq;
spin_lock_irqsave(&desc->lock, flags);
/* 設置中斷狀態 */
desc->status |= IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
if (iflags & IRQF_VALID)
desc->status &= ~IRQ_NOREQUEST;
if (iflags & IRQF_PROBE)
desc->status &= ~IRQ_NOPROBE;
if (!(iflags & IRQF_NOAUTOEN))
desc->status &= ~IRQ_NOAUTOEN;
spin_unlock_irqrestore(&desc->lock, flags);
}
#四、總結
中斷處理體系結構的初始化的過程其實就是對
irq_desc[]
數組的每一項初始化進行初始化. 一個中斷或者一組中斷通過irq_desc[]
的一個數組項來管理。 數組項里面的handle_irq
chip
action
三個重要的結構體成員。
- handle_irq是當前的中斷處理函數
- chip底層硬件相關的處理函數(設置中斷的觸發方式,屏蔽中斷,使能中斷)
- action鏈表頭,用戶注冊的處理函數都鏈接到這個鏈表里面,發生中斷的時候,就會從之里面調用用戶注冊進來的中斷服務函數