FreeBSD 与 Linux:内核调用约定的性能

Eva*_*oll 3 linux performance freebsd conventions system-calls

来自int80h.org,FreeBSD 汇编语言教程

[Linux 调用] 约定与 Unix 方式相比有很大的缺点,至少就汇编语言编程而言:每次进行内核调用时,您必须压入寄存器,然后再弹出它们。这会使您的代码体积更大,速度更慢。

继续说 FreeBSD 支持 Linux 约定和“Unix 约定”

如果您专门为 FreeBSD 编码,您应该始终使用 Unix 约定:它更快,您可以将全局变量存储在寄存器中,您不必标记可执行文件,并且您不会将 Linux 仿真包的安装强加于目标系统。

对我来说,Linux 方式会更笨重和更慢,这似乎很奇怪。好像有两种选择,

  • 只保存您需要保留的寄存器
    • 那些可能被系统调用破坏的易失性寄存器(据我所知 ecx
    • 或者,将适当的参数发送到内核以生成syscall(可能是eax, ecx, edx, esi, edi, ebp)所需的寄存器
  • 将 100% 的参数保存到堆栈上的内核。

看起来 FreeBSD 是Linux 公约中最糟糕的情况。我错过了什么?FreeBSD 约定(他们称之为“Unix 方式”)如何变得更小、更快?

Ste*_*itt 9

在我看来,这实际上归结为作者的意见。

在 FreeBSD (“Unix”) 约定中,您将参数压入堆栈,在 中指定系统调用号EAX,然后调用中断 0x80(堆栈上有一个额外的操作数,因为它期望从单独的函数调用)。

在 Linux i386 约定中,您将参数放在适当的寄存器中,并调用中断 0x80。

庞大/缓慢的论点大概来自这样一个事实,即根据 Linux 约定,调用者需要处理其对寄存器的使用。如果系统调用需要包含调用者关心的值的寄存器中的参数,则需要保留它们,这会导致额外的工作;请参阅 C 库中的此示例。在这个例子中,系统调用需要 EAX、EBX、EDX、EDI 和 ESI 中的值;但是调用者只关心保留 EBX、EDI 和 ESI,所以它只将它们压入堆栈。一般情况要复杂一些(但这也是处理 C 和汇编语言混合的结果,试图在所有情况下生成最佳代码),但是当用汇编语言编写时,这是您所指的站点的重点,那不会问题不大。

在我看来,它是六个半:在 FreeBSD 约定中,您在所有情况下都推送到堆栈,在 Linux 约定中,您推送到堆栈(或其他地方),具体取决于您在做什么呼叫站点。您可能会争辩说 Linux 约定可以实现更快的代码,因为您可以在寄存器中执行所有计算......然而,正如Rob指出的那样,在 Linux 上,寄存器仍然最终被推送(构建struct pt_regs用于提供参数的实例)处理系统调用的 C 函数),因此在 Linux 端的总体成本比在 FreeBSD 端要大。

考虑到执行系统调用本身的成本,在谈论系统调用周围的堆栈或基于寄存器的代码时,在任何情况下争论性能似乎都相当迂腐。保存的任何周期从绝对值来看当然是好的,但相对的改进将是微乎其微的。