系统调用与函数调用之间的性能差异

时间:2012-06-23 13:17:27

标签: performance x86 kernel system-calls

我经常听司机开发人员说尽可能避免内核模式切换。我无法理解确切的原因。从我的理解开始是 -

  1. 系统调用是软件中断。在x86上,它们由使用指令sysenter触发。这实际上看起来像是从机器特定寄存器中获取目标的分支指令。
  2. 系统调用实际上不必更改地址空间或进程上下文。
  3. 虽然,他们确实在进程堆栈上保存了寄存器,并将堆栈指针更改为内核堆栈。
  4. 在这些操作中,系统调用几乎像普通函数调用一样工作。虽然sysenter可能表现得像一个错误预测的分支,这可能导致处理器流水线中的ROB刷新。即使这不是很糟糕,它就像任何其他错误预测的分支一样。

    我听到一些人在Stack Overflow上回答:

    1. 你永远都不知道系统调用需要多长时间 - [我]是的,但这可以解决任何问题。所需的时间取决于功能
    2. 经常安排现场。 - [me]进程可以重新安排,即使它一直在用户模式下运行。例如,while(1);并不保证无上下文切换。
    3. 实际的系统调用成本来自哪里?

2 个答案:

答案 0 :(得分:3)

您没有说明您询问的操作系统。无论如何,让我尝试一下答案。

不应将CPU指令syscallsysentersystem call的概念及其在相应操作系统中的表示相混淆。

通过阅读Intel®64和IA-32架构开发人员手册操作部分,可以得出每条指令产生的开销差异的最佳解释。 > volume 2A(适用于int,请参阅第3-392页)和volume 2B(适用于sysenter请参阅第4-463页)。另外,请不要忘记在iretdsysexit时看一眼。

随意计算操作的伪代码:

  • int
  • 的408行
  • sysenter
  • 的55行

注意:虽然现有的答案是正确的,sysentersyscall不是中断或与中断有任何关系,但 Linux中的旧内核Windows世界使用中断来实现其系统调用机制 。在Linux上,这曾经是int 0x80和Windows int 0x2E。因此,在那些内核版本上,IDT必须准备为相应的中断提供中断处理程序。在较新的系统上,这是真的,sysentersyscall指令完全取代了旧方法。使用sysenter,它是MSR(机器特定寄存器)0x176,它使用sysenter处理程序的地址进行准备(参见下面链接的阅读材料)。


在Windows上......

在Windows上进行系统调用,就像在Linux上一样,导致切换到内核模式。 NT的调度程序不提供有关授予线程的时间的任何保证。它也会从线程中抽走时间,甚至可能最终导致线程匮乏。一般来说,可以说用户模式代码可以被内核模式代码抢占(很少有非常特殊的例外,你肯定会在“高级驱动程序编写类”中获得这些代码)。如果我们只看一个例子,这是完全合理的。用户模式代码可以换出 - 或者就此而言,它试图访问的数据。现在CPU没有丝毫的线索如何访问交换/分页文件中的页面,因此需要一个中间步骤。这也是内核模式代码必须能够抢占用户模式代码的原因。这也是在Windows上看到的最多产的错误检查代码之一的原因,主要是由第三方驱动程序引起的:IRQL_NOT_LESS_OR_EQUAL。这意味着当无法抢占触及该内存的代码时,驱动程序访问了分页内存。


进一步阅读

    Geoff Chappell的
  1. SYSENTER and SYSEXIT in Windows(根据我的经验值得一读!)
  2. Sysenter Based System Call Mechanism in Linux 2.6
  3. Windows NT平台特定讨论:How Do Windows NT System Calls REALLY Work?
  4. Windows NT平台特定讨论:System Call Optimization with the SYSENTER Instruction
  5. Windows Internals,第5版,Russinovich等。人。 - 第125至132页。
  6. ReactOS implementation of KiFastSystemCall

答案 1 :(得分:2)

SYSENTER / SYSCALL 不是软件中断;这些指令的重点是避免因发出IRQ和调用中断处理程序而导致的开销。

在堆栈上节省寄存器需要花费时间,这是系统调用成本来源的地方。

另一个地方来自内核模式切换本身。它涉及改变段寄存器--CS,DS,ES,FS,GS,它们都必须改变(它在x86-64上成本较低,因为分段大多未使用,但你仍然需要实质上远远超过内核代码)并且还会更改CPU执行环。

总结:函数调用是(在现代系统上,不使用分段)接近调用,而系统调用涉及远程调用和环切换。