Fork vs Clone na jądrze 2.6 jądra Linux


37

Mam pewne wątpliwości dotyczące rozwidlenia i klonowania. Widziałem to:

  • widelec służy do procesów, a klon do wątków

  • fork po prostu wywołuje klon, klon jest używany do wszystkich procesów i wątków

Czy któreś z nich są dokładne? Jaka jest różnica między tymi dwoma syscallami z jądrem Linuksa 2.6?

Odpowiedzi:


52

fork()było oryginalnym wywołaniem systemowym UNIX. Można go używać tylko do tworzenia nowych procesów, a nie wątków. Jest także przenośny.

W systemie Linux clone()jest to nowe, wszechstronne wywołanie systemowe, którego można użyć do utworzenia nowego wątku wykonania. W zależności od przekazanych opcji nowy wątek wykonania może być zgodny z semantyką procesu UNIX, wątkiem POSIX, czymś pośrednim lub czymś zupełnie innym (jak inny kontener). Możesz określić wszelkiego rodzaju opcje decydujące o tym, czy pamięć, deskryptory plików, różne przestrzenie nazw, programy obsługi sygnałów itd. Mają być udostępniane czy kopiowane.

Ponieważ clone()jest to superset wywołanie systemowe, implementacja fork()opakowania wywołania systemowego w glibc faktycznie wywołuje clone(), ale jest to szczegół implementacji, o którym programiści nie muszą wiedzieć. Rzeczywiste fork()wywołanie systemowe nadal istnieje w jądrze Linuksa z powodów kompatybilności wstecznej, nawet jeśli stało się nadmiarowe, ponieważ programy, które używają bardzo starych wersji libc lub innej biblioteki libc oprócz glibc, mogą z niego korzystać.

clone()służy również do implementacji pthread_create()funkcji POSIX do tworzenia wątków.

Programy przenośne powinny wywoływać fork()i pthread_create()nie clone().


2
posix_spawn to kolejna istotna funkcja - pod pewnymi względami bardziej i mniej przenośna niż fork.
Random832

10

Wygląda na to, że clone()w Linuksie 2.6 krążą dwie rzeczy

Jest wywołanie systemowe:

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

Jest to „clone ()” opisane przez działanie man 2 clone.

Jeśli przeczytasz tę stronę podręcznika wystarczająco blisko, zobaczysz to:

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

Najwyraźniej należy wdrożyć wątki za pomocą „funkcji bibliotecznej” warstwowej w myląco identycznej nazwie wywołania systemowego.

Napisałem krótki program:

#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;
}

Skompilowałem go za pomocą: c99 -Wall -Wextrai uruchomiłem pod nim, strace -faby zobaczyć, co faktycznie robi rozwiązywanie połączeń systemowych. Mam to na komputerze z stracesystemem Linux 2.6.18 (procesor 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)

Na stracewyjściu nie pojawia się wywołanie „fork” . clone()Wezwanie, które pojawia się w straceprodukcji ma bardzo różne argumenty od człowieka-page-klon. child_stack=0ponieważ pierwszy argument jest inny niż int (*fn)(void *).

Wygląda na to, że fork(2)wywołanie systemowe zostało zaimplementowane w kategoriach rzeczywistych clone() , podobnie jak clone()zaimplementowana została „funkcja biblioteczna” . Prawdziwy clone() ma inny zestaw argumentów od człowieka-page-klon.

Upraszczając, oba twoje pozornie sprzeczne stwierdzenia dotyczące fork()i clone()są prawidłowe. Jednak zaangażowany „klon” jest inny.


9
„W rzeczywistości jest to funkcja biblioteczna nałożona na wywołanie systemowe clone ().” - ogólnie rzecz biorąc, dotyczy to każdego wywołania systemowego. Programiści praktycznie zawsze wywołują funkcje w libc, które są nazwane po wywołaniu systemowym. Wynika to z faktu, że wykonanie rzeczywistego wywołania systemowego bezpośrednio z C wymaga magii specyficznej dla platformy (zwykle przez wymuszenie jakiejś pułapki procesora, zależy od architektury ABI) i kodu maszynowego najlepiej pozostawić przekazanego do libc.
Celada

1
@Celada - tak, zgodził się. man 2 cloneTyle tylko, że sformułował to dokładnie w ten sposób, co - jak myślałem - pomieszało problem i uniemożliwiło pytającemu uzyskanie dobrej odpowiedzi.
Bruce Ediger,

2
Wierzę środki podręcznika, aby wskazać, że lista jej argumentów z clonefunkcji bibliotecznych różni się znacznie od listy argumentów akceptowanych przez bazowego wywołania systemowego. W szczególności wywołanie systemowe zawsze zwraca dwa razy na tym samym stosie, tak jak tradycyjne fork; wszystkie argumenty związane ze stosem potomnym są obsługiwane wyłącznie w przestrzeni użytkownika. Patrz na przykład sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/...
Zwol

1
Chciałem dać twoją odpowiedź najlepszą odpowiedź, ponieważ to kołysze, ale stado zachwiało mnie i wybrałem pierwszą odpowiedź. Dostaje punkty za czas reakcji. Dziękuję za wyjaśnienie.
Gregg Leventhal

6

fork()jest tylko szczególnym zestawem flag w wywołaniu systemowym clone(). clone()jest wystarczająco ogólny, aby utworzyć „proces” lub „wątek”, a nawet dziwne rzeczy, które są gdzieś pomiędzy procesami i wątkami (na przykład różne „procesy”, które mają tę samą tabelę deskryptorów plików).

Zasadniczo dla każdego „typu” informacji powiązanej z kontekstem wykonania w jądrze clone()daje wybór aliasingu tych informacji lub ich skopiowania. Wątki odpowiadają aliasingowi, procesy odpowiadają kopiowaniu. Określając pośrednie kombinacje flag clone(), możesz tworzyć dziwne rzeczy, które nie są wątkami ani procesami. Zwykle nie powinieneś tego robić i wyobrażam sobie, że podczas rozwoju jądra Linuksa była debata na temat tego, czy powinien on pozwolić na taki ogólny mechanizm jak clone().

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.