Fork vs Clone no 2.6 Kernel Linux

37

Eu tenho alguma confusão sobre garfo e clone. Eu já vi isso:

  • fork é para processos e clone é para threads

  • fork apenas chama clone, clone é usado para todos os processos e threads

Algum deles é preciso? Qual é a distinção entre esses dois syscalls com um kernel Linux 2.6?

Gregg Leventhal
fonte

Respostas:

52

fork()foi a chamada do sistema UNIX original. Só pode ser usado para criar novos processos, não threads. Além disso, é portátil.

No Linux, clone()é uma nova chamada de sistema versátil que pode ser usada para criar um novo encadeamento de execução. Dependendo das opções passadas, o novo encadeamento de execução pode aderir à semântica de um processo UNIX, um encadeamento POSIX, algo intermediário ou algo completamente diferente (como um contêiner diferente). Você pode especificar todos os tipos de opções que determinam se a memória, os descritores de arquivo, os vários namespaces, os manipuladores de sinais etc. são compartilhados ou copiados.

Como clone()é a chamada de superconjunto do sistema, a implementação do fork()wrapper de chamada do sistema na glibc realmente chama clone(), mas esse é um detalhe da implementação que os programadores não precisam conhecer. A fork()chamada real do sistema real ainda existe no kernel do Linux por motivos de compatibilidade com versões anteriores, mesmo que tenha se tornado redundante, porque programas que usam versões muito antigas do libc, ou outra libc além do glibc, podem usá-lo.

clone()também é usado para implementar a pthread_create()função POSIX para criar threads.

Programas portáteis devem chamar fork()e pthread_create(), não clone().

Celada
fonte
2
O posix_spawn é outra função relevante - mais e menos portátil que o fork, em alguns aspectos.
usar o seguinte código
10

Parece que há duas clone()coisas flutuando no Linux 2.6

Há uma chamada do sistema:

int clone(int (*fn)(void *), void *child_stack,
          int flags, void *arg, ...
          /* pid_t *ptid, struct user_desc *tls, pid_t *ctid */ );

Este é o "clone ()" descrito ao fazer man 2 clone.

Se você ler essa página de manual o suficiente, verá o seguinte:

It is actually a library function layered on top of the
underlying clone() system call.

Aparentemente, você deve implementar o encadeamento usando a "função de biblioteca" em camadas na chamada do sistema com nome idêntico e confuso.

Eu escrevi um pequeno programa:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int
main(int ac, char **av)
{
    pid_t cpid;
    switch (cpid = fork()) {
    case 0:   // Child process
        break;
    case -1:  // Error
        break;
    default:  // parent process
        break;
    }
    return 0;
}

Compilou-o com:, c99 -Wall -Wextrae o executou strace -fpara ver o que as chamadas de bifurcação do sistema realmente fazem. Tirei isso de straceuma máquina Linux 2.6.18 (CPU x86_64):

20097 clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x2b4ee9213770) = 20098
20097 exit_group(0)                     = ?
20098 exit_group(0)

Nenhuma chamada "fork" aparece na stracesaída. A clone()chamada que aparece na stracesaída tem argumentos muito diferentes do clone da página de manual. child_stack=0como o primeiro argumento é diferente de int (*fn)(void *).

Parece que a fork(2)chamada do sistema é implementada em termos do real clone() , assim como a "função de biblioteca" clone()é implementada. O real clone() tem um conjunto diferente de argumentos do clone da página de manual.

Simplisticamente, ambas as suas declarações aparentemente contraditórias sobre fork()e clone()estão corretas. O "clone" envolvido é diferente, no entanto.

Bruce Ediger
fonte
9
"Na verdade, é uma função de biblioteca em camadas na parte superior da chamada do sistema clone () subjacente." - em geral, isso se aplica a todas as chamadas do sistema. Na verdade, os programadores praticamente sempre chamam funções na libc, nomeadas após a chamada do sistema. Isso ocorre porque fazer uma chamada real real ao sistema diretamente de C requer mágica específica da plataforma (geralmente forçando algum tipo de interceptação de CPU, depende da arquitetura ABI) e o código da máquina é melhor delegado à libc.
Celada
1
@ Celada - sim, concordou. É exatamente isso que man 2 clonediz exatamente dessa maneira, o que eu pensei que estava confundindo o problema e impedindo que o questionador obtivesse uma boa resposta.
precisa
2
Creio que a página de manual significa indicar que a lista de argumentos da clonefunção de biblioteca difere substancialmente da lista de argumentos aceita pela chamada do sistema subjacente. Especificamente, a chamada do sistema sempre retorna duas vezes na mesma pilha, da maneira tradicional fork; todos os argumentos relacionados à pilha filho são tratados estritamente no espaço do usuário. Veja, por exemplo, sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/…
zwol
1
Eu queria dar a sua melhor resposta, porque ela balança, mas fui influenciado pelo rebanho e fui com a primeira resposta. Ela ganha pontos pelo tempo de resposta. Obrigado pela sua explicação.
Gregg Leventhal
6

fork()é apenas um conjunto específico de sinalizadores para a chamada do sistema clone(). clone()é geral o suficiente para criar um "processo" ou um "encadeamento" ou até coisas estranhas que estão em algum lugar entre processos e encadeamentos (por exemplo, diferentes "processos" que compartilham a mesma tabela de descritores de arquivos).

Essencialmente, para cada "tipo" de informação associado a um contexto de execução no kernel, clone()você pode escolher como aliasing ou copiá-las. Os threads correspondem ao alias, os processos correspondem à cópia. Ao especificar combinações intermediárias de sinalizadores para clone(), você pode criar coisas estranhas que não são threads ou processos. Você normalmente não deveria fazer isso, e imagino que houve algum debate durante o desenvolvimento do kernel do Linux sobre se deveria permitir um mecanismo tão geral como o clone().

Atsby
fonte