Como a cópia na gravação no fork () lida com o fork múltiplo?

23

Segundo a Wikipedia (o que pode estar errado)

Quando uma chamada do sistema fork () é emitida, uma cópia de todas as páginas correspondentes ao processo pai é criada, carregada em um local de memória separado pelo SO para o processo filho. Mas isso não é necessário em certos casos. Considere o caso em que um filho " exec" executa uma chamada do sistema (usada para executar qualquer arquivo executável de um programa C) ou sai logo após o fork(). Quando o filho é necessário apenas para executar um comando para o processo pai, não há necessidade de copiar as páginas do processo pai, pois execsubstitui o espaço de endereço do processo que o invocou pelo comando a ser executado.

Nesses casos, é utilizada uma técnica chamada cópia na gravação (COW). Com essa técnica, quando ocorre uma bifurcação, as páginas do processo pai não são copiadas para o processo filho. Em vez disso, as páginas são compartilhadas entre o filho e o processo pai. Sempre que um processo (pai ou filho) modifica uma página, é feita apenas uma cópia separada dessa página específica para o processo (pai ou filho) que executou a modificação. Esse processo usará a página recém-copiada em vez da compartilhada em todas as referências futuras. O outro processo (aquele que não modificou a página compartilhada) continua usando a cópia original da página (que agora não é mais compartilhada). Essa técnica é chamada de copiar na gravação, pois a página é copiada quando algum processo é gravado nela.

Parece que quando um dos processos tenta gravar na página, uma nova cópia da página é alocada e atribuída ao processo que gerou a falha da página. A página original é marcada como gravável posteriormente.

Minha pergunta é: o que acontece se a fork()chamada é chamada várias vezes antes de qualquer um dos processos tentar gravar em uma página compartilhada?

ssgao
fonte
A Wikipedia está correta neste caso, apenas mais alto nível.
precisa
1
Sim, copiar na gravação é uma cópia lenta, processo filho copia a página ao tentar gravá-la. Então, basicamente, depois de um garfo, quase a memória da criança é compartilhada com os pais. No entanto, antes de qualquer processo ser feito, todo processo filho ainda possui alguma memória privada, modificada da alocação dos pais ou da nova alocação. Isso significa que, mesmo sem nenhuma ação, o processo filho bifurcado tem alguma memória privada. Podemos verificar com pmap -XX PIDou cat /proc/PID/smap.
23
Em relação a "A página original é marcada como gravável posteriormente.", Quem será o proprietário? Aqui o outro processo que ainda não tentou escrevê-lo?
Adil
Isso é adorável. Vamos começar a ensinar isso em jardins de infância
ed22

Respostas:

18

Nada de especial acontece. Todos os processos estão compartilhando o mesmo conjunto de páginas e cada um obtém sua própria cópia privada quando deseja modificar uma página.

jlliagre
fonte
Direita. O ponto é que é o processo filho que é especial, que tem o trabalho de copiar se tentar gravar na página compartilhada. Nem os pais nem os outros filhos precisam saber da mudança, se ela for feita corretamente.
Charles Stewart
9
O processo filho não é tão especial. Os processos filho e pai têm o mesmo conjunto de páginas somente leitura após a bifurcação. No que diz respeito a estas páginas, o tratamento de páginas é simétrico.
Jlliagre
3

O comportamento do fork () depende se o sistema * nix possui uma MMU ou não. Em um sistema não-MMU (como os primeiros PDP-11s), a chamada do sistema fork () copiava toda a memória dos pais de cada filho. Em um sistema * nix baseado em MMU, o kernel marca todas as páginas que não são de pilha como R / O e as compartilha entre pai e filho. Então, quando um dos processos grava em qualquer página, o MMU captura a tentativa, o kernel aloca uma página gravável e atualiza as tabelas de páginas do MMU para apontar para a página agora gravável. Esse comportamento de copiar na gravação fornece uma aceleração, pois inicialmente apenas uma pilha privada precisa ser alocada e clonada para cada processo filho.

Se você executar algum código pai entre cada chamada fork (), os processos filhos resultantes serão diferentes pelas páginas que foram alteradas pelo pai. Por outro lado, se o pai simplesmente emitir várias chamadas fork (), por exemplo, em um loop, os processos filhos serão quase idênticos. Se uma variável de loop local for usada, isso será diferente na pilha de cada criança.

CyberFonic
fonte
0

Quando o sistema pré-forma uma bifurcação, normalmente (isso pode depender da implementação) também marca as páginas como somente leitura e marca o processo pai como o mestre dessas páginas.
Ao tentar gravar nessas páginas, ocorre uma falha de página e o sistema operacional assume o controle, copiando toda a lista de páginas ou apenas as alteradas (novamente, dependendo da implementação), para que o processo de gravação tenha uma cópia gravável.
Quando há vários processos bifurcados no mesmo, quando o processo "mestre" grava em sua memória, os outros processos obtêm suas páginas equivalentes copiadas.

Didi Kohen
fonte
qual sistema faz isso? linux usa um copy-on-write implementação
brauliobo
É assim que as obras copy-on-write ...
Didi Kohen
3
@DavidKohen não é assim que a cópia em gravação funciona em qualquer versão que eu já tenha ouvido falar. Não há processo "mestre". Se um processo único grava as páginas compartilhadas, sua cópia é transferida para uma privada, enquanto todos os outros processos continuam a compartilhá-la.
Celada
1
Eu acho que David Kohen está certo em algum ponto. Essa é uma maneira de implementar a cópia na gravação. A essência seria que, com essa marcação, a gravação nessa página dispararia um manipulador de falhas de página que executaria as ações apropriadas, ou seja, copiar na gravação. Infelizmente, esse detalhe (que seria específico do sistema) é principalmente irrelevante para a questão. Lembre-se de que o CoW tem duas dimensões: a visível para o processo e a de como o kernel pode implementá-lo.
0xC0000022L