這期內(nèi)容當(dāng)中小編將會給大家?guī)碛嘘P(guān)什么是linux異常體系結(jié)構(gòu),文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
在新洲等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供成都網(wǎng)站制作、網(wǎng)站建設(shè) 網(wǎng)站設(shè)計制作按需網(wǎng)站策劃,公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),成都品牌網(wǎng)站建設(shè),成都營銷網(wǎng)站建設(shè),外貿(mào)網(wǎng)站制作,新洲網(wǎng)站建設(shè)費(fèi)用合理。
以arm處理器為例, 外部中斷和處理器內(nèi)核異常(soc內(nèi)部)都屬于異常, 異常都是相對于主程序來講的, 當(dāng)soc正常執(zhí)行主程序時, 中斷和異常都可以打斷它, 依據(jù)異常對于主程序所體現(xiàn)出來的"中斷"性質(zhì)可以區(qū)分出中斷和異常的區(qū)別:
異常: 由于soc本身的內(nèi)核活動產(chǎn)生的, 如當(dāng)執(zhí)行主程序時候由于arm soc預(yù)取指令/數(shù)據(jù)而產(chǎn)生異常等, 這個異常來自soc的內(nèi)核, 所以對于soc內(nèi)核來說是"同步"的.
中斷: 當(dāng)soc在執(zhí)行主程序的時候, 各種片上外設(shè)和外部中斷引腳可以產(chǎn)生一個異常來中斷soc當(dāng)前正在執(zhí)行的程序, 這個異常信號來自soc的內(nèi)核以外, 所以對于soc內(nèi)核來說他是"異步"的
在arm架構(gòu)的linux系統(tǒng)下, 共使用了5種異常: 1. 未定義指令異常 2. 指令預(yù)取中止異常 3. 數(shù)據(jù)訪問中止異常 4. 中斷異常 5. swi異常.
linux內(nèi)核在(linux)/include/asm-arm/arch-s3c2410/irqs.h 中將所有的中斷統(tǒng)一編號, 另外 linux使用一個結(jié)構(gòu)體數(shù)組來描述中斷, 并且這個數(shù)組的下標(biāo)就是中斷號. 每個數(shù)組項(xiàng)對應(yīng)一組中斷,
file: (linux)/include/linux/irq.h struct irq_desc { irq_flow_handler_t handle_irq; /* 當(dāng)前中斷的處理函數(shù)入口 */ struct irq_chip *chip; /* 底層的硬件訪問 */ ... struct irqaction *action; /* 用戶提供的中斷處理函數(shù)鏈表 */ unsigned int status; /* IRQ 狀態(tài) */ ... const char *name; /* cat /proc/interrupts 顯示的中斷名稱 */ } ____cacheline_internodealigned_in_smp;
其中的 chip 提供了底層的硬件訪問:
file: (linux)/include/linux/irq.h struct irq_chip { const char *name; unsigned int (*startup)(unsigned int irq); /* 啟動中斷, 缺省為 "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)前中斷使得可以接收下一個中斷 */ void (*mask)(unsigned int irq); /* 屏蔽中斷源 */ void (*mask_ack)(unsigned int irq); /* 屏蔽和響應(yīng)中斷 */ void (*unmask)(unsigned int irq); /* 開啟中斷源 */ ... };
另外irqaction結(jié)構(gòu)體為:
file: (linux)/include/linux/interrupt.h struct irqaction { irq_handler_t handler; //用戶注冊的中斷處理函數(shù) unsigned long flags; //中斷標(biāo)志 cpumask_t mask; //用于smp const char *name; //用戶注冊的名字 "cat /proc/interrupts" 可以看到 void *dev_id; //用戶傳給上面的handler參數(shù),還可以用來區(qū)分共享中斷 struct irqaction *next; //指向下一個irqaction結(jié)構(gòu) int irq; //中斷號 struct proc_dir_entry *dir; };
中斷執(zhí)行的流程是: 中斷到來時總中斷入口函數(shù) asm_do_IRQ()根據(jù) 中斷號找到 irq_desc 結(jié)構(gòu)體數(shù)組中的對應(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鏈表中注冊的中斷處理函數(shù).
在 (linux)/init/main.c 的 start_kernel() 函數(shù)中的 trap_init() / init_IRQ() 來設(shè)置異常的處理函數(shù).
1. trap_init() 和 early_trap_init() 函數(shù) 通過下邊語句將異常向量復(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,因其與具體開發(fā)板密切相關(guān),所以需要單獨(dú)列出來. 它用來初始化中斷的框架,設(shè)置各個中斷的默認(rèn)處理函數(shù). 當(dāng)發(fā)生中斷時候進(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 (未請求 | 未探測)
2 init_arch_irq 其實(shí)是一個函數(shù)指針, 定義如下:
file: (linux)/arch/arm/kernel/irq.c void (*init_arch_irq)(void) __initdata = NULL;
2440移植好的linux系統(tǒng)在啟動的時候通過(linux)/arch/arm/kernel/setup.c 的 setup_arch() 獲取到 machine_desc 結(jié)構(gòu)體, 然后將 init_arch_irq 這個函數(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)體的成員來設(shè)置觸發(fā)方式/使能中斷/禁止中斷等
set_irq_handler(irqno, handle_edge_irq) 結(jié)果是: irq_desc[irqno].handle_irq = handle_edge_irq; 這是該中斷號對應(yīng)的中斷函數(shù)入口
set_irq_flags(irqno, IRQF_VALID) 結(jié)果是: 消除 irqno 對應(yīng)的IRQ_NOREQUEST標(biāo)志, 告訴系統(tǒng)該中斷被可申請使用了.中斷申請的時候會查看該標(biāo)志如果設(shè)置了表示中斷尚不可用
執(zhí)行完 init_IRQ之后 irq_desc[] 中各個數(shù)組項(xiàng)的chip, handle_irq字段都被設(shè)置好了.
驅(qū)動程序使用 request_irq 來向內(nèi)核注冊中斷處理函數(shù), request_irq 根據(jù)中斷號找到 irq_desc[] 數(shù)組項(xiàng), 在數(shù)組項(xiàng)的 action 鏈表中添加一個表項(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)體各個元素 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ù)有幾個功能:
1. 將 action 連接入 irq_desc[irq]的action鏈表中
2. irq_desc[].chip 結(jié)構(gòu)體中沒有被 init_irq() 設(shè)置的元素設(shè)置為默認(rèn)值
3. 使用傳入的 irqflags 作為參數(shù) 調(diào)用 chip->set_type來設(shè)置中斷: 設(shè)置為外部中斷, 設(shè)置觸發(fā)方式(高電平/低電平/上升沿/下降沿)
4. 啟動中斷, 調(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個元素, 因?yàn)橛行﹤€中斷是共用同一個位, 如 EINT8~23. 所以當(dāng) asm_do_IRQ 的 irq 是對應(yīng)于"一組"子中斷的時候, irq_desc[irq].handle_irq 函數(shù)還需要判斷出到底是哪個子中斷申請的中斷, 假設(shè)該子中斷號為irqno 那么繼續(xù)調(diào)用 irq_decs[irqno].handle_irq 來處理
以外部中斷EINT8~23為例來講解中斷調(diào)用的詳細(xì)過程:
1 當(dāng)EINT8~23 中任意一個中斷觸發(fā)的時候, INTOFFSET 寄存器的值都是 5. asm_do_IRQ 的 irq 就是IRQ_EINT0+5 即 IRQ_EINT8t23, 然后調(diào)用 irq_desc[IRQ_EINT0t23].handle_irq 來處理
2 irq_desc[IRQ_EINT8t23].handle_irq 設(shè)置是在中斷初始化的時候完成的: init_IRQ -> init_arch_irq() 該函數(shù)指針在linux啟動的時候被賦值為 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);
接下來看看 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<他首先讀取了EINTPEND/EINTMASK寄存器, 查找出具體發(fā)生的中斷源, 然后重新計算中斷號, 最后使用新計算出的這個中斷號調(diào)用 irq_desc[新中斷號].handle_irq
s3c_irq_demux_extint8 與 handle_edge_irq / handle_level_irq 是什么關(guān)系?
s3c_irq_demux_extint8最后調(diào)用 irq_desc[新中斷號].handle_irq 這個入口函數(shù)就是在 s3c24xx_init_irq 中定義的中斷入口函數(shù) set_irq_handler(irqno, handle_edge_irq); 中的 handle_edge_irq
上述就是小編為大家分享的什么是linux異常體系結(jié)構(gòu)了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
文章名稱:什么是linux異常體系結(jié)構(gòu)
當(dāng)前路徑:http://weahome.cn/article/jpepgg.html