Który plik w jądrze określa fork (), vfork ()… do użycia wywołania systemowego sys_clone ()

9

Gdy ltrace jest używany do śledzenia wywołań systemowych, widziałem, że fork () używa sys_clone () zamiast sys_fork (). Ale nie mogłem znaleźć źródła linuksa, w którym jest zdefiniowane.

Mój program to

#include<stdio.h>
main()
{
        int pid,i=0,j=0;
        pid=fork();
        if(pid==0)
                printf("\nI am child\n");
        else
                printf("\nI am parent\n");

}

I wyjście ltrace jest

SYS_brk(NULL)                                                                               = 0x019d0000
SYS_access("/etc/ld.so.nohwcap", 00)                                                        = -2
SYS_mmap(0, 8192, 3, 34, 0xffffffff)                                                        = 0x7fe3cf84f000
SYS_access("/etc/ld.so.preload", 04)                                                        = -2
SYS_open("/etc/ld.so.cache", 0, 01)                                                         = 3
SYS_fstat(3, 0x7fff47007890)                                                                = 0
SYS_mmap(0, 103967, 1, 2, 3)                                                                = 0x7fe3cf835000
SYS_close(3)                                                                                = 0
SYS_access("/etc/ld.so.nohwcap", 00)                                                        = -2
SYS_open("/lib/x86_64-linux-gnu/libc.so.6", 0, 00)                                          = 3
SYS_read(3, "\177ELF\002\001\001", 832)                                                     = 832
SYS_fstat(3, 0x7fff470078e0)                                                                = 0
SYS_mmap(0, 0x389858, 5, 2050, 3)                                                           = 0x7fe3cf2a8000
SYS_mprotect(0x7fe3cf428000, 2097152, 0)                                                    = 0
SYS_mmap(0x7fe3cf628000, 20480, 3, 2066, 3)                                                 = 0x7fe3cf628000
SYS_mmap(0x7fe3cf62d000, 18520, 3, 50, 0xffffffff)                                          = 0x7fe3cf62d000
SYS_close(3)                                                                                = 0
SYS_mmap(0, 4096, 3, 34, 0xffffffff)                                                        = 0x7fe3cf834000
SYS_mmap(0, 4096, 3, 34, 0xffffffff)                                                        = 0x7fe3cf833000
SYS_mmap(0, 4096, 3, 34, 0xffffffff)                                                        = 0x7fe3cf832000
SYS_arch_prctl(4098, 0x7fe3cf833700, 0x7fe3cf832000, 34, 0xffffffff)                        = 0
SYS_mprotect(0x7fe3cf628000, 16384, 1)                                                      = 0
SYS_mprotect(0x7fe3cf851000, 4096, 1)                                                       = 0
SYS_munmap(0x7fe3cf835000, 103967)                                                          = 0
__libc_start_main(0x40054c, 1, 0x7fff47008298, 0x4005a0, 0x400590 <unfinished ...>
fork( <unfinished ...>
SYS_clone(0x1200011, 0, 0, 0x7fe3cf8339d0, 0)                                               = 5967
<... fork resumed> )                                                                        = 5967
puts("\nI am parent" <unfinished ...>
SYS_fstat(1, 0x7fff47008060)                                                                = 0
SYS_mmap(0, 4096, 3, 34, 0xffffffff
)                                                        = 0x7fe3cf84e000
I am child
SYS_write(1, "\n", 1
)                                                                       = 1
SYS_write(1, "I am parent\n", 12)                                                           = -512
--- SIGCHLD (Child exited) ---
SYS_write(1, "I am parent\n", 12I am parent
)                                                           = 12
<... puts resumed> )                                                                        = 13
SYS_exit_group(13 <no return ...>
+++ exited (status 13) +++
użytkownik3539
źródło
Może to być dla ciebie pomocne
powtórka
@ mauro.stettler Nie mogłem go znaleźć w lxr
user3539,
Czy masz na myśli git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/… wokół linii 1700? Czego chciałeś się dowiedzieć?
msw

Odpowiedzi:

29

Te fork()i vfork()owijarki w glibc są realizowane poprzez clone()wywołanie systemowe. Aby lepiej zrozumieć związek między fork()i clone(), musimy wziąć pod uwagę związek między procesami i wątkami w systemie Linux.

Tradycyjnie fork()publikowałby wszystkie zasoby będące własnością procesu nadrzędnego i przypisywałby kopię do procesu potomnego. Takie podejście pociąga za sobą znaczne koszty ogólne, które wszystko może być na darmo, jeśli dziecko natychmiast zadzwoni exec(). W systemie Linux fork()wykorzystuje strony kopiowania przy zapisie w celu opóźnienia lub całkowitego uniknięcia kopiowania danych, które mogą być współużytkowane przez procesy nadrzędne i potomne. Tak więc jedynym kosztem, który powstaje podczas normalnego działania, fork()jest kopiowanie tabel stron nadrzędnych i przypisanie unikalnej struktury deskryptora procesu task_structdla dziecka.

Linux ma także wyjątkowe podejście do wątków. W Linuksie wątki są zwykłymi procesami, które dzielą niektóre zasoby z innymi procesami. Jest to całkowicie odmienne podejście do wątków w porównaniu do innych systemów operacyjnych, takich jak Windows lub Solaris, w których procesy i wątki są całkowicie różnymi rodzajami bestii. W Linuksie każdy wątek ma task_structswój własny zwyczaj, który jest tak skonfigurowany, że dzieli pewne zasoby, takie jak przestrzeń adresowa, z procesem nadrzędnym.

flagsParametr clone()wywołania systemowego zawiera zestaw flagi oznaczające zasobów, jeśli w ogóle, procesy rodzic i dziecko powinni udostępnić. Procesy i wątki są tworzone za pośrednictwem clone(), jedyną różnicą jest zestaw flag, które są przekazywane clone().

Normalny fork()może być zaimplementowany jako:

clone(SIGCHLD, 0);

Tworzy to zadanie, które nie dzieli żadnych zasobów z rodzicem i jest ustawione tak, aby wysyłać SIGCHLDsygnał zakończenia do rodzica, gdy wychodzi.

Natomiast zadanie, które współdzieli przestrzeń nadrzędną, zasoby systemu plików, deskryptory plików i programy obsługi sygnałów z rodzicem, innymi słowy wątek , można utworzyć za pomocą:

clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0);

vfork()z kolei jest implementowany za pomocą oddzielnej CLONE_VFORKflagi, co spowoduje, że proces nadrzędny przejdzie w stan uśpienia, dopóki proces potomny nie obudzi go sygnałem. Dziecko będzie jedynym wątkiem wykonania w przestrzeni nazw rodzica, dopóki nie zadzwoni exec()lub nie wyjdzie. Dziecko nie może pisać do pamięci. Odpowiednie clone()połączenie może wyglądać następująco:

clone(CLONE_VFORK | CLONE_VM | SIGCHLD, 0)

Implementacja sys_clone()jest specyficzna dla architektury, ale większość pracy odbywa się w do_fork()zdefiniowanym w kernel/fork.c. Ta funkcja wywołuje funkcję statyczną clone_process(), która tworzy nowy proces jako kopię elementu nadrzędnego, ale jeszcze go nie uruchamia. clone_process()kopiuje rejestry, przypisuje PID do nowego zadania i albo publikuje lub udostępnia odpowiednie części środowiska procesowego określone przez klon flags. Po clone_process()powrocie do_clone()obudzi nowo utworzony proces i zaplanuje jego uruchomienie.

Thomas Nyman
źródło
2
+1 Dobre wyjaśnienie znaczenia clone()w odniesieniu do wątków i widelców.
goldilocks,
1
Usunąłem wszystkie moje wątpliwości
użytkownik3539,
2

Komponent odpowiedzialny za tłumaczenie funkcji wywołania systemowego użytkownika na wywołania systemowe jądra w systemie Linux to libc. W GLibC biblioteka NPTL przekierowuje to do clone(2)wywołania systemowego.

Ignacio Vazquez-Abrams
źródło