中断——中断描述符表的定义和初始化(二) (基于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)
.section .init.rodata,"a"
ENTRY(interrupt)
.section .entry.text, "ax"
.p2align
.p2align CONFIG_X86_L1_CACHE_SHIFT
ENTRY(irq_entries_start)
RING0_INT_FRAME
vector=FIRST_EXTERNAL_VECTOR
.rept (NR_VECTORS-FIRST_EXTERNAL_VECTOR+)/
.balign
.rept
.if vector < NR_VECTORS
.if vector <> FIRST_EXTERNAL_VECTOR
CFI_ADJUST_CFA_OFFSET -
.endif
: pushl_cfi $(~vector+0x80) /* Note: always in signed byte range */
.if ((vector-FIRST_EXTERNAL_VECTOR)%) <>
jmp 2f
.endif
.previous
.long 1b
.section .entry.text, "ax"
vector=vector+
.endif
.endr
: jmp common_interrupt
.endr
END(irq_entries_start) .previous
END(interrupt)
.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)/值为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)
void __init native_init_IRQ(void)
{
int i; /* Execute any quirks before the call gates are initialised: */
x86_init.irqs.pre_vector_init(); apic_intr_init(); /*
* Cover the whole vector space, no vector can escape
* us. (some of these will be overridden and become
* 'special' SMP interrupts)
*/
i = FIRST_EXTERNAL_VECTOR;
for_each_clear_bit_from(i, used_vectors, NR_VECTORS) {
/* IA32_SYSCALL_VECTOR could be used in trap_init already. */
set_intr_gate(i, interrupt[i - FIRST_EXTERNAL_VECTOR]);
} if (!acpi_ioapic && !of_ioapic)
setup_irq(, &irq2); #ifdef CONFIG_X86_32
irq_ctx_init(smp_processor_id());
#endif
}
从16-19行,可以看出,用interrupt数组中存放的所有中断处理程序地址来初始化IDT的中断门描述符。set_intr_gate函数上篇博文已经分析过来,这里不再分析。
至此,所有IDT中的异常和中断门描述符就初始化完成了。
中断——中断描述符表的定义和初始化(二) (基于3.16-rc4)的更多相关文章
- 中断——中断描述符表的定义和初始化(一) (基于3.16-rc4)
1.中断描述符表的定义(arch/x86/kernel/traps.c) gate_desc debug_idt_table[NR_VECTORS] __page_aligned_bss; 定义的描述 ...
- linux中断源码分析 - 初始化(二)
本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 本篇文章主要讲述源码中是如何对中断进行一系列的初始化的. 回顾 在上一篇概述中,介绍了几个对于中断来说非常重要的 ...
- linux中断子系统:中断号的映射与维护初始化mmap过程
本文均属自己阅读源代码的点滴总结.转账请注明出处谢谢. 欢迎和大家交流.qq:1037701636 email:gzzaigcn2009@163.com 写在前沿: 好久好久没有静下心来整理一些东西了 ...
- js二维数组定义和初始化的三种方法总结
js二维数组定义和初始化的三种方法总结 方法一:直接定义并且初始化,这种遇到数量少的情况可以用var _TheArray = [["0-1","0-2"],[& ...
- Linux内核学习之中断 中断本质【转】
转自:http://www.linuxidc.com/Linux/2011-11/47657.htm [中断概述] 中断本质上是一种特殊的电信号,由硬件设备发向处理器.异常和中断的不同是异常在产生时必 ...
- 软中断与硬中断 & 中断抢占 中断嵌套
参考了这篇文章:http://blog.csdn.net/zhangskd/article/details/21992933 从本质上来讲,中断是一种电信号,当设备有某种事件发生时,它就会产生中断,通 ...
- 你好,C++(7)第三部分 C++世界众生相 3.2.1 变量的定义与初始化
第3部分 C++世界众生相 在听过了HelloWorld.exe的自我介绍,完成了与C++世界的第一次亲密接触后,大家是不是都急不可待地想要一试身手,开始编写C++程序了呢?程序的两大任务是描述数据和 ...
- C++中数组定义及初始化
一.一维数组 静态 int array[100]; 定义了数组array,并未对数组进行初始化 静态 int array[100] = {1,2}: 定义并初始化了数组array 动态 int* ar ...
- C语言中二维字符数组的定义和初始化
本篇日志关于二维字符数组的定义和初始化.我相信这篇文章属于菜鸟级的,高手请直接无视. 一般来说,我们可能会希望定义一个二维字符数组并且在定义的时候就用一些字符串来初始化它.比如说: ][MAX_LEN ...
随机推荐
- (转)关于Struts 2 拦截器参数丢失问题
from:http://www.cnblogs.com/huzx/archive/2011/06/09/2076328.html 今天在做用户的登陆认证的时候出现的问题. 在传参数的时候,发现参数丢失 ...
- uva 10369
数组开小了 还RE了一遍....... 最小生成树 按费用从小到大排... #include <iostream> #include <algorithm> #inc ...
- POJ3204+DInic+maxflow
Dinic+maxflow题意:找这样一种边的个数,就是增加该边的容量,可以使得最大流变大思路:求maxflow,再枚举流量为0的边,增加容量,看是否能找到增广路径. /* Dinic+maxflow ...
- javaweb学习总结(四十五)——监听器(Listener)学习二
一.监听域对象中属性的变更的监听器 域对象中属性的变更的事件监听器就是用来监听 ServletContext, HttpSession, HttpServletRequest 这三个对象中的属性变更信 ...
- SPRING IN ACTION 第4版笔记-第九章Securing web applications-006-用LDAP比较密码(passwordCompare()、passwordAttribute("passcode")、passwordEncoder(new Md5PasswordEncoder()))
一. The default strategy for authenticating against LDAP is to perform a bind operation,authenticatin ...
- Spring中的实例生成方式及其生命周期
三种实例化bean的方式1.使用类构造器实例化 <!-- 使用类构造器实例化,class属性表示要使用的类的全限定名 --> <bean id="userDao1" ...
- windows命令行编码与nodejs编码格式冲突的解决方式
今天写一个工具,由于大部分人使用的机器都是windows,在和nodejs结合的时候出问题了. win命令行的编码格式是gbk,而nodejs支持的编码只有:utf8 ascii和base64,必须让 ...
- python学习链接
http://www.cnblogs.com/dkblog/archive/2011/06/24/2089026.html 异常处理 http://xiagu1.iteye.com/blog/6195 ...
- 【HDOJ】1914 The Stable Marriage Problem
稳定婚姻问题,Gale-Shapley算法可解. /* 1914 */ #include <iostream> #include <sstream> #include < ...
- Complete The Pattern #2
Complete The Pattern #2 Description: Task: You have to write a function pattern which creates the fo ...