linux中断处理体系结构分析(一),Linux中断处理体系结构分析(二)
1.中斷處理的體系結(jié)構(gòu)
我們知道編寫設(shè)備驅(qū)動程序一定要用到中斷處理函數(shù),這在驅(qū)動程序的編寫中,占據(jù)很重要的一部分。在響應(yīng)一個特定的中斷的時候,內(nèi)核會執(zhí)行一個函數(shù),該函數(shù)叫做中斷處理程序(interrupt handler)或中斷服務(wù)例程(interrupt service routine ,ISP).產(chǎn)生中斷的每個設(shè)備都有一個相應(yīng)的中斷處理程序,中斷處理程序通常不和特定的設(shè)備關(guān)聯(lián),而是和特定的中斷關(guān)聯(lián)的,也就是說,如果一個設(shè)備可以產(chǎn)生多種不同的中斷,那么該就可以對應(yīng)多個中斷處理程序,相應(yīng)的,該設(shè)備的驅(qū)動程序也就要準(zhǔn)備多個這樣的函數(shù)。在Linux內(nèi)核中處理中斷是分為上半部(top half),和下半部(bottom half)之分的。上半部只做有嚴(yán)格時限的工作,例如對接收到的中斷進(jìn)行應(yīng)答或復(fù)位硬件,這些工作是在所有的中斷被禁止的
情況下完成的,能夠被允許稍后完成的工作會推遲到下半部去。要想了解上半部和下半部的機(jī)制可以閱讀一下《Linux內(nèi)核設(shè)計與實現(xiàn)》的第七章的內(nèi)容。
Linux內(nèi)核將所有的中斷統(tǒng)一編號,使用一個irq_desc結(jié)構(gòu)數(shù)組來描述這些中斷;每個數(shù)組項對應(yīng)一個中斷,也可能是一組中斷,它們共用相同的中斷號,里面記錄了中斷的名稱、中斷狀態(tài)、中斷標(biāo)記(比如中斷類型、是否共享中斷等),并提供了中斷的低層硬件訪問函數(shù)(清除、屏蔽、使能中斷),提供了這個中斷的處理函數(shù)入口,通過它可以調(diào)用用戶注冊的中斷處理函數(shù)。
通過irq_desc結(jié)構(gòu)數(shù)組就可以了解中斷處理體系結(jié)構(gòu),irq_desc結(jié)構(gòu)的數(shù)據(jù)類型include/linux/irq.h
中定義,
struct irq_desc {
unsigned int????????irq;
struct timer_rand_state *timer_rand_state;
unsigned int *kstat_irqs;
#ifdef CONFIG_INTR_REMAP
struct irq_2_iommu *irq_2_iommu;
#endif
irq_flow_handler_t????handle_irq; // 當(dāng)前中斷的處理函數(shù)入口
struct irq_chip????????*chip; //低層的硬件訪問
struct msi_desc????????*msi_desc;
void????????????*handler_data;
void????????????*chip_data;
struct irqaction????*action;????// 用戶提供的中斷處理函數(shù)鏈表
unsigned int????????status;????????//IRQ狀態(tài)
........
const char????????*name; //中斷的名稱
} ____cacheline_internodealigned_in_smp;?? handle_irq是這個或這組中斷的處理函數(shù)入口。發(fā)生中斷時,總?cè)肟诤瘮?shù)asm_do_IRQ將根據(jù)中斷號調(diào)用相應(yīng)irq_desc數(shù)組項中handle_irq.handle_irq使用chip結(jié)構(gòu)中的函數(shù)清除、屏蔽或者重新使能中斷,還要調(diào)用用戶在action鏈表中注冊的中斷處理函數(shù)。
irq_chip結(jié)構(gòu)類型也是在include/linux/irq.h中定義,其中的成員大多用于操作底層硬件,比如設(shè)置寄存器以屏蔽中斷,使能中斷,清除中斷等。
struct irq_chip {
const char????*name;
unsigned int????(*startup)(unsigned int irq);//啟動中斷,如果不設(shè)置,缺省為“enable
void????????(*shutdown)(unsigned int irq);/*關(guān)閉中斷,如果不設(shè)置,缺省為"disable"*/
void????????(*enable)(unsigned int irq);// 使用中斷,如果不設(shè)置,缺省為"unmask"
void????????(*disable)(unsigned int irq);//禁止中斷,如果不設(shè)置,缺省為“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);//開啟中斷源
void????????(*eoi)(unsigned int irq);
........
const char????*typename;
};irq_desc結(jié)構(gòu)中的irqaction結(jié)構(gòu)類型在include/linux/iterrupt.h中定義。用戶注冊的每個中斷
處理函數(shù)用一個irqaction結(jié)構(gòu)來表示,一個中斷比如共享中斷可以有多個處理函數(shù),它們的irqaction結(jié)
構(gòu)鏈接成一個鏈表,以action為表頭。irqation結(jié)構(gòu)定義如下:
struct irqaction {
irq_handler_t handler; //用戶注冊的中斷處理函數(shù)
unsigned long flags; //中斷標(biāo)志,比如是否共享中斷,電平觸發(fā)還是邊沿觸發(fā)
const char *name; //用戶注冊的中斷名字
void *dev_id; //用戶傳給上面的handler的參數(shù),還可以用來區(qū)分共享中斷
struct irqaction *next; //指向下一個用戶注冊函數(shù)的指針
int irq; //中斷號
struct proc_dir_entry *dir;
irq_handler_t thread_fn;
struct task_struct *thread;
unsigned long thread_flags;
};?? irq_desc結(jié)構(gòu)數(shù)組、它的成員“struct irq_chip *chip” "struct irqaction *action",這3種數(shù)據(jù)結(jié)構(gòu)構(gòu)成了中斷處理體系的框架。下圖中描述了Linxu中斷處理體系結(jié)構(gòu)的關(guān)系圖:
中斷處理流程如下
(1)發(fā)生中斷時,CPU執(zhí)行異常向量vector_irq的代碼
(2)在vector_irq里面,最終會調(diào)用中斷處理的總?cè)肟诤瘮?shù)asm_do_IRQ
(3)asm_do_IRQ根據(jù)中斷號調(diào)用irq_desc數(shù)組項中的handle_irq。
(4)handle_irq會使用chip成員中的函數(shù)來設(shè)置硬件,比如清除中斷、禁止中斷、重新使能中斷等
(5)handle_irq逐個調(diào)用用戶在aciton鏈表中注冊的處理函數(shù)
中斷體系結(jié)構(gòu)的初始化就是構(gòu)造這些數(shù)據(jù)結(jié)構(gòu),比如irq_desc數(shù)組項中的handle_irq、chip等成員;用戶注冊中斷時就是構(gòu)造action鏈表;用戶卸載中斷時就是從action鏈表中去除不需要的項。
2.中斷處理體系結(jié)構(gòu)的初始化
init_IRQ函數(shù)被用來初始化中斷處理體系結(jié)構(gòu),代碼在arch/arm/kernel/irq.c中
153 void __init init_IRQ(void)
154 {
155 int irq;
156
157 for (irq = 0; irq < NR_IRQS; irq++)
158 irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE;
159
160 init_arch_irq();
161 }157~~158行 初始化irq_desc結(jié)構(gòu)數(shù)組中每一項的中斷狀態(tài)
第160行調(diào)用架構(gòu)相關(guān)的中斷初始化函數(shù)。對于S3C2440開發(fā)板,這個函數(shù)就是s3c24xx_init_irq,移植machine_desc結(jié)構(gòu)中的init_irq成員就指向這個函數(shù)s3c24xx_init_irq函數(shù)在arch/arm/plat-s3c24xx/irq.c中定義,它為所有中斷設(shè)置了芯片相關(guān)的數(shù)據(jù)結(jié)構(gòu)(irq_desc[irq].chip),設(shè)置了處理函數(shù)入口(irq_desc[irq].handle_irq)。以外部中斷EINT4-EINT23為例,用來設(shè)置它們的代碼如下:
void __init s3c24xx_init_irq(void)
534 {
535 unsigned long pend;
536 unsigned long last;
537 int irqno;
538 int i;
........
637 for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) {
638 irqdbf("registering irq %d (extended s3c irq)\n", irqno);
639 set_irq_chip(irqno, &s3c_irqext_chip);
640 set_irq_handler(irqno, handle_edge_irq);
641 set_irq_flags(irqno, IRQF_VALID);
...............655 for (irqno = IRQ_S3CUART_RX1; irqno <= IRQ_S3CUART_ERR1; irqno++) {
656 irqdbf("registering irq %d (s3c uart1 irq)\n", irqno);
657 set_irq_chip(irqno, &s3c_irq_uart1);
658 set_irq_handler(irqno, handle_level_irq);
659 set_irq_flags(irqno, IRQF_VALID);
660 }
..........
676 irqdbf("s3c2410: registered interrupt handlers\n");
677 }
678
在639行set_irq_chip函數(shù)的作用就是“irq_desc[irno].chip = &s3c_irqext_chip”,以后就可能通過irq_desc[irqno].chip結(jié)構(gòu)中的函數(shù)指針設(shè)置這些外部中斷的觸發(fā)方式(電平觸發(fā),邊沿觸發(fā)),使能中斷,禁止中斷。
在640行設(shè)置這些中斷的處理函數(shù)入口為handle_edge_irq,即“irq_desc[irqno].handle_irq =handle_edge_irq”.發(fā)生中斷時,handle_edge_irq函數(shù)會調(diào)用用戶注冊的具體處理函數(shù);?在641行設(shè)置中斷標(biāo)志為“IRQF_VALID”,表示可以使用它們。init_IRQ函數(shù)執(zhí)行完后,irq_desc數(shù)組項的chip,handl_irq成員都被設(shè)置
2.2 用戶注冊中斷處理函數(shù)的過程
用戶驅(qū)動程序通過request_irq函數(shù)向內(nèi)核注冊中斷處理函數(shù),request_irq函數(shù)根據(jù)中斷號找到irq_desc數(shù)組項,然后在它的action鏈表添加一個表項。原先的內(nèi)核中requset_irq函數(shù)在kernel/irq/manage.c中定義,而現(xiàn)在2.6.32版本中,進(jìn)行了改變,可以看這篇文章,這里解釋了,在2.6.32內(nèi)核中我們可以看到找不到了request_irq函數(shù)的實現(xiàn),而是用request_threaded_irq()函數(shù)給替換了。我們可以在inclue/linux/interrupt.h中找到這個函數(shù)的原型。
110 #ifdef CONFIG_GENERIC_HARDIRQS
111 extern int __must_check
112 request_threaded_irq(unsigned int irq, irq_handler_t handler,
113 irq_handler_t thread_fn,
114 unsigned long flags, const char *name, void *dev);
115
116 static inline int __must_check
117 request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
118 const char *name, void *dev)
119 {
120 return request_threaded_irq(irq, handler, NULL, flags, name, dev);
121 }
123 extern void exit_irq_thread(void);
124 #else
126 extern int __must_check
127 request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
128 const char *name, void *dev);
136 static inline int __must_check
137 request_threaded_irq(unsigned int irq, irq_handler_t handler,
138 irq_handler_t thread_fn,
139 unsigned long flags, const char *name, void *dev)
140 {
141 return request_irq(irq, handler, flags, name, dev);
142 }
143
144 static inline void exit_irq_thread(void) { }
145 #endif其實具體的實現(xiàn)在request_threaded_irq函數(shù)中,也是在/kernel/irq/manage.c中定義,requset_threaded_irq函數(shù)首先使用這4個參數(shù)構(gòu)造一個irqaction結(jié)構(gòu),然后調(diào)用setup_irq函數(shù)將它鏈入鏈表中,
1003 int request_threaded_irq(unsigned int irq, irq_handler_t handler,
1004 irq_handler_t thread_fn, unsigned long irqflags,
const char *devname, void *dev_id)
.............
1056 action->handler = handler;
1057 action->thread_fn = thread_fn;
1058 action->flags = irqflags;
1059 action->name = devname;
1060 action->dev_id = dev_id;
1061
1062 chip_bus_lock(irq, desc);
1084 local_irq_restore(flags);
1085 enable_irq(irq);
...........
1088 return retval;
1089 }
1090 EXPORT_SYMBOL(request_threaded_irq);
setup_irq函數(shù)也是在kernel/irq.manage.c中定義,它完成如下3個主要功能
(1)將新建的irqaction結(jié)構(gòu)鏈入irq_desc[irq]結(jié)構(gòu)的action鏈表中,這有兩種可能。
如果action鏈表為空,則直接鏈入,否則先判斷新建的irqaction結(jié)構(gòu)和鏈表中的irqaction結(jié)構(gòu)所表示的中斷類型是否一致,即是否都聲明為"可共享的"(IRQF_SHARED)、是否都使用相同的觸發(fā)方式,如果一致,則將新建的irqation結(jié)構(gòu)鏈入
(2)設(shè)置irq_desc[irq]結(jié)構(gòu)中chip成員的還沒設(shè)置的指針,讓它們指向一些默認(rèn)函數(shù)
chip成員在init_IRQ函數(shù)初始化中斷體系結(jié)構(gòu)的時候已經(jīng)設(shè)置了,這里只是設(shè)置其中還沒設(shè)置的指針這通過irq_chip_set_defaults函數(shù)來完成,它在kernel/irq/chip.c中定義
296 void irq_chip_set_defaults(struct irq_chip *chip)
297 {
298 if (!chip->enable)
299 chip->enable = default_enable;//調(diào)用chip->unmask
300 if (!chip->disable)
301 chip->disable = default_disable;//此函數(shù)為空
302 if (!chip->startup)
303 chip->startup = default_startup;//調(diào)用chip->enable
310 if (!chip->shutdown)
311 chip->shutdown = chip->disable != default_disable ?
312 chip->disable : default_shutdown;
313 if (!chip->name)
314 chip->name = chip->typename;
315 if (!chip->end)
316 chip->end = dummy_irq_chip.end;
317 }
(4)啟動中斷
如果irq_desc[irq]結(jié)構(gòu)中status成員沒有被指明IRQ_NOAUTOEN(表示注冊中斷時不要使用中斷),還要調(diào)用chip->startup或chip->enable來啟動中斷,所謂啟動中斷通常就是使用中斷。一般情況下,只有那些“可以自動使能的”中斷對應(yīng)的irq_desc[irq].status才會被指明為IRQ_NOAUTOEN,所以,無論哪種情況,執(zhí)行request_irq注冊中斷之后,這個中斷就已經(jīng)被使能了。
總結(jié)一下request_irq函數(shù)注冊
(1)irq_des[irq]結(jié)構(gòu)中的action鏈表中已經(jīng)鏈入了用戶注冊的中斷處理函數(shù)
(2)中斷的觸發(fā)方式已經(jīng)被設(shè)好
(3)中斷已經(jīng)被使能
2.3 中斷的處理過程
asm_do_IRQ是中斷的C語言總?cè)肟诤瘮?shù),它在/arch/arm/kernel/irq.c中定義,
106 asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
107 {
108 struct pt_regs *old_regs = set_irq_regs(regs);
109
110 irq_enter();
111
112 /*
113 * Some hardware gives randomly wrong interrupts. Rather
114 * than crashing, do something sensible.
115 */
116 if (unlikely(irq >= NR_IRQS)) {
117 if (printk_ratelimit())
118 printk(KERN_WARNING "Bad IRQ%u\n", irq);
119 ack_bad_irq(irq);
120 } else {
121 generic_handle_irq(irq);
122 }
123
124 /* AT91 specific workaround */
125 irq_finish(irq);
126
127 irq_exit();
128 set_irq_regs(old_regs);
129 }
desc_hand_irq函數(shù)直接調(diào)用desc結(jié)構(gòu)中的hand_irq成員函數(shù),它就是irq_desc[irq].handle.irq
asm_do_IRQ函數(shù)中參數(shù)irq的取值范圍為IRQ_EINT0~(IRQ_EINT0 + 31),只有32個取值。它可能是一個實際的中斷號,也可能是一組中斷的中斷號。這里有S3C2440的芯片特性決定的:發(fā)生中斷時,INTPND寄存器的某一位被置1,INTOFFSET寄存器中記錄了是哪一位(0--31),中斷向量調(diào)用asm_do_IRQ之前要把INTOFFSET寄存器的值確定irq參數(shù)。每一個實際的中斷在irq_desc數(shù)組中都有一項與它對應(yīng),它們的數(shù)目不止32.當(dāng)asm_do_IRQ函數(shù)參數(shù)irq表示的是“一組”中斷時,irq_desc[irq].handle_irq成員函數(shù)還需要先分辨出是哪一個中斷,然后調(diào)用irq_desc[irqno].handle_irq來進(jìn)一步處理。
以外部中斷EINT8—EINT23為例,它們通常是邊沿觸發(fā)
(1)它們被觸發(fā)里,INTOFFSET寄存器中的值都是5,asm_do_IRQ函數(shù)中參數(shù)irq的值為(IRQ_EINTO+5),即IRQ_EINT8t23,
(2)irq_desc[IRQ_EINT8t23].handle_irq在前面init_IRQ函數(shù)初始化中斷體系結(jié)構(gòu)的時候被設(shè)為s3c_irq_demux_extint8.
(3)s3c_irq_demux_extint8函數(shù)的代碼在arch/arm/plat-s3c24xx/irq.c中,它首先讀取EINTPEND、EINTMASK寄存器,確定發(fā)生了哪些中斷,重新計算它們的中斷號,然后調(diào)用irq_desc數(shù)組項中的handle_irq成員函數(shù)
453 s3c_irq_demux_extint8(unsigned int irq,
454 struct irq_desc *desc)
455 {
456 unsigned long eintpnd = __raw_readl(S3C24XX_EINTPEND); //EINT8-EINT23 發(fā)生時,相應(yīng)位被置1
457 unsigned long eintmsk = __raw_readl(S3C24XX_EINTMASK);//屏蔽寄存器
458
459 eintpnd &= ~eintmsk; //清除被屏蔽的位
460 eintpnd &= ~0xff; /* 清除低8位(EINT8對應(yīng)位8)ignore lower irqs */
461
462 /* 循環(huán)處理所有子中斷*/
463
464 while (eintpnd) {
465 irq = __ffs(eintpnd); //確定eintpnd中為1的最高位
466 eintpnd &= ~(1<
467
468 irq += (IRQ_EINT4 - 4);//重新計算中斷號,前面計算出irq等于8時,中斷號為
IRQ_EINT8
469 generic_handle_irq(irq);//調(diào)用這中斷的真正的處理函數(shù)
470 }
471
472 }
void
(4)IRQ_EINT8--IRQ_EINT23這幾個中斷的處理函數(shù)入口,在init_IRQ函數(shù)初始化中斷體系結(jié)構(gòu)的時候已經(jīng)被設(shè)置為handle_edge_irq函數(shù),desc_handle_irq(irq,irq_desc+irq)就是調(diào)用這個函數(shù),它在kernel/irq/chip.c中定義,它用來處理邊沿觸發(fā)的中斷,
中斷發(fā)生的次數(shù)統(tǒng)計
531 handle_edge_irq(unsigned int irq, struct irq_desc *desc)
532 {
533 spin_lock(&desc->lock);
534
535 desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
536
537 /*
538 * If we're currently running this IRQ, or its disabled,
539 * we shouldn't process the IRQ. Mark it pending, handle
540 * the necessary masking and go out
541 */
542 if (unlikely((desc->status & (IRQ_INPROGRESS | IRQ_DISABLED)) ||
543 !desc->action)) {
544 desc->status |= (IRQ_PENDING | IRQ_MASKED);
545 mask_ack_irq(desc, irq);
546 goto out_unlock;
547 }
548 kstat_incr_irqs_this_cpu(irq, desc);
549
550 /* Start handling the irq */
551 if (desc->chip->ack)
552 desc->chip->ack(irq);
553
554 /* Mark the IRQ currently in progress.*/
555 desc->status |= IRQ_INPROGRESS;
556
557 do {
558 struct irqaction *action = desc->action;
559 irqreturn_t action_ret;
560
561 if (unlikely(!action)) {
562 desc->chip->mask(irq);
563 goto out_unlock;
564 }
565
566 /*
567 * When another irq arrived while we were handling
568 * one, we could have masked the irq.
569 * Renable it, if it was not disabled in meantime.
570 */
571 if (unlikely((desc->status &
572 (IRQ_PENDING | IRQ_MASKED | IRQ_DISABLED)) ==
573 (IRQ_PENDING | IRQ_MASKED))) {
574 desc->chip->unmask(irq);
575 desc->status &= ~IRQ_MASKED;
576 }
577
578 desc->status &= ~IRQ_PENDING;
579 spin_unlock(&desc->lock);
580 action_ret = handle_IRQ_event(irq, action);
581 if (!noirqdebug)
582 note_interrupt(irq, desc, action_ret);
583 spin_lock(&desc->lock);
584
585 } while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING);
586
587 desc->status &= ~IRQ_INPROGRESS;
588 out_unlock:
589 spin_unlock(&desc->lock);
590 }
591
響應(yīng)中斷,通常是清除當(dāng)前中斷使得可以接收下一個中斷,對于IRQ_EINT8~IRQ_EINT23這幾個中斷,desc->chip在前面init_IRQ函數(shù)初始化中斷體系結(jié)構(gòu)的時候被設(shè)為s3c_irqext_chip.desc->chip->ack就是s3c_irqext_ack函數(shù),(arch/armplat-s3c24xx/irq.c)它用來清除中斷
handle_IRQ_event函數(shù)來逐個執(zhí)行action鏈表中用戶注冊的中斷處理函數(shù),它在kernel/irq/handle.c中定義。
do {
379 trace_irq_handler_entry(irq, action);
380 ret = action->handler(irq, action->dev_id);//執(zhí)行用戶注冊的中斷處理函數(shù)
381 trace_irq_handler_exit(irq, action, ret);
382
383 switch (ret) {
384 case IRQ_WAKE_THREAD:
385 /*
386 * Set result to handled so the spurious check
387 * does not trigger.
388 */
389 ret = IRQ_HANDLED;
390
391 /*
392 * Catch drivers which return WAKE_THREAD but
393 * did not set up a thread function
394 */
395 if (unlikely(!action->thread_fn)) {
396 warn_no_thread(irq, action);
397 break;
398 }
399
400 /*
408 if (likely(!test_bit(IRQTF_DIED,
409 &action->thread_flags))) {
410 set_bit(IRQTF_RUNTHREAD, &action->thread_flags);
411 wake_up_process(action->thread);
412 }
413
414 /* Fall through to add to randomness */
415 case IRQ_HANDLED:
416 status |= action->flags;
417 break;
418
419 default:
420 break;
421 }
422
423 retval |= ret;
424 action = action->next; //下一個
425 } while (action);
用戶注冊的中斷處理函數(shù)的參數(shù)為中斷號irq,action->dev_id。后一個參數(shù)是通過request_irq函數(shù)注冊中斷時傳入的dev_id參數(shù),它由用戶自己指定、自己使用,可以為空,當(dāng)這個中斷是“共享中斷”時除外。
對于電平觸發(fā)的中斷,它們的irq_desc[irq].handle_irq通常是handle_level_irq函數(shù)。它也是在kernel/irq/chip.c中定義,其功能與上述handle_edge_irq函數(shù)相似,
對于handle_level_irq函數(shù)已經(jīng)清除了中斷,但是它只限于清除SoC內(nèi)部的的信號,如果外設(shè)輸入到SoC的中斷信號仍然有效,這就會導(dǎo)致當(dāng)前中斷處理完成后,會誤認(rèn)為再次發(fā)生了中斷,對于這種情況,需要用戶注冊的中斷處理函數(shù)中清除中斷,先清除外設(shè)的中斷,然后再清除SoC內(nèi)部的中斷號。
中斷的處理流程可以總結(jié)如下
(1)中斷向量調(diào)用總?cè)肟诤瘮?shù)asm_do_IRQ,傳入根據(jù)中斷號irq
(2)asm_do_IRQ函數(shù)根據(jù)中斷號irq調(diào)用irq_desc[irq].handle_irq,它是這個中斷的處理函數(shù)入口,對于電平觸發(fā)的中斷,這個入口函數(shù)通常為handle_level_irq,對于邊沿觸發(fā)的中斷,這個入口通常為handle_edge_irq
(3)入口函數(shù)首先清除中斷,入口函數(shù)是handle_level_irq時還要屏蔽中斷
(4)逐個調(diào)用用戶在irq_desc[irq].aciton鏈表中注冊的中斷處理函數(shù)
(5)入口函數(shù)是handle_level_irq時還要重新開啟中斷
卸載中斷處理函數(shù)這通過free_irq函數(shù)來實現(xiàn),它與request_irq一樣,也是在kernel/irq/mangage.c中定義。
它需要用到兩個參數(shù):irq和dev_id,它們與通過request_irq注冊中斷函數(shù)時使用的參數(shù)一樣,使用中斷號irq定位action鏈表,再使用dev_id在action鏈表中找到要卸載的表項。同一個中斷的不同中斷處理函數(shù)必須使用不同的dev_id來區(qū)分,在注冊共享中斷時參數(shù)dev_id必惟一。
free_irq函數(shù)的處理過程與request_irq函數(shù)相反
(1)根據(jù)中斷號irq,dev_id從action鏈表中找到表項,將它移除
(2)如果它是惟一的表項,還要調(diào)用IRQ_DESC[IRQ].CHIP->SHUTDOWN或IRQ_DESC[IRQ].CHIP->DISABLW來關(guān)閉中斷。
在響應(yīng)一個特定的中斷的時候,內(nèi)核會執(zhí)行一個函數(shù),該函數(shù)叫做中斷處理程序(interrupt handler)或中斷服務(wù)例程(interrupt service routine ,ISP).產(chǎn)生中斷的每個設(shè)備都有一個相應(yīng)的中斷處理程序,中斷處理程序通常不和特定的設(shè)備關(guān)聯(lián),而是和特定的中斷關(guān)聯(lián)的,也就是說,如果一個設(shè)備可以產(chǎn)生多種不同的中斷,那么該就可以對應(yīng)多個中斷處理程序,相應(yīng)的,該設(shè)備的驅(qū)動程序也就要準(zhǔn)備多個這樣的函數(shù)。在Linux內(nèi)核中處理中斷是分為上半部(top
half),和下半部(bottom
half)之分的。上半部只做有嚴(yán)格時限的工作,例如對接收到的中斷進(jìn)行應(yīng)答或復(fù)位硬件,這些工作是在所有的中斷被禁止的情況下完成的,能夠被允許稍后完成的工作會推遲到下半部去。要想了解上半部和下半部的機(jī)制可以閱讀一下《Linux內(nèi)核設(shè)計與實現(xiàn)》
總結(jié)
以上是生活随笔為你收集整理的linux中断处理体系结构分析(一),Linux中断处理体系结构分析(二)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ML Backpropagation算法
- 下一篇: java 提高性能的 容器库_容器隔离性