1. 从“黑盒子”到“传话筒”:系统调用到底在干什么?
如果你刚开始接触汇编,可能会觉得系统调用(system call)这个词儿有点唬人。别怕,咱们先把它想成一个“黑盒子”。你的程序,比如一个简单的打印“Hello World”的程序,它自己没法直接让屏幕亮起来、也没法直接读写硬盘上的文件。这些“硬件活儿”都得交给操作系统这个“大管家”来统一安排,否则大家乱抢资源,电脑就乱套了。系统调用,就是你(用户程序)向操作系统大管家“递纸条”、提请求的那个标准流程。你按照约定写好纸条(设置好参数),敲敲管家的窗户(执行一条特殊指令),管家看了纸条就去帮你把事儿办了。
在x86_64的Linux世界里,这个“递纸条”的机制核心就是寄存器和一条叫做 syscall 的指令。这和我们之前在32位时代用的 int 0x80(中断指令)方式有了很大不同,效率更高,是64位时代的标配。今天这篇,我就带你钻进去看看,这张“纸条”到底怎么写——参数怎么放、寄存器怎么用、顺序有啥讲究。我会用大量你能立刻上手跑的代码例子,把原理掰开揉碎了讲。不管你是想深入理解操作系统,还是打算自己写点底层的工具,搞明白这套“传话”机制,绝对是打通任督二脉的关键一步。
2. 核心机制拆解:寄存器如何扮演“参数快递员”
在x86_64架构下进行系统调用,有一套非常明确且固定的“快递规则”。理解这套规则,你就掌握了汇编里调用任何系统功能的钥匙。
2.1 系统调用号:你要办什么业务?
首先,你得告诉操作系统你想干嘛。是写文件(write)?开新进程(fork)?还是退出程序(exit)?每一种操作都有一个唯一的编号,叫做系统调用号。这个号码,你必须放在 %rax 这个寄存器里。%rax 在这里的角色就像是快递单上的“业务类型”栏。
怎么知道某个系统调用的号码呢?在Linux系统里,它们定义在头文件里。比如,你可以快速在终端里用命令查一下:
grep '__NR_write' /usr/include/asm/unistd_64.h
你可能会看到类似 #define __NR_write 1 的输出。这意味着 write 系统调用在x86_64 Linux下的号码是 1。是的,和你之前可能在32位系统里学的号码(32位下write是4)完全不同,这一点千万要区分开!所以,调用write的第一步永远是:
movq $1, %rax ; 将系统调用号 1 (write) 存入 %rax
2.2 参数寄存器:你的具体要求是什么?
光说“我要寄快递”不行,你得告诉快递员收件人、地址和物品。系统调用也是同理,大部分调用都需要额外的参数。x86_64架构规定了最多6个参数,通过6个特定的寄存器来传递,顺序是固定的:
- 第一个参数 ->
%rdi - 第二个参数 ->
%rsi - 第三个参数 ->
%rdx - 第四个参数 ->
%r10(注意!这里不是%rcx) - 第五个参数 ->
%r8 - 第六个参数 ->
%r9
这个顺序必须死记硬背下来,它是 syscall 指令的“语言规范”。我刚开始经常把第四个参数记成 %rcx,结果程序总是行为诡异,调试了半天才揪出这个坑。%rcx 寄存器在 syscall 指


2784

被折叠的 条评论
为什么被折叠?



