为什么在 Windows 上创建一个新进程比在 Linux 上更昂贵?

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

27125 次浏览

Unix 有一个“ fork”系统调用,它将当前进程“一分为二”,并为您提供与第一个进程相同的第二个进程(模拟 fork 调用的返回)。由于新进程的地址空间已经启动并正在运行,这应该比在 Windows 中调用“ CreateProcess”并让它加载 exe 映像、相关的 dlls 等要便宜。

In the fork case the OS can use 'copy-on-write' semantics for the memory pages associated with both new processes to ensure that each one gets their own copy of the pages they subsequently modify.

除了 Rob Walker 的回答: 现在你有了 Native POSIX Thread Library 之类的东西——如果你想要的话。 但是在很长一段时间里,在 unix 世界中“委托”工作的唯一方法是使用 fork ()(在很多很多情况下,它仍然是首选)。 例如,某种套接字服务器

socket_accept()
fork()
if (child)
handleRequest()
else
goOnBeingParent()
Therefore the implementation of fork had to be fast and lots optimizations have been implemented over time. Microsoft endorsed CreateThread or even fibers instead of creating new processes and usage of interprocess communication. I think it's not "fair" to compare CreateProcess to fork since they are not interchangeable. It's probably more appropriate to compare fork/exec to CreateProcess.

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

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

It is clear that at least historically the need for Unix-based systems to have fast process creation is far greater than for Windows systems. I think this is still the case because Unix-based systems are still very process oriented, while Windows, due to its history, has probably been more thread oriented (threads being useful to make responsive applications).

Disclaimer: I'm by no means an expert on this matter, so forgive me if I got it wrong.

NT 从一开始就是为多用户设计的,所以这不是一个真正的原因。然而,您是对的,进程创建在 NT 上的作用不如在 Unix 上重要,因为与 Unix 相比,NT 更青睐多线程而不是多处理。

Rob, it is true that fork is relatively cheap when COW is used, but as a matter of fact, fork is mostly followed by an exec. And an exec has to load all images as well. Discussing the performance of fork therefore is only part of the truth.

在讨论进程创建的速度时,区分 NT 和 Windows/Win32可能是一个好主意。就 NT (即内核本身)而言,我不认为进程创建(NtCreateProcess)和线程创建(NtCreateThread)的速度比一般的 Unix 慢得多。可能还有一些其他的原因,但是我看不出这里性能差异的主要原因。

但是,如果您查看 Win32,您会注意到它为进程创建增加了相当多的开销。首先,它需要通知 CSRSS 进程的创建,这涉及到 LPC。它需要额外加载至少内核32,并且在该流程被认为是一个成熟的 Win32流程之前,它必须执行许多额外的簿记工作项。我们不要忘记解析清单带来的额外开销,检查映像是否需要兼容性垫片,检查软件限制策略是否适用,等等。

也就是说,除了原始创建流程、 VA 空间和初始线程之外,我还看到了所有这些必须完成的小事情的总和的总体放缓。但是正如一开始所说的——由于多线程比多任务更受青睐,唯一受到这种额外费用严重影响的软件是移植性差的 Unix 软件。尽管当像 Chrome 和 IE8这样的软件突然重新发现多处理的好处并开始频繁地启动和拆卸处理时,情况发生了变化..。

All that plus there's the fact that on the Win machine most probably an antivirus software will kick in during the CreateProcess... That's usually the biggest slowdown.

好像有很多“这样更好”的理由。

我认为人们可以从阅读“ Showstopper”这本关于 WindowsNT 开发的书中获益。

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

If you got down and dirty you'd find that the library loading strategy is the problem.

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

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

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

因此,NT 进程的创建实际上是相当昂贵的。不利的一面是,它使得 DLL 在内存方面没有明显的节省,但却有可能导致应用程序之间的依赖性问题。

有时候在工程学上退一步说,“现在,如果我们要把这个设计得非常糟糕,它看起来会是什么样子?”

I worked with an embedded system that was quite temperamental once upon a time, and one day looked at it and realized it was a cavity magnetron, with the electronics in the microwave cavity. We made it much more stable (and less like a microwave) after that.

增加了 JP 所说的: 大部分的开销属于 Win32启动进程。

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

In addition to the out-of-process LPC calls to CSRSS, in XP and later there is an out of process call to the application compatibility engine to find the program in the application compatibility database. This step causes enough overhead that Microsoft provides a group policy option to 禁用 WS2003上的兼容性引擎 for performance reasons.

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

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

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

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

LXSS processes are based on native processes, not Win32 processes. All the Win32 specific stuff like the compatibility engine aren't engaged at all.

The short answer is "software layers and components".

Windows SW 体系结构有几个附加的层和组件,这些层和组件在 Unix 上不存在,或者在 Unix 上的内核中被简化和处理。

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

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

很长一段时间以来,研究人员和企业一直试图以类似的方式分解 Unix,通常是基于 马赫内核进行实验; 一个著名的例子是 操作系统。。然而,每次他们尝试,它变得如此缓慢,以至于他们最终至少部分地将这些片段合并回内核,要么是永久性的,要么是用于生产发货。

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

因为在一些答案中似乎有一些对 MS-Windows 的解释,例如。

  • ”NT 内核和 Win32不是一回事。如果你的程序是 NT 内核,那就没有那么糟糕”ーー没错,但是除非你正在编写一个 Posix 子系统,那么谁在乎呢。你要写信给 Win32。
  • “将 fork 与 ProcessCreate 进行比较是不公平的,因为它们执行不同的操作,而 Windows 没有 fork”ー, 所以我会比较喜欢与喜欢。不过我也会比较 fork,因为它有很多用例,比如进程隔离(例如,web 浏览器的每个标签都运行在不同的进程中)。

现在让我们看看事实,在表现上有什么不同?

http://www.bitsnbites.eu/benchmarking-os-primitives/ 汇总的数据。
因为偏见是不可避免的,在总结时,我选择了 MS-Windows
Hardware for most tests i7 8 core 3.2GHz. Except Raspberry-Pi running Gnu/Linux

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

A comparison of MS-Windows process create vs Linux

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

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

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

  • Linux CreateThread 12
  • Mac CreateThread15
  • Linux Fork 19
  • WindowsCreateThread25
  • Linux CreateProcess (fork + exec)45
  • Mac Fork 105
  • Mac CreateProcess (fork + exec)453
  • Raspberry-Pi CreateProcess (fork+exec) 501
  • WindowsCreateProcess787
  • 带病毒扫描器2850的 Windows 创建进程
  • WindowsFork (使用 CreateProcess + fixup 进行模拟)大于2850

Numbers for other measurements

  • 创建文件。
    • Linux 13
    • Mac 113
    • 视窗225
    • Raspberry-Pi (带有慢速 SD 卡)241
    • 带防御程序和病毒扫描器的窗口等12950
  • Allocating memory
    • Linux 79
    • Windows 93
    • Mac 152