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

上篇博文对中断描述符表(IDT)中异常和非屏蔽中断部分的初始化做了说明,这篇文章将分析中断部分的初始化。

在上篇博文中,可以看到,内核在setup_once汇编片段中,对中断和异常部分做了初步的初始化,用early_idt_handlers函数的地址来初始化异常门描述符,用ignore_int函数地址来初始化剩下的中断门描述符。接着,内核在trap_init函数中对IDT做了进一步的初始化,用有效的异常处理程序来初始化中断向量号为0-31的描述符。细心的你应该可以发现,在这一步初始化过程中,仅仅对异常和非屏蔽中断做了初始化(也就是中断向量号前32个),并没有对后256-32=224个中断门描述符初始化,也就是说后244个中断门描述符依然指向的是ignore_int这个无用的函数。下面将分析中断门描述符的最终初始化。首先介绍下interrupt全局数组,该数组中装有了所有的中断处理程序,如下所示:(arch/x86/kernel/entrt_32.S)

 1 .section .init.rodata,"a"
 2 ENTRY(interrupt)
 3 .section .entry.text, "ax"
 4     .p2align 5
 5     .p2align CONFIG_X86_L1_CACHE_SHIFT
 6 ENTRY(irq_entries_start)
 7     RING0_INT_FRAME
 8 vector=FIRST_EXTERNAL_VECTOR
 9 .rept (NR_VECTORS-FIRST_EXTERNAL_VECTOR+6)/7
10     .balign 32
11   .rept    7
12     .if vector < NR_VECTORS
13       .if vector <> FIRST_EXTERNAL_VECTOR
14     CFI_ADJUST_CFA_OFFSET -4
15       .endif
16 1:    pushl_cfi $(~vector+0x80)    /* Note: always in signed byte range */
17       .if ((vector-FIRST_EXTERNAL_VECTOR)%7) <> 6
18     jmp 2f
19       .endif
20       .previous
21     .long 1b
22       .section .entry.text, "ax"
23 vector=vector+1
24     .endif
25   .endr
26 2:    jmp common_interrupt
27 .endr
28 END(irq_entries_start)
29
30 .previous
31 END(interrupt)
32 .previous

这段代码定义了一个interrupt全局数组,如果不懂ATT汇编的话,这段代码看起来非常吃力。下面笔者粗略分析下这段代码,第1行声明了一个数据段,第2行给这个数据段起了个名字,叫做‘inerrupt’,第3行又声明了一个代码段,该代码段被包在了前边的数据段当中,从第6行可看出这个代码段名字叫做‘irq_entries_start’。接着4-5行说明了代码段对齐的方式,接下来第7行给vector进行赋值,vector=32,实际上,interrup这个数组中存放的全是外部中断,没有异常,异常初始化已经在trap_init函数中完成了,而外部中断的向量号从32开始,所以vector赋值32。接下来在第9,11行大家可以看到出现了伪指令.rept,这个伪指令是循环的意思,你可以把它当成for循环去理解,指令后边的数字是循环次数。这个伪指令实际上告诉编译器要把后边的内容在内存中复制若干次。第9行的(NR_VECTORS-FIRST_EXTERNAL_VECTOR+6)/7值为32,要求其后的内容被循环复制32次。因此第9行和第11行合起来,就相当于一个双重for循环,总共循环32*7=224次,这刚好就是外部中断向量号的数量,每执行一次内部循环,就将一个外部中断处理程序放入了一个数组元素中。接着第20行出现了.previous伪指令。该指令的意思是返回到上一个段中,在这里就是要返回到interrupt数据段中,第21行,在interrupt数据段中定义了一个long型数据,值为标号1,标号1实际上就是第16行代码的地址。接着第22行又回到了当前代码段中,让vector自加1,然后第25行进入内重循环的下一次循环。第26行,标号2的这个指令夹在了内外两重循环之间,说明每执行7次内循环就要将jmp common_interrupt复制一次。然后第27行进入外重循环的下一次循环。总共执行32*7次循环后,这段代码就结束了。通过使用.preivous伪指令,最终实际上就定义了两个数组,一个是interrput数组,该数组的每个元素均为.long 1b(第21行),另外一个数组是irq_entries_start,该数组每个元素中放入了若干条汇编指令(16-18行,26行)。这就是.previous的用处,每在irq_entries_start数组中初始化完一个元素,立马返回到interrupt数组中定义一个指向irq_entries_start数组中刚初始化过的元素的指针(.long 1b),作为interrupt数组的元素。最终interrput数组中存放了224个指针(每个中断处理程序的地址),分别指向了irq_entries_start数组中的对应元素。irq_entries_start数组每个元素存放的是几条汇编指令(这些汇编指令就是中断处理程序的开头公共部分)。而且,通过第26行,可以看到,irq_entries_start数组每个元素都包含jmp common_interrupt指令,跳入到一段公共的代码中。

