Por que criar um novo processo é mais caro no Windows do que no Linux?

100

Ouvi dizer que criar um novo processo em uma máquina Windows é mais caro do que no Linux. Isso é verdade? Alguém pode explicar as razões técnicas por que é mais caro e fornecer quaisquer razões históricas para as decisões de design por trás dessas razões?

Somente leitura
fonte

Respostas:

68

mweerden: O NT foi projetado para vários usuários desde o primeiro dia, então esse não é realmente um motivo. No entanto, você está certo quanto ao fato de que a criação de processos desempenha um papel menos importante no NT do que no Unix, pois o NT, ao contrário do Unix, favorece o multithreading ao invés do multiprocessamento.

Rob, é verdade que fork é relativamente barato quando COW é usado, mas, na verdade, fork é geralmente seguido por um executivo. E um executivo deve carregar todas as imagens também. Discutir o desempenho do garfo, portanto, é apenas parte da verdade.

Ao discutir a velocidade de criação do processo, provavelmente é uma boa idéia distinguir entre NT e Windows / Win32. No que diz respeito ao NT (ou seja, o próprio kernel), não acho que a criação de processos (NtCreateProcess) e a criação de threads (NtCreateThread) seja significativamente mais lenta do que no Unix médio. Pode haver um pouco mais acontecendo, mas não vejo o motivo principal para a diferença de desempenho aqui.

Se você observar o Win32, entretanto, perceberá que ele adiciona um pouco de sobrecarga à criação do processo. Por um lado, requer que o CSRSS seja notificado sobre a criação do processo, que envolve o LPC. Ele exige que pelo menos o kernel32 seja carregado adicionalmente e precisa realizar vários itens de trabalho de contabilidade adicionais antes que o processo seja considerado um processo Win32 completo. E não nos esqueçamos de toda a sobrecarga adicional imposta pela análise de manifestos, verificando se a imagem requer um shim de compatibilidade, verificando se as políticas de restrição de software se aplicam, blá, blá.

Dito isso, vejo a desaceleração geral na soma de todas aquelas pequenas coisas que precisam ser feitas, além da criação bruta de um processo, espaço VA e thread inicial. Mas, como disse no início - devido ao favorecimento do multithreading em relação à multitarefa, o único software que é seriamente afetado por essa despesa adicional é o software Unix mal portado. Embora esta situação mude quando softwares como o Chrome e o IE8 de repente redescobrem os benefícios do multiprocessamento e começam a inicializar e desmontar com frequência os processos ...

Johannes Passing
fonte
8
Fork nem sempre é seguido por exec (), e as pessoas se preocupam apenas com fork (). O Apache 1.3 usa fork () (sem exec) no Linux e threads no Windows, mesmo se em muitos casos os processos forem bifurcados antes de serem necessários e mantidos em um pool.
Blaisorblade
5
Não esquecendo, é claro, o comando 'vfork', que é projetado para o cenário 'just call exec' que você descreve.
Chris Huang-Leaver
4
Outro tipo de software que é seriamente afetado por isso é qualquer tipo de script de shell que envolva a coordenação de vários processos. O script Bash dentro do Cygwin, por exemplo, sofre muito com isso. Considere um loop de shell que gera muitos sed, awk e grep em pipelines. Cada comando gera um processo e cada pipe gera um subshell e um novo processo nesse subshell. O Unix foi projetado com esse tipo de uso em mente, e é por isso que a criação rápida de processos continua sendo a norma lá.
Dan Molding
5
-1. A alegação de que o software é 'mal portado' porque não funciona bem em um sistema operacional mal projetado e cheio de problemas de compatibilidade que retarda a criação de processos é ridícula.
Miles Rout
6
@MilesRout o objetivo da portabilidade é modificar o software para rodar em um novo sistema de destino, com os pontos fortes e fracos desse sistema em mente. Software portado com desempenho insatisfatório é software portado mal, independentemente dos obstáculos que o sistema operacional oferece.
Dizzyspiral
28

O Unix tem uma chamada de sistema 'fork' que 'divide' o processo atual em dois e dá a você um segundo processo que é idêntico ao primeiro (módulo o retorno da chamada fork). Como o espaço de endereço do novo processo já está instalado e funcionando, isso deve ser mais barato do que chamar 'CreateProcess' no Windows e fazer com que carregue a imagem exe, dlls associados, etc.

