32获取外部中断状态_Linux中断一网打尽(1) — 中断及其初始化

5a30eb3ba77efe998291507882f99e63.png

1

中断是什么

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

2

中断的分类

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

异步中断

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

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

同步中断

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

3c25c5f25f151018a1de9aca435513b3.png

两点说明:

  • 处理完毕后的返回位置:发生异常时,CPU最终会进入到相应的异常处理程序中(简单说就是CPU需要执行一次跳转)在执行具体操作前会设置好的异常处理完成后跳转回的CS:IP, 即代码段寄存器和程序指针寄存器,不同类型的异常其设置的CS:IP不同而已;

  • 有些分类方法还会有一种叫可编程异常的,比如说把系统调用算作这一类,也可以。但是如果按处理完毕后的返回位置来说系统调用是可以归入陷阱这一类的。

3

面对如此众多的服务器

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

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

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

  • IO APIC: 用来连接各种外设的硬件控制器,接收其发送的中断请求信号,然后将其传送到Local APIC, 这个IO APIC一般会封装在主板南板芯片上;

  • Local APIC: 基本上集成在了CPU里, 向CPU通知中断发生。

放张网上的图:

9b49a708e563cafcb00b5086b8c1cf64.png4

中断的初始化

Linux的启动流程

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

  • 64位Linux启动大的方向上需要经过 实模式 -> 保护模式 -> 长模式 第三种模式的转换;

  • 电源接通,CPU启动并重置各寄存器后运行于实模式下,CS:IP加载存储于ROM中的一跳转指令,跳转到BIOS中;

  • BIOS启动,硬件自测,读取MRB;

  • BIOS运行第一阶段引导程序,第一阶段引导程序运行第二阶段引导程序,通常是 grub;

  • Grub开始引导内核运行;

  • 相关初始化后进行保护模式,再进入长模式,内核解压缩;

  • 体系无关初始化部分;

  • 体系相关初始化部分;

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

034676a1add6f53d9fb7a8d72ceb45df.png

中断描述符表

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

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

中断的第一次初始化

实模式下的初始化

  • 上面那张Linux启动流程图如果你仔细看的话会发现在BIOS程序加载运行时,在实模式下也有一个BIOS的中断向量表,这个中断向量表提供了一些类似于BIOS的系统调用一样的方法。比如Linux在初始化时需要获取物理内存的详情,就 是调用了BIOS的相应中断来获取的。见下图:

c787551ae9802da2ce2039ad48a84079.png

中断的第二次初始化

  • 在进入到保护模式后,会全新初始化一个空的中断描述符表 IDT, 供 kernel 使用;

  • Linux Kernel提供256个大小的中断描述符表

#define IDT_ENTRIES           256gate_desc idt_table[IDT_ENTRIES] __page_aligned_bss;

中断的第三次初始化

在进入到长模式后,在x86_64_start_kernel先初始化前32个异常类型的中断(即上面定义的 idt_table 的前32项);

void __init idt_setup_early_handler(void){  int i;  for (i = 0; i < NUM_EXCEPTION_VECTORS; i++)      set_intr_gate(i, early_idt_handler_array[i]);  load_idt(&idt_descr);}

其中 early_idt_handler_array这个数组放置了32个异常类型的中断处理程序,我们先看一下它的定义:

const char early_idt_handler_array[32][9];

二维数组,每一个early_idt_handler_array[i]有9个字节。

这个 early_idt_handler_array的初始化很有意思,它用AT&T的汇编代码完成,在文件arch/x86/kernel/head_64.S中:

ENTRY(early_idt_handler_array)  i = 0  .rept NUM_EXCEPTION_VECTORS  .if ((EXCEPTION_ERRCODE_MASK >> i) & 1) == 0      UNWIND_HINT_IRET_REGS      pushq $0    # Dummy error code, to make stack frame uniform  .else      UNWIND_HINT_IRET_REGS offset=8  .endif  pushq $i        # 72(%rsp) Vector number  jmp early_idt_handler_common  UNWIND_HINT_IRET_REGS  i = i + 1  .fill early_idt_handler_array + i*EARLY_IDT_HANDLER_SIZE - ., 1, 0xcc  .endr  UNWIND_HINT_IRET_REGS offset=16END(early_idt_handler_array)

这段汇编循环遍历32次来初始化每一个early_idt_handler_array[i], 也就是填充它的9个字节:其中2个字节是压栈错误码指令,2个字节是压栈向量号指令,余下的5个字节是函数跳转指令(jmp early_idt_handler_common)。由此我们可以看出,这前32个异常类型的中断处理函数最终都会调用到early_idt_handler_common, 这个函数这里就不贴它的代码了,我们说下它的大致流程:

a. 先将各寄存器的值压栈保存;b. 如果是 缺页异常,就调用 `early_make_patable`; c. 如果是 其他异常,就调用 `early_fixup_exception`;

体系结构相关的中断初始化:

这也是一次部分初始化,它发生在 start_kernel的setup_arch中,即发生在 Linux 启动流程中的体系结构初始化部分。这部分实际上是更新上面已初始化的32个异常类中的X86_TRAP_DB(1号, 用于debug)和X86_TRAP_BP(3号, 用于debug时的断点);

static const __initconst struct idt_data early_idts[] = {  INTG(X86_TRAP_DB,       debug),  SYSG(X86_TRAP_BP,       int3),};void __init idt_setup_early_traps(void){  idt_setup_from_table(idt_table, early_idts, ARRAY_SIZE(early_idts),               true);  load_idt(&idt_descr);}

debug和int3这两个汇编实现的中断处理程序这里我们就不详述了。

更新 X86_TRAP_PF 缺页异常的中断处理程序:

void __init idt_setup_early_pf(void){  idt_setup_from_table(idt_table, early_pf_idts,               ARRAY_SIZE(early_pf_idts), true);}static const __initconst struct idt_data early_pf_idts[] = {  INTG(X86_TRAP_PF,       page_fault),};

在trap_init中调用 idt_setup_traps更新部分异常的中断处理程序:

void __init idt_setup_traps(void){  idt_setup_from_table(idt_table, def_idts, ARRAY_SIZE(def_idts), true);}static const __initconst struct idt_data def_idts[] = {  INTG(X86_TRAP_DE,       divide_error),  INTG(X86_TRAP_NMI,      nmi),  INTG(X86_TRAP_BR,       bounds),  INTG(X86_TRAP_UD,       invalid_op),  INTG(X86_TRAP_NM,       device_not_available),  INTG(X86_TRAP_OLD_MF,       coprocessor_segment_overrun),  INTG(X86_TRAP_TS,       invalid_TSS),  INTG(X86_TRAP_NP,       segment_not_present),  INTG(X86_TRAP_SS,       stack_segment),  INTG(X86_TRAP_GP,       general_protection),  INTG(X86_TRAP_SPURIOUS,     spurious_interrupt_bug),  INTG(X86_TRAP_MF,       coprocessor_error),  INTG(X86_TRAP_AC,       alignment_check),  INTG(X86_TRAP_XF,       simd_coprocessor_error),#ifdef CONFIG_X86_32  TSKG(X86_TRAP_DF,       GDT_ENTRY_DOUBLEFAULT_TSS),#else  INTG(X86_TRAP_DF,       double_fault),#endif  INTG(X86_TRAP_DB,       debug),#ifdef CONFIG_X86_MCE  INTG(X86_TRAP_MC,       &machine_check),#endif  SYSG(X86_TRAP_OF,       overflow),#if defined(CONFIG_IA32_EMULATION)  SYSG(IA32_SYSCALL_VECTOR,   entry_INT80_compat),#elif defined(CONFIG_X86_32)  SYSG(IA32_SYSCALL_VECTOR,   entry_INT80_32),#endif};

在trap_init中调用 idt_setup_ist_traps更新部分异常的中断处理程序

看到这里您可能问,上面不是调用了idt_setup_traps,怎么这时又调用idt_setup_ist_traps? 这两者有什么区别?说起来话有点长,我们尽量从流程上给大家讲清楚,但不深入到具体的细节。

  1. 想说明这个问题,我们先来讲下栈这个东西:

    a.  首先每个进程都有自己的用户态栈,对应进程虚拟地址空间内的stack部分,用于进程在用户态变量申请,函数调用等操作;

    b. 除了用户态栈,每个进程在创建时(内核对应创建 task_struct结构)同时会创建对应的内核栈,这里进程由用户态进入到内核态执行函数时,相应的所用的栈也会切换到内核栈;

    c. 如果内核进入到中断处理程序,早期的kernel针对中断处理程序的执行会使用当前中断task的内核栈,这里有存在一定的问题,存在栈溢出的风险。举个例子,如果在中断处理程序里又发生了异常中断,此时会触发double fault,但其在处理过程中依然要使用当前task的内核栈,并且当前task内核栈已满,double fault无法被正确处理。为了解决这样的内部,linux kernel引出了独立的内核栈,针对SMP系统,它还是pre-cpu的。我们来看一下其初始化,还特别贴心地为softirq也开辟了单独的栈:

void irq_ctx_init(int cpu){   union irq_ctx *irqctx;   if (hardirq_ctx[cpu])       return;    // 硬中断独立栈   irqctx = (union irq_ctx *)&hardirq_stack[cpu * THREAD_SIZE];   irqctx->tinfo.task      = NULL;   irqctx->tinfo.cpu       = cpu;   irqctx->tinfo.preempt_count = HARDIRQ_OFFSET;   irqctx->tinfo.addr_limit    = MAKE_MM_SEG(0);   hardirq_ctx[cpu] = irqctx;    //软中断独立栈   irqctx = (union irq_ctx *)&softirq_stack[cpu * THREAD_SIZE];   irqctx->tinfo.task      = NULL;   irqctx->tinfo.cpu       = cpu;   irqctx->tinfo.preempt_count = 0;   irqctx->tinfo.addr_limit    = MAKE_MM_SEG(0);   softirq_ctx[cpu] = irqctx;   printk("CPU %u irqstacks, hard=%p soft=%p\n",       cpu, hardirq_ctx[cpu], softirq_ctx[cpu]);}

2. 在x86_64位系统中,还引入了一种新的栈配置:IST(Interrupt Stack Table)。目前Linux kernel中每个cpu最多支持7个IST,可以通过tss.ist[]来访问。

3. 现在我们再来看idt_setup_ist_traps,其实就是重新初始化一个异常处理,让这些异常处理使用IST作为中断栈。其中 IST_INDEX_DB IST_INDEX_NMI IST_INDEX_DF IST_INDEX_MCE就是要使用的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)

剩下的最后一部分就是硬件中断的初始化了,它同样在start_kernel中执行:

early_irq_init();init_IRQ();

这部分具体细节我们在Linux中断一网打尽(2) - IDT及中断处理的实现介绍。

本文转载自云计算


往期精彩回顾

2019年度精选文章

httprunner使用总结

全国新型肺炎实时动态
1db18f119479a985a713121a99c4bf4f.png9e4c06b06c90090d5772b409d53326a9.png

360技术公众号

技术干货|一手资讯|精彩活动

扫码关注我们

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/502999.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

文件夹_【教程】创建透明文件夹(非隐藏文件夹哦)

Hello 今天 不夜君 来教大家如何创建 一个 透明的 文件夹 效果类似这样哦~~是不是什么都看不到呢 大家不要慌 下面来看一下选中后的样子呀~~怎么样 是不是十分的酷炫呢其实这个方法的原理 我也不是很懂 所以就不献丑了 直接附上教程吧&#xff01;&#xff01;当然 有多种实现方…

山东栋梁机器人比赛_谁是最强“移动机器人”?来深技师这场全国大赛一决高下!...

9月24日第一届全国技能大赛世赛移动机器人项目全国机械行业选拔赛在深圳技师学院闭幕&#xff01;为期三天的赛程中来自全国职业院校、机械行业相关企业的40支参赛队伍、130多名选手和专家教练相聚在深技师美丽的鹤鸣湖畔展开了激烈的比拼~点击边框调出视频工具条 ▲ 点击视频&…

python办公自动化excel_python办公自动化:Excel操作入门

1.安装 pip install xlsxwriter or easy_install xlsxwriter or tar -zxvf xlsxwriter-*.*.*.tar.gz python setup.py install 2.操作一个简单的Excel文档 操作注释及代码&#xff1a; # 引入依赖模块 import xlsxwriter # 数据准备 datas ( [Rent, 1000], [Gas, 100 ], [Food…

c++回到起点旅行商问题_不要犯同样的错,提C型房车回渝上牌长途旅行的心路历程...

视频版看这里➡️ 第一次驾驶三吨重房车到成功在村道脱险&#xff0c;这段旅程值得回顾我们俩房车旅行近两年&#xff0c;为了更好的迎接新一年的到来&#xff0c;我们来简单回顾一下这一年多的房车旅程。我们俩的旅行口号是&#xff1a;奔跑的影视工作室&#xff0c;行走的房车…

中怎样载入选区_PS----关于选区的选取

对于刚刚接触到PS的小伙伴来说&#xff0c;怎样下手呢&#xff1f;&#xff1f;&#xff1f;嘿嘿&#xff0c;我来了&#xff0c;废话不多&#xff0c;直接上干货1.关于“矩形"或者“圆形”选取&#xff0c;快捷键M,取消选区ctrld,2."正圆“以及”正方形“ 怎么办呢&…

期刊投稿状态_追踪期刊在线系统投稿状态(十七)

大部分的期刊都有在线投稿系统&#xff0c;让作者能够快速简单完成投稿&#xff0c;一旦论文递交出去后&#xff0c;作者可以通过系统查看投稿状态&#xff0c;了解期刊处理论文的进度。作者在投稿出去后都会感到很焦虑&#xff0c;一直刷新系统状态&#xff0c;有时候出现了不…

添加公共引用目录_原来Word还可以自动生成图片和图表目录!

小李接到了一个任务&#xff0c;将技术文档中的示意图目录做出来&#xff0c;让客户能根据需求快速找到操作示意图&#xff0c;可是这份文档有三百多张图片&#xff0c;不过&#xff0c;这可难不倒小李。说到Word目录&#xff0c;大多数人都知道&#xff0c;可以为文档的标题设…

python类实例化_python基础8之类的实例化过程剖析

一、概述 之前我们说关于python中的类&#xff0c;都一脸懵逼&#xff0c;都想说&#xff0c;类这么牛逼到底是什么&#xff0c;什么才是类&#xff1f;下面我们就来讲讲&#xff0c;什么是类&#xff1f;它具有哪些特性。 二、类的语法 2.1 语法 class dog(object): #用class定…

db2 sql 判断select是否为空_学会复杂一点的SQL语句:Oracle DDL和DML

create&#xff1a;创建表创建用户创建视图创建表create table student(id int,score int) ;student后面与括号之间可以有空格可以没有创建用户create user liuyifei identified by 4852396;drop&#xff1a;删除整个表、删除指定的用户、删除指定的存储空间drop table table_n…

dataframe 一列的不同值_python数据分析包|Pandas-02之缺失值(NA)处理

本篇详解pandas中缺失值&#xff08;Missing data handling&#xff09;处理常用操作。缺失值处理常用于数据分析数据清洗阶段&#xff1b;Pandas中将如下类型定义为缺失值&#xff1a;NaN: ‘’, ‘#N/A’, ‘#N/A N/A’, ‘#NA’, ‘-1.#IND’, ‘-1.#QNAN’,‘-NaN’, ‘-na…

https抓包_从Wireshark抓包看HTTPS的加密功能

近几年&#xff0c;互联网发生着翻天覆地的变化&#xff0c;尤其是我们一直习以为常的HTTP协议&#xff0c;在逐渐的被HTTPS协议所取代&#xff0c;在浏览器、搜索引擎、CA机构、大型互联网企业的共同促进下&#xff0c;互联网迎来了“HTTPS加密时代”&#xff0c;HTTPS将在未来…

【Python】pip管理Python包

命令&#xff1a;pip install <包名> 安装指定的包。 pip install ipython #或者 pip install ipython -i https://mirrors.aliyun.com/pypi/simple/ 命令&#xff1a;pip uninstall <包名> 删除指定的包。 pip uninstall ipython 命令&#xff1a;pip list 显…

python整数类型在每一台计算机上的取值范围是一样的_关于python统计一个整数列表中不同数值种类数的问题。...

下面这段代码中&#xff0c;kind_num用于统计那个整数列表中有几种不同数值的整数。 class Solution(object): def distributeCandies(self, candies): """ :type candies: List[int] :rtype: int """ loc len(candies) mol loc % 2 if not (2…

实验报告总结_小学四年级数学下册全册知识点总结 ,家有四年级学生的家长收藏...

一、观察物体知识技能目标知道从低到高观察物体&#xff0c;体会出不同的位置看到的范围是不一样的&#xff0c;由远到近看到的范围是越来越小&#xff0c;从而体会不同距离观察物体形状的变化。知识检测摆一种物体&#xff0c;从不同的面去观察&#xff0c;看到的面是什么图形…

配置中心_Nacos做配置中心

一、简单使用Nacos官方快速开始文档&#xff1a;https://nacos.io/zh-cn/docs/quick-start.html在每个服务中编写bootstrap文件&#xff0c;服务使用该文件启动&#xff0c;并根据该文件的指引到Nacos获取配置信息bootstrap文件示例&#xff0c;可以不用写端口&#xff0c;需要…

mysql lock_MySQL-锁总结

锁锁机制用于管理对共享资源的并发访问。lock和latch在数据库中&#xff0c;lock和Latch都称为锁&#xff0c;但是两者意义不同。latch称为闩锁(shuang suo)&#xff0c;其要求锁定的时间必须非常短。若持续的时间长&#xff0c;则应用的性能会非常差。在InnoDB存储引擎中&…

hive 两个没有null指定的表左关联的结果有null_Hive的优化原则

原文链接&#xff1a;https://zhuanlan.zhihu.com/p/174469951本篇将 Hive 的优化分成三个部分:第一部分是 SQL 通用语法优化&#xff0c;第二部分是针对 Hive 所具有的数据倾斜的优化&#xff0c;第三部分则介绍一些通用性的 Hive 参数设置优化。一、语法优化 SQL 的语法优化本…

python数据分析兼职能挣钱吗_Python开发能从事数据分析吗

Python不仅是人工智能时代最佳的编程语言&#xff0c;同时也是数据分析、科学运算的首选编程语言。学习Python就业方向多&#xff0c;比如Web网站开发、人工智能等。Python数据分析师需要掌握哪些技能&#xff0c;从各大招聘网站对其的基本任职要求可以了解一二&#xff1a; 1、…

python中打开文件open_Python中打开文件的方式(With open)

1.读文件 要以读文件的模式打开一个文件对象&#xff0c;使用Python内置的open()函数&#xff0c;传入文件名和标 示符&#xff1a; f open( /Users/michael/test.txt, r ) 标示符’r’表示读&#xff0c;这样&#xff0c;我们就成功地打开了一个文件。 如果文件不存在&#x…

jdk 安装_Linux入门之Linux CentOS安装jdk

第一步&#xff1a;创建jdk安装目录(该/usr/local/src 目录是空的,最好把我们自己下载的放到这,容易区分)mkdir -p /usr/local/src/jdk第二步&#xff1a;查看之前是否安装rpm -qa | grep -i jdk若之前安装过jdk&#xff0c;下次安装一定把之前的删除干净第三步&#xff1a;命令…