我听说在 Windows 机器上创建一个新进程比在 Linux 上更昂贵。这是真的?有人可以解释为什么它更昂贵的技术原因,并为这些原因背后的设计决策提供任何历史原因吗?
10 回答
mweerden: NT has been designed for multi-user from day one, so this is not really a reason. However, you are right about that process creation plays a less important role on NT than on Unix as NT, in contrast to Unix, favors multithreading over multiprocessing.
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.
When discussing the speed of process creation, it is probably a good idea to distinguish between NT and Windows/Win32. As far as NT (i.e. the kernel itself) goes, I do not think process creation (NtCreateProcess) and thread creation (NtCreateThread) is significantly slower as on the average Unix. There might be a little bit more going on, but I do not see the primary reason for the performance difference here.
If you look at Win32, however, you'll notice that it adds quite a bit of overhead to process creation. For one, it requires the CSRSS to be notified about process creation, which involves LPC. It requires at least kernel32 to be loaded additionally, and it has to perform a number of additional bookkeeping work items to be done before the process is considered to be a full-fledged Win32 process. And let's not forget about all the additional overhead imposed by parsing manifests, checking if the image requires a compatbility shim, checking whether software restriction policies apply, yada yada.
That said, I see the overall slowdown in the sum of all those little things that have to be done in addition to the raw creation of a process, VA space, and initial thread. But as said in the beginning -- due to the favoring of multithreading over multitasking, the only software that is seriously affected by this additional expense is poorly ported Unix software. Although this sitatuion changes when software like Chrome and IE8 suddenly rediscover the benefits of multiprocessing and begin to frequently start up and teardown processes...
补充 JP 所说的:大部分开销属于进程的 Win32 启动。
Windows NT 内核实际上确实支持 COW 分叉。SFU(Microsoft 的 Windows UNIX 环境)使用它们。但是,Win32 不支持 fork。SFU 进程不是 Win32 进程。SFU 与 Win32 正交:它们都是构建在同一内核上的环境子系统。
除了对 的进程外 LPC 调用CSRSS
外,在 XP 及更高版本中,还有对应用程序兼容性引擎的进程外调用,以在应用程序兼容性数据库中查找程序。此步骤会导致足够的开销,以至于 Microsoft出于性能原因提供了一个组策略选项来禁用 WS2003 上的兼容性引擎。
Win32 运行时库(kernel32.dll 等)还在启动时进行大量注册表读取和初始化,这不适用于 UNIX、SFU 或本机进程。
本机进程(没有环境子系统)的创建速度非常快。SFU在进程创建方面比Win32少很多,所以它的进程创建速度也很快。
2019 年更新:添加 LXSS:适用于 Linux 的 Windows 子系统
替换 Windows 10 的 SFU 是 LXSS 环境子系统。它是 100% 内核模式,不需要 Win32 继续拥有的任何 IPC。这些进程的系统调用直接指向 lxss.sys/lxcore.sys,因此 fork() 或其他进程创建调用仅花费创建者的 1 个系统调用,总计。[称为实例的数据区域] 跟踪所有 LX 进程、线程和运行时状态。
LXSS 进程基于本机进程,而不是 Win32 进程。像兼容性引擎这样的所有 Win32 特定的东西根本没有参与。
Unix 有一个“fork”系统调用,它将当前进程“拆分”为两个,并为您提供与第一个进程相同的第二个进程(以 fork 调用的返回为模)。由于新进程的地址空间已经启动并运行,这应该比在 Windows 中调用“CreateProcess”并让它加载 exe 映像、关联的 dll 等便宜。
在 fork 的情况下,操作系统可以对与两个新进程关联的内存页面使用“写时复制”语义,以确保每个进程都获得他们自己随后修改的页面的副本。
除了 Rob Walker 的回答:现在你有像 Native POSIX Thread Library 这样的东西——如果你愿意的话。但是很长一段时间以来,在 unix 世界中“委托”工作的唯一方法是使用 fork()(在许多情况下它仍然是首选)。例如某种套接字服务器
socket_accept() 叉子() 如果(孩子) 处理请求() 别的 goOnBeingParent()因此,fork 的实现必须快速,并且随着时间的推移已经实现了很多优化。微软支持 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”——没错,所以我将 like 与 like 进行比较。不过我也会比较fork,因为它有很多很多用例,例如进程隔离(例如,Web 浏览器的每个选项卡都在不同的进程中运行)。
现在让我们看看事实,性能上有什么不同?
数据汇总自http://www.bitsnbites.eu/benchmarking-os-primitives/。
因为偏见是不可避免的,所以在总结时,我在
大多数测试 i7 8 核 3.2GHz 时都支持 MS-Windows 硬件。除了运行 Gnu/Linux 的 Raspberry-Pi
注意:在 linux 上,fork
比 MS-Window 的首选方法更快CreateThread
。
进程创建类型操作的数字(因为在图表中很难看到 Linux 的值)。
按照速度从快到慢的顺序(数字是时间,越小越好)。
- Linux 创建线程 12
- Mac 创建线程 15
- Linux 分叉 19
- Windows 创建线程 25
- Linux CreateProcess (fork+exec) 45
- 麦克叉 105
- Mac CreateProcess (fork+exec) 453
- 树莓派 CreateProcess (fork+exec) 501
- Windows 创建进程 787
- Windows CreateProcess 带病毒扫描程序 2850
- Windows Fork(使用 CreateProcess + fixup 模拟)大于 2850
其他测量的数字
- 创建文件。
- Linux 13
- 麦克 113
- 视窗 225
- 树莓派(带慢速 SD 卡) 241
- 带有防御者和病毒扫描程序等的 Windows 12950
- 分配内存
- Linux 79
- 视窗 93
- 麦克 152
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.
还值得注意的是,Windows 中的安全模型比基于 unix 的操作系统复杂得多,这在进程创建过程中增加了很多开销。在 Windows 中多线程优于多处理的另一个原因。