我听说在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正交:它们都是在同一内核上构建的环境子系统.
除了进程外LPC调用之外CSRSS
,在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()(在许多情况下它仍然是首选).例如某种套接字服务器
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由于其历史,可能更多地面向线程(线程对于生成响应式应用程序很有用).
免责声明:我不是这方面的专家,如果我弄错了,请原谅我.
呃,似乎有很多"这种方式更好"的理由正在进行中.
我认为人们可以从阅读"Showstopper"中受益; 关于Windows NT开发的书.
服务作为DLL在Windows NT上的一个进程中运行的全部原因是它们作为单独的进程太慢.
如果你沮丧和肮脏,你会发现库加载策略是问题所在.
在Unices(通常)上,共享库(DLL)的代码段实际上是共享的.
Windows NT在每个进程中加载DLL的副本,因为它在加载后操作库代码段(和可执行代码段).(告诉它你的数据在哪里?)
这导致库中的代码段不可重用.
因此,NT进程创建实际上非常昂贵.并且在不利方面,它使得DLL在内存中没有明显的节省,但是存在应用程序间依赖性问题的可能性.
有时候工程学会退后一步说:"现在,如果我们要把它设计得真的很糟糕,那会是什么样子?"
我曾经使用过一种非常有气势的嵌入式系统,有一天看着它并意识到它是一个腔体磁控管,电子设备在微波腔中.之后我们让它变得更加稳定(而不像微波炉).
Windows SW体系结构有一些额外的层和组件,这些层和组件在Unix上不存在,或者在Unix内核中进行了简化和处理.
在Unix上,fork和exec是对内核的直接调用.
在Windows上,内核API不是直接使用的,它上面有win32和某些其他组件,因此创建进程必须经过额外的层,然后新进程必须启动或连接到这些层和组件.
很长一段时间以来,研究人员和企业都试图以模糊的方式分解Unix,通常是将他们的实验基于Mach内核 ; 一个众所周知的例子是OS X..然而,每次他们尝试时,它都会变得如此缓慢,最终至少部分地将这些碎片合并到内核中,无论是永久性还是生产出货.