为什么在Windows上创建比Linux更昂贵的新流程?

Why is creating a new process more expensive on Windows than Linux?

我听说在Windows机器上创建一个新进程比在Linux上更昂贵。 这是真的? 有人可以解释为什么它更昂贵的技术原因,并为这些原因背后的设计决策提供任何历史原因?


mweerden:NT从第一天开始就是为多用户设计的,所以这不是真正的原因。但是,你肯定的是,在Unix上,进程创建在NT上比在Unix上扮演一个不太重要的角色,与Unix相反,它支持多线程而不是多处理。

Rob,使用COW时叉子确实相对便宜,但事实上,fork主要是一个exec。并且exec也必须加载所有图像。因此,讨论fork的性能只是事实的一部分。

在讨论创建进程的速度时,区分NT和Windows / Win32可能是一个好主意。至于NT(即内核本身),我认为进程创建(NtCreateProcess)和线程创建(NtCreateThread)并不像普通Unix那样慢得多。可能会有更多的事情发生,但我没有看到这里性能差异的主要原因。

但是,如果你看一下Win32,你会注意到它为进程创建增加了相当多的开销。首先,它要求CSRSS通知涉及LPC的流程创建。它至少需要加载kernel32,并且在将该进程视为完整的Win32进程之前,它必须执行许多额外的簿记工作项。让我们不要忘记解析清单所带来的所有额外开销,检查图像是否需要兼容性垫片,检查软件限制策略是否适用,yada yada。

也就是说,除了原始创建流程,VA空间和初始线程之外,我还看到了所有必须完成的小事情总和的整体放缓。但正如开头所说 - 由于多线程优于多任务处理,唯一受此额外费用影响的软件是移植不良的Unix软件。虽然当Chrome和IE8等软件突然重新发现多处理的好处并开始频繁启动和拆卸流程时,这种情况会发生变化......


Unix有一个'fork'系统调用,它将当前进程"拆分"为两个,并为你提供第二个进程,它与第一个进程相同(以fork调用返回为模)。由于新进程的地址空间已经启动并运行,因此应该比在Windows中调用'CreateProcess'并使其加载exe映像,关联的dll等更便宜。

在fork情况下,OS可以对与两个新进程相关联的内存页使用"copy-on-write"语义,以确保每个进程都获得自己随后修改的页面副本。


添加到JP所说的:大部分开销都属于Win32启动过程。

Windows NT内核实际上支持COW fork。 SFU(Microsoft的Windows UNIX环境)使用它们。但是,Win32不支持fork。 SFU进程不是Win32进程。 SFU与Win32正交:它们都是在同一内核上构建的环境子系统。

除了对CSRSS的进程外LPC调用之外,在XP及更高版本中还有一个对应用程序兼容性引擎的进程外调用,以便在应用程序兼容性数据库中查找该程序。此步骤导致足够的开销,Microsoft提供了一个组策略选项,以便出于性能原因禁用WS2003上的兼容性引擎。

Win32运行时库(kernel32.dll等)也在启动时执行大量注册表读取和初始化,这些不适用于UNIX,SFU或本机进程。

本机进程(没有环境子系统)创建速度非常快。 SFU在创建流程方面比Win32少得多,因此其流程也很快创建。

更新2019年:添加LXSS:适用于Linux的Windows子系统

替换SFU for Windows 10是LXSS环境子系统。它是100%内核模式,不需要Win32继续拥有的任何IPC。这些进程的Syscall直接指向lxss.sys / lxcore.sys,因此fork()或其他创建调用的进程只需要为创建者进行1次系统调用,总计。 [称为实例的数据区]跟踪所有LX进程,线程和运行时状态。

LXSS进程基于本机进程,而不是Win32进程。所有Win32特定的东西,如兼容性引擎都没有参与。


除了Rob Walker的答案:
如今你有像Native POSIX Thread Library这样的东西 - 如果你愿意的话。
但是很长一段时间,在unix世界中"委托"工作的唯一方法是使用fork()(在许多情况下它仍然是首选)。
例如某种套接字服务器

1
2
3
4
5
6
socket_accept()
fork()
if (child)
    handleRequest()
else
    goOnBeingParent()

因此,fork的实现必须很快,并且随着时间的推移已经实现了很多优化。
Microsoft支持CreateThread甚至是光纤,而不是创建新进程和进程间通信的使用。我认为将CreateProcess与fork进行比较并不"公平",因为它们不可互换。将fork / exec与CreateProcess进行比较可能更合适。


我认为,这个问题的关键是两个系统的历史用法。 Windows(以及之前的DOS)最初是个人计算机的单用户系统。因此,这些系统通常不必一直创建大量进程; (非常)简单地说,只有当这个孤独的用户请求它时才创建一个进程(而且我们人类相对来说操作速度不是很快)。

基于Unix的系统最初是多用户系统和服务器。特别是对于后者,具有分离进程以处理特定作业(例如,处理一个传入连接)的进程(例如,邮件或http守护进程)并不罕见。这样做的一个重要因素是廉价的fork方法(如Rob Walker(47865)所述,最初对新创建的进程使用相同的内存),这非常有用,因为新进程立即拥有所有信息需要。

很明显,至少从历史上看,基于Unix的系统对快速创建流程的需求远远大于Windows系统。我认为情况仍然如此,因为基于Unix的系统仍然是面向流程的,而Windows由于其历史,可能更多地面向线程(线程对于生成响应式应用程序很有用)。

免责声明:我不是这方面的专家,如果我弄错了,请原谅我。


简短的回答是"软件层和组件"。

Windows SW体系结构有一些额外的层和组件,这些层和组件在Unix上不存在,或者在Unix内核中进行了简化和处理。

在Unix上,fork和exec是对内核的直接调用。

在Windows上,内核API不是直接使用的,它上面有win32和某些其他组件,因此创建进程必须经过额外的层,然后新进程必须启动或连接到这些层和组件。

很长一段时间以来,研究人员和企业都试图以模糊的方式分解Unix,通常是将他们的实验基于Mach内核;一个众所周知的例子是OS X ..但是,每当他们尝试时,它变得如此缓慢,他们最终至少部分地将这些部分合并到内核中,无论是永久性还是生产出货。


呃,似乎有很多"这种方式更好"的理由正在进行中。

我认为人们可以从阅读"Showstopper"中受益;关于Windows NT开发的书。

服务作为DLL在Windows NT上的一个进程中运行的全部原因是它们作为单独的进程太慢。

如果你沮丧和肮脏,你会发现库加载策略是问题所在。

在Unices(通常)上,共享库(DLL)的代码段实际上是共享的。

Windows NT在每个进程中加载??DLL的副本,因为它在加载后操作库代码段(和可执行代码段)。 (告诉它你的数据在哪里?)

这导致库中的代码段不可重用。

因此,NT进程创建实际上非常昂贵。并且在不利方面,它使得DLL在内存中没有明显的节省,但是存在应用程序间依赖性问题的可能性。

有时候工程学会退后一步说:"现在,如果我们要把它设计成真的很糟糕,那会是什么样子?"

我曾经使用过一个非常有气势的嵌入式系统,有一天看着它并意识到它是一个腔磁控管,电子器件在微波腔中。之后我们让它变得更加稳定(而不像微波炉)。


因为在某些答案中似乎有一些MS-Windows的理由,例如

  • "NT内核和Win32,不是一回事。如果您编程到NT内核那么它就不那么糟了" - 是的,但除非您正在编写Posix子系统,否则谁在乎。你将写入win32。
  • "将fork与ProcessCreate进行比较是不公平的,因为他们做了不同的事情,Windows没有fork" - 的确如此,
    所以我会比较喜欢。但是我也会比较fork,因为它有许多用例,例如进程隔离(例如,Web浏览器的每个选项卡在不同的进程中运行)。

现在让我们看看事实,性能有何不同?

数据来自http://www.bitsnbites.eu/benchmarking-os-primitives/。
因为偏见是不可避免的,所以在总结时,我做的是支持MS-Windows
大多数测试的硬件i7 8核心3.2GHz。运行Gnu / Linux的Raspberry-Pi除外

A comparison of various basic operations, on Gnu/Linux, Apple-Mac, and Microsofts Windows (smaller is better)

A comparison of MS-Windows process create vs Linux

笔记:
在linux上,fork比MS-Window的首选方法CreateThread更快。

进程创建类型操作的数字(因为很难在图表中看到Linux的值)。

按速度顺序,最快到最慢(数字是时间,小是更好)。

  • Linux CreateThread 12
  • Mac CreateThread 15
  • Linux Fork 19
  • Windows CreateThread 25
  • Linux CreateProcess(fork + exec)45
  • Mac Fork 105
  • Mac CreateProcess(fork + exec)453
  • Raspberry-Pi CreateProcess(fork + exec)501
  • Windows CreateProcess 787
  • Windows CreateProcess使用病毒扫描程序2850
  • Windows Fork(使用CreateProcess + fixup进行模拟)大于2850

其他测量的数字

  • 创建文件。

    • Linux 13
    • Mac 113
    • Windows 225
    • Raspberry-Pi(带有慢速SD卡)241
    • 带有防御者和病毒扫描程序等的Windows 12950
  • 分配内存

    • Linux 79
    • Windows 93
    • Mac 152

值得注意的是,Windows中的安全模型比基于unix的操作系统复杂得多,后者在创建进程时增加了大量开销。多线程的另一个原因是Windows中的多处理优先。


所有这些加上事实是,在Win机器上,很可能一个防病毒软件将在CreateProcess期间启动...这通常是最大的减速。


推荐阅读