【问题标题】:How are parameters passed to Linux system call ? Via register or stack?参数如何传递给 Linux 系统调用?通过寄存器还是堆栈?
【发布时间】:2020-08-25 01:29:56
【问题描述】:

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

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

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

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

  1. 所有系统调用都使用asmlinkage 选项定义。这意味着参数是always to be found on the stack and not the register。那么,这些与寄存器有关的业务是什么?
  2. 可能在执行系统调用之前将值复制到内核堆栈。我不知道为什么这样做会很有效,但它可能是一种可能性。

【问题讨论】:

    标签: c linux linux-kernel system-calls interrupt


    【解决方案1】:

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

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

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

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

    【讨论】:

    • 在内核堆栈上再次复制它们有什么好处?为什么不直接从寄存器中使用它们,反正它更快。
    • 嗯,在堆栈上传递参数一直是 x86-32 上 C 函数的默认约定; asmlinkage 只是确保编译器不会试图偏离它。原则上,sys_XXX() 函数可以用另一种约定(例如fastcall)声明,其中参数在寄存器中传递;但是,此约定在相同的寄存器中没有参数,其顺序与 int $0x80 API 规定的顺序相同,因此需要对寄存器进行一些改组。
    • 并且将寄存器推送到内核堆栈非常便宜 - 我认为这与fastcall 的必要寄存器改组之间的开销不会有太大差异。因此,由于代码最初会假设堆栈传递(fastcall 是后来的创新),我认为没有充分的理由重写它。
    猜你喜欢
    • 2014-10-15
    • 1970-01-01
    • 2010-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-19
    • 1970-01-01
    • 2011-08-11
    相关资源
    最近更新 更多