No caso da bifurcação, o sistema operacional pode usar a semântica de 'cópia na gravação' para as páginas de memória associadas aos dois novos processos para garantir que cada um obtenha sua própria cópia das páginas que eles modificam subsequentemente.

Rob Walker
fonte
22
Esse argumento só é válido quando você está realmente bifurcando. Se você está iniciando um novo processo, no Unix você ainda precisa fazer um fork e executar. Tanto o Windows quanto o Unix possuem cópia na gravação. O Windows certamente reutilizará um EXE carregado se você executar uma segunda cópia de um aplicativo. Não acho que sua explicação esteja correta, desculpe.
Joel Spolsky
1
Mais sobre exec () e fork () vipinkrsahu.blogspot.com/search/label/system%20programming
webkul
Eu adicionei alguns dados de desempenho em minha resposta. stackoverflow.com/a/51396188/537980 Você pode ver que é mais rápido.
ctrl-alt-delor
25

Adicionando ao que JP disse: a maior parte da sobrecarga pertence à inicialização do Win32 para o processo.

O kernel do Windows NT realmente suporta bifurcação COW. SFU (ambiente UNIX da Microsoft para Windows) os utiliza. No entanto, o Win32 não oferece suporte a fork. Os processos SFU não são processos Win32. SFU é ortogonal ao Win32: ambos são subsistemas de ambiente construídos no mesmo kernel.

Além das chamadas LPC fora do processo para CSRSS, no XP e posterior, há uma chamada fora do processo para o mecanismo de compatibilidade de aplicativos para localizar o programa no banco de dados de compatibilidade de aplicativos. Esta etapa causa sobrecarga suficiente para que a Microsoft forneça uma opção de política de grupo para desabilitar o mecanismo de compatibilidade no WS2003 por motivos de desempenho.

As bibliotecas de tempo de execução Win32 (kernel32.dll, etc.) também fazem muitas leituras de registro e inicialização na inicialização que não se aplicam a UNIX, SFU ou processos nativos.

Os processos nativos (sem subsistema de ambiente) são muito rápidos de criar. SFU faz muito menos do que Win32 para a criação de processos, portanto, seus processos também são rápidos de criar.

ATUALIZAÇÃO PARA 2019: adicionar LXSS: Subsistema Windows para Linux

Substituindo SFU para Windows 10 é o subsistema de ambiente LXSS. É 100% do modo kernel e não requer nenhum IPC que o Win32 continua a ter. Syscall para esses processos é direcionado diretamente para lxss.sys / lxcore.sys, portanto, o fork () ou outro processo de criação de chamada custa apenas 1 chamada de sistema para o criador, no total. [Uma área de dados chamada instância] mantém o controle de todos os processos LX, threads e estado de tempo de execução.

Os processos LXSS são baseados em processos nativos, não em processos Win32. Todas as coisas específicas do Win32, como o mecanismo de compatibilidade, não são acionadas.

Chris Smith
fonte
16

Além da resposta de Rob Walker: Hoje em dia você tem coisas como a Native POSIX Thread Library - se quiser. Mas por muito tempo, a única maneira de "delegar" o trabalho no mundo unix era usar fork () (e ainda é preferido em muitas, muitas circunstâncias). por exemplo, algum tipo de servidor de socket

socket_accept ()
garfo()
se (criança)
    handleRequest ()
outro
    goOnBeingParent ()
Portanto, a implementação do fork teve que ser rápida e muitas otimizações foram implementadas ao longo do tempo. A Microsoft endossou CreateThread ou mesmo fibras em vez de criar novos processos e uso de comunicação entre processos. Eu acho que não é "justo" comparar CreateProcess com fork, pois eles não são intercambiáveis. Provavelmente é mais apropriado comparar fork / exec com CreateProcess.

VolkerK
fonte
2
Sobre o seu último ponto: fork () não pode ser trocado por CreateProcess (), mas também se pode dizer que o Windows deve implementar fork () então, porque isso dá mais flexibilidade.
Blaisorblade
Ah, o verbo To Bee.
acib708
Mas fork + exec no Linux, é mais rápido que CreateThread no MS-Windows. E o Linux pode fazer o fork sozinho para ser ainda mais rápido. Independentemente de como você compare, MS é mais lento.
ctrl-alt-delor
13

