为什么在Windows上创建比Linux更昂贵的新流程?

Rea*_*nly 98 linux windows performance

我听说在Windows机器上创建一个新进程比在Linux上更昂贵.这是真的?有人可以解释为什么它更昂贵的技术原因,并为这些原因背后的设计决策提供任何历史原因?

Joh*_*ing 66

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等软件突然重新发现多处理的好处并开始频繁启动和拆卸流程时,这种情况会发生变化......

  • fork并不总是后跟exec(),人们只关心fork().Apache 1.3在Linux上使用fork()(不使用exec),在Windows上使用线程,即使在许多情况下,进程在需要之前分叉并保存在池中. (8认同)
  • 当然不要忘记'vfork'命令,它是为你描述的'just call exec'场景而设计的. (5认同)
  • -1.声称软件"移植不良"的说法是因为它在设计不良的操作系统上运行不佳,这种操作系统充满了兼容性,这减慢了创建过程的速度,这是荒谬的. (5认同)
  • @MilesRout移植的目标是修改软件以在新的目标系统上运行,并考虑到该系统的优点和缺点.无论操作系统提供什么样的障碍,移植软件都很糟糕_是移植不良的软件. (5认同)
  • 另一种受此严重影响的软件是任何涉及多个进程协调的shell脚本.例如,Cygwin中的Bash脚本遭受了很大的损失.考虑一个shell循环,它在管道中产生大量的sed,awk和grep.每个命令都会生成一个进程,每个管道都会在子shell中生成一个子shell和一个新进程.Unix的设计考虑到了这种用法,这就是为什么快速创建流程仍然是常态的原因. (2认同)
  • @Dizzyspiral完全没有.无法在其他所有操作系统上执行完全正常运行的程序的操作系统是一个设计缓慢,设计不佳的操作系统. (2认同)
  • @MilesRout 您的论点没有意义,因为如上所述,减慢速度的不是“兼容性工艺”,而是架构灵活性,甚至在某种程度上甚至是安全性。只需向使用 Linux 的朋友询问有关 ACL 的信息,看看他们眼中的恐惧。:-) (2认同)

Rob*_*ker 27

Unix有一个'fork'系统调用,它将当前进程"拆分"为两个,并为你提供第二个进程,它与第一个进程相同(以fork调用返回为模).由于新进程的地址空间已经启动并运行,因此应该比在Windows中调用'CreateProcess'并使其加载exe映像,关联的dll等更便宜.

在fork情况下,OS可以对与两个新进程相关联的内存页使用"copy-on-write"语义,以确保每个进程都获得自己随后修改的页面副本.

  • 这个论点只适用于你真正要求的时候.如果你正在开始一个新的进程,在Unix上你仍然需要fork和exec.Windows和Unix都有写拷贝.如果您运行应用程序的第二个副本,Windows肯定会重用已加载的EXE.抱歉,我不认为你的解释是正确的. (20认同)

Chr*_*ith 23

添加到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特定的东西,如兼容性引擎都没有参与.


Vol*_*erK 16

除了Rob Walker的答案:现在你有像Native POSIX Thread Library这样的东西 - 如果你愿意的话.但是很长一段时间,在unix世界中"委托"工作的唯一方法是使用fork()(在许多情况下它仍然是首选).例如某种套接字服务器

socket_accept()
fork()
if (child)
    handleRequest()
else
    goOnBeingParent()
因此,fork的实现必须快速,并且随着时间的推移已经实现了许多优化.Microsoft支持CreateThread甚至是光纤,而不是创建新进程和进程间通信的使用.我认为将CreateProcess与fork进行比较并不"公平",因为它们不可互换.将fork/exec与CreateProcess进行比较可能更合适.

  • 关于你的最后一点:fork()不能与CreateProcess()交换,但是也可以说Windows应该实现fork(),因为这样可以提供更大的灵活性. (2认同)

mwe*_*den 13

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

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

很明显,至少从历史上看,基于Unix的系统对快速创建流程的需求远远大于Windows系统.我认为情况仍然如此,因为基于Unix的系统仍然是面向流程的,而Windows由于其历史,可能更多地面向线程(线程对于生成响应式应用程序很有用).

免责声明:我不是这方面的专家,如果我弄错了,请原谅我.


Tim*_*oft 9

呃,似乎有很多"这种方式更好"的理由正在进行中.

我认为人们可以从阅读"Showstopper"中受益; 关于Windows NT开发的书.

服务作为DLL在Windows NT上的一个进程中运行的全部原因是它们作为单独的进程太慢.

如果你沮丧和肮脏,你会发现库加载策略是问题所在.

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

Windows NT在每个进程中加载​​DLL的副本,因为它在加载后操作库代码段(和可执行代码段).(告诉它你的数据在哪里?)

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

因此,NT进程创建实际上非常昂贵.并且在不利方面,它使得DLL在内存中没有明显的节省,但是存在应用程序间依赖性问题的可能性.

有时候工程学会退后一步说:"现在,如果我们要把它设计得真的很糟糕,那会是什么样子?"

我曾经使用过一种非常有气势的嵌入式系统,有一天看着它并意识到它是一个腔体磁控管,电子设备在微波腔中.之后我们让它变得更加稳定(而不像微波炉).

  • 代码段的共享也适用于支持ASLR的系统. (3认同)
  • 只要DLL加载到其首选基址,代码段就是可重用的.传统上,您应确保为将加载到进程中的所有DLL设置非冲突的基址,但这不适用于ASLR. (2认同)

Dig*_*oss 9

简短的回答是"软件层和组件".

Windows SW体系结构有一些额外的层和组件,这些层和组件在Unix上不存在,或者在Unix内核中进行了简化和处理.

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

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

很长一段时间以来,研究人员和企业都试图以模糊的方式分解Unix,通常是将他们的实验基于Mach内核 ; 一个众所周知的例子是OS X..然而,每次他们尝试时,它都会变得如此缓慢,最终至少部分地将这些碎片合并到内核中,无论是永久性还是生产出货.


ctr*_*lor 5

因为在一些答案中似乎有一些 MS-Windows 的合理性,例如

\n
    \n
  • \xe2\x80\x9cNT内核和Win32,不是一回事。如果您对 NT 内核进行编程,那么它还不错\xe2\x80\x9d \xe2\x80\x94 确实如此,但除非您正在编写 Posix 子系统,否则谁在乎呢。您将写入 win32。
  • \n
  • \xe2\x80\x9c将 fork 与 ProcessCreate 进行比较是不公平的,因为它们做不同的事情,而 Windows 没有 fork\xe2\x80\x9c \xe2\x80\x94 True,\n所以我会比较 like 。不过,我也会比较 fork,因为它有很多用例,例如进程隔离(例如,Web 浏览器的每个选项卡在不同的进程中运行)。
  • \n
\n

现在让我们看看事实,性能上有什么区别?

\n

数据汇总自http://www.bitsnbites.eu/benchmarking-os-primitives/
\n因为偏见是不可避免的,所以在总结时,我倾向于 MS-Windows
\n大多数测试的硬件是 i7 8 核 3.2GHz。运行 Gnu/Linux 的 Raspberry-Pi 除外

\n

Gnu/Linux、Apple-Mac 和 Microsoft Windows 上各种基本操作的比较(越小越好)

\n

MS-Windows 进程创建与 Linux 的比较

\n

注意:\n在 Linux 上,fork比 MS-Window 的首选方法更快CreateThread

\n

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

\n

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

\n
    \n
  • Linux 创建线程 12
  • \n
  • Mac 创建线程 15
  • \n
  • Linux 分叉 19
  • \n
  • Windows 创建线程 25
  • \n
  • Linux CreateProcess(fork+exec)45
  • \n
  • 麦克叉105
  • \n
  • Mac CreateProcess(fork+exec)453
  • \n
  • Raspberry-Pi CreateProcess(fork+exec)501
  • \n
  • Windows 创建进程 787
  • \n
  • Windows CreateProcess 带有病毒扫描程序 2850
  • \n
  • Windows Fork(使用 CreateProcess + 修复进行模拟)大于 2850
  • \n
\n

其他测量的数字

\n
    \n
  • 创建文件。\n
      \n
    • Linux 13
    • \n
    • 麦克113
    • \n
    • 视窗225
    • \n
    • Raspberry-Pi(带慢速 SD 卡)241
    • \n
    • 带有防御者和病毒扫描程序等的 Windows 12950
    • \n
    \n
  • \n
  • 分配内存\n
      \n
    • Linux 79
    • \n
    • 视窗93
    • \n
    • 麦克152
    • \n
    \n
  • \n
\n