系统调用入口机制:多架构对比理解(以 ARM64 为主)
📖 推荐阅读:《Yocto项目实战教程:高效定制嵌入式Linux系统》
🎥 更多学习视频请关注 B 站:嵌入式Jerry
系统调用入口机制:多架构对比理解(以 ARM64 为主)
本篇内容聚焦于系统调用的入口实现机制,重点以 ARM64 架构为例,同时对比 x86 和 RISC-V 架构的实现方式,从多角度帮助构建系统调用的总体认知体系。
一、系统调用的核心概念
- 定义:系统调用是用户态程序请求内核服务的一种受控方式。
- 目的:实现从用户态到内核态的“安全切换”,如文件访问、进程创建、内存管理等。
- 典型例子:
open()
,read()
,write()
,fork()
等。
二、系统调用的触发方式对比
架构 | 触发指令 | 注释 | 触发入口源文件位置 |
---|---|---|---|
ARM64 | svc #0 | 使用 SVC(Supervisor Call)陷入内核 | arch/arm64/kernel/entry.S |
x86 | int $0x80 / syscall | 前者为老方式,后者为现代 CPU 使用方式 | arch/x86/entry/entry_64.S |
RISC-V | ecall | 通过环境调用指令陷入内核 | arch/riscv/kernel/entry.S |
三、以 ARM64 为例的系统调用执行流程
🔹 1. 用户态触发
int fd = open("/etc/passwd", O_RDONLY);
- glibc 中
open()
→syscall(SYS_open, ...)
- 执行
svc #0
指令,触发异常
🔹 2. 异常向量入口
// arch/arm64/kernel/entry.S
el0_sync:bl el0_svc
说明:el0_sync
是从 EL0(用户态)同步异常进入 EL1(内核态)的处理入口。
🔹 3. C 语言调用链
el0_sync└── el0_svc (arch/arm64/kernel/entry-common.c)└── do_el0_svc() (arch/arm64/kernel/syscall.c)└── syscall_trace_enter() + invoke_syscall()
-
invoke_syscall()
中执行:- 读取
x8
(系统调用号) - 查表:
sys_call_table[x8]
- 执行对应系统调用实现函数,如
__arm64_sys_open()
- 读取
四、系统调用参数与返回值对比
架构 | 参数传递寄存器 | 系统调用号寄存器 | 返回值寄存器 |
---|---|---|---|
ARM64 | x0 ~x5 | x8 | x0 |
x86 | eax , ebx , ecx … | eax | eax |
RISC-V | a0 ~a5 | a7 | a0 |
五、系统调用表与绑定机制
系统调用表是 syscall number 与实际内核函数之间的映射桥梁,实现“按号调用”的机制。
🔹 syscall 表文件位置
架构 | 系统调用表路径 |
---|---|
ARM64 | arch/arm64/kernel/syscall_table.S |
x86 | arch/x86/entry/syscalls/syscall_64.tbl |
RISC-V | arch/riscv/kernel/syscall_table.c |
🔹 syscall 映射机制
- 用户态设置 syscall number(如 ARM64 用
x8
) - 内核读取 syscall number,从 syscall 表中查找对应函数指针
- 执行绑定的系统调用函数(如
__arm64_sys_open()
)
🔸 例子(ARM64 的 syscall_table.S):
.long __arm64_sys_open // 对应 __NR_open
.long __arm64_sys_read // 对应 __NR_read
🔹 SYSCALL_DEFINE 展开示意
SYSCALL_DEFINE3(open, const char __user *filename, int flags, umode_t mode)
- 宏展开后生成
__arm64_sys_open()
- 并作为 syscall 表的一项注册
六、完整调用路径梳理(ARM64)
用户态↓
svc #0 (用户态发起陷入)↓
el0_sync (arch/arm64/kernel/entry.S)↓
el0_svc (arch/arm64/kernel/entry-common.c)↓
do_el0_svc (arch/arm64/kernel/syscall.c)↓
syscall_trace_enter → invoke_syscall()↓
sys_call_table[x8] → __arm64_sys_open()
七、调试与分析工具推荐
工具 | 用途 |
---|---|
strace | 跟踪用户态发起的系统调用 |
ftrace | 跟踪内核态 syscall 调用链 |
gdb | 可调试汇编入口与寄存器设置 |
objdump / readelf | 查看符号表与 ELF 结构 |
八、常见问题总结
问题 | 答案 |
---|---|
系统调用是中断吗? | 是一种同步异常(软中断),可类比中断处理但不同于 IRQ。 |
为什么每种架构入口不一样? | 不同指令集的陷入方式、特权级切换方式、寄存器约定不一样。 |
系统调用号在哪设置? | 通常在用户态库中写入指定寄存器(如 ARM64 的 x8 )。 |
怎么找到系统调用函数? | 查 syscall 表,通过 syscall number 定位函数指针。 |
参数怎么传? | 不同架构采用不同寄存器(x0 |
syscall 表作用是什么? | 它是内核中系统调用号和函数之间的查找映射表,按号定位函数地址执行。 |
📌 本文重点理解系统调用的“陷入路径”,构建从用户态到内核态的调用跳转逻辑。后续配合“系统调用如何连接内核子系统”篇章,深入剖析 syscall 如何与 VFS、进程管理等模块协作。