参数如何传递给 Linux 系统调用?通过寄存器还是堆栈?

ng.*_*bie 1 c linux interrupt system-calls linux-kernel

我试图通过阅读Robert Love 的 Linux Kernel Development来了解 Linux 内核的内部结构。

在第 74 页上,他说将参数传递给 a 的最简单方法syscall是通过:

不知何故,用户空间必须在陷阱期间将参数传递给内核。最简单的方法是通过传递系统调用号的相同方式:参数存储在寄存器中。在 x86-32 上,寄存器 ebx、ecx、edx、esi 和 edi 按顺序包含前五个参数。

现在这让我感到困扰,原因有很多:

  1. 所有系统调用都使用该asmlinkage选项定义。这意味着参数总是在堆栈上而不是寄存器上找到。那么寄存器的所有这些业务是什么?
  2. 有可能在执行系统调用之前将值复制到内核堆栈上。我不知道为什么这会很有效,但它可能是一种可能性。

Nat*_*dge 5

(此答案适用于 32 位 x86 Linux 以匹配您的问题;64 位 x86 和其他架构的情况略有不同。)

正如 Love 所说,参数是从用户空间通过寄存器传递的。

当用户空间使用 调用系统调用时int $0x80,内核系统调用入口代码将获得控制权。这是用汇编语言编写的,例如可以在这里看到。这段代码所做的一件事是从寄存器中获取参数并将它们压入堆栈,然后调用适当的内核sys_XXX()函数(用 C 编写)。所以这些函数确实希望它们的参数在堆栈上。

尝试将参数从用户空间传递到堆栈上的内核是行不通的。当进行系统调用时,CPU 切换到单独的内核堆栈,因此必须将参数从用户空间堆栈复制到内核堆栈,这有点复杂。即使对于只需要几个数字参数的非常简单的系统调用,也必须这样做,否则根本不需要访问用户空间内存(close()例如考虑)。