要使用中断肯定得初始化,这些初始化在系统启动时已经为你做好了,但是我们还是来看看怎样初始化的,这样就能更好的理解中断机制了。
先看下面函数:

355 void __init init_ISA_irqs (void)
356 {
357 int i;
358 
//省略了一些代码

362 init_8259A(0);
363 
364 for (i = 0; i < NR_IRQS; i++) {
365 irq_desc[i].status = IRQ_DISABLED;
366 irq_desc[i].action = NULL;
367 irq_desc[i].depth = 1;
368 
369 if (i < 16) {
370 /*
371 * 16 old-style INTA-cycle interrupts:
372 */
373 set_irq_chip_and_handler_name(i, &i8259A_chip,
374 handle_level_irq, "XT");
375 } else {
376 /*
377 * 'high' PCI IRQs filled in on demand
378 */
379 irq_desc[i].chip = &no_irq_chip;
380 }
381 }
382 }

上面的函数分为两个部分,一 个是init_8259A(0),另一个就是for循环了。从名字上我们就可以看出init_8259A()是干什么的了,它是用来初始化8259A的, 设置它的相关寄存器,指明了它的工作方式。下面的for循环,一共循环了NR_IRQS(224)次,将刚才所讲的irq_desc[]初始化,初始化了 status,action,depth字段,然后如果是前16个调用set_irq_chip_and_handler_name(),再往下看如果大 于16,那么给字段chip赋no_irq_chiq这个定义如下 (kernel/irq/handle.c):

88 struct irq_chip no_irq_chip = { 
 89 .name = "none",
 90 .startup = noop_ret,
 91 .shutdown = noop,
 92 .enable = noop,
 93 .disable = noop,
 94 .ack = ack_bad,
 95 .end = noop,
 96 };

也就是说大于16是不需要中断控制器的。
然后再来看函数set_irq_chip_and_handler_name(),设置irq_desc[]数组中的chip和handle_irq字段,其函数定义如下:

594 set_irq_chip_and_handler_name(unsigned int irq, struct irq_chip *chip,
595 irq_flow_handler_t handle, const char *name)
596 {
597 set_irq_chip(irq, chip);//设置字段chip
598 __set_irq_handler(irq, handle, 0, name);//设置字段handler
599 } 

函数set_irq_chip()中的主要代码为(kernel/irq/chip.c):

int set_irq_chip(unsigned int irq, struct irq_chip *chip)

struct irq_desc *desc;
desc = irq_desc + irq;//找到所要初始化的irq_desc

desc->chip = chip;

函数__set_irq_handler()定义如下(kernel/irq/chip.c):

