『阿男的Linux内核世界』*15 从User Space到Kernel Space(三)*
我们目前知道了,当interrupt发生时,Kernel会暂时打断Process在User Space的运行,转到Kernel Space处理中断。此外,我们知道了interrupt大体可以分为三类,分别是硬件中断,软件中断,还有CPU的Exceptions。
其中硬件中断是硬件设备直接发信号给CPU,比如用户按键盘输入数据。软件中断是程序主动调用CPU的INT
指令来触发某一个Interrupt,CPU异常则是CPU在运算过程中发生的各种错误状态。
我们还知道了,Intel CPU架构下,一共有256个编号可以给以上面三种interrupts使用。因此,在操作系统实现方面,大部分工作就是把这些编号和具体的处理这些interrupts的函数给对应起来。处理Interrupts的函数叫做Interrupt Handler
。
那么编号和实现函数肯定要有一个对应关系,这个对应关系需要一张表格来保存,这张表格叫做IDT
,全称就是Interrupt Descriptors Table
。而这张表格在哪里呢?这张表格在操作系统启动的时候由操作系统加载到内存里,然后这张表格的起始位置保存在IDTR
这个寄存器里面。注意这个寄存器也是个高权限寄存器,Kernel可以访问,而User Space无法访问到的。
关于IDT
表格的设置代码,我们可以看下desc.h
^1里面相关的代码:
#define load_tr(tr) asm volatile("ltr %0"::"m" (tr))
#define load_ldt(ldt) asm volatile("lldt %0"::"m" (ldt))
可以看到CPU的指令ltr
和lldt
都是和设置IDTR
寄存器相关的指令,具体的细节阿男就不在这篇文章里面详述了,大家如果真的对Intel CPU的指令集和架构设计感兴趣,就需要花功夫阅读Intel的官方手册Intel® 64 and IA-32 Architectures Developer's Manual
^2,非常厚的一本手册,但是写得非常好,大家自己下载PDF留一份,经常看一看,收获会非常大。
关于IDT
表格的默认内容,我们在前一篇文章中讲解过了,分别在traps.h
和irq_vectors.h
里面有定义。我们这次再回顾一下arch/x86/include/asm/irq_vectors.h
^3。注意和硬件架构相关的代码,往往具体的实现有差异,所以Linux在kernel代码里面会把不同架构的不同代码用arch/
然后后面加上具体的架构名字,比如x86
。这次我们来看看irq_vectors.h
里面的注释:
/*
* Linux IRQ vector layout.
*
* There are 256 IDT entries (per CPU - each entry is 8 bytes) which can
* be defined by Linux. They are used as a jump table by the CPU when a
* given vector is triggered - by a CPU-external, CPU-internal or
* software-triggered event.
*
* Linux sets the kernel code address each entry jumps to early during
* bootup, and never changes them. This is the general layout of the
* IDT entries:
*
* Vectors 0 ... 31 : system traps and exceptions - hardcoded events
* Vectors 32 ... 127 : device interrupts
* Vector 128 : legacy int80 syscall interface
* Vectors 129 ... INVALIDATE_TLB_VECTOR_START-1 except 204 : device interrupts
* Vectors INVALIDATE_TLB_VECTOR_START ... 255 : special interrupts
*
* 64-bit x86 has per CPU IDT tables, 32-bit has one shared IDT table.
*
* This file enumerates the exact layout of them:
*/
上面这段代码告诉我们Linux是如何使用IDT
的,比如0到31号中断时system traps,32到127时设备中断,128是system calls,等等。
其中有一个128
号中断,也就是十六进制的0x80
号软件中断是所有system calls的入口。也就是说0x80
号中断对应system calls的handler function,然后这个handler再根据寄存器里面,程序请求的system call编号来执行相应的system call function。那么为什么文档里说这个128号是legacy int80 syscall interface
呢?所谓legacy
就是过时的,会被淘汰的。因为新的Intel x86_64 CPU,也就是64位的CPU架构里,提供了一个更为直接的指令,叫做syscall
,这个指令比int 0x80
更高效。