A chave para essa questão é o uso histórico de ambos os sistemas, eu acho. O Windows (e o DOS antes disso) foram originalmente sistemas de usuário único para computadores pessoais . Como tal, esses sistemas normalmente não precisam criar muitos processos o tempo todo; (muito) simplesmente, um processo só é criado quando esse usuário solitário o solicita (e nós, humanos, não operamos muito rápido, relativamente falando).

Os sistemas baseados em Unix foram originalmente sistemas e servidores multiusuário. Especialmente para o último, não é incomum ter processos (por exemplo, mail ou daemons http) que separam processos para lidar com tarefas específicas (por exemplo, cuidar de uma conexão de entrada). Um fator importante para fazer isso é o forkmétodo barato (que, como mencionado por Rob Walker ( 47865 ), usa inicialmente a mesma memória para o processo recém-criado), que é muito útil porque o novo processo tem imediatamente todas as informações de que precisa.

É claro que, pelo menos historicamente, a necessidade de sistemas baseados em Unix para criação rápida de processos é muito maior do que para sistemas Windows. Acho que ainda é o caso porque os sistemas baseados em Unix ainda são muito orientados a processos, enquanto o Windows, devido à sua história, provavelmente foi mais orientado a threads (threads sendo úteis para fazer aplicativos responsivos).

Isenção de responsabilidade: eu não sou de forma alguma um especialista neste assunto, então me perdoe se eu entendi errado.

Mweerden
fonte
9

Uh, parece haver um monte de justificativa do tipo "é melhor assim".

Acho que as pessoas poderiam se beneficiar lendo "Showstopper"; o livro sobre o desenvolvimento do Windows NT.

O motivo pelo qual os serviços são executados como DLLs em um processo no Windows NT é que eles eram lentos demais como processos separados.

Se você se complicar, descobrirá que a estratégia de carregamento da biblioteca é o problema.