上边的工作,内核只是把所有的外部中断处理程序用两个数组管理起来了,接下来,就要在IDT(中断描述符表)中初始化所有外部中断的门描述符。代码如下:(arch/x86/kernel/irqinit.c)

 1 void __init native_init_IRQ(void)
 2 {
 3     int i;
 4
 5     /* Execute any quirks before the call gates are initialised: */
 6     x86_init.irqs.pre_vector_init();
 7
 8     apic_intr_init();
 9
10     /*
11      * Cover the whole vector space, no vector can escape
12      * us. (some of these will be overridden and become
13      * ‘special‘ SMP interrupts)
14      */
15     i = FIRST_EXTERNAL_VECTOR;
16     for_each_clear_bit_from(i, used_vectors, NR_VECTORS) {
17         /* IA32_SYSCALL_VECTOR could be used in trap_init already. */
18         set_intr_gate(i, interrupt[i - FIRST_EXTERNAL_VECTOR]);
19     }
20
21     if (!acpi_ioapic && !of_ioapic)
22         setup_irq(2, &irq2);
23
24 #ifdef CONFIG_X86_32
25     irq_ctx_init(smp_processor_id());
26 #endif
27 }

从16-19行,可以看出,用interrupt数组中存放的所有中断处理程序地址来初始化IDT的中断门描述符。set_intr_gate函数上篇博文已经分析过来,这里不再分析。

至此,所有IDT中的异常和中断门描述符就初始化完成了。

中断——中断描述符表的定义和初始化(二) (基于3.16-rc4),布布扣,bubuko.com

时间: 2024-10-11 21:36:50

中断——中断描述符表的定义和初始化(二) (基于3.16-rc4)的相关文章

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

1.中断描述符表的定义(arch/x86/kernel/traps.c) 1 gate_desc debug_idt_table[NR_VECTORS] __page_aligned_bss; 定义的描述符表为一个结构体数组,数组元素类型为gate_desc,大小为8B.NR_VECTORS宏为256,即描述符表大小为256*8B. 2.idt_descr变量的定义(arch/x86/kernel/head_32.S) 1 idt_descr: 2 .word IDT_ENTRIES*8-1 #

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

本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 本篇文章主要讲述源码中是如何对中断进行一系列的初始化的. 回顾 在上一篇概述中,介绍了几个对于中断来说非常重要的数据结构,分别是:中断描述符表,中断描述符数组,中断描述符,中断控制器描述符,中断服务例程.可以说这几个结构组成了整个内核中断框架主体,所以内核对整个中断的初始化工作大多集中在了这几个结构上. 在系统中,当一个中断产生时,首先CPU会从中断描述符表中获取相应的中断向量,并根据中断向量的权限位判断是否

&lt;24&gt;【掌握】二维数组指针定义、初始化+

