Linux学习|Gentoo/Arch/FreeBSDLinuxLinux学习之路

Linux中断一网打尽(1) —— 中断及其初始化

2020-02-24  本文已影响0人  扫帚的影子

[toc]

Linux中断一网打尽 —— 中断及其初始化

前情提要

通过本文您可以了解到如下内容:

中断是什么

既然叫中断, 那我们首先就会想到这个中断是中断谁?想一想计算机最核心的部分是什么?没错, CPU, 计算机上绝大部分的计算都在CPU中完成,因此这个中断也就是中断CPU当前的运行,让CPU转而先处理这个引起中断的事件,通常来说这个中断的事件比较紧急,处理完毕后再继续执行之前被中断的task。比如,我们敲击键盘,CPU就必须立即响应这个操作,不然我们打字就全变成了慢动作~。说白了中断其实就是一种主动通知机制,如果中断源不主动通知,那想知道其发生了什么事情,只能一次次地轮询了,白白耗费CPU。

中断的分类

大的方向上一般分为两大类:同步中断和异步中断,按Intel的说法,将异步中断称为中断,将同步中断称为异常。

异步中断

主要是指由CPU以外的硬件产生的中断,比如鼠标,键盘等。它的特点是相对CPU来说随时随机发生,事先完全没有预兆,不可预期的。异步中断发生时,CPU基本上都正在执行某条指令。

异步中断可分为可屏蔽和不可屏蔽两种,字如其义不用多解释。

同步中断

主要是指由CPU在执行命令过程中产生的异常,它一定是在CPU执行完一条命令后才会发出,产生于CPU内部。按其被CPU处理后返回位置的不同,我们将同步中断分为故障(fault), 陷阱(trap)和终止(abort)三类。我们通过一个表格来作下对比区分:

异步中断分类 特点 处理完毕后的返回位置 例子
故障(fault) 潜在可能恢复的错误 重新执行引起此故障的指令 缺页中断
陷阱(trap) 为了实现某种功能有意而为之发生的错误 执行引发当前陷阱的指令的下一条指令 系统调用
终止(abort) 不可恢复的错误 没有返回,进程将被终止

两点说明:

硬件中断的管理模型

我们都知道CPU上只有有限多的脚针,负责与外部通讯,比如有数据线,地址线等,也有中断线,但一般只有两条NMI(不可屏蔽中断线)和INTR(可屏蔽中断线), 新的CPU有LINT0和LINT1脚针。那您会问了,电脑上有那么多外设,CPU就这两根线,怎么接收这么多外设的中断信号呢?确实,因此CPU找了一个管理这些众多中断的代理人——中断控制器。

就目前我们使用的SMP多核架构里,我们经常使用高级可编程中断控制器APIC, 老式的 8259A 可编程中断控制器大家有兴趣可自行搜索。

APIC分为两部分,IO APIC和Local APIC,从名字上我们就可略知一二。

中断的初始化
Linux 启动流程

中断的初始化是穿插在Linux本身启动和初始化过程中的,因此我们在这里简要说一下Linux本身的初始化。

总结了一张图,仅供参考:

linux启动流程.png
中断描述符表

外设千万种,CPU统统不知道。所有的中断到了CPU这里就只是一个中断号,然后初始化阶段设置好中断号到中断处理程序的对应关系,CPU获取到一个中断号后,查到对应的中断处理程序调用就好了。

这两者的对应关系最后会抽象成了中断向量表, 现在叫 IDT中断描述符表。

中断的第一次初始化

实模式下的初始化

选区_035.png
中断的第二次初始化
中断的第三次初始化
  1. 在x86_64位系统中,还引入了一种新的栈配置:IST(Interrupt Stack Table)。目前Linux kernel中每个cpu最多支持7个IST,可以通过tss.ist[]来访问。

  2. 现在我们再来看idt_setup_ist_traps,其实就是重新初始化一个异常处理,让这些异常处理使用IST作为中断栈。

    void __init idt_setup_ist_traps(void)
    {
     idt_setup_from_table(idt_table, ist_idts, ARRAY_SIZE(ist_idts), true);
    }
    
    static const __initconst struct idt_data ist_idts[] = {
     ISTG(X86_TRAP_DB,   debug,      IST_INDEX_DB),
     ISTG(X86_TRAP_NMI,  nmi,        IST_INDEX_NMI),
     ISTG(X86_TRAP_DF,   double_fault,   IST_INDEX_DF),
    #ifdef CONFIG_X86_MCE
     ISTG(X86_TRAP_MC,   &machine_check, IST_INDEX_MCE),
    #endif
    };
    
    #define ISTG(_vector, _addr, _ist)           \
     G(_vector, _addr, _ist + 1, GATE_INTERRUPT, DPL0, __KERNEL_CS)
    

    其中 IST_INDEX_DB IST_INDEX_NMI IST_INDEX_DF IST_INDEX_MCE就是要使用的ist[]的索引。

上一篇下一篇

猜你喜欢

热点阅读