【发布时间】:2010-09-08 01:13:37
【问题描述】:
我听说在 Windows 机器上创建新进程比在 Linux 上更昂贵。这是真的?有人可以解释为什么它更昂贵的技术原因,并提供这些原因背后的设计决策的任何历史原因吗?
【问题讨论】:
标签: windows linux performance
我听说在 Windows 机器上创建新进程比在 Linux 上更昂贵。这是真的?有人可以解释为什么它更昂贵的技术原因,并提供这些原因背后的设计决策的任何历史原因吗?
【问题讨论】:
标签: windows linux performance
呃,似乎有很多“这样更好”的理由。
我认为人们可以从阅读“Showstopper”中受益;一本关于 Windows NT 开发的书。
服务作为 DLL 在 Windows NT 上的一个进程中运行的全部原因是它们作为单独的进程太慢了。
如果你感到沮丧和肮脏,你会发现库加载策略是问题所在。
在 Unices(通常)上,共享库 (DLL) 代码段实际上是共享的。
Windows NT 为每个进程加载一个 DLL 的副本,因为它在加载后操纵库代码段(和可执行代码段)。 (告诉它你的数据在哪里?)
这会导致库中的代码段不可重用。
因此,NT 进程创建实际上非常昂贵。不利的一面是,它使 DLL 不会显着节省内存,但可能会出现应用间依赖问题。
有时在工程方面需要退后一步说,“现在,如果我们要把它设计得非常糟糕,它会是什么样子?”
曾几何时,我使用过一个非常喜怒无常的嵌入式系统,有一天看着它并意识到它是一个腔磁控管,电子设备位于微波腔中。在那之后,我们让它变得更加稳定(而不像微波炉)。
【讨论】:
因为在某些答案中似乎有一些 MS-Windows 的理由,例如
数据来自 http://www.bitsnbites.eu/benchmarking-os-primitives/。
因为偏见是不可避免的,所以在总结时,我这样做是为了支持 MS-Windows
大多数测试的硬件 i7 8 核 3.2GHz。运行 Gnu/Linux 的 Raspberry-Pi 除外
注意事项:
在 linux 上,fork 比 MS-Window 的首选方法 CreateThread 更快。
按照速度从快到慢的顺序(数字就是时间,越小越好)。
【讨论】:
补充JP所说的:大部分开销属于进程的Win32启动。
Windows NT 内核实际上确实支持 COW 分叉。 SFU(Microsoft 的 Windows UNIX 环境)使用它们。但是,Win32 不支持 fork。 SFU 进程不是 Win32 进程。 SFU 与 Win32 正交:它们都是构建在同一个内核上的环境子系统。
除了对CSRSS 的进程外LPC 调用外,在XP 和更高版本中,还有对应用程序兼容性引擎的进程外调用,以在应用程序兼容性数据库中查找程序。此步骤会导致足够的开销,因此 Microsoft 出于性能原因向disable the compatibility engine on WS2003 提供组策略选项。
Win32 运行时库(kernel32.dll 等)还在启动时执行大量注册表读取和初始化,这不适用于 UNIX、SFU 或本机进程。
本地进程(没有环境子系统)的创建速度非常快。 SFU在进程创建方面比Win32少很多,所以它的进程创建速度也很快。
2019 年更新:添加 LXSS:Windows Subsystem for Linux
替换 Windows 10 的 SFU 是 LXSS 环境子系统。它是 100% 内核模式,不需要 Win32 继续拥有的任何 IPC。这些进程的系统调用直接指向 lxss.sys/lxcore.sys,因此 fork() 或其他进程创建调用只需要创建者的 1 个系统调用,总计。 [A data area called the instance] keeps track of all LX processes, threads, and runtime state.
LXSS 进程基于本机进程,而不是 Win32 进程。像兼容性引擎这样的所有 Win32 特定的东西都没有参与。
【讨论】:
除了 Rob Walker 的回答: 现在你有像 Native POSIX Thread Library 这样的东西——如果你愿意的话。 但是很长一段时间以来,在 unix 世界中“委托”工作的唯一方法是使用 fork() (在许多情况下它仍然是首选)。 例如某种套接字服务器
socket_accept()
叉()
如果(孩子)
处理请求()
别的
goOnBeingParent()
因此,fork 的实现必须很快,并且随着时间的推移已经实现了很多优化。
微软支持 CreateThread 甚至是纤程,而不是创建新进程和使用进程间通信。我认为将 CreateProcess 与 fork 进行比较是不“公平的”,因为它们不可互换。将 fork/exec 与 CreateProcess 进行比较可能更合适。
【讨论】:
还值得注意的是,Windows 中的安全模型比基于 unix 的操作系统复杂得多,这在进程创建过程中增加了很多开销。在 Windows 中多线程优于多处理的另一个原因。
【讨论】:
fork() 带来很大的开销
我认为,这件事的关键在于这两个系统的历史用法。 Windows(以及之前的 DOS)最初是用于个人计算机的单用户系统。因此,这些系统通常不必一直创建大量进程。 (非常)简单地说,只有当这个孤独的用户请求时才会创建一个进程(相对而言,我们人类的操作速度不是很快)。
基于 Unix 的系统最初是多用户系统和服务器。特别是对于后者来说,拥有分离进程以处理特定工作(例如处理一个传入连接)的进程(例如邮件或 http 守护程序)并不少见。这样做的一个重要因素是便宜的fork 方法(正如 Rob Walker (47865) 所提到的,它最初为新创建的进程使用相同的内存)非常有用,因为新进程立即拥有所有它需要的信息。
很明显,至少从历史上看,基于 Unix 的系统对快速创建进程的需求远大于 Windows 系统。我认为情况仍然如此,因为基于 Unix 的系统仍然非常面向进程,而 Windows,由于其历史原因,可能更面向线程(线程对于制作响应式应用程序很有用)。
免责声明:我绝不是这方面的专家,如果我错了,请原谅我。
【讨论】:
windows 软件架构有几个额外的层和组件,这些层和组件在 Unix 上不存在,或者在 Unix 的内核中被简化和处理。
在 Unix 上,fork 和 exec 是对内核的直接调用。
在 Windows 上,内核 API 不是直接使用的,上面有 win32 和某些其他组件,因此进程创建必须经过额外的层,然后新进程必须启动或连接到这些层和组件。
很长一段时间以来,研究人员和公司都试图以一种类似的方式分解 Unix,通常基于Mach kernel 进行实验;一个著名的例子是OS X.。但是,每次他们尝试时,速度都会变得如此缓慢,以至于最终至少将部分片段永久地合并回内核中,或者用于生产出货。
【讨论】:
除此之外,Win 机器上很可能会在 CreateProcess 期间启动防病毒软件......这通常是最大的减速。
【讨论】:
mweerden:NT 从一开始就是为多用户设计的,所以这并不是一个真正的原因。但是,您说得对,在 NT 上,进程创建所起的作用不如在 Unix 上重要,因为 NT 与 Unix 相比,更喜欢多线程而不是多处理。
Rob,确实使用COW时fork相对便宜,但事实上,fork之后大多是exec。并且 exec 还必须加载所有图像。因此,讨论 fork 的性能只是事实的一部分。
在讨论进程创建速度时,最好区分 NT 和 Windows/Win32。就 NT(即内核本身)而言,我不认为进程创建 (NtCreateProcess) 和线程创建 (NtCreateThread) 比普通 Unix 慢得多。可能会有更多的事情发生,但我看不出这里出现性能差异的主要原因。
但是,如果您查看 Win32,您会注意到它为进程创建增加了相当多的开销。一方面,它需要通知 CSRSS 进程创建,这涉及 LPC。它至少需要额外加载 kernel32,并且在进程被认为是成熟的 Win32 进程之前,它必须执行许多额外的簿记工作项。我们不要忘记解析清单所带来的所有额外开销,检查图像是否需要兼容性 shim,检查软件限制策略是否适用,yada yada。
也就是说,除了原始创建进程、VA 空间和初始线程之外,我看到所有必须完成的小事情的总和总体放缓。但正如开头所说的那样——由于多线程优于多任务处理,唯一受到这种额外费用严重影响的软件是移植不良的 Unix 软件。尽管当 Chrome 和 IE8 等软件突然重新发现多处理的好处并开始频繁启动和关闭进程时,这种情况发生了变化......
【讨论】:
Unix 有一个“fork”系统调用,它将当前进程“拆分”为两个,并为您提供与第一个进程相同的第二个进程(以 fork 调用的返回为模)。由于新进程的地址空间已经启动并运行,这应该比在 Windows 中调用“CreateProcess”并让它加载 exe 映像、关联的 dll 等便宜。
在 fork 情况下,操作系统可以对与两个新进程关联的内存页面使用“写时复制”语义,以确保每个进程都获得自己随后修改的页面的副本。
【讨论】: