我经常听司机开发人员说尽可能避免内核模式切换。我无法理解确切的原因。从我的理解开始是 -
在这些操作中,系统调用几乎像普通函数调用一样工作。虽然sysenter可能表现得像一个错误预测的分支,这可能导致处理器流水线中的ROB刷新。即使这不是很糟糕,它就像任何其他错误预测的分支一样。
我听到一些人在Stack Overflow上回答:
while(1);
并不保证无上下文切换。实际的系统调用成本来自哪里?
答案 0 :(得分:3)
您没有说明您询问的操作系统。无论如何,让我尝试一下答案。
不应将CPU指令syscall
和sysenter
与system call的概念及其在相应操作系统中的表示相混淆。
通过阅读Intel®64和IA-32架构开发人员手册的操作部分,可以得出每条指令产生的开销差异的最佳解释。 > volume 2A(适用于int
,请参阅第3-392页)和volume 2B(适用于sysenter
请参阅第4-463页)。另外,请不要忘记在iretd
和sysexit
时看一眼。
随意计算操作的伪代码:
int
sysenter
注意:虽然现有的答案是正确的,sysenter
和syscall
不是中断或与中断有任何关系,但 Linux中的旧内核Windows世界使用中断来实现其系统调用机制 。在Linux上,这曾经是int 0x80
和Windows int 0x2E
。因此,在那些内核版本上,IDT必须准备为相应的中断提供中断处理程序。在较新的系统上,这是真的,sysenter
和syscall
指令完全取代了旧方法。使用sysenter
,它是MSR(机器特定寄存器)0x176
,它使用sysenter
处理程序的地址进行准备(参见下面链接的阅读材料)。
在Windows上进行系统调用,就像在Linux上一样,导致切换到内核模式。 NT的调度程序不提供有关授予线程的时间的任何保证。它也会从线程中抽走时间,甚至可能最终导致线程匮乏。一般来说,可以说用户模式代码可以被内核模式代码抢占(很少有非常特殊的例外,你肯定会在“高级驱动程序编写类”中获得这些代码)。如果我们只看一个例子,这是完全合理的。用户模式代码可以换出 - 或者就此而言,它试图访问的数据。现在CPU没有丝毫的线索如何访问交换/分页文件中的页面,因此需要一个中间步骤。这也是内核模式代码必须能够抢占用户模式代码的原因。这也是在Windows上看到的最多产的错误检查代码之一的原因,主要是由第三方驱动程序引起的:IRQL_NOT_LESS_OR_EQUAL
。这意味着当无法抢占触及该内存的代码时,驱动程序访问了分页内存。
KiFastSystemCall
答案 1 :(得分:2)
SYSENTER
/ SYSCALL
不是软件中断;这些指令的重点是避免因发出IRQ和调用中断处理程序而导致的开销。
在堆栈上节省寄存器需要花费时间,这是系统调用成本来源的地方。
另一个地方来自内核模式切换本身。它涉及改变段寄存器--CS,DS,ES,FS,GS,它们都必须改变(它在x86-64上成本较低,因为分段大多未使用,但你仍然需要实质上远远超过内核代码)并且还会更改CPU执行环。
总结:函数调用是(在现代系统上,不使用分段)接近调用,而系统调用涉及远程调用和环切换。