本节首先介绍 386 处理器提供的用于处理系统调用的机制,然后展示 Linux 如何使用这些机制。这不是对各个系统调用的参考:系统调用非常多,偶尔会添加新的系统调用,并且它们都记录在您的 Linux 系统上的 man 手册页中。
386 处理器识别两种事件类别:异常和中断。两者都会强制上下文切换到新的过程或任务。中断可能在程序执行期间意外发生,用于响应来自硬件的信号。异常是由指令执行引起的。
386 处理器识别两种中断源:可屏蔽中断和非可屏蔽中断。386 处理器识别两种异常源:处理器检测到的异常和程序异常。
每个中断或异常都有一个编号,386 文档中称之为向量。NMI 中断和处理器检测到的异常的向量范围已分配为 0 到 31(包括 0 和 31)。可屏蔽中断的向量由硬件确定。外部中断控制器在中断确认周期期间将向量放在总线上。范围在 32 到 255(包括 32 和 255)之间的任何向量都可用于可屏蔽中断或程序异常。以下是所有可能的中断和异常的列表
0 | 除法错误 |
---|---|
1 | 调试异常 |
2 | NMI 中断 |
3 | 断点 |
4 | INTO 检测到的溢出 |
5 | BOUND 范围超出 |
6 | 无效操作码 |
7 | 协处理器不可用 |
8 | 双重错误 |
9 | 协处理器段溢出 |
10 | 无效任务状态段 |
11 | 段不存在 |
12 | 堆栈错误 |
13 | 常规保护 |
14 | 页错误 |
15 | 保留 |
16 | 协处理器错误 |
17-31 | 保留 |
32-255 | 可屏蔽中断 |
同时发生的中断和异常的优先级是
最高 | 故障,调试故障除外 |
---|---|
. | 陷阱指令 INTO、INT n、INT 3 |
. | 此指令的调试陷阱 |
. | 下一条指令的调试陷阱 |
. | NMI 中断 |
最低 | INTR 中断 |
在 Linux 下,系统调用的执行是通过可屏蔽中断或异常类转移来调用的,这是由指令int 0x80引起的。我们使用向量 0x80 将控制权转移到内核。此中断向量在系统启动期间与其他重要向量(如系统时钟向量)一起初始化。
iBCS2 要求一个lcall 0,7指令,如果正在执行符合 iBCS2 标准的二进制文件,Linux 可以将其发送到相应的 iBCS2 兼容性模块。实际上,如果执行了lcall 0,7call,Linux 将假定正在执行符合 iBCS2 标准的二进制文件,并将自动切换模式。
截至 Linux 版本 0.99.2,共有 116 个系统调用。这些文档可以在 man (2) 手册页中找到。当用户调用系统调用时,执行流程如下
例如,setuid 系统调用编码为
_syscall1(int,setuid,uid_t,uid);
它将扩展为
_setuid: subl $4,%exp pushl %ebx movzwl 12(%esp),%eax movl %eax,4(%esp) movl $23,%eax movl 4(%esp),%ebx int $0x80 movl %eax,%edx testl %edx,%edx jge L2 negl %edx movl %edx,_errno movl $-1,%eax popl %ebx addl $4,%esp ret L2: movl %edx,%eax popl %ebx addl $4,%esp ret的宏定义syscallX()宏可以在 /usr/include/linux/unistd.h 中找到,用户空间系统调用库代码可以在 /usr/src/libc/syscall/ 中找到
的实际代码system_call入口点可以在 /usr/src/linux/kernel/sys_call.S 中找到。许多系统调用的实际代码可以在 /usr/src/linux/kernel/sys.c 中找到,其余的可以在其他地方找到。find是你的朋友。
Linux 如何初始化系统调用向量在 /usr/src/linux/boot/head.S 中找到的startup_32()代码通过调用setup_idt()开始一切。此例程设置一个包含 256 个条目的 IDT(中断描述符表)。此例程实际上没有加载任何中断入口点,因为只有在启用分页并将内核移动到 0xC0000000 后才会这样做。IDT 有 256 个条目,每个条目 4 个字节长,总共 1024 个字节。当start_kernel()(在 /usr/src/linux/init/main.c 中找到)被调用时,它会调用trap_init()(在 /usr/src/linux/init/main.c 中找到)被调用时,它会调用(在 /usr/src/linux/kernel/traps.c 中找到)。通过宏set_trap_gate()(在 /usr/src/linux/init/main.c 中找到)被调用时,它会调用(在 /usr/include/asm/system.h 中找到)设置 IDT。
0 | 初始化中断描述符表,如下所示 |
---|---|
1 | divide_error |
2 | debug |
3 | nmi |
4 | int3 |
5 | overflow |
6 | bounds |
7 | invalid_op |
8 | device_not_available |
9 | double_fault |
10 | coprocessor_segment_overrun |
11 | invalid_TSS |
12 | segment_not_present |
13 | stack_segment |
14 | general_protection |
15 | 保留 |
16 | page_fault |
17 | coprocessor_error |
18-48 | 保留 |
版权 (C) 1993, 1996 Michael K. Johnson, johnsonm@redhat.com。
版权 (C) 1993 Stanley Scalsky