什么是linux異常體系結(jié)構(gòu)

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)什么是linux異常體系結(jié)構(gòu),文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

在新洲等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場(chǎng)前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供成都網(wǎng)站制作、網(wǎng)站建設(shè) 網(wǎng)站設(shè)計(jì)制作按需網(wǎng)站策劃,公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),成都品牌網(wǎng)站建設(shè),成都營(yíng)銷(xiāo)網(wǎng)站建設(shè),外貿(mào)網(wǎng)站制作,新洲網(wǎng)站建設(shè)費(fèi)用合理。

以arm處理器為例, 外部中斷和處理器內(nèi)核異常(soc內(nèi)部)都屬于異常, 異常都是相對(duì)于主程序來(lái)講的, 當(dāng)soc正常執(zhí)行主程序時(shí), 中斷和異常都可以打斷它, 依據(jù)異常對(duì)于主程序所體現(xiàn)出來(lái)的"中斷"性質(zhì)可以區(qū)分出中斷和異常的區(qū)別:

異常: 由于soc本身的內(nèi)核活動(dòng)產(chǎn)生的, 如當(dāng)執(zhí)行主程序時(shí)候由于arm soc預(yù)取指令/數(shù)據(jù)而產(chǎn)生異常等, 這個(gè)異常來(lái)自soc的內(nèi)核, 所以對(duì)于soc內(nèi)核來(lái)說(shuō)是"同步"的.

中斷: 當(dāng)soc在執(zhí)行主程序的時(shí)候, 各種片上外設(shè)和外部中斷引腳可以產(chǎn)生一個(gè)異常來(lái)中斷soc當(dāng)前正在執(zhí)行的程序, 這個(gè)異常信號(hào)來(lái)自soc的內(nèi)核以外, 所以對(duì)于soc內(nèi)核來(lái)說(shuō)他是"異步"的

在arm架構(gòu)的linux系統(tǒng)下, 共使用了5種異常: 1. 未定義指令異常 2. 指令預(yù)取中止異常 3. 數(shù)據(jù)訪問(wèn)中止異常 4. 中斷異常 5. swi異常. 

(1) linux中斷體系結(jié)構(gòu)

linux內(nèi)核在(linux)/include/asm-arm/arch-s3c2410/irqs.h 中將所有的中斷統(tǒng)一編號(hào), 另外 linux使用一個(gè)結(jié)構(gòu)體數(shù)組來(lái)描述中斷, 并且這個(gè)數(shù)組的下標(biāo)就是中斷號(hào). 每個(gè)數(shù)組項(xiàng)對(duì)應(yīng)一組中斷,

file: (linux)/include/linux/irq.h 
struct irq_desc {
	irq_flow_handler_t	handle_irq;     /* 當(dāng)前中斷的處理函數(shù)入口 */
	struct irq_chip		*chip;          /* 底層的硬件訪問(wèn) */
        ...
	struct irqaction	*action;	/* 用戶提供的中斷處理函數(shù)鏈表 */
	unsigned int		status;		/* IRQ 狀態(tài) */
        ...
	const char		*name;          /* cat /proc/interrupts 顯示的中斷名稱 */
} ____cacheline_internodealigned_in_smp;

其中的 chip 提供了底層的硬件訪問(wèn):

file: (linux)/include/linux/irq.h
struct irq_chip {
	const char	*name;	                            
	unsigned int	(*startup)(unsigned int irq);    /* 啟動(dòng)中斷, 缺省為 "enable" */
	void		(*shutdown)(unsigned int irq);   /* 關(guān)閉中斷, 缺省為 "disable" */
	void		(*enable)(unsigned int irq);     /* 使能中斷, 缺省為 "unmask" */
	void		(*disable)(unsigned int irq);    /* 關(guān)閉中斷, 缺省為 "mask" */
	void		(*ack)(unsigned int irq);        /* 響應(yīng)中斷, 通常為清除當(dāng)前中斷使得可以接收下一個(gè)中斷 */
	void		(*mask)(unsigned int irq);       /* 屏蔽中斷源 */
	void		(*mask_ack)(unsigned int irq);   /* 屏蔽和響應(yīng)中斷 */
	void		(*unmask)(unsigned int irq);     /* 開(kāi)啟中斷源 */
	...
};

另外irqaction結(jié)構(gòu)體為:

file: (linux)/include/linux/interrupt.h 
struct irqaction {
	irq_handler_t handler;    //用戶注冊(cè)的中斷處理函數(shù)
	unsigned long flags;	  //中斷標(biāo)志
	cpumask_t mask;	          //用于smp
	const char *name;         //用戶注冊(cè)的名字 "cat /proc/interrupts" 可以看到
	void *dev_id;	          //用戶傳給上面的handler參數(shù),還可以用來(lái)區(qū)分共享中斷
	struct irqaction *next;   //指向下一個(gè)irqaction結(jié)構(gòu)
	int irq;                  //中斷號(hào)
	struct proc_dir_entry *dir;
};

中斷執(zhí)行的流程是: 中斷到來(lái)時(shí)總中斷入口函數(shù) asm_do_IRQ()根據(jù) 中斷號(hào)找到 irq_desc 結(jié)構(gòu)體數(shù)組中的對(duì)應(yīng)項(xiàng), 并調(diào)用 irq_desc 結(jié)構(gòu)體中的 handle_irq()函數(shù), handle_irq 函數(shù)使用 chip結(jié)構(gòu)體中的函數(shù) 清除/屏蔽/重新使能中斷, 最后一一調(diào)用 action鏈表中注冊(cè)的中斷處理函數(shù).

什么是linux異常體系結(jié)構(gòu)

(2) 中斷體系結(jié)構(gòu)初始化

在 (linux)/init/main.c 的 start_kernel() 函數(shù)中的 trap_init()  / init_IRQ()  來(lái)設(shè)置異常的處理函數(shù). 

1. trap_init()  和 early_trap_init() 函數(shù) 通過(guò)下邊語(yǔ)句將異常向量復(fù)制到 0xffff0000 地址處, 將中斷服務(wù)函數(shù)復(fù)制到 0xffff0000 + 0x200地址處

file: (linux)/arch/arm/kernel 
memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);

其中start和end如下:

.globl	__vectors_start
__vectors_start:
	swi	SYS_ERROR0
	b	vector_und + stubs_offset
	ldr	pc, .LCvswi + stubs_offset
	b	vector_pabt + stubs_offset
	b	vector_dabt + stubs_offset
	b	vector_addrexcptn + stubs_offset
	b	vector_irq + stubs_offset
	b	vector_fiq + stubs_offset

	.globl	__vectors_end
__vectors_end:

負(fù)責(zé)初始化的為 init_IRQ,因其與具體開(kāi)發(fā)板密切相關(guān),所以需要單獨(dú)列出來(lái).  它用來(lái)初始化中斷的框架,設(shè)置各個(gè)中斷的默認(rèn)處理函數(shù). 當(dāng)發(fā)生中斷時(shí)候進(jìn)入中斷總?cè)肟?asm_do_IRQ()  調(diào)用init_IRQ()設(shè)置的函數(shù)

file: (linux)/arch/arm/kernel/irq.c
void __init init_IRQ(void)
{
	int irq;
	for (irq = 0; irq < NR_IRQS; irq++)
		irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE;	
	init_arch_irq();
}

1 for循環(huán)中將 irq_desc[] 數(shù)組中每一項(xiàng)的狀態(tài)都設(shè)置為 IRQ_NOREQUEST | IRQ_NOPROBE (未請(qǐng)求 | 未探測(cè))

2 init_arch_irq 其實(shí)是一個(gè)函數(shù)指針, 定義如下: 

file: (linux)/arch/arm/kernel/irq.c 
void (*init_arch_irq)(void) __initdata = NULL;

2440移植好的linux系統(tǒng)在啟動(dòng)的時(shí)候通過(guò)(linux)/arch/arm/kernel/setup.c 的 setup_arch() 獲取到 machine_desc 結(jié)構(gòu)體, 然后將 init_arch_irq 這個(gè)函數(shù)指針初始化為 s3c24xx_init_irq() . 

file: (linux/arch/plat-s3c24xx/irq.c 的 s3c24xx_init_irq)
void __init s3c24xx_init_irq(void)
{
    ...
    /* first, clear all interrupts pending... */
    ...
    /* register the main interrupts */
    ...
    /* setup the cascade irq handlers */
    set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint4t7);
    set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8);
    ...
    /* external interrupts */
    //外部中斷 0 ~ 3
    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);
    }
    //外部中斷4 ~ 23
    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);
    }
    /* register the uart interrupts */
    ...
}

1. 清除所有中斷的 pending 位

2. 進(jìn)一步設(shè)置 irq_desc[] 中元素的 chip , handle_irq字段.

set_irq_chip(irqno, &s3c_irqext_chip)  結(jié)果是: irq_desc[irqno]. chip = &s3c_irqext_chip; 之后就可以使用 irq_desc[irqno].chip 結(jié)構(gòu)體的成員來(lái)設(shè)置觸發(fā)方式/使能中斷/禁止中斷等

set_irq_handler(irqno, handle_edge_irq) 結(jié)果是: irq_desc[irqno].handle_irq = handle_edge_irq; 這是該中斷號(hào)對(duì)應(yīng)的中斷函數(shù)入口

set_irq_flags(irqno, IRQF_VALID) 結(jié)果是: 消除 irqno 對(duì)應(yīng)的IRQ_NOREQUEST標(biāo)志, 告訴系統(tǒng)該中斷被可申請(qǐng)使用了.中斷申請(qǐng)的時(shí)候會(huì)查看該標(biāo)志如果設(shè)置了表示中斷尚不可用

執(zhí)行完 init_IRQ之后 irq_desc[] 中各個(gè)數(shù)組項(xiàng)的chip, handle_irq字段都被設(shè)置好了.

(3) 用戶注冊(cè)中斷處理函數(shù)流程

驅(qū)動(dòng)程序使用 request_irq 來(lái)向內(nèi)核注冊(cè)中斷處理函數(shù), request_irq 根據(jù)中斷號(hào)找到 irq_desc[] 數(shù)組項(xiàng), 在數(shù)組項(xiàng)的 action 鏈表中添加一個(gè)表項(xiàng).

file: (linux)/kernel/irq/manage.c
int request_irq(unsigned int irq, irq_handle_t handler, unsinged long irqflags, const char *devname, void *dev_id)
{
    struct irqaction *action;             //創(chuàng)建 irqaction 結(jié)構(gòu)體指針   
    ...
    action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);//填充 irqaction 結(jié)構(gòu)體各個(gè)元素
    action->handler = handler;    
    action->flags = irqflags;
    cpus_clear(action->mask);
    action->name = devname;
    action->next = NULL;
    action->dev_id = dev_id;
    
    retval = setup_irq(irq, action);      
    ...
}

setup_irq函數(shù)有幾個(gè)功能: 

1. 將 action 連接入 irq_desc[irq]的action鏈表中

2. irq_desc[].chip 結(jié)構(gòu)體中沒(méi)有被 init_irq() 設(shè)置的元素設(shè)置為默認(rèn)值

3. 使用傳入的 irqflags 作為參數(shù) 調(diào)用 chip->set_type來(lái)設(shè)置中斷: 設(shè)置為外部中斷, 設(shè)置觸發(fā)方式(高電平/低電平/上升沿/下降沿)

4. 啟動(dòng)中斷, 調(diào)用chip->startup 或 chip->enable 使能中斷, 

總之, 調(diào)用了request_irq之后: irq_desc[]的action結(jié)構(gòu)體已鏈入action鏈表, 中斷觸發(fā)方式也設(shè)置好了, 中斷已被使能. 現(xiàn)在中斷已經(jīng)可以發(fā)生并處理了

(4) 中斷具體的執(zhí)行流程

中斷的總?cè)肟谑?asm_do_IRQ()

file: (linux)/arch/arm/kernel/irq.c 
asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs){
        ...
	struct irq_desc *desc = irq_desc + irq;
        ...
        desc_handle_irq(irq, desc);
        ...
}

file: (linux)/include/asm-arm/mach/irq.h
static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc){
	desc->handle_irq(irq, desc);
}

irq 取值范圍: IRQ0_EINT0~(IRQ_EINT0+31).  因?yàn)?440的INTPND共32bit, 但是irq_desc[]卻遠(yuǎn)不止32個(gè)元素, 因?yàn)橛行﹤€(gè)中斷是共用同一個(gè)位, 如 EINT8~23. 所以當(dāng) asm_do_IRQ 的 irq 是對(duì)應(yīng)于"一組"子中斷的時(shí)候, irq_desc[irq].handle_irq 函數(shù)還需要判斷出到底是哪個(gè)子中斷申請(qǐng)的中斷, 假設(shè)該子中斷號(hào)為irqno 那么繼續(xù)調(diào)用 irq_decs[irqno].handle_irq 來(lái)處理

  以外部中斷EINT8~23為例來(lái)講解中斷調(diào)用的詳細(xì)過(guò)程:

1 當(dāng)EINT8~23 中任意一個(gè)中斷觸發(fā)的時(shí)候, INTOFFSET 寄存器的值都是 5.  asm_do_IRQ 的 irq 就是IRQ_EINT0+5 即 IRQ_EINT8t23, 然后調(diào)用 irq_desc[IRQ_EINT0t23].handle_irq 來(lái)處理

2 irq_desc[IRQ_EINT8t23].handle_irq 設(shè)置是在中斷初始化的時(shí)候完成的: init_IRQ -> init_arch_irq() 該函數(shù)指針在linux啟動(dòng)的時(shí)候被賦值為 s3c24xx_init_irq ->

file: (linux)/arch/plat-s3c24xx/irq.c
    set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint4t7);
    set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8); //irq_desc[IRQ_EINT8t23].handle_irq 設(shè)置為 s3c_irq_demux_extint8
    set_irq_chained_handler(IRQ_UART0, s3c_irq_demux_uart0);
    set_irq_chained_handler(IRQ_UART1, s3c_irq_demux_uart1);
    set_irq_chained_handler(IRQ_UART2, s3c_irq_demux_uart2);
    set_irq_chained_handler(IRQ_ADCPARENT, s3c_irq_demux_adc);

接下來(lái)看看 s3c_irq_demux_extint8 做了些什么

static void s3c_irq_demux_extint8(unsigned int irq, struct irq_desc *desc){
	unsigned long eintpnd = __raw_readl(S3C24XX_EINTPEND);
	unsigned long eintmsk = __raw_readl(S3C24XX_EINTMASK);

	eintpnd &= ~eintmsk;
	eintpnd &= ~0xff;	/* ignore lower irqs */

	/* we may as well handle all the pending IRQs here */
	while (eintpnd) {
		irq = __ffs(eintpnd);
		eintpnd &= ~(1<<irq);

		irq += (IRQ_EINT4 - 4);
		desc_handle_irq(irq, irq_desc + irq);
	}
}

他首先讀取了EINTPEND/EINTMASK寄存器, 查找出具體發(fā)生的中斷源, 然后重新計(jì)算中斷號(hào), 最后使用新計(jì)算出的這個(gè)中斷號(hào)調(diào)用 irq_desc[新中斷號(hào)].handle_irq

 s3c_irq_demux_extint8 與 handle_edge_irq / handle_level_irq 是什么關(guān)系?

s3c_irq_demux_extint8最后調(diào)用 irq_desc[新中斷號(hào)].handle_irq 這個(gè)入口函數(shù)就是在 s3c24xx_init_irq 中定義的中斷入口函數(shù) set_irq_handler(irqno, handle_edge_irq); 中的 handle_edge_irq 

上述就是小編為大家分享的什么是linux異常體系結(jié)構(gòu)了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

當(dāng)前文章:什么是linux異常體系結(jié)構(gòu)
文章源于:http://muchs.cn/article16/jpepgg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站內(nèi)鏈、小程序開(kāi)發(fā)、網(wǎng)站建設(shè)、網(wǎng)站策劃、云服務(wù)器、網(wǎng)站收錄

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設(shè)