简体   繁体   English

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

[英]Performance difference between system call vs function call

I quite often listen to driver developers saying its good to avoid kernel mode switches as much as possible. 我经常听到驱动程序开发人员说,这样做有助于避免内核模式切换。 I couldn't understand the precise reason. 我不明白确切的原因。 To start with my understanding is - 首先我的理解是-

  1. System calls are software interrupts. 系统调用是软件中断。 On x86 they are triggered by using instruction sysenter. 在x86上,它们是通过使用指令sysenter触发的。 Which actually looks like a branch instruction which takes the target from a machine specific register. 它实际上看起来像是一条分支指令,它从机器特定的寄存器中获取目标。
  2. System calls don't really have to change the address space or process context. 系统调用实际上不必更改地址空间或进程上下文。
  3. Though, they do save registers on process stack and and change stack pointer to kernel stack. 但是,它们确实将寄存器保存在进程堆栈上,并且将堆栈指针更改为内核堆栈。

Among these operations syscall pretty much works like a normal function call. 在这些操作中,syscall几乎像正常的函数调用一样工作。 Though the sysenter could behave like a mis-predicted branch which could lead to ROB flush in processor pipeline. 虽然sysenter的行为可能像一个错误预测的分支,但可能导致处理器管道中的ROB刷新。 Even that is not really bad, its just like any other mis-predicted branch. 即使那还算不错,就像其他任何错误预测的分支一样。

I heard a few people answering on Stack Overflow: 我听说有人在Stack Overflow上回答:

  1. You never know how long syscall takes - [me] yeah, but thats case with any function. 您永远不会知道syscall需要多长时间-[我]是的,但是任何功能都是这样。 Amount of time it takes depends on the function 所需时间取决于功能
  2. It is often scheduling spot. 它通常是安排地点。 - [me] process can get rescheduled, even if it is running all the time in user mode. -[me]进程可以重新安排,即使它始终在用户模式下运行。 ex, while(1); 例如, while(1); doesnt guarantee a no-context switch. 不保证无上下文切换。

Where is the actual syscall cost coming from? 实际的系统调用成本来自哪里?

You don't indicate what OS you are asking about. 您没有指明要询问的操作系统。 Let me attempt an answer anyway. 无论如何,让我尝试一个答案。

The CPU instructions syscall and sysenter should not be confused with the concept of a system call and its representation in the respective OSs. CPU指令syscallsysenter不应与系统调用的概念及其在各个OS中的表示相混淆。

The best explanation for the difference in the overhead incurred by each respective instruction is given by reading through the Operation sections of the Intel® 64 and IA-32 Architectures Developer's Manual volume 2A (for int , see page 3-392) and volume 2B (for sysenter see page 4-463). 用于在由每个相应的指令所引起的开销的差最好的解释是通过在64和IA-32架构开发者手册操作部分读取给定的体积2A (对于int ,见3-392页)和体积2B (对于sysenter见4-463页)。 Also don't forget to glance at iretd and sysexit while at it. 同时也不要忘记浏览iretdsysexit

A casual counting of the pseudo-code for the operations yields: 对该操作的伪代码进行随意计数会得出:

  • 408 lines for int int 408行
  • 55 lines for sysenter sysenter 55行

Note: Although the existing answer is right in that sysenter and syscall are not interrupts or in any way related to interrupts, older kernels in the Linux and the Windows world used interrupts to implement their system call mechanism . 注意:尽管现有的答案很正确,因为sysentersyscall不是中断,或与中断没有任何关系,但是Linux和Windows世界中的较早内核使用中断来实现其系统调用机制 On Linux this used to be int 0x80 and on Windows int 0x2E . 在Linux上,它以前是int 0x80 ,在Windows上是int 0x2E And consequently on those kernel versions the IDT had to be primed to provide an interrupt handler for the respective interrupt. 因此,在那些内核版本上,必须对IDT进行灌注以为相应的中断提供中断处理程序。 On newer systems, that's true, the sysenter and syscall instructions have completely replaced the old ways. 在较新的系统上,的确如此, sysentersyscall指令已完全取代了旧方法。 With sysenter it's the MSR (machine specific register) 0x176 which gets primed with the address of the handler for sysenter (see the reading material linked below). 对于sysenter它是MSR(机器专用寄存器) 0x176 ,它使用0x176处理程序的地址进行sysenter (请参阅下面的阅读材料链接)。


On Windows ... 在Windows上...

A system call on Windows, just like on Linux, results in the switch to kernel mode. 与Linux一样,Windows上的系统调用也会导致切换到内核模式。 The scheduler of NT doesn't provide any guarantees about the time a thread is granted. NT的调度程序不保证线程被授予的时间。 Also it yanks away time from threads and can even end up starving threads. 同样,它浪费了线程的时间,甚至可能导致线程饥饿。 In general one can say that user mode code can be preempted by kernel mode code (with very few very specific exceptions to which you'll certainly get in the "advanced driver writing class"). 通常,可以说用户模式代码可以被内核模式代码抢占(只有很少的非常具体的异常,您肯定会在“高级驱动程序编写类”中得到这些异常)。 This makes perfect sense if we only look at one example. 如果只看一个例子,这是很合理的。 User mode code can be swapped out - or, for that matter, the data it's trying to access. 用户模式代码可以换出-或换而言之,它正在尝试访问的数据。 Now the CPU doesn't have the slightest clue how to access pages in the swap/paging file, so an intermediate step is required. 现在,CPU没有丝毫线索了解如何访问交换/分页文件中的页面,因此需要一个中间步骤。 And that's also why kernel mode code must be able to preempt user mode code. 这也是为什么内核模式代码必须能够抢占用户模式代码的原因。 It is also the reason for one of the most prolific bug-check codes seen on Windows and mostly caused by third-party drivers: IRQL_NOT_LESS_OR_EQUAL . 这也是Windows上出现的最多产的bug检查代码之一,并且主要由第三方驱动程序引起的原因: IRQL_NOT_LESS_OR_EQUAL It means that a driver accessed paged memory when it wasn't possible to preempt the code touching that memory. 这意味着驱动程序在无法抢占接触该内存的代码时访问了页面内存。


Further reading 进一步阅读

  1. SYSENTER and SYSEXIT in Windows by Geoff Chappell (always worth a read in my experience!) Windows中SYSENTER和SYSEXIT,作者Geoff Chappell(根据我的经验,这始终值得一读!)
  2. Sysenter Based System Call Mechanism in Linux 2.6 Linux 2.6中基于Sysenter的系统调用机制
  3. Windows NT platform specific discussion: How Do Windows NT System Calls REALLY Work? Windows NT平台特定的讨论: Windows NT系统调用如何真正起作用?
  4. Windows NT platform specific discussion: System Call Optimization with the SYSENTER Instruction Windows NT平台的特定讨论: 使用SYSENTER指令进行系统调用优化
  5. Windows Internals, 5th ed., by Russinovich et. Windows Internals,第5版,Russinovich等。 al. - pages 125 through 132. -第125至132页。
  6. ReactOS implementation of KiFastSystemCall KiFastSystemCall ReactOS实现

SYSENTER / SYSCALL is not a software interrupt; SYSENTER / SYSCALL 不是软件中断; whole point of those instructions is to avoid overhead caused by issuing IRQ and calling interrupt handler. 这些指令的全部目的是为了避免由于发出IRQ和调用中断处理程序而引起的开销。

Saving registers on stack costs time, this is one place where the syscall cost comes from. 将寄存器保存在堆栈上会花费时间,这是系统调用成本的来源之一。

Another place comes from the kernel mode switch itself. 另一个地方来自内核模式开关本身。 It involves changing segment registers - CS, DS, ES, FS, GS, they all have to be changed (it's less costly on x86-64, as segmentation is mostly unused, but you still need to essentially make far jump to kernel code) and also changes CPU ring of execution. 它涉及到更改段寄存器-CS,DS,ES,FS,GS,所有这些都必须更改(在x86-64上成本较低,因为段未使用,但是您仍然需要从根本上跳到内核代码)并更改CPU执行环。

To conclude: function call is (on modern systems, where segmentation is not used) near call, while syscall involves far call and ring switch. 得出的结论是:函数调用是(在现代系统中,不使用分段的)近调用,而syscall涉及远调用和振铃切换。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM