zoukankan      html  css  js  c++  java
  • 中断——中断描述符表的定义和初始化(二) (基于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中的异常和中断门描述符就初始化完成了。

  • 相关阅读:
    [Leetcode] Merge Intervals
    [Leetcode] Sort Colors
    junit
    DBUnit的使用
    xml简介---来自百度百科
    今天开始深入学习XML
    Java 用Myeclipse部署项目基础坏境搭建
    properties配置文件读取方法
    Java web做服务器之间的通信方法
    Java Socket简单的客服端及其服务器端
  • 原文地址:https://www.cnblogs.com/liangning/p/3873884.html
Copyright © 2011-2022 走看看