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

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

mweerden: O NT foi projetado para multiusuários desde o primeiro dia, então este não é realmente um motivo. No entanto, você está certo sobre a criação desse processo desempenha um papel menos importante no NT do que no Unix, pois o NT, em contraste com o Unix, favorece o multithreading sobre o multiprocessing.

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

Ao discutir a velocidade da criação do processo, é provavelmente uma boa ideia distinguir entre o NT e o Windows / Win32. Quanto NT (ou seja, o próprio kernel) vai, eu não acho que a criação de processos (NtCreateProcess) e criação de threads (NtCreateThread) é significativamente mais lenta como na média Unix. Pode haver um pouco mais acontecendo, mas não vejo a principal razão para a diferença de desempenho aqui.

Se você olhar para o Win32, no entanto, você notará que ele adiciona um pouco de sobrecarga para processar a criação. Por um lado, exige 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 ele precisa executar vários itens de trabalho de contabilidade adicionais para serem feitos antes que o processo seja considerado um processo completo do Win32. E não nos esqueçamos de toda a sobrecarga adicional imposta pela análise de manifestos, verificando se a imagem requer um shims de compatibilidade, verificando se as políticas de restrição de software se aplicam, yada yada.

Dito isso, vejo a lentidão geral na sum de todas essas pequenas coisas que precisam ser feitas além da criação bruta de um processo, espaço VA e thread inicial. Mas, como foi dito no começo – devido ao favorecimento do multiencadeamento em multitarefas, o único software que é seriamente afetado por essa despesa adicional é o mal portado do software Unix. Embora esta situação mude quando softwares como o Chrome e o IE8 repentinamente redescobrem os benefícios do multiprocessing e começam a iniciar com frequência processos de desassembly …

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 de retorno da chamada fork). Como o espaço de endereçamento do novo processo já está ativo e funcionando, isso deve ser mais barato do que chamar ‘CreateProcess’ no Windows e fazer com que ele carregue a imagem exe, dlls associadas, etc.

No caso do sistema operacional, o sistema operacional pode usar a semântica “copy-on-write” para as páginas de memory associadas a ambos os novos processos, para garantir que cada um receba sua própria cópia das páginas que posteriormente modificam.

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

O kernel do Windows NT realmente suporta o fork do COW. SFU (ambiente UNIX da Microsoft para Windows) usa-os. No entanto, o Win32 não suporta fork. Processos SFU não são processos Win32. O SFU é ortogonal ao Win32: ambos são subsistemas de ambiente criados no mesmo kernel.

Além das chamadas LPC fora de processo para o CSRSS , no XP e mais tarde, há uma chamada fora de processo para o mecanismo de compatibilidade de aplicativo para localizar o programa no database de compatibilidade de aplicativo. Essa etapa causa sobrecarga suficiente que a Microsoft fornece uma opção de diretiva de grupo para desabilitar o mecanismo de compatibilidade no WS2003 por motivos de desempenho.

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

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

Além da resposta de Rob Walker: Nowadys você tem coisas como a Native POSIX Thread Library – se você quiser. Mas por muito tempo a única maneira de “delegar” o trabalho no mundo unix foi usar fork () (e ainda é preferível em muitas, muitas circunstâncias). por exemplo, algum tipo de servidor de soquete

  socket_accept ()
 garfo()
 if (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 o CreateThread ou até mesmo fibras, em vez de criar novos processos e usar a comunicação entre processos. Eu acho que não é “justo” para comparar CreateProcess para bifurcação, uma vez que não são intercambiáveis. É provavelmente mais apropriado comparar fork / exec a CreateProcess.

A chave para esse assunto é o uso histórico de ambos os sistemas, eu acho. O Windows (e o DOS antes disso) eram 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 este usuário solitário o solicita (e nós humanos não operamos muito rápido, falando relativamente).

Os sistemas baseados em Unix foram originalmente sistemas e servidores multiusuários. Especialmente para o último, não é incomum ter processos (por exemplo, daemons de correio ou http) que dividam os processos para lidar com tarefas específicas (por exemplo, cuidar de uma conexão de input). Um fator importante para fazer isso é o método fork barato (que, como mencionado por Rob Walker ( 47865 ), usa inicialmente a mesma memory para o processo recém-criado), que é muito útil, pois o novo processo tem imediatamente todas as informações necessárias.

É claro que, pelo menos historicamente, a necessidade de sistemas baseados em Unix terem uma rápida criação de processos é muito maior do que em sistemas Windows. Eu acho que este ainda é o caso, porque os sistemas baseados em Unix ainda são muito orientados a processos, enquanto o Windows, devido à sua história, provavelmente tem sido mais orientado a threads (threads sendo úteis para fazer aplicações responsivas).

Disclaimer: Eu não sou de modo algum um especialista sobre este assunto, então me perdoe se eu entendi errado.

A resposta curta é “camadas e componentes de software”.

A arquitetura SW do Windows possui 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á o win32 e alguns outros componentes, portanto, a criação do processo deve passar por camadas extras e, em seguida, o novo processo deve iniciar ou conectar-se a essas camadas e componentes.

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

Parece haver um tipo de justificativa do tipo “é melhor assim”.

Eu acho que as pessoas poderiam se beneficiar da leitura de “Showstopper”; o livro sobre o desenvolvimento do Windows NT.

A razão pela qual os serviços são executados como DLLs em um processo no Windows NT é que eles eram muito lentos como processos separados.

Se você desceu e sujo, você acha que a estratégia de carregamento da biblioteca é o problema.

Em Unices (em geral), os segmentos de código das bibliotecas compartilhadas (DLLs) são realmente compartilhados.

Windows NT carrega uma cópia da DLL por processo, porque ele manipula o segmento de código de biblioteca (e 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.

Então, o processo de criação do NT é realmente muito caro. E no lado negativo, isso faz com que o DLL não aumente a economia de memory, mas uma chance de problemas de dependência entre aplicativos.

Às vezes, compensa em engenharia recuar e dizer: “Agora, se nós projetássemos isso para realmente ser uma droga, como seria?”

Eu 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 eletrônica na cavidade de microondas. Nós fizemos isso muito mais estável (e menos como um microondas) depois disso.

Tudo isso, além do fato de que na máquina Win, provavelmente, um software antivírus vai entrar em ação durante o CreateProcess … Essa é geralmente a maior lentidão.

Como parece haver alguma justificativa do 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 o win32.
  • “Não é justo comparar para comparar fork, com ProcessCreate, pois eles fazem coisas diferentes, e o Windows não tem fork” – – True, mas fork é muito, muito útil. Se você quiser o isolamento do processo (por exemplo, entre as guias em um navegador da Web), essa é a maneira mais fácil de fazer isso.

Agora vamos olhar para os fatos, qual é a diferença no desempenho?

Data summerised de http://www.bitsnbites.eu/benchmarking-os-primitives/ .
Porque preconceito é inevitável, ao resumir, eu fiz em favor do MS-Windows
Hardware para a maioria dos testes i7 8 core 3.2GHz. Isso só é relevante ao comparar o MS-Windows com o Raspberry-Pi executando o Gnu / Linux

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

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

Notas: No linux fork é mais rápido que o método preferido do MS-Windows CreateThread.

Agora para algumas outras figuras

  • Criando um arquivo.
    • Linux 13
    • Mac 113
    • Windows 225
    • Framboesa-Pi (com cartão SD lento) 241
    • Janelas com defensor e scanner de vírus, etc 12950
  • Alocando memory
    • Linux 79
    • Windows 93
    • Mac 152

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. Ainda outra razão pela qual o multithreading é preferido para o multiprocessing no Windows.