news 2026/7/22 1:25:11

Linux内核-0.1版本的中断流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux内核-0.1版本的中断流程

Linux中断工作流程

  1. 中断处理过程:

    1. 将所有寄存器的值入栈

    2. 将异常码(中断号)入栈

    3. 将当前的函数返回值入栈(为了能够在中断执行完成后恢复到被中断的地方)

  2. 中断执行过程: 调用对应的中断执行函数

  3. 中断恢复过程

    1. 函数返回值出栈
    2. 寄存器值出栈

Linux0.11的内核版本还没有中断上下半部的产生,内核代码非常精简,其设计哲学就是简单直接。它会在中断处理函数(如do_divide_error)中,一次性完成所有相关工作,处理完后再返回被中断的进程。

对应源码

中断前和中断后的过程中断执行过程
硬件中断处理过程asm.straps.c
软件及系统调用的处理过程system_call.sfork.c signal.c exit.c sys.c

asm.s

8086CPU寄存器

8086CPU中寄存器总共14个,均为16位

分别是AX,BX,CX,DX,SP,BP,SI,DI,IP,FLAG,CS,DS,SS,ES

其中AX,BX,CX,DX称为数据寄存器

SP,BP称为指针寄存器

SI,DI称为变址寄存器

IP,FLAG成为控制寄存器

CS,DS,SS,ES是段寄存器

中断时栈图

这里使用的是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函数地址了,所以根据栈图前面的ss,ESP,EFLAGSCS,以及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(索引为 2,RPL=0),它指向 GDT(全局描述符表)中的内核数据段描述符,该段的基址是 0,大小是 4GB。

mov %dx, %ds/es/fs是去替换段寄存器,在这几行代码执行之前CPU一直处于用户态,指向用户进程自己的数据空间。DSESFS里存的是用户数据段的选择子(比如0x17,即USER_DS),指向用户进程自己的数据空间。

而接下来马上要执行call *%eax,这会跳转到 C 语言编写的内核函数(如do_divide_error)。C 函数编译后的代码,默认假设DSES指向内核数据段,因为:

  • 它要访问内核全局变量(比如current进程结构)。

  • 它要访问内核栈上的数据。

  • 它不能依赖用户态的段寄存器,否则会寻址错误,甚至导致 CPU 产生保护异常。

所以,在调用 C 函数之前,必须强制把DSESFS切换为内核数据段0x10

5.调用中断处理函数call *%eax,这里ax存放的就是中断处理函数的地址

出栈

在调用完成中断处理函数并且切换回用户态之后,就要开始进行数据出栈

addl $8,%esp这里是把sp指针加8回到存放FS寄存器的地方

然后依次pop出栈数据,最后调用iret返回到用户进程

trap.c

die函数

仍然以上面divide_error为例,调用到trap.c中的do_divide_error

void 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 ESP(Extended Stack Pointer)为扩展栈指针寄存器,存放函数栈顶指针;EBP(Extended 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,nr&0xffff); 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 (i=0;i<4;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(i=0;i<10;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 (i=17;i<48;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_gateset_system_gate,他们都是内核初始化阶段**设置中断描述符表(IDT,Interrupt 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 3int 0x80

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/22 1:23:12

KVM虚拟化中分页与固定内存的性能差异与应用

1. 分页内存与固定内存的核心差异在虚拟化环境中&#xff0c;内存管理机制直接影响着系统性能和资源利用率。分页内存&#xff08;Pageable Memory&#xff09;和固定内存&#xff08;Pinned Memory&#xff09;是两种截然不同的内存管理模式&#xff0c;它们的底层实现原理决定…

作者头像 李华
网站建设 2026/7/22 1:23:08

鱼油选购指南:核心因素与品牌评测

1. 鱼油选购的核心考量因素鱼油作为常见的膳食补充剂&#xff0c;其品质差异主要体现在原料来源、生产工艺和纯度标准三个方面。EPA和DHA含量是衡量鱼油功效性的直接指标&#xff0c;优质鱼油产品这两者的总含量通常不低于60%。分子蒸馏技术能有效去除重金属和污染物&#xff0…

作者头像 李华
网站建设 2026/7/22 1:22:42

Windows+WSL2部署OpenClaw AI员工实战指南

1. 为什么选择WindowsWSL2部署OpenClaw AI员工在Windows环境下部署AI工作流时&#xff0c;WSL2&#xff08;Windows Subsystem for Linux 2&#xff09;提供了近乎原生的Linux内核支持&#xff0c;这解决了传统Windows环境对AI工具链支持不足的核心痛点。OpenClaw作为新一代AI员…

作者头像 李华
网站建设 2026/7/22 1:22:38

WPS占用C盘空间解决方案与替代软件评测

1. 问题现象&#xff1a;WPS为何会吞噬C盘空间&#xff1f;最近帮同事检修一台运行缓慢的办公电脑时&#xff0c;发现C盘仅剩3GB可用空间。通过SpaceSniffer扫描发现&#xff0c;WPS Office相关文件竟占用了28GB空间&#xff01;这种情况并非个例&#xff0c;根据用户反馈统计&…

作者头像 李华
网站建设 2026/7/22 1:22:13

嵌入式存储文件系统:ext4、jffs2、ubifs 格式选型与适配

嵌入式存储文件系统&#xff1a;ext4、jffs2、ubifs 格式选型与适配选文件系统就像选鞋子——跑步穿跑鞋&#xff0c;登山穿登山靴。把 ext4 硬塞进 NOR Flash&#xff0c;就像穿高跟鞋踢足球&#xff0c;不是不行&#xff0c;是疼。引言 嵌入式设备的存储介质种类繁多&#xf…

作者头像 李华