HyperNews Linux KHG 讨论页面

Linux/i86 系统调用工作原理

本节首先介绍 386 处理器提供的用于处理系统调用的机制,然后展示 Linux 如何使用这些机制。这不是对各个系统调用的参考:系统调用非常多,偶尔会添加新的系统调用,并且它们都记录在您的 Linux 系统上的 man 手册页中。

386 处理器提供了什么?

386 处理器识别两种事件类别:异常和中断。两者都会强制上下文切换到新的过程或任务。中断可能在程序执行期间意外发生,用于响应来自硬件的信号。异常是由指令执行引起的。

386 处理器识别两种中断源:可屏蔽中断和非可屏蔽中断。386 处理器识别两种异常源:处理器检测到的异常和程序异常。

每个中断或异常都有一个编号,386 文档中称之为向量。NMI 中断和处理器检测到的异常的向量范围已分配为 0 到 31(包括 0 和 31)。可屏蔽中断的向量由硬件确定。外部中断控制器在中断确认周期期间将向量放在总线上。范围在 32 到 255(包括 32 和 255)之间的任何向量都可用于可屏蔽中断或程序异常。以下是所有可能的中断和异常的列表
0除法错误
1调试异常
2NMI 中断
3断点
4INTO 检测到的溢出
5BOUND 范围超出
6无效操作码
7协处理器不可用
8双重错误
9协处理器段溢出
10无效任务状态段
11段不存在
12堆栈错误
13常规保护
14页错误
15保留
16协处理器错误
17-31保留
32-255可屏蔽中断

同时发生的中断和异常的优先级是
最高故障,调试故障除外
.陷阱指令 INTO、INT n、INT 3
.此指令的调试陷阱
.下一条指令的调试陷阱
.NMI 中断
最低INTR 中断

Linux 如何使用中断和异常

在 Linux 下,系统调用的执行是通过可屏蔽中断或异常类转移来调用的,这是由指令int 0x80引起的。我们使用向量 0x80 将控制权转移到内核。此中断向量在系统启动期间与其他重要向量(如系统时钟向量)一起初始化。

iBCS2 要求一个lcall 0,7指令,如果正在执行符合 iBCS2 标准的二进制文件,Linux 可以将其发送到相应的 iBCS2 兼容性模块。实际上,如果执行了lcall 0,7call,Linux 将假定正在执行符合 iBCS2 标准的二进制文件,并将自动切换模式。

截至 Linux 版本 0.99.2,共有 116 个系统调用。这些文档可以在 man (2) 手册页中找到。当用户调用系统调用时,执行流程如下

的代码访问它。

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初始化中断描述符表,如下所示
1divide_error
2debug
3nmi
4int3
5overflow
6bounds
7invalid_op
8device_not_available
9double_fault
10coprocessor_segment_overrun
11invalid_TSS
12segment_not_present
13stack_segment
14general_protection
15保留
16page_fault
17coprocessor_error
18-48保留
alignment_check此时,系统调用的中断向量尚未设置。它由sched_init()(在 /usr/src/linux/kernel/sched.c 中找到)初始化。调用set_system_gate (0x80, &system_call)将中断 0x80 设置为system_call()

入口点的向量。

  1. 如何添加您自己的系统调用
  2. 在 /usr/src/linux/ 目录下创建一个目录来保存您的代码。
  3. 将任何包含文件放在 /usr/include/sys/ 和 /usr/include/linux/ 中。将链接您的新内核代码生成的、可重定位模块添加到ARCHIVES和子目录添加到顶级 Makefile 的SUBDIRS
  4. 行。有关示例,请参见 fs/Makefile,目标 fs.o。添加一个#define __NR_xx到 unistd.h 以为您自己的系统调用分配一个调用号,其中xx(索引)是一些描述您的系统调用的内容。它将用于通过sys_call_table
  5. 设置向量以调用您的代码。(索引)是一些描述您的系统调用的内容。它将用于通过为您的系统调用在 sys.h 中添加一个入口点。它应该与您在上一步中分配的索引 (到 unistd.h 以为您自己的系统调用分配一个调用号,其中) 匹配。NR_syscalls变量将自动重新计算。
  6. 修改 kernel/fs/mm/ 等中的任何内核代码,以考虑支持您的新代码所需的环境。
  7. 从顶层运行 make 以生成包含您的新代码的新内核。
此时,您将必须向您的库中添加一个 syscall,或者在您的用户程序中使用适当的_syscalln()宏,以便您的程序可以访问新的系统调用。《386DX 微处理器程序员参考手册》和 James Turley 的《高级 80386 编程技术》是很有帮助的参考资料。请参阅带注释的参考书目

版权 (C) 1993, 1996 Michael K. Johnson, johnsonm@redhat.com。
版权 (C) 1993 Stanley Scalsky


消息

4. 注意: system_call 代码的文件错误 作者:Tim Bird
3. 注意: 最好解释一下 syscall 宏 作者:Tim Bird
2. 注意: syscallX() 宏的文件错误 作者:Tim Bird
1. 反馈: 目录 /usr/src/libc/syscall/ 作者:vijay gupta
1. 注意: ...不再存在。 作者:Michael K. Johnson
-> 反馈: 问题的解决方案 作者:Vijay Gupta