8

我经常听到驱动程序开发人员说尽可能避免内核模式切换是件好事。我无法理解确切的原因。首先我的理解是——

  1. 系统调用是软件中断。在 x86 上,它们是通过使用 sysenter 指令触发的。它实际上看起来像一个分支指令,它从机器特定的寄存器中获取目标。
  2. 系统调用实际上不必更改地址空间或进程上下文。
  3. 不过,它们确实将寄存器保存在进程堆栈上,并将堆栈指针更改为内核堆栈。

在这些操作中,系统调用几乎就像一个普通的函数调用一样工作。尽管 sysenter 可能表现得像一个错误预测的分支,这可能导致处理器管道中的 ROB 刷新。即使这也不是很糟糕,它就像任何其他错误预测的分支一样。

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

  1. 你永远不知道系统调用需要多长时间 - [我] 是的,但任何功能都是如此。所需时间取决于功能
  2. 它经常是调度点。- [me] 进程可以重新调度,即使它一直在用户模式下运行。例如,while(1);不保证无上下文切换。

实际的系统调用成本来自哪里?

4

2 回答 2

3

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

CPU 指令不应与系统调用的概念syscall及其各自操作系统中的表示相混淆。sysenter

通过阅读英特尔® 64 和 IA-32 架构开发人员手册第 2A 卷(对于,请参阅第 3-392 页)和第2B(对于请参阅第 4-463 页)。也不要忘记看看它。 intsysenteriretdsysexit

随意计算操作的伪代码会得出:

  • 408行int
  • 55行sysenter

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


在 Windows ...

就像在 Linux 上一样,Windows 上的系统调用会导致切换到内核模式。NT 的调度程序不提供关于授予线程的时间的任何保证。它还从线程中抽出时间,甚至可能最终导致线程饥饿。一般来说,可以说用户模式代码可以被内核模式代码抢占(除了极少数非常具体的例外情况,您肯定会在“高级驱动程序编写课程”中获得这些例外情况)。如果我们只看一个例子,这是完全有道理的。用户模式代码可以被换出——或者,就此而言,它试图访问的数据。现在 CPU 一点也不知道如何访问交换/分页文件中的页面,因此需要一个中间步骤。这也是为什么内核模式代码必须能够抢占用户模式代码的原因。IRQL_NOT_LESS_OR_EQUAL. 这意味着当无法抢占触及该内存的代码时,驱动程序访问了分页内存。


进一步阅读

  1. Geoff Chappell在 Windows 中的 SYSENTER 和 SYSEXIT(根据我的经验总是值得一读!)
  2. Linux 2.6 中基于 Sysenter 的系统调用机制
  3. Windows NT 平台特定讨论:Windows NT 系统调用如何真正工作?
  4. Windows NT 平台特定讨论:使用 SYSENTER 指令优化系统调用
  5. Windows Internals,第 5 版,Russinovich 等人。人。- 第 125 至 132 页。
  6. ReactOS 的实现KiFastSystemCall
于 2013-03-05T01:14:43.840 回答
2

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

在堆栈上保存寄存器会花费时间,这是系统调用成本的来源之一。

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

总而言之:函数调用(在不使用分段的现代系统上)是近调用,而系统调用涉及远调用和响铃切换。

于 2012-06-23T13:30:55.250 回答