[掌握]二维数组指针定义.初始化 数组指针: 定义一个指针变量,让这个指针变量指向一维数组的元素 二维数组指针 行指针,用来指向二维数组的每一行,存放的是行的首地址 定义格式: 数据类型 (*行指针变量名)[数组第二维的长度]; 二维数组指针的初始化 int a[2][3]; int b[2][2]; float f1[4][4]; //假设我要定义一个指向数组a的一个行指针 // a = &a[0] = &a[0][0] = a[0] int (*p)[3] = a; 二维数组指针的使用

js之二维数组定义和初始化三种方法

方法一:直接定义并且初始化,这种遇到数量少的情况可以用 var _TheArray = [["0-1","0-2"],["1-1","1-2"],["2-1","2-2"]] 方法二:未知长度的二维数组 var tArray = new Array();   //先声明一维 for(var k=0;k<i;k++){        //一维长度为i,i为变量,可以根据实际情况改变

让我们来看一看C++ 二.对象的定义、初始化和赋值

---恢复内容开始--- 距离写完第一篇已经过了好几个月,一直想写,不知道从哪里下笔. 第一篇被很多网站爬了去,其实我并不排斥被爬,因为这不是什么盈利性的文章,只是希望诸位能最后署上一个萧瑟风声或者Xstsow的名字. 回归正题,我在第一章中讲到了main是一个主函数,int是主函数的数据类型,在开始下面的内容之前,还是很有必要把数据类型重新或者说更详细地介绍一遍. 数据类型包括一个集合和一系列运算,不同的数据类型对应的集合也不尽相同,比如int对应的就是-2^(32-1) ~ 2^(32-1)

中断——中断处理程序的进入与退出(三) (基于3.16-rc4)

上一篇博文我们分析了中断描述符表的中断门初始化过程,并且在interrupt数组中初始化过程中,可以看到每个中断处理程序都会跳入common_interrupt中.下面我们分析下common_interrupt汇编片段(arch/x86/kernel/entrt_32.S). 1 .p2align CONFIG_X86_L1_CACHE_SHIFT 2 common_interrupt: 3 ASM_CLAC 4 addl $-0x80,(%esp) /* Adjust vector into

软中断与硬中断 &amp; 中断抢占 中断嵌套

参考了这篇文章:http://blog.csdn.net/zhangskd/article/details/21992933 从本质上来讲,中断是一种电信号,当设备有某种事件发生时,它就会产生中断,通过总线把电信号发送给中断控制器. 如果中断的线是激活的,中断控制器就把电信号发送给处理器的某个特定引脚.处理器于是立即停止自己正在做的事, 跳到中断处理程序的入口点,进行中断处理. (1) 硬中断 由与系统相连的外设(比如网卡.硬盘)自动产生的.主要是用来通知操作系统系统外设状态的变化.比如当网卡收

你好,C++(7)第三部分 C++世界众生相 3.2.1 变量的定义与初始化

第3部分 C++世界众生相 在听过了HelloWorld.exe的自我介绍,完成了与C++世界的第一次亲密接触后,大家是不是都急不可待地想要一试身手,开始编写C++程序了呢?程序的两大任务是描述数据和处理数据.那么,接下来我们将面临的第一个问题就是:如何在C++中描述数据? 3.1  C++中的数据类型 编程就是使用程序设计语言来描述和表达现实世界.现实世界中有很多客观存在的事物,例如,电脑.人.汽车等.我们总是用各种数据来描述这些事物的不同属性,比如,我们用一个字符串“ChenLiangqia

数组的定义和初始化

一.定义 数组的维数必须用大于等于1的常量表达式来定义 整形字面值常量.枚举常量或者常量表达式初始化的整形const对象: 二.初始化 1.显示初始化数组元素 *在函数体外定义的内置数组,其元素均初始化为0: *在函数体内定义的内置数组,其元素无初始化: *不管数组在哪里定义,如果其元素为类类型,则自动调用该类的默认构造函数进行初始:如果该类没有默认构造函数,则必须为该数组的元素提供显示初始化 2.特殊的字符数组 3.不允许数组直接复制和赋值 // SHUZU.cpp : Defines the