2016-05-11 49 views
6

W eglibc'S nptl/sysdeps/unix/sysv/linux/i386/fork.c istnieje definicja:Dlaczego sys_fork nie jest używany przez implementację widelca glibc?

#define ARCH_FORK() \ 
    INLINE_SYSCALL (clone, 5,       \ 
      CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD, 0,  \ 
      NULL, NULL, &THREAD_SELF->tid) 

który jest stosowany w rzeczywistej __libc_fork() jako serce realizacji. Ale np. w Linuksie arch/x86/entry/syscalls/syscall_32.tbl istnieje wpis sys_fork, a także w syscalls_64.tbl. Najwyraźniej Linux ma swoją specjalną wersję dla fork.

Zastanawiam się teraz: dlaczego glibc implementuje fork() pod względem clone, jeśli jądro już dostarcza syscall fork?

Odpowiedz

6

Spojrzałem na commit, w którym Ulrich Drepper dodał ten kod do glibc, a nie było żadnego wyjaśnienia w dzienniku zatwierdzenia (lub w innym miejscu).

Wystarczy popatrzeć na wdrożenie Linuksa z fork, choć:

return _do_fork(SIGCHLD, 0, 0, NULL, NULL, 0); 

A oto clone:

return _do_fork(clone_flags, newsp, 0, parent_tidptr, child_tidptr, tls); 

Oczywiście, są one niemal identyczne. Jedyna różnica polega na tym, że wywołując clone, możesz ustawić różne flagi, określić rozmiar stosu dla nowego procesu itp. fork nie przyjmuje żadnych argumentów.

Patrząc na kod Dreppera, flagi clone to CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD. Jeśli użyto fork, jedyną flagą byłaby SIGCHLD.

Oto co clone podręcznika mówi o tych dodatkowych flag:

CLONE_CHILD_CLEARTID (since Linux 2.5.49) 
      Erase child thread ID at location ctid in child memory when the child 
      exits, and do a wakeup on the futex at that address. The address 
      involved may be changed by the set_tid_address(2) system call. This is 
      used by threading libraries. 

CLONE_CHILD_SETTID (since Linux 2.5.49) 
      Store child thread ID at location ctid in child memory. 

... I widać, że ma przekazać wskaźnik do miejsca, gdzie jądro należy najpierw zapisać dziecka identyfikator wątku, a później zrobić przebudzenie futex. Czy Glibc robi gdzieś futex na ten adres? Nie wiem Jeśli tak, to wyjaśnia to, dlaczego Drepper zdecydował się użyć clone.

(A jeśli nie, byłby to tylko jeden przykład ekstremalnej akumulacji cruftu, który jest naszą ukochaną glibc! Jeśli chcesz znaleźć ładny, czysty, dobrze utrzymany kod, po prostu ruszaj się i idź spójrz na musl libc!)

+1

'pthread_join()' robi przebudzenie. Podobnie jak w przypadku łączenia z wątkami, glibc wywołuje 'set_tid_address()', więc można połączyć wątek "główny": http://stackoverflow.com/questions/6975098/when-is-the-system-call-set -tid-address-used – ninjalj

+0

@usr: ten nowy proces może spawnować wątki. Jeśli chcesz, aby te wątki były w stanie 'pthread_join()' w głównym wątku nowego procesu, glibc potrzebuje adresu futex. – ninjalj

+0

@usr: 'execve()' zajmuje się tym, wywołując 'set_tid_address()'. BTW, jeśli od razu wykonasz exec, istnieje 'vfork()' lub 'posix_spawn()', jeśli twoja libc to zapewnia. – ninjalj

0

Krótko mówiąc: dlaczego nie?

Masz jeden system operacyjny, który jest gwarantowany na wszystkich platformach (zdajesz sobie sprawę, że Intel nie jest jedyną platformą, prawda?), I inny, który jest przestarzały, ponieważ jest niepotrzebny. Obaj niosą dokładnie tę samą semantykę. Twój kod jest znacznie bardziej zwarty, gdy dzwonisz tylko do gwarantowanego.

Opiszę to trochę.

Widelec jest definiowany przez Posix, podczas gdy klon jest specyficzny dla systemu Linux. Jednak czasami Linux przyjmuje, że Posix definiuje "wywołania systemowe" i implementuje je w przestrzeni użytkownika. Tak jest w przypadku fork (i vfork i pthread_create). Wszystkie są zaimplementowane w przestrzeni użytkownika przez wywołanie "clone".

Jako taki, widelec jest uważany za niepotrzebny na poziomie jądra. Jeśli cienka przestrzeń użytkownika może ją zaimplementować, jądro jest w porządku z tym. Jako taki, na Linuksie, klon jest dostępny na wszystkich platformach, podczas gdy fork może lub nie może istnieć, w zależności od konkretnej platformy.

+0

Wierzę, że 'fork (2)' istniało przed 'clone (2)'. –

+0

To jest poprawne i nieistotne. Zmieniłem odpowiedź, aby wyjaśnić, dlaczego. –