void
 __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, 
                   const char *name)
{
struct irq_desc *desc;
desc = irq_desc + irq;
desc->handle_irq = handle;//给字段handle_irq赋一个函数

desc->name = name;

也就是说执行 set_irq_chip_and_handler_name(i, &i8259A_chip,handle_level_irq, "XT")后给数组irq_desc[]的第i个字段chip,handle_irq,name分别赋值 i8259A_chip,handle_level_irq,XT。而其中handle_level_irq是一个函数指针,定义在kernel/irq /chip.c重要代码如下:

void fastcall
handle_level_irq(unsigned int irq, struct irq_desc *desc)
{
struct irqaction *action;
irqreturn_t action_ret;
     action = desc->action;
     action_ret = handle_IRQ_event(irq, action);/* 注意这个函数*/
}

至此init_ISA_irqs (void)到这里结束,那么谁来调用它了?看下面代码:

387 void __init native_init_IRQ(void)
388 {
389 int i;
//...

392 pre_intr_init_hook();
//...

399 for (i = 0; i < (NR_VECTORS - FIRST_EXTERNAL_VECTOR); i++) {
400 int vector = FIRST_EXTERNAL_VECTOR + i;
401 if (i >= NR_IRQS)
402 break;
403 if (vector != SYSCALL_VECTOR) 
404 set_intr_gate(vector, interrupt[i]);
405 }
420 } 

其中将392行的函数pre_intr_init_hook()展开后就会发现:

30 void __init pre_intr_init_hook(void)
 31 {
 32 init_ISA_irqs();
 33 }

也就是在函数 native_init_IRQ()里调用了init_ISA_irqs (void)。接着看native_init_IRQ()中的for循环,循环了224次(NR_VECTORS - FIRST_EXTERNAL_VECTOR),vector从0x20(FIRST_EXTERNAL_VECTOR)开始。且不等于 0x80(vector != SYSCALL_VECTOR),即系统调用,循环执行set_intr_gate(vector, interrupt[i]),这个函数就是设置IDT中的中断门描述符(前面讲了,占8字节,主要保存中断处理程序的如够函数地址),将中断处理函数入口 函数地址设置为interrupt[i],当第i个中断发生后跳转到此地址执行。那么interrupt[]数组又是怎么实现的了?

69 #define IRQ(x,y) \
 70 IRQ##x##y##_interrupt
 71 
 72 #define IRQLIST_16(x) \
 73 IRQ(x,0), IRQ(x,1), IRQ(x,2), IRQ(x,3), \
 74 IRQ(x,4), IRQ(x,5), IRQ(x,6), IRQ(x,7), \
 75 IRQ(x,8), IRQ(x,9), IRQ(x,a), IRQ(x,b), \
 76 IRQ(x,c), IRQ(x,d), IRQ(x,e), IRQ(x,f)
 77 
 78 /* for the irq vectors */
 79 static void (*interrupt[NR_VECTORS - FIRST_EXTERNAL_VECTOR])(void) = {
 80 IRQLIST_16(0x2), IRQLIST_16(0x3),
 81 IRQLIST_16(0x4), IRQLIST_16(0x5), IRQLIST_16(0x6), IRQLIST_16(0x7),
 82 IRQLIST_16(0x8), IRQLIST_16(0x9), IRQLIST_16(0xa), IRQLIST_16(0xb),
 83 IRQLIST_16(0xc), IRQLIST_16(0xd), IRQLIST_16(0xe), IRQLIST_16(0xf)
 84 };

先看79行,这个就是函数指针数组interrupt[],注意这个数组共224项,定义了从0x2到0xf的14个IRQLIST_16(),而每一个IRQLIST_16()又是16个IRQ(x,y),也就是说一共有224个IRQ(x,y)。
接着再看宏#define IRQ(x,y) IRQ##x##y##_interrupt
## 表示将字符串连接起来,比如IRQ(0x2,0)就是IRQ0x20_interrupt,这样以来就会生成224个这样的函数,从 IRQ0x20_interrupt一直到IRQ0xff_interupt。那么这些函数是如何定义的了?往下看include/asm-x86_64 /hw_irq.h:

156 #define IRQ_NAME2(nr) nr##_interrupt(void)
157 #define IRQ_NAME(nr) IRQ_NAME2(IRQ##nr)

163 #define BUILD_IRQ(nr) \
164 asmlinkage void IRQ_NAME(nr); \
165 __asm__( \
166 "\n.p2align\n" \
167 "IRQ" #nr "_interrupt:\n\t" \
168 "push $~(" #nr ") ; " \
169 "jmp common_interrupt");

根据156和157两行的宏 定义可以知道IRQ_NAME(nr)是一个函数IRQnr_interrupt()就是我们刚才上面所提到的函数数组中interrupt[]的一个, 至于是哪一个就看nr是多少了。这个函数将nr取反(正数留给系统调用)压入堆栈中,后面用到的时候再取反就可以知道中断号了,然后跳转到 common_interrupt,这是一个公共的程序,暂且不说。
这样我们就定义了一个interrupt[]数组中的一个函数,这只是一个函数,通过以下的宏就可以定义224个了arch/x86_64/kernel/i8259.c:

60 BUILD_16_IRQS(0x2) BUILD_16_IRQS(0x3)
 61 BUILD_16_IRQS(0x4) BUILD_16_IRQS(0x5) BUILD_16_IRQS(0x6)BUILD_16_IRQS(0x7)
 62 BUILD_16_IRQS(0x8) BUILD_16_IRQS(0x9) BUILD_16_IRQS(0xa)BUILD_16_IRQS(0xb)
 63 BUILD_16_IRQS(0xc) BUILD_16_IRQS(0xd) BUILD_16_IRQS(0xe)BUILD_16_IRQS(0xf)

这样以后就定义整个interrupt[]中的每一函数。
至此set_intr_gate(vector, interrupt[i]);执行结束。这样我们就对IDT初始化成功。

From https://sites.google.com/site/xylinuxkernel/Home/interrupt

[Fw]中断的初始化的更多相关文章

  1. [fw]LINUX中断描述符初始化

    LINUX中断描述符初始化 @CopyLeft by ICANTH,I Can do ANy THing that I CAN THink!~ Author: WenHui, WuHan Univer ...

  2. 中断——中断描述符表的定义和初始化(一) (基于3.16-rc4)

    1.中断描述符表的定义(arch/x86/kernel/traps.c) gate_desc debug_idt_table[NR_VECTORS] __page_aligned_bss; 定义的描述 ...

  3. Linux x86_64内核中断初始化

    Linux x86_64内核中断初始化 [TOC] 中断分类 Linux系统中,中断分为: 硬中断:由外部设备或者执行异常产生的需要快速处理的中断.如缺页中断.定时器硬件中断. 根据内部产生还是外部产 ...

  4. linux中断源码分析 - 初始化(二)

    本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 本篇文章主要讲述源码中是如何对中断进行一系列的初始化的. 回顾 在上一篇概述中,介绍了几个对于中断来说非常重要的 ...

  5. linux中断子系统:中断号的映射与维护初始化mmap过程

    本文均属自己阅读源代码的点滴总结.转账请注明出处谢谢. 欢迎和大家交流.qq:1037701636 email:gzzaigcn2009@163.com 写在前沿: 好久好久没有静下心来整理一些东西了 ...

  6. [fW]中断处理函数数组interrupt[]初始化

    中断处理函数数组interrupt[]初始化 2011-05-13 15:51:40 分类: LINUX 在系统初始化期间,trap_init()函数将对中断描述符表IDT进行第二次初始化(第一次只是 ...

  7. [Fw]初探linux中断系统(2)

    初探linux中断系统(2) 中断系统初始化的过程 用来初始化中断系统的函数位于arch/x86/kernel/irqinit.c,定义如下 void __init init_IRQ(void){ i ...

  8. [fw]IDT表的初始化

    IDT表的初始化  linux内核的中断描述符表IDT是一个全局的数据,在i386平台上被定义为: struct desc_struct idt_table[256] __attribute__((_ ...

  9. Intel 80x86 Linux Kernel Interrupt(中断)、Interrupt Priority、Interrupt nesting、Prohibit Things Whthin CPU In The Interrupt Off State

    目录 . 引言 . Linux 中断的概念 . 中断处理流程 . Linux 中断相关的源代码分析 . Linux 硬件中断 . Linux 软中断 . 中断优先级 . CPU在关中断状态下编程要注意 ...

随机推荐

  1. 下载了包在node_modules中,但没有在package.json中保存该包信息。

    发现安装了包,但没有在package.json中保存该包信息,而且没有创建package-lock.json. 经过测试,发现是使用cnpm的原因,使用npm安装不会出现这样的问题,(与cnpm版本无 ...

  2. <select multiple="multiple"> 数据回显

    var names = yunying_name.split(","); for (var i = 0; i < names.length; i++) { names[i] ...

  3. reids 持久化

    RDB: RDB是整个内存压缩过的Snapshot,RDB 的数据结构,可以配置符合的快照触发条件,默认如下 900s  1次修改 300s 10次修改 60s 10000 次修改 自动备份为dump ...

  4. 应用程序无法正常启动(0xc000007b)。请单击“确定”关闭应用程序

    一开始是报缺少dll,随便在电脑里找个同名的dll放下面就报这个错误,网上查的都没有用.后来又找了一个dll,问题就解决了,所以是dll不对造成的.

  5. Java反射初识

    反射Class类 Class类是反射的根源,很多很多的类,经过抽象,得出了一个Class类,包括类名,构造方法,属性方法等.得到Class类的对象的三种方式: Object类中的getClass()方 ...

  6. shiro 安全框架 详解

    ---恢复内容开始--- Shiro 简介 简介• Apache Shiro 是 Java 的一个安全(权限)框架.• Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE 环境, ...

  7. 注解@requestBody自动封装复杂对象 (成功,自己的例子封装的不是一个复杂对象,只是一个简单的User对象,将jsp页面的name转成json字符串,再用JSON.stringify()传参就行了)

    注意:ajax向后台传值的时候,必须加上contentType:"application/json"; springmvc的注解@requestBody可以通过页面提交json来自 ...

  8. Java programming language does not use call by reference for objects!

    Instead, object references are passed by value! A method cannot modify a parameter of a primitive ty ...

  9. 【Flutter学习】之button按钮

    一,概述 由于Flutter是跨平台的,所以有适用于Android和iOS的两种风格的组件.一套是Google极力推崇的Material,一套是iOS的Cupertino风格的组件.无论哪种风格,都是 ...

  10. 最强DE战斗力

    最强DE战斗力 时间限制: 1 Sec  内存限制: 128 MB提交: 40  解决: 14[提交][状态] 题目描述 春秋战国时期,赵国地大物博,资源非常丰富,人民安居乐业.但许多国家对它虎视眈眈 ...