No Unices (em geral), os segmentos de código das Bibliotecas compartilhadas (DLL's) são realmente compartilhados.

O Windows NT carrega uma cópia da DLL por processo, porque ele manipula o segmento de código da biblioteca (e o segmento de código executável) após o carregamento. (Diz onde estão seus dados?)

Isso resulta em segmentos de código em bibliotecas que não são reutilizáveis.

Portanto, o processo de criação do NT é realmente muito caro. E no lado negativo, faz com que o DLL não economize de forma apreciável na memória, mas uma chance para problemas de dependência entre aplicativos.

Às vezes, vale a pena para a engenharia dar um passo atrás e dizer: "agora, se fôssemos projetar isso para realmente uma merda, como seria?"

Trabalhei com um sistema embarcado que era bastante temperamental uma vez, e um dia olhei para ele e percebi que era um magnetron de cavidade, com a parte eletrônica na cavidade de microondas. Depois disso, o tornamos muito mais estável (e menos parecido com um micro-ondas).

Tim Williscroft
fonte
3
Os segmentos de código são reutilizáveis, desde que a DLL seja carregada em seu endereço base preferencial. Tradicionalmente, você deve garantir que define endereços de base não conflitantes para todas as DLLs que carregam em seus processos, mas isso não funciona com ASLR.
Mike Dimmick,
Existe alguma ferramenta para realocar todas as DLLs, não existe? Não tenho certeza do que faz com ASLR.
Zan Lynx,
3
O compartilhamento de seções de código também funciona em sistemas habilitados para ASLR.
Passagem de Johannes em
@MikeDimmick então todo mundo, fazendo uma DLL tem que cooperar, para garantir que não haja conflitos, ou você corrige todos eles em um nível de sistema, antes de carregar?
ctrl-alt-delor
9

A resposta curta é "camadas e componentes de software".

A arquitetura do Windows SW tem algumas camadas e componentes adicionais que não existem no Unix ou são simplificados e manipulados dentro do kernel no Unix.

No Unix, fork e exec são chamadas diretas ao kernel.

No Windows, a API do kernel não é usada diretamente, há win32 e alguns outros componentes em cima dela, portanto, a criação do processo deve passar por camadas extras e, em seguida, o novo processo deve ser iniciado ou se conectar a essas camadas e componentes.

Por algum tempo, pesquisadores e corporações tentaram quebrar o Unix de uma forma vagamente semelhante, geralmente baseando seus experimentos no kernel Mach ; um exemplo conhecido é o OS X .. Cada vez que eles tentam, no entanto, fica tão lento que eles acabam, pelo menos parcialmente, mesclando as peças de volta ao kernel permanentemente ou para remessas de produção.

DigitalRoss
fonte
Camadas não necessariamente tornam as coisas lentas: eu escrevi um driver de dispositivo, com muitas camadas, em C. Código limpo, programação letrada, fácil de ler. Foi mais rápido (marginalmente) do que uma versão escrita em assembler altamente otimizado, sem camadas.
ctrl-alt-delor
A ironia é que o NT é um kernel enorme (não um micro kernel)
ctrl-alt-delor
2

Como parece haver alguma justificativa para o MS-Windows em algumas das respostas, por exemplo

  • “Kernel NT e Win32 não são a mesma coisa. Se você programar para o kernel NT, então não é tão ruim ”- Verdade, mas a menos que você esteja escrevendo um subsistema Posix, então quem se importa. Você estará escrevendo para win32.
  • “Não é justo comparar fork, com ProcessCreate, pois eles fazem coisas diferentes, e o Windows não tem fork“ - Verdade, então vou comparar de igual para igual. No entanto, também vou comparar fork, porque ele tem muitos casos de uso, como isolamento de processo (por exemplo, cada guia de um navegador da web é executada em um processo diferente).

Agora, vejamos os fatos, qual é a diferença de desempenho?

Dados resumidos de http://www.bitsnbites.eu/benchmarking-os-primitives/ .
Como o preconceito é inevitável, ao resumir, fiz isso em favor do Hardware MS-Windows
para a maioria dos testes i7 8 core 3.2GHz. Exceto Raspberry-Pi executando Gnu / Linux

Uma comparação de várias operações básicas, no Gnu / Linux, Apple-Mac e Windows da Microsoft (quanto menor, melhor)

Uma comparação entre o processo de criação do MS-Windows e o Linux

Notas: No Linux, forké mais rápido que o método preferido do MS-Window CreateThread.

Números para operações de tipo de criação de processo (porque é difícil ver o valor do Linux no gráfico).

Em ordem de velocidade, do mais rápido para o mais lento (os números são o tempo, menor é melhor).

  • Linux CreateThread 12
  • Mac CreateThread 15
  • Linux Fork 19
  • Windows CreateThread 25
  • Linux CreateProcess (fork + exec) 45
  • Mac Fork 105
  • Mac CreateProcess (fork + exec) 453
  • Raspberry-Pi CreateProcess (fork + exec) 501
  • Windows CreateProcess 787
  • Windows CreateProcess com antivírus 2850
  • Windows Fork (simular com CreateProcess + correção) maior que 2850

Números para outras medidas

  • Criando um arquivo.
    • Linux 13
    • Mac 113
    • Windows 225
    • Raspberry-Pi (com cartão SD lento) 241
    • Windows com defesa e scanner de vírus, etc. 12950
  • Alocando memória
    • Linux 79
    • Windows 93
    • Mac 152
ctrl-alt-delor
fonte
1

Além disso, há o fato de que na máquina Win, muito provavelmente, um software antivírus será ativado durante o CreateProcess ... Essa é geralmente a maior lentidão.

gabr
fonte
1
Sim, é a maior, mas não a única desaceleração significativa.
ctrl-alt-delor 01 de
1

Também é importante notar que o modelo de segurança no Windows é muito mais complicado do que em sistemas operacionais baseados em Unix, o que adiciona muita sobrecarga durante a criação do processo. Mais uma razão pela qual o multithreading é preferido ao multiprocessamento no Windows.

hacksoncode
fonte
1
Eu esperaria que um modelo de segurança mais complicado fosse mais seguro; mas os fatos mostram o contrário.
Lie Ryan
4
SELinux também é um modelo de segurança muito complexo e não impõe uma sobrecarga significativa emfork()
Spudd86
6
@LieRyan, Em design de software (em minha experiência), mais complicado raramente significa mais seguro.
Woodrow Douglass