Linux内核-0.1版本的中断流程
Linux中断工作流程中断处理过程将所有寄存器的值入栈将异常码中断号入栈将当前的函数返回值入栈为了能够在中断执行完成后恢复到被中断的地方中断执行过程 调用对应的中断执行函数中断恢复过程函数返回值出栈寄存器值出栈Linux0.11的内核版本还没有中断上下半部的产生内核代码非常精简其设计哲学就是简单直接。它会在中断处理函数如do_divide_error中一次性完成所有相关工作处理完后再返回被中断的进程。对应源码中断前和中断后的过程中断执行过程硬件中断处理过程asm.straps.c软件及系统调用的处理过程system_call.sfork.c signal.c exit.c sys.casm.s8086CPU寄存器8086CPU中寄存器总共14个均为16位分别是AX,BX,CX,DX,SP,BP,SI,DI,IP,FLAG,CS,DS,SS,ES其中AX,BX,CX,DX称为数据寄存器SPBP称为指针寄存器SI,DI称为变址寄存器IPFLAG成为控制寄存器CSDSSSES是段寄存器中断时栈图这里使用的是IA32处理器几个寄存器变量名字不一样但是本质功能都是相同的这里以没有错误码的no_error_code为例_divide_error: pushl $_do_divide_error no_error_code: xchgl %eax,(%esp) pushl %ebx pushl %ecx pushl %edx pushl %edi pushl %esi pushl %ebp push %ds push %es push %fs pushl $0 # error code lea 44(%esp),%edx pushl %edx movl $0x10,%edx mov %dx,%ds mov %dx,%es mov %dx,%fs call *%eax addl $8,%esp pop %fs pop %es pop %ds popl %ebp popl %esi popl %edi popl %edx popl %ecx popl %ebx popl %eax iret硬件压栈在上面代码中可以看到在最开始就直接pushdo_divide_error函数地址了所以根据栈图前面的ssESPEFLAGSCS以及EIP程序断点地址都是中断发生时硬件自动压栈软件压栈1.在_divide_error一开始可以看到是先把do_divide_error函数地址压栈然后进入了no_error_code流程此时栈顶指针sp指向了这块内存。2.然后xchgl命令是交换ax和sp指向地址的值%eax寄存器拿到了_do_divide_error的函数入口地址。这样后续执行call *%eax时就能正确跳转到 C 语言写的除法异常处理函数。栈顶(%esp)被写入了原始 EAX 寄存器的值。这样原始 EAX 的数据就被安全地保存在栈里了后续和其他寄存器EBX、ECX 等一起作为“现场”被保存下来。3.然后正常push其余各个寄存器4.当前由于没有错误码所以直接push一个0,作为占位5.接下来有一个指令lea 44(%esp),%edx这是取sp指针加44的内存位置存放到dx寄存器当中而sp在执行命令时正指向错误码他与EIP正好相差44个所以也就是获取得到EIP值在内存中的位置并且后面push将EIP的地址放入栈。后续会把这个地址作为参数传递给中断处理函数4.准备内核数据movl $0x10, %edx是准备内核数据段选择子0x10在 Linux 0.11 的内核中是内核数据段Kernel Data Segment的选择子也就是KERNEL_DS。在 i386 保护模式下段寄存器存放的是描述符索引。0x10对应的二进制是0001 0000索引为 2RPL0它指向 GDT全局描述符表中的内核数据段描述符该段的基址是 0大小是 4GB。mov %dx, %ds/es/fs是去替换段寄存器在这几行代码执行之前CPU一直处于用户态指向用户进程自己的数据空间。DS、ES、FS里存的是用户数据段的选择子比如0x17即USER_DS指向用户进程自己的数据空间。而接下来马上要执行call *%eax这会跳转到 C 语言编写的内核函数如do_divide_error。C 函数编译后的代码默认假设DS和ES指向内核数据段因为它要访问内核全局变量比如current进程结构。它要访问内核栈上的数据。它不能依赖用户态的段寄存器否则会寻址错误甚至导致 CPU 产生保护异常。所以在调用 C 函数之前必须强制把DS、ES、FS切换为内核数据段0x10。5.调用中断处理函数call *%eax这里ax存放的就是中断处理函数的地址出栈在调用完成中断处理函数并且切换回用户态之后就要开始进行数据出栈addl $8,%esp这里是把sp指针加8回到存放FS寄存器的地方然后依次pop出栈数据最后调用iret返回到用户进程trap.cdie函数仍然以上面divide_error为例调用到trap.c中的do_divide_errorvoid do_divide_error(long esp, long error_code) { die(divide error,esp,error_code); }这个函数里面只做了一件事就是调用die函数/** * brief 该子程序用来打印出错中断的名称、出错号、调用程序的 EIP、EFLAGS、ESP、fs 段寄存器值、 * 段的基址、段的长度、进程号 pid、任务号、10 字节指令码。如果堆栈在用户数据段则还 * 打印 16 字节的堆栈内容 display segment exception * * param str 提示字符串 * param esp_ptr ESPExtended Stack Pointer为扩展栈指针寄存器,存放函数栈顶指针;EBPExtended Base Pointer用于存放函数栈底指针 * param nr error code */ static void die(char * str,long esp_ptr,long nr) { long * esp (long *) esp_ptr; int i; printk(%s: %04x\n\r,str,nr0xffff); printk(EIP:\t%04x:%p\nEFLAGS:\t%p\nESP:\t%04x:%p\n, esp[1],esp[0],esp[2],esp[4],esp[3]); printk(fs: %04x\n,_fs()); printk(base: %p, limit: %p\n,get_base(current-ldt[1]),get_limit(0x17)); //get_base is in sched.h if (esp[4] 0x17) { printk(Stack: ); for (i0;i4;i) printk(%p ,get_seg_long(0x17,i(long *)esp[3])); printk(\n); } str(i); printk(Pid: %d, process nr: %d\n\r,current-pid,0xffff i); for(i0;i10;i) printk(%02x ,0xff get_seg_byte(esp[1],(i(char *)esp[0]))); printk(\n\r); do_exit(11); /* play segment exception */ }die函数主要是用来打印出错中断名称出错号调用程序的EIP寄存器段等值其中esp_ptr就是前面传递近来的参数指向的EIP在内存中的地址在die中long * esp (long *) esp_ptr;让esp指向了这个位置。从这个地址开始从低地址到高地址的内存布局如下数组下标内存地址相对存储内容解释esp[0]0原始EIP被中断时的指令指针偏移esp[1]4原始CS被中断时的代码段选择子esp[2]8原始EFLAGS被中断时的标志寄存器esp[3]12原始ESP用户态被中断时的栈指针偏移esp[4]16原始SS用户态被中断时的栈段选择子trap_init中断程序初始化子程序void trap_init(void) { int i; set_trap_gate(0,divide_error); set_trap_gate(1,debug); set_trap_gate(2,nmi); set_system_gate(3,int3); /* int3-5 can be called from all */ set_system_gate(4,overflow); set_system_gate(5,bounds); set_trap_gate(6,invalid_op); set_trap_gate(7,device_not_available); set_trap_gate(8,double_fault); set_trap_gate(9,coprocessor_segment_overrun); set_trap_gate(10,invalid_TSS); set_trap_gate(11,segment_not_present); set_trap_gate(12,stack_segment); set_trap_gate(13,general_protection); set_trap_gate(14,page_fault); set_trap_gate(15,reserved); set_trap_gate(16,coprocessor_error); for (i17;i48;i) set_trap_gate(i,reserved); set_trap_gate(45,irq13); outb_p(inb_p(0x21)0xfb,0x21); outb(inb_p(0xA1)0xdf,0xA1); set_trap_gate(39,parallel_interrupt); }这里有两个函数set_trap_gate和set_system_gate他们都是内核初始化阶段**设置中断描述符表IDTInterrupt Descriptor Table**的核心操作// 伪代码逻辑 #define set_trap_gate(n, addr) _set_gate(idt[n], 15, 0, addr) #define set_system_gate(n, addr) _set_gate(idt[n], 15, 3, addr)它们往 IDT 表中写入一个门描述符8 字节包含处理函数地址addr如divide_error段选择子固定为内核代码段0x08门类型15表示陷阱门不屏蔽外部中断DPL描述符特权级——这就是两者的关键差异。二者的不同在于函数DPL特权级谁可以调用int触发它典型用途set_trap_gate0内核态仅内核代码可以触发。用户态程序执行int 0会触发GPF通用保护错。内核专用的严重异常除零、页错误、NMI 等。set_system_gate3用户态用户态和内核态都可以触发。允许用户程序主动调用的陷阱调试断点int3、溢出into、以及系统调用int 0x80。普通异常如除零、缺页默认设为set_trap_gate只允许内核触发。用户态若发生CPU 会先陷入内核由内核的异常处理函数来“杀死”该进程而不会被恶意利用。故意留给用户的接口如断点调试、系统调用必须设为set_system_gate否则用户程序根本无权执行int 3或int 0x80。

相关新闻

【亲测可用】千问无门槛优惠券激活码:千问新用户专属220372

【亲测可用】千问无门槛优惠券激活码:千问新用户专属220372

最近发现一个能给我省钱,一番研究后发现了一个激活入口。现整理给大家。这个就是千问官方推出的一个特定专属活动,只给新用户提供的福利。你可以在APP内输入指定口令:千问新用户专属220372,即可领取8元无门槛立减券。这个口令实测…

2026/7/22 1:25:21 阅读更多 →
Python 项目 CI/CD 最佳实践:RAG 服务的测试、构建和自动部署流水线

Python 项目 CI/CD 最佳实践:RAG 服务的测试、构建和自动部署流水线

Python 项目 CI/CD 最佳实践:RAG 服务的测试、构建和自动部署流水线 一、深度引言与场景痛点 我们RAG服务上线初期有个很尴尬的习惯:测试靠人手点、部署靠scp传文件、回滚靠祈祷。直到有一次,我在周五下午五点改了一个"无关紧要"…

2026/7/22 1:25:21 阅读更多 →
北京本地有哪些值得信赖的标书代写企业?这份实用选择指南请您收好

北京本地有哪些值得信赖的标书代写企业?这份实用选择指南请您收好

不少在北京参与招投标的企业经营者、项目负责人都有过遗憾经历:团队明明有匹配的项目资质、落地经验充足,却因为标书漏填了一项属地要求的承诺函、报价测算偏差、技术方案没踩中评分点,连进入最终评审的资格都没拿到。想找专业的标书服务机构…

2026/7/22 1:25:21 阅读更多 →

最新新闻

前端转大模型应用,为什么我让你先啃权限和可观测?

前端转大模型应用,为什么我让你先啃权限和可观测?

聊《别急着换赛道:前端经验在 AI 项目里到底值多少?》之前,先说一句实在的:别急着背概念,先看它在真实项目里到底解决什么问题。摘要先把这篇文章的目标说清楚:看完之后,你应该能判断这件事值不…

2026/7/22 3:43:09 阅读更多 →
电商平台开发记录:从商品治理到隐私与算法审计

电商平台开发记录:从商品治理到隐私与算法审计

今天主要继续完善电商项目的平台管理端,重点不是增加几个展示页面,而是把之前缺少的业务流程补完整,让按钮点下去以后真正产生结果。一、商品质量治理闭环今天先把商品质量治理流程重新梳理了一遍。平台发现商品存在问题后,可以向…

2026/7/22 3:43:09 阅读更多 →
n8n自动化平台CVE-2026-25049漏洞分析与防护

n8n自动化平台CVE-2026-25049漏洞分析与防护

1. 漏洞背景与影响范围解析n8n作为一款开源的自动化工作流平台,近年来在企业级应用中快速普及。其通过可视化界面连接各类云服务、API和本地系统的能力,使其成为数字化转型中的重要工具。然而2026年初披露的CVE-2026-25049漏洞,暴露了这类自动…

2026/7/22 3:43:09 阅读更多 →
鸿蒙 韶非 UI 系列:关系数据库 @ohos.data.relationalStore,鸿蒙 SQLite 封装,结构化数据存取入门

鸿蒙 韶非 UI 系列:关系数据库 @ohos.data.relationalStore,鸿蒙 SQLite 封装,结构化数据存取入门

写在前面 如果你写过鸿蒙 ArkUI 应用,大概率遇到过这个场景:你写了个记账应用,每笔账有「金额/类型/时间/备注」四个字段。你想「用文件存 JSON 数组」——结果改一笔账要读全表、改、写全表,10 笔账卡得飞起。 你想「用 Preferences 存」——Preference…

2026/7/22 3:43:09 阅读更多 →
ZFX山海证券:从公开信息出发,盘点服务体系与风险提示

ZFX山海证券:从公开信息出发,盘点服务体系与风险提示

外汇市场信息更新频繁,平台口碑的形成更依赖长期一致性:入口是否好找、说明是否前后一致、提示是否稳定出现。围绕ZFX山海证券,下面从稳定体验与信息呈现等角度做一次正面观察。外汇相关信息更新频繁,平台将关键提示与解释呈现得更…

2026/7/22 3:43:09 阅读更多 →
海外红队面试经验分享

海外红队面试经验分享

互联网公司A 老牌头部互联网公司,Top 10 职位:高级红队操作员 (Senior Red Team Operator) 流程 简历筛选 招聘人员电话面试: 背景、沟通能力、项目经验概述、对他们公司技术栈的初解 在线评估 : 基础编码/脚本能力测试 核心安全概念问答 (网络、操作系统、加密、认证) 重…

2026/7/22 3:42:09 阅读更多 →

日新闻

TI DSP系统配置模块SYSCFG详解:中断机制与主设备优先级配置实战

TI DSP系统配置模块SYSCFG详解:中断机制与主设备优先级配置实战

1. 项目概述与SYSCFG模块的核心价值在嵌入式系统,尤其是像TI C6000系列这样的高性能DSP开发中,我们常常会与芯片手册里那些密密麻麻的寄存器打交道。很多开发者可能更关注算法实现、内存优化或者外设驱动,但对于一个稳定、高效的系统而言&…

2026/7/22 0:00:26 阅读更多 →
微信Server酱:高到达率的应急通知方案实践

微信Server酱:高到达率的应急通知方案实践

1. 为什么我们需要"最次"的通知方案? 在数字化协作环境中,消息通知系统的重要性不言而喻明。但现实情况是,企业级通知方案往往需要复杂的API对接(如企业微信、钉钉、飞书),个人开发者的小项目又经…

2026/7/22 0:00:26 阅读更多 →
甲方要的“简洁“PPT,到底是简洁还是省事?

甲方要的“简洁“PPT,到底是简洁还是省事?

甲方说"简洁一点",乙方听到的是"少做几页"。甲方说"不要太复杂",乙方理解成"别放图表了"。结果交过去,甲方说"我说的简洁不是这个意思"。"简洁"这个词在PPT语境里,是…

2026/7/22 0:00:26 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/21 8:48:31 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/21 5:34:47 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/21 8:25:39 阅读更多 →

月新闻