为啥在 Windows 上创建新进程比在 Linux 上更昂贵?
Posted
技术标签:
【中文标题】为啥在 Windows 上创建新进程比在 Linux 上更昂贵?【英文标题】:Why is creating a new process more expensive on Windows than Linux?为什么在 Windows 上创建新进程比在 Linux 上更昂贵? 【发布时间】:2010-09-08 01:13:37 【问题描述】:我听说在 Windows 机器上创建新进程比在 Linux 上更昂贵。这是真的?有人可以解释为什么它更昂贵的技术原因,并提供这些原因背后的设计决策的任何历史原因吗?
【问题讨论】:
【参考方案1】:呃,似乎有很多“这样更好”的理由。
我认为人们可以从阅读“Showstopper”中受益;一本关于 Windows NT 开发的书。
服务作为 DLL 在 Windows NT 上的一个进程中运行的全部原因是它们作为单独的进程太慢了。
如果你感到沮丧和肮脏,你会发现库加载策略是问题所在。
在 Unices(通常)上,共享库 (DLL) 代码段实际上是共享的。
Windows NT 为每个进程加载一个 DLL 的副本,因为它在加载后操纵库代码段(和可执行代码段)。 (告诉它你的数据在哪里?)
这会导致库中的代码段不可重用。
因此,NT 进程创建实际上非常昂贵。不利的一面是,它使 DLL 不会显着节省内存,但可能会出现应用间依赖问题。
有时在工程方面需要退后一步说,“现在,如果我们要把它设计得非常糟糕,它会是什么样子?”
曾几何时,我使用过一个非常喜怒无常的嵌入式系统,有一天看着它并意识到它是一个腔磁控管,电子设备位于微波腔中。在那之后,我们让它变得更加稳定(而不像微波炉)。
【讨论】:
代码段是可重用的,只要 DLL 在其首选基地址加载。传统上,您应该确保为将加载到您的进程中的所有 DLL 设置不冲突的基地址,但这不适用于 ASLR。 有一些工具可以重新设置所有 DLL 的基础,不是吗?不确定它对 ASLR 有什么作用。 共享代码段也适用于启用 ASLR 的系统。 @MikeDimmick 所以每个人,制作 DLL 都必须合作,以确保没有冲突,或者在加载之前在系统级别对它们进行修补?【参考方案2】:因为在某些答案中似乎有一些 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 除外
注意事项:
在 linux 上,fork
比 MS-Window 的首选方法 CreateThread
更快。
进程创建类型操作的数字(因为在图表中很难看到 Linux 的值)。
按照速度从快到慢的顺序(数字就是时间,越小越好)。
Linux CreateThread 12 Mac CreateThread 15 Linux Fork 19 Windows CreateThread 25 Linux CreateProcess (fork+exec) 45 Mac 叉 105 Mac CreateProcess (fork+exec) 453 Raspberry-Pi CreateProcess (fork+exec) 501 Windows 创建进程 787 带有病毒扫描程序 2850 的 Windows CreateProcess Windows Fork(使用 CreateProcess + fixup 模拟)大于 2850其他测量的数字
创建文件。 Linux 13 Mac 113 Windows 225 Raspberry-Pi(带慢速 SD 卡)241 带有防御者和病毒扫描程序等的 Windows 12950 分配内存 Linux 79 Windows 93 Mac 152【讨论】:
【参考方案3】:补充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 特定的东西都没有参与。
【讨论】:
【参考方案4】:除了 Rob Walker 的回答: 现在你有像 Native POSIX Thread Library 这样的东西——如果你愿意的话。 但是很长一段时间以来,在 unix 世界中“委托”工作的唯一方法是使用 fork() (在许多情况下它仍然是首选)。 例如某种套接字服务器
socket_accept() 叉() 如果(孩子) 处理请求() 别的 goOnBeingParent()因此,fork 的实现必须很快,并且随着时间的推移已经实现了很多优化。 微软支持 CreateThread 甚至是纤程,而不是创建新进程和使用进程间通信。我认为将 CreateProcess 与 fork 进行比较是不“公平的”,因为它们不可互换。将 fork/exec 与 CreateProcess 进行比较可能更合适。
【讨论】:
关于你的最后一点:fork() 不能与 CreateProcess() 交换,但也可以说 Windows 应该实现 fork(),因为这样可以提供更大的灵活性。 但 Linux 中的 fork+exec 比 MS-Windows 上的 CreateThread 快。 Linux 可以自己进行 fork 以更快。无论您如何比较,MS 都比较慢。【参考方案5】:还值得注意的是,Windows 中的安全模型比基于 unix 的操作系统复杂得多,这在进程创建过程中增加了很多开销。在 Windows 中多线程优于多处理的另一个原因。
【讨论】:
我希望更复杂的安全模型更安全;但事实并非如此。 SELinux 也是一个非常复杂的安全模型,它不会给fork()
带来很大的开销
@LieRyan,在软件设计中(根据我的经验),更复杂的很少意味着更安全。【参考方案6】:
我认为,这件事的关键在于这两个系统的历史用法。 Windows(以及之前的 DOS)最初是用于个人计算机的单用户系统。因此,这些系统通常不必一直创建大量进程。 (非常)简单地说,只有当这个孤独的用户请求时才会创建一个进程(相对而言,我们人类的操作速度不是很快)。
基于 Unix 的系统最初是多用户系统和服务器。特别是对于后者来说,拥有分离进程以处理特定工作(例如处理一个传入连接)的进程(例如邮件或 http 守护程序)并不少见。这样做的一个重要因素是便宜的fork
方法(正如 Rob Walker (47865) 所提到的,它最初为新创建的进程使用相同的内存)非常有用,因为新进程立即拥有所有它需要的信息。
很明显,至少从历史上看,基于 Unix 的系统对快速创建进程的需求远大于 Windows 系统。我认为情况仍然如此,因为基于 Unix 的系统仍然非常面向进程,而 Windows,由于其历史原因,可能更面向线程(线程对于制作响应式应用程序很有用)。
免责声明:我绝不是这方面的专家,如果我错了,请原谅我。
【讨论】:
【参考方案7】:简短的回答是“软件层和组件”。
windows 软件架构有几个额外的层和组件,这些层和组件在 Unix 上不存在,或者在 Unix 的内核中被简化和处理。
在 Unix 上,fork 和 exec 是对内核的直接调用。
在 Windows 上,内核 API 不是直接使用的,上面有 win32 和某些其他组件,因此进程创建必须经过额外的层,然后新进程必须启动或连接到这些层和组件。
很长一段时间以来,研究人员和公司都试图以一种类似的方式分解 Unix,通常基于Mach kernel 进行实验;一个著名的例子是OS X.。但是,每次他们尝试时,速度都会变得如此缓慢,以至于最终至少将部分片段永久地合并回内核中,或者用于生产出货。
【讨论】:
层不一定会减慢速度:我用 C 语言编写了一个有很多层的设备驱动程序。干净的代码,有文化的编程,易于阅读。它比用高度优化的汇编器编写的版本更快(略微),没有层。 讽刺的是NT是一个巨大的内核(不是微内核)【参考方案8】:除此之外,Win 机器上很可能会在 CreateProcess 期间启动防病毒软件......这通常是最大的减速。
【讨论】:
是的,这是最大的,但不是唯一显着的放缓。【参考方案9】: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 等软件突然重新发现多处理的好处并开始频繁启动和关闭进程时,这种情况发生了变化......
【讨论】:
fork 后面并不总是跟 exec(),人们只关心 fork()。 Apache 1.3 在 Linux 上使用 fork()(不带 exec),在 Windows 上使用线程,即使在许多情况下,进程在需要之前就被分叉并保存在池中。 当然不要忘记'vfork'命令,它是为你描述的'just call exec'场景设计的。 另一种严重受此影响的软件是涉及多个进程协调的任何类型的 shell 脚本。例如,Cygwin 中的 Bash 脚本就深受其害。考虑一个在管道中产生大量 sed、awk 和 grep 的 shell 循环。每个命令都会产生一个进程,每个管道都会产生一个子shell 和该子shell 中的新进程。 Unix 在设计时就考虑到了这种用法,这就是为什么快速创建进程仍然是那里的常态。 -1。声称软件“移植不良”是因为它不能在设计不佳的操作系统上运行良好,该操作系统充满了兼容性问题,会减慢进程的创建速度,这种说法是荒谬的。 @MilesRout 移植的目标是修改软件以在新的目标系统上运行,同时考虑到该系统的优点和缺点。性能不佳的移植软件是移植不良的软件,无论操作系统提供何种障碍。【参考方案10】:Unix 有一个“fork”系统调用,它将当前进程“拆分”为两个,并为您提供与第一个进程相同的第二个进程(以 fork 调用的返回为模)。由于新进程的地址空间已经启动并运行,这应该比在 Windows 中调用“CreateProcess”并让它加载 exe 映像、关联的 dll 等便宜。
在 fork 情况下,操作系统可以对与两个新进程关联的内存页面使用“写时复制”语义,以确保每个进程都获得自己随后修改的页面的副本。
【讨论】:
这个论点只在你真正分叉时成立。如果你正在启动一个新进程,在 Unix 上你仍然需要 fork 和 exec。 Windows 和 Unix 都有写时复制。如果您运行应用程序的第二个副本,Windows 肯定会重用已加载的 EXE。我不认为你的解释是正确的,对不起。 更多关于 exec() 和 fork() vipinkrsahu.blogspot.com/search/label/system%20programming 我在回答中添加了一些性能数据。 ***.com/a/51396188/537980你可以看到它更快。以上是关于为啥在 Windows 上创建新进程比在 Linux 上更昂贵?的主要内容,如果未能解决你的问题,请参考以下文章
为啥这个 C++ 程序在 Windows 上比在 Linux 上慢?
为啥 Python 的多处理模块在 Windows 上启动新进程时会导入 __main__?
HTML5 Canvas 在 Firefox 上比在 Chrome 上更快!为啥?