table of contents
- bookworm-backports 1:4.24.0-2~bpo12+1
- testing 1:4.24.0-2
- unstable 1:4.24.0-2
clone(2) | System Calls Manual | clone(2) |
NAZWA¶
clone, __clone2, clone3 - tworzy proces potomny
BIBLIOTEKA¶
Standardowa biblioteka C (libc, -lc)
SKŁADNIA¶
/* Prototyp funkcji opakowującej z glibc */
#define _GNU_SOURCE #include <sched.h>
int clone(int (*fn)(void *_Nullable), void *stack, int flags, void *_Nullable arg, ... /* pid_t *_Nullable parent_tid, void *_Nullable tls, pid_t *_Nullable child_tid */ );
/* Zob. UWAGI dot. prototypu surowego wywołania syst. clone() */
#include <linux/sched.h> /* Definicja struct clone_args */ #include <sched.h> /* Definicja stałych CLONE_* */ #include <sys/syscall.h> /* Definicja stałych SYS_* */ #include <unistd.h>
long syscall(SYS_clone3, struct clone_args *cl_args, size_t size);
Uwaga: glibc nie udostępnia opakowania do clone3(), zatem wymagane jest użycie syscall(2).
OPIS¶
Niniejsze wywołania systemowe tworzą nowy proces („potomny”), w sposób podobny do fork(2).
W przeciwieństwie do fork(2), te wywołania systemowe udostępniają precyzyjniejszą kontrolę wobec kontekstu wykonania, który jest dzielony między procesem wywołującym a procesem potomnym. Przykładowo, za pomocą niniejszych wywołań systemowych, wywołujący może kontrolować czy oba procesy dzielą wirtualną przestrzeń adresową, tablicę deskryptorów pliku i tablicę procedur obsługi sygnałów. Te wywołania systemowego umożliwiają również umieszczenie procesu potomnego w oddzielnych przestrzeniach nazw (zob. namespaces(7)).
Proszę zauważyć, że w niniejszym podręczniku systemowym „proces wywołujący” odpowiada zwykle „procesowy macierzystemu”. Proszę jednak sprawdzić opisy CLONE_PARENT i CLONE_THREAD niżej.
Niniejsza strona podręcznika opisuje następujące interfejsy:
- •
- Funkcję opakowującą clone() z glibc i podległe wywołanie systemowe, w oparciu o które działa. Główna część podręcznika opisuje funkcję opakowującą; różnice w stosunku do surowego wywołania systemowego opisano bliżej końca.
- •
- Nowsze wywołanie systemowe clone3().
W pozostałej treści niniejszego podręcznika, pojęcie „wywołanie clone” lub „wywołanie klonowania” używane jest przy opisywaniu szczegółów odnoszących się do wszystkich tych interfejsów.
Funkcja opakowująca clone()¶
Gdy proces potomny tworzony jest za pomocą funkcji opakowującej clone(), rozpoczyna on wykonanie od wywołania funkcji, na którą wskazuje argument fn (różni się to od fork(2), gdzie proces potomny kontynuuje wykonanie od miejsca wywołania fork(2)). Argument arg jest przekazywany jako argument do funkcji fn.
Gdy funkcja fn(arg) powróci, proces potomny kończy działanie. Liczba całkowita zwrócona przez fn jest statusem zakończenia procesu potomnego. Proces potomny może również zakończyć się jawnie wołając exit(2) lub po otrzymaniu krytycznego sygnału.
Argument stack określa położenie stosu używanego przez proces potomny. Ponieważ potomek i proces wywołujący mogą współdzielić pamięć, nie jest możliwe, aby proces potomny korzystał z tego samego stosu, co proces wywołujący. Proces wywołujący musi więc przydzielić obszar pamięci przeznaczony na stos potomka i przekazać wskaźnik do tego obszaru w clone. Stosy rosną w dół na wszystkich procesorach, na których działa Linux (z wyjątkiem procesorów HP PA), więc stack zazwyczaj wskazuje na najwyższy adres obszaru pamięci zarezerwowanego na stos potomka. Proszę zauważyć, że clone() nie zapewnia mechanizmu, w którym wywołujący mógłby poinformować jądro o wielkości obszaru stosu.
Pozostałe argumenty clone() opisano poniżej.
clone3()¶
Wywołanie systemowe clone3() udostępnia nadzbiór funkcjonalności wobec starszego interfejsu clone(). Zawiera również wiele usprawnień API m.in: przestrzeń na dodatkowe bity znaczników, przejrzystszy podział stosowania różnych argumentów oraz możliwość określenia rozmiaru przestrzeni stosu procesu potomnego.
Podobnie jak fork(2), clone3() powraca zarówno w procesie macierzystym, jak i potomnym. Zwraca 0 do procesu potomnego, natomiast procesowi macierzystemu zwraca PID procesu potomnego.
Argumentem cl_args clone3() jest struktura w następującej postaci:
struct clone_args {
u64 flags; /* Maska bitowa znaczników */
u64 pidfd; /* Gdzie przechowywać deskryptor pliku PID
(int *) */
u64 child_tid; /* Gdzie przechowywać TID p. potomnego,
w pamięci p. potomnego (pid_t *) */
u64 parent_tid; /* Gdzie przechowywać TID, w pamięci
procesu macierzystego (pid_t *) */
u64 exit_signal; /* Sygnał do dostarcz. przy zakończeniu
procesu potomnego */
u64 stack; /* Wskaźnik do najniższych bajtów stosu */
u64 stack_size; /* Rozmiar stosu */
u64 tls; /* Położenie nowego TLS */
u64 set_tid; /* Wskaźnik do tablicy pid_t
(od Linuksa 5.5) */
u64 set_tid_size; /* Liczba elementów w set_tid
(od Linuksa 5.5) */
u64 cgroup; /* Deskryptor pliku docelowej gr. kontr.
procesu potomnego (od Linuksa 5.7) */ };
Argument size dostarczany do clone3() powinien być zainicjowany z rozmiarem tej struktury (obecność argumentu size pozwala na przyszłe poszerzanie struktury clone_args).
Stos procesu potomnego podaje się za pomocą cl_args.stack, które wskazuje na najniższe bajty w przestrzeni stosu oraz za pomocą cl_args.stack_size, które określa rozmiar stosu w bajtach. W przypadku gdy poda się znacznik CLONE_VM (zob. niżej), stos musi być jawnie zaalokowany i określony. W przeciwnym przypadku, te dwa pola można podać jako NULL i 0, co powoduje używanie przez proces potomny tej samej przestrzeni stosu, z jakiej korzysta proces macierzysty (we własnej wirtualnej przestrzeni adresowej procesu potomnego).
Pozostałe pola argumentu cl_args opisano niżej.
Równoważność pomiędzy argumentami clone() i clone3()¶
W odróżnieniu do starszego interfejsu clone(), którego argumenty są przekazywane pojedynczo, w nowszym interfejsie clone3() argumenty są łączone w strukturze clone_args pokazanej wyżej. Struktura pozwala na przekazanie nadzbioru informacji, przekazywanych za pomocą argumentów clone().
Poniższa tabela ukazuje równoważność pomiędzy argumentami clone() i polami w argumencie clone_args przekazywanym clone3():
clone() | clone3() | Uwagi |
pole cl_args | ||
flags & ~0xff | flags | Do większości znaczników; szczegóły niżej |
parent_tid | pidfd | Zob. CLONE_PIDFD |
child_tid | child_tid | Zob. CLONE_CHILD_SETTID |
parent_tid | parent_tid | Zob. CLONE_PARENT_SETTID |
flags & 0xff | exit_signal | |
stack | stack | |
--- | stack_size | |
tls | tls | Zob. CLONE_SETTLS |
--- | set_tid | Zob. niżej aby poznać szczegóły |
--- | set_tid_size | |
--- | cgroup | Zob. CLONE_INTO_CGROUP |
Sygnał zakończenia potomka¶
Gdy proces potomny zostanie zakończony, do rodzica może być wysłany sygnał. Sygnał zakończenia jest określany niższym bajtem flags (clone()) lub cl_args.exit_signal (clone3()). Jeśli określono inny sygnał niż SIGCHLD, to proces macierzysty musi podać opcję __WALL lub __WCLONE czekając na potomka w wait(2). Gdy sygnał nie zostanie określony (tj. podano zero), to proces macierzysty nie zostanie zawiadomiony o zakończeniu pracy potomka.
Tablica set_tid¶
Domyślnie, jądro wybiera następny numer PID dla nowego procesu, w każdej przestrzeni nazw PID, w której jest on obecny. Przy tworzeniu procesu za pomocą clone3(), tablicę set_tid (dostępną od Linuksa 5.5) można użyć do wybrania konkretnych PID-ów w niektórych lub we wszystkich przestrzeniach nazw PID, w których jest on obecny. Jeśli PID nowo tworzonego procesu ma być ustawiony tylko w bieżącej przestrzeni nazw PID lub w nowo tworzonej przestrzeni nazw PID (jeśli flags zawiera CLONE_NEWPID), to pierwszym elementem w tablicy set_tid musi być żądany PID, a set_tid_size musi wynosić 1.
Jeśli PID nowo tworzonego procesu ma mieć określoną wartość w wielu przestrzeniach nazw PID, to tablica set_tid może zawierać wiele wpisów. Pierwszy wpis definiuje PID najbardziej zagnieżdżonej przestrzeni nazw PID, a każdy kolejny zawiera PID w odpowiadającej przestrzeni nazw PID przodka. Liczba przestrzeni nazw PID, w której PID ma być ustawiony, jest definiowana przez set_tid_size, które nie może być większe od liczby aktualnie zagnieżdżonych przestrzeni nazw.
Aby utworzyć proces z następującymi PID-ami w hierarchii przestrzeni nazw PID:
Poziom zagn. PID | Żądany PID | Uwagi |
0 | 31496 | Najbardziej zewnętrzna p. n. PID |
1 | 42 | |
2 | 7 | Najbardziej wewnętrzna p. n. PID |
Należy ustawić tablicę na
set_tid[0] = 7; set_tid[1] = 42; set_tid[2] = 31496; set_tid_size = 3;
Jeśli mają być określone jedynie PID-y w dwóch najbardziej wewnętrznych przestrzeniach nazw PID, należy ustawić tablicę na:
set_tid[0] = 7; set_tid[1] = 42; set_tid_size = 2;
PID w przestrzeni nazw PID poza dwoma najbardziej wewnętrznymi przestrzeniami nazw PID jest wybierany w ten sam sposób, jak inne PID-y.
Funkcja set_tid wymaga przywileju (ang. capability) CAP_SYS_ADMIN lub (od Linuksa 5.9) CAP_CHECKPOINT_RESTORE we wszystkich posiadanych przestrzeniach nazw użytkownika, w których PID ma być zmieniany.
Wywołujący mogą wybrać PID większy od 1 jedynie, gdy w danej przestrzeni nazw PID istnieje już proces init (tj. proces z PID 1). W przeciwnym przypadku, wpis PID dla tej przestrzeni nazw musi wynosić 1.
Maska znaczników¶
clone() i clone3() umożliwiają użycie maski bitowej znaczników, które modyfikują ich zachowanie i pozwalają wywołującemu na określenie tego, co ma być dzielone między procesem wywołującym a potomnym. Maska bitowa — argument flags clone() lub pole cl_args.flags przekazywane do clone3() — jest nazywana w pozostałem części niniejszego podręcznika maską flags.
Maskę flags można podać jako sumę bitową (OR) zera lub więcej z poniższych zmiennych. Poza wskazanymi wyjątkami, znaczniki te są dostępne (i mają takie samo zastosowanie) w clone() i clone3().
- CLONE_CHILD_CLEARTID (od Linuksa 2.5.49)
- Czyści (zeruje) identyfikator wątku potomnego w położeniu, na które wskazuje child_tid (clone()) lub cl_args.child_tid (clone3()) w pamięci potomka, gdy potomek istnieje i wybudza zatrzask (mutex) pod tym adresem. Adres można zmienić wywołaniem systemowym set_tid_address(2). Używane przez biblioteki związane z wątkami.
- CLONE_CHILD_SETTID (od Linuksa 2.5.49)
- Przechowuje identyfikator wątku potomnego w położeniu, na które wskazuje child_tid (clone()) lub cl_args.child_tid (clone3()) w pamięci potomka. Operacja przechowania kończy się przed zwróceniem kontroli przez wywołanie clone do przestrzeni użytkownika w procesie potomnym (proszę zauważyć, że operacja przechowania może nie zakończyć się przed powrotem przez wywołanie clone do procesu macierzystego, co ma znaczenie, jeśli używa się również znacznika CLONE_VM).
- CLONE_CLEAR_SIGHAND (od Linuksa 5.5)
- Domyślnie, dyspozycje sygnału w wątku potomnym są takie same jak w wątku macierzystym. Przy podaniu tego znacznika, wszystkie sygnały, które są obsługiwane przez wątek macierzysty (i nie ustawione na SIG_IGN) są resetowane do swych domyślnych dyspozycji (SIG_DFL) w potomku.
- Podanie tego znacznika razem z CLONE_SIGHAND jest bezsensowne i niedozwolone.
- CLONE_DETACHED (historyczny)
- Przez pewien czas (w trakcie serii rozwojowej Linuksa 2.5) istniał znacznik CLONE_DETACHED, który powodował nieotrzymywanie przez rodzica sygnału przy przerwaniu potomka. Ostatecznie, efekt tego znacznika został włączony do znacznika CLONE_THREAD i w momencie wydania Linuksa 2.6.0, znacznik już nie działał. Począwszy od Linuksa 2.6.2, potrzeba podawania tego znacznika razem z CLONE_THREAD zanikła.
- Znacznik jest wciąż zdefiniowany, lecz z reguły jest ignorowany przy wywoływaniu clone(). Pewne wyjątki opisano przy znaczniku CLONE_PIDFD.
- CLONE_FILES (od Linuksa 2.0)
- Jeśli CLONE_FILES będzie ustawione, to proces wywołujący i proces potomny będą współdzielić tablicę deskryptorów plików. Dowolny deskryptor pliku utworzony przez proces wywołujący, jak też przez proces potomny będzie obowiązywać również w drugim procesie. Podobnie, jeśli jeden z procesów zamknie deskryptor pliku lub zmieni stowarzyszone z nim znaczniki (za pomocą operacji F_SETFD fcntl(2)), będzie to obowiązywać również w drugim procesie. Jeśli proces dzielący tablicę deskryptorów pliku wywoła execve(2), to jego tablica deskryptorów pliku zostanie zduplikowana (przestanie być współdzielona).
- Jeśli CLONE_FILES nie zostanie ustawione, to proces potomny odziedziczy kopię wszystkich deskryptorów plików otwartych w procesie macierzystym w chwili wywołania klonowania. Kolejne operacja otwierające lub zamykające deskryptory pliku przeprowadzone później przez proces wywołujący lub przez proces potomny nie będą miały wpływu na drugi proces. Proszę jednak zauważyć, że zduplikowane deskryptory pliku w potomku odnoszą się tych samym opisów otwartego pliku (ODF) jak odpowiadające im deskryptory pliku w procesie wywołującym; będą zatem dzielić przesunięcie pliku i znaczniki statusu pliku (zob. open(2)).
- CLONE_FS (od Linuksa 2.0)
- Jeśli ustawione będzie CLONE_FS, to wywołujący i proces potomny będą współdzielić informacje o systemie plików. Informacje te obejmują katalog główny systemu plików, bieżący katalog roboczy i umaskę. Dowolne z wywołań chroot(2), chdir(2) lub umask(2) wykonane przez proces wywołujący lub proces potomny będzie wpływać również na drugi proces.
- Jeśli CLONE_FS nie zostanie ustawione, to proces potomny będzie pracować na kopii informacji o systemie plików procesu wywołującego z chwili wywołania klonowania. Wywołania chroot(2), chdir(2) lub umask(2) wykonane później przez jeden z procesów nie będą mieć wpływu na drugi proces.
- CLONE_INTO_CGROUP (od Linuksa 5.7)
- Domyślnie, proces potomny jest umieszczany w tej samej grupie kontrolnej (cgroup) w wersji 2, jak rodzic. Znacznik CLONE_INTO_CGROUP pozwala na utworzenie procesu potomnego w innej grupie kontrolnej w wersji 2 (proszę zauważyć, że CLONE_INTO_CGROUP dotyczy tylko grup kontrolnych w wersji 2).
- Aby umieścić proces potomny w innej grupie kontrolnej, wywołujący określa CLONE_INTO_CGROUP w cl_args.flags i przekazuje deskryptor pliku, który odnosi się do grupy kontrolnej w wersji 2 w polu cl_args.cgroup (ten deskryptor pliku można uzyskać otwierając katalog grupy kontrolnej v2, za pomocą znacznika O_RDONLY lub O_PATH). Proszę zauważyć, że obowiązują wszystkie zwykłe ograniczenia na umieszczanie procesu w grupie kontrolnej w wersji 2 (opisane w cgroups(7)).
- Pośród możliwych zastosowań CLONE_INTO_CGROUP są następujące:
- •
- Utworzenie procesu w grupie kontrolnej innej niż grupa kontrolna rodzica, umożliwia menedżerowi usług bezpośrednie tworzenie nowych usług w oddzielnych grupach kontrolnych. Eliminuje się w ten sposób narzut księgowania, który spowodowany byłby tworzeniem procesu potomnego pierwotnie w tej samej grupie kontrolnej co rodzic, a dopiero później przenoszenie go do docelowej grupy kontrolnej. Co więcej, tworzenie procesu potomnego od razu w docelowej grupie kontrolnej jest zdecydowanie tańsze, niż przenoszenie procesu potomnego do docelowej grupy kontrolnej dopiero po utworzeniu.
- •
- Znacznik CLONE_INTO_CGROUP pozwala również na utworzenie zamrożonego procesu potomnego, przez utworzenie go w zamrożonej grupie kontrolnej (zob. cgroups(7) aby dowiedzieć się więcej o kontrolerze freezer).
- •
- W przypadku aplikacji korzystających z wątków (lub choćby implementacji wątków korzystających z grup kontrolnych do limitowania poszczególnych wątków), da się ustanowić ustalony schemat grupy kontrolnej, przed utworzeniem każdego wątku bezpośrednio w jego docelowej grupie kontrolnej.
- CLONE_IO (od Linuksa 2.6.25)
- Jeśli CLONE_IO jest ustawiony, to nowy proces dzieli kontekst wejścia/wyjścia z procesem wywołującym. Jeśli znacznik nie jest ustawiony, to (jak przy fork(2)) nowy proces posiada swój kontekst wejścia/wyjścia.
- Kontekst wejścia/wyjścia (we/wy) jest zakresem we/wy planisty dysku (tj. tym, co planista we/wy używa do planowania we/wy procesu). Jeśli procesy dzielą ten sam kontekst we/wy, to są traktowane jako jedność przez planistę we/wy. Muszą zatem dzielić czas dysku. W przypadku pewnych planistów we/wy, jeśli dwa procesy dzielą kontekst we/wy, to pozwala się im na przeplatanie dostępu do dysku. Jeśli wiele wątków korzysta z we/wy w imieniu jakiegoś procesu (np. aio_read(3)), to aby uzyskać lepszą wydajność wejścia/wyjścia, powinny korzystać z CLONE_IO.
- Jeśli jądra nie skonfigurowano z opcją CONFIG_BLOCK, to ten znacznik nie daje żadnego efektu.
- CLONE_NEWCGROUP (od Linuksa 4.6)
- Tworzy proces w nowej przestrzeni nazw cgroup. Jeśli znacznik nie jest ustawiony (jak w przypadku fork(2)), to proces jest tworzony w tej samej przestrzeni nazw cgroup, co proces wywołujący.
- Więcej informacji o przestrzeniach nazw cgroup znajduje się w podręczniku cgroup_namespaces(7).
- Jedynie proces uprzywilejowany (CAP_SYS_ADMIN) może użyć CLONE_NEWCGROUP.
- CLONE_NEWIPC (od Linuksa 2.6.19)
- Jeśli CLONE_NEWIPC jest ustawiony, to proces jest tworzony w nowej przestrzeni nazw IPC. Jeśli znacznik nie jest ustawiony (jak w przypadku fork(2)), to proces jest tworzony w tej samej przestrzeni nazw IPC, co proces wywołujący.
- Więcej informacji o przestrzeniach nazw IPC znajduje się w podręczniku ipc_namespaces(7).
- Jedynie proces uprzywilejowany (CAP_SYS_ADMIN) może użyć CLONE_NEWIPC. Niniejszy znacznik nie może być podany razem z CLONE_SYSVSEM.
- CLONE_NEWNET (od Linuksa 2.6.24)
- (Implementacja tej flagi została ukończona dopiero w okolicy Linuksa 2.6.29).
- Jest CLONE_NEWNET jest ustawiony, to proces jest tworzony w nowej przestrzeni nazw sieci. Jeśli znacznik nie jest ustawiony (jak w przypadku fork(2)), to proces jest tworzony w tej samej przestrzeni nazw sieci, co proces wywołujący.
- Więcej informacji o przestrzeniach nazw sieci znajduje się w podręczniku network_namespaces(7).
- Jedynie proces uprzywilejowany (CAP_SYS_ADMIN) może użyć CLONE_NEWNET.
- CLONE_NEWNS (od Linuksa 2.4.19)
- Jeśli ustawiono CLONE_NEWNS, sklonowany potomek jest uruchamiany w nowej przestrzeni nazw montowań, inicjowanej jako kopia przestrzeni nazw rodzica. Jeśli nie ustawiono CLONE_NEWNS, to potomek istnieje w tej samej przestrzeni nazw montowań, co rodzic.
- Więcej informacji o przestrzeniach nazw montowań znajduje się w podręcznikach namespaces(7) i mount_namespaces(7).
- Znacznik CLONE_NEWNS może zostać użyty jedynie przez proces uprzywilejowany (CAP_SYS_ADMIN). Zabronione jest podanie w tym samym wywołaniu klonowania zarówno CLONE_NEWNS, jak i CLONE_FS.
- CLONE_NEWPID (od Linuksa 2.6.24)
- Jest CLONE_NEWPID jest ustawiony, to proces jest tworzony w nowej przestrzeni nazw PID. Jeśli znacznik nie jest ustawiony (jak w przypadku fork(2)), to proces jest tworzony w tej samej przestrzeni nazw PID, co proces wywołujący.
- Więcej informacji o przestrzeniach nazw PID znajduje się w podręcznikach namespaces(7) i pid_namespaces(7).
- CLONE_NEWPID może zostać użyty jedynie przez proces uprzywilejowany (CAP_SYS_ADMIN). Nie można podać tego znacznika razem z CLONE_THREAD.
- CLONE_NEWUSER
- (Flaga ta nabrała znaczenia dla clone() w Linuksie 2.6.23, bieżąca semantyka clone() pojawiła się w Linuksie 3.5, a ostatnie elementy dające pełną funkcjonalność przestrzeni nazw użytkownika ukończono w Linuksie 3.8).
- Jest CLONE_NEWUSER jest ustawiony, to proces jest tworzony w nowej przestrzeni nazw użytkownika. Jeśli znacznik nie jest ustawiony (jak w przypadku fork(2)), to proces jest tworzony w tej samej przestrzeni nazw użytkownika, co proces wywołujący.
- Więcej informacji o przestrzeniach nazw użytkownika znajduje się w podręcznikach namespaces(7) i user_namespaces(7).
- Przed Linuksem 3.8, użycie CLONE_NEWUSER wymagało posiadania trzech przywilejów (ang. capabilities) przez wywołującego: CAP_SYS_ADMIN, CAP_SETUID i CAP_SETGID. Od Linuksa 3.8, do utworzenia przestrzeni nazw użytkownika nie są wymagane przywileje.
- Znacznika tego nie można podać razem z CLONE_THREAD lub CLONE_PARENT. Ze względów bezpieczeństwa, CLONE_NEWUSER nie można podać razem z CLONE_FS.
- CLONE_NEWUTS (od Linuksa 2.6.19)
- Jest CLONE_NEWUTS jest ustawiony, to proces jest tworzony w nowej przestrzeni nazw UTS, której identyfikatory są inicjowane przez zduplikowanie identyfikatorów z przestrzeni nazw UTS procesu wywołującego. Jeśli znacznik nie jest ustawiony (jak w przypadku fork(2)), to proces jest tworzony w tej samej przestrzeni nazw UTS, co proces wywołujący.
- Więcej informacji o przestrzeniach nazw UTS znajduje się w podręczniku uts_namespaces(7).
- Jedynie proces uprzywilejowany (CAP_SYS_ADMIN) może użyć CLONE_NEWUTS.
- CLONE_PARENT (od Linuksa 2.3.12)
- Jeśli CLONE_PARENT będzie ustawione, to rodzic nowego procesu potomnego (zwrócony przez getppid(2)) będzie ten sam, co dla procesu wywołującego.
- Jeśli CLONE_PARENT nie zostanie ustawione, to (jak dla fork(2)) rodzicem potomka będzie proces wywołujący.
- Należy zauważyć, że to proces macierzysty, zwracany przez getppid(2), zostanie powiadomiony o zakończeniu pracy przez potomka, więc jeśli CLONE_PARENT będzie ustawione, to zawiadomiony zostanie rodzic procesu wywołującego, a nie sam proces wywołujący.
- Znacznika CLONE_PARENT nie można użyć w wywołaniach klonowania przez globalny proces init (o PID 1 w pierwotnej przestrzeni nazw PID) oraz procesy init w innych przestrzeniach nazw PID. To ograniczenie zapobiega tworzeniu zakorzenionych w wielu miejscach drzew procesów oraz tworzeniu zombie w pierwotnej przestrzeni nazw, których nie da się dorżnąć (unreapable).
- CLONE_PARENT_SETTID (od Linuksa 2.5.49)
- Przechowuje identyfikator wątku potomka w położeniu, na które wskazuje parent_tid (clone()) lub cl_args.parent_tid (clone3()) w pamięci rodzica (w Linuksie 2.5.32-2.5.48 istniał znacznik CLONE_SETTID, który działał w ten sam sposób). Operacja przechowania kończy się, przed zwróceniem kontroli do przestrzeni użytkownika przez wywołanie klonowania.
- CLONE_PID (od Linuksa 2.0 do Linuksa 2.5.15)
- Jeśli CLONE_PID jest ustawiony, to proces potomny jest tworzony z tym samym identyfikatorem jak proces wywołujący. Trudno wymyślić jego przydatne zastosowanie, poza hakowaniem systemu. Od Linuksa 2.3.21, ten znacznik mógł być podany tylko przez systemowy proces rozruchu (PID 0). Znacznik zupełnie zniknął ze źródeł jądra w Linuksie 2.5.16. Następnie jądro po cichu ignorowało ten bit, gdy był podany w masce flags. Znacznie później, ten sam bit użyto do znacznika CLONE_PIDFD.
- CLONE_PIDFD (od Linuksa 5.2)
- Jeśli znacznik jest podany, to deskryptor pliku PID odnoszącego się do procesu potomnego jest alokowany i umieszczany w określonym położeniu pamięci rodzica. Na tym nowym deskryptorze pliku ustawiany jest znacznik zamknij-przy-wykonaniu. Deskryptory pliku PID można wykorzystać w celach opisanych w podręczniku pidfd_open(2).
- •
- Jeśli korzysta się z clone3(), to deskryptor pliku PID jest umieszczany w położeniu, na które wskazuje cl_args.pidfd.
- •
- Jeśli korzysta się z clone(), to deskryptor pliku PID jest umieszczany w położeniu, na które wskazuje parent_tid. Ponieważ argument parent_tid jest używany do zwrócenia deskryptora pliku PID, nie można użyć CLONE_PIDFD razem z CLONE_PARENT_SETTID przy wywoływaniu clone().
- Nie da się obecnie korzystać z tego znacznika razem z CLONE_THREAD. Oznacza to, że proces identyfikowany przez deskryptor pliku PID będzie zawsze liderem grupy wątków.
- Jeśli przestarzały znacznik CLONE_DETACHED poda się razem z CLONE_PIDFD przy wywoływaniu clone(), to zwracany jest błąd. Błąd występuje również jeśli poda się CLONE_DETACHED przy wywoływaniu clone3(). Zwracanie błędu zapewnia, że bit odnoszący się do CLONE_DETACHED może być w przyszłości użyty ponownie do następnych funkcji deskryptora pliku PID.
- CLONE_PTRACE (od Linuksa 2.2)
- Jeśli zostanie podane CLONE_PTRACE, a proces wywołujący będzie śledzony, to śledzenie obejmie również potomka (zobacz ptrace(2)).
- CLONE_SETTLS (od Linuksa 2.5.32)
- Deskryptor TLS (Thread Local Storage — pamięć lokalna wątku) jest ustawiony na tls.
- Interpretacja tls i jego skutek zależy od architektury. Na x86, tls jest interpretowane jako struct user_desc * (zob. set_thread_area(2)). Na x86-64 jest to nowa wartość, jaka ma być ustawiona w bazowym rejestrze %fs (zob. argument ARCH_SET_FS do arch_prctl(2)). Na architekturach ze specjalnym rejestrem TLS, jest to nowa wartość tego rejestru.
- Znacznik ten wymaga szczegółowej wiedzy i zwykle nie powinno się go używać poza bibliotekami implementującymi wątkowanie.
- CLONE_SIGHAND (od Linuksa 2.0)
- Jeśli CLONE_SIGHAND będzie ustawione, to proces wywołujący i procesy potomne będą współdzielić tablicę programów obsługi sygnałów. Jeśli proces wywołujący lub proces potomny wywoła sigaction(2), aby zmienić zachowanie towarzyszące sygnałowi, zachowanie to zostanie zmienione również w drugim procesie. Jednakże, proces wywołujący i proces potomny wciąż będą posiadać osobne maski sygnałów i zestawy sygnałów oczekujących. Zatem jeden z nich może zablokować lub odblokować niektóre sygnały za pomocą sigprocmask(2) nie wpływając na drugi proces.
- Jeśli CLONE_SIGHAND nie zostanie ustawione, to proces potomny odziedziczy kopię programów obsługi sygnałów od procesu wywołującego z chwili wywołania klonowania. Wywołania sigaction(2) przeprowadzone później przez jeden z procesów nie będą mieć wpływu na drugi proces.
- Od Linuksa 2.6.0, maska flags musi również zawierać CLONE_VM, jeśli podano CLONE_SIGHAND.
- CLONE_STOPPED (od Linuksa 2.6.0)
- Jeśli CLONE_STOPPED jest ustawione, to potomek jest początkowo zatrzymany (jakby otrzymał sygnał SIGSTOP) i musi być wznowiony sygnałem SIGCONT.
- Znacznik był oznaczony jako przestarzały od Linuksa 2.6.25 i został zupełnie usunięty w Linuksie 2.6.38. Od tego czasu jądro po cichu ignoruje go, nie wypisując błędu. Od Linuksa 4.6, ten sam bit służy znacznikowi CLONE_NEWCGROUP.
- CLONE_SYSVSEM (od Linuksa 2.5.10)
- Jeśli ustawiony jest CLONE_SYSVSEM to potomek i proces wywołujący dzielą jedną listę wartości dostosowań semaforów Systemu V (semadj; zob. semop(2)). W tym przypadku wspólna lista zbiera wartości semadj ze wszystkich procesów dzielących listę, a dostosowania semaforów są wykonywane tylko gdy ostatni proces dzielący listę zostanie zakończony (lub przestanie dzielić listę, za pomocą unshare(2)). Jeśli znacznik ten nie jest ustawiony, to potomek posiada oddzielną listę semadj, która początkowo jest pusta.
- CLONE_THREAD (od Linuksa 2.4.0)
- Jeśli ustawiony jest CLONE_THREAD to potomek jest umieszczany w tej samej grupie wątków, co proces wywołujący. Aby dalsza część opisu CLONE_THREAD była bardziej przejrzysta, termin „wątek” oznaczać będzie tu procesy w grupie wątków.
- Grupy wątków zostały dodane w Linuksie 2.4 do obsługi wątków POSIX dla zbioru procesów współdzielących ten sam PID. Wewnętrznie, ten wspólny PID jest tzw. identyfikatorem grupy wątków (ang. thread group ID — TGID) dla grupy wątków. Od Linuksa 2.4 wywołania getpid(2) zwracają TGID wywołującego.
- Wątki wewnątrz grupy można rozróżnić za pomocą ich unikatowego (w systemie) identyfikatora wątku (ang. thread ID — TID). TID nowego wątku jest dostępny jako wynik funkcji zwracany do wywołującego, a sam wątek może uzyskać swój TID za pomocą gettid(2).
- Gdy wywołanie clone ma miejsce bez podania CLONE_THREAD, to wynikowy wątek jest umieszczany w nowej grupie wątków, której TGID jest taki sam jak TID wątku. Wątek ten staje się liderem nowej grupy wątków.
- Nowy wątek utworzony przy podaniu CLONE_THREAD ma ten sam proces macierzysty jak proces, który wykonał wywołanie klonowania (tj. jak CLONE_PARENT), tak więc wywołanie getppid(2) zwróci tę samą wartość dla wszystkich wątków w grupie wątków. Gdy wątek z CLONE_THREAD zostanie zakończony, wątek który go utworzył nie otrzymuje sygnału SIGCHLD (ani innego sygnału przerwania); statusu takiego wątku nie da się również pozyskać za pomocą wait(2) (taki wątek jest nazywany oddzielonym — ang. detached).
- Po tym, jak wszystkie wątki w grupie wątków zakończą się, proces macierzysty grupy wątków otrzymuje sygnał SIGCHLD (lub inny sygnał przerwania).
- Jeśli któryś z wątków w grupie wątków wykona execve(2), to wszystkie wątki poza liderem grupy wątków są zakańczane i nowy program wykonywany jest przez lidera grupy wątków.
- Jeśli jeden z wątków w grupie wątków tworzy potomka za pomocą fork(2), to każdy wątek w grupie może czekać (wait(2)) na tego potomka.
- Od Linuksa 2.5.35, maska flags musi zawierać również CLONE_SIGHAND jeśli podano CLONE_THREAD (i proszę zauważyć, że od Linuksa 2.6.0, CLONE_SIGHAND wymaga również zamieszczenia CLONE_VM).
- Akcje i dyspozycje sygnałów mają znaczenie dla całego procesu: jeśli do wątku dostarczony zostanie nieobsłużony sygnał, to dotknie on (przerwie, zatrzyma, wznowi, ustawi ignorowanie) wszystkich członków grupy wątków.
- Każdy wątek ma swoją maskę sygnałów, jak ustawianą przez sigprocmask(2).
- Sygnał może być kierowany do procesu lub kierowany do wątku. Sygnał kierowany do procesu jest przeznaczony do grupy wątku (tj. TGID) i jest dostarczany do dowolnie wybranego wątku spośród tych, które nie blokują sygnału. Sygnał może być kierowany do procesu, ponieważ został wygenerowany przez jądro z powodów innych niż wyjątek sprzętowy, albo ponieważ został wysłany za pomocą kill(2) lub sigqueue(3). Sygnał kierowany do wątku jest przeznaczony (tj. dostarczany) do określonego wątku. Sygnał może być kierowany do wątku, ponieważ został wysłany za pomocą tgkill(2) lub pthread_sigqueue(3), albo ponieważ wątek wykonał instrukcję języka maszynowego, która wyzwoliła wyjątek sprzętowy (np. nieprawidłowy dostęp do pamięci wyzwalający SIGSEGV lub wyjątek zmiennoprzecinkowy wyzwalający SIGFPE).
- Wywołanie do sigpending(2) zwraca sygnał, który jest ustawiany na sumę oczekującego sygnału skierowanego do procesu oraz sygnałów które są oczekujące dla wątku wywołującego.
- Jeśli sygnał kierowany do procesu zostanie dostarczony do grupy wątków, a grupa ta ma zainstalowaną procedurę obsługi sygnału, to jest ona wywoływana w dokładnie jednym, dowolnie wybranym członku grupy wątków, który nie zablokował sygnału. Jeśli na zaakceptowanie tego samego sygnału za pomocą sigwaitinfo(2) czeka wiele wątków w grupie, to jądro wybierze w sposób dowolny jeden z wątków, który otrzyma sygnał.
- CLONE_UNTRACED (od Linuksa 2.5.46)
- Jeśli podano CLONE_UNTRACED, to proces śledzący nie może wymusić CLONE_PTRACE na tym procesie potomnym.
- CLONE_VFORK (od Linuksa 2.2)
- Jeśli CLONE_VFORK będzie ustawione, wykonywanie procesu wywołującego zostanie wstrzymane do chwili, gdy potomek zwolni swoją pamięć wirtualną za pomocą execve(2) lub _exit(2) (jak w przypadku vfork(2)).
- Jeśli CLONE_VFORK nie zostanie ustawione, wtedy zarówno proces wywołujący, jak i potomny podlegają po wywołaniu clone szeregowaniu zadań i aplikacja nie może zakładać, że ich wykonywanie będzie się odbywać w określonej kolejności.
- CLONE_VM (od Linuksa 2.0)
- Jeśli CLONE_VM będzie ustawione, to proces wywołujący i potomny będą działać w tym samym obszarze pamięci. W szczególności, zapisy do pamięci wykonywane przez proces wywołujący lub przez proces potomny będą widoczne dla drugiego z procesów. Ponadto, dowolne mapowania pamięci i usunięcia mapowań wykonane przez jeden z tych procesów za pomocą mmap(2) lub munmap(2) będą dotyczyć również drugiego procesu.
- Jeśli CLONE_VM nie zostanie ustawione, to proces potomny będzie działać w kopii obszaru pamięci procesu wywołującego, wykonanej w chwili wywołania klonowania. Zapisy do pamięci oraz mapowania i usunięcia mapowań wykonane przez jeden z tych procesów nie będą dotyczyć drugiego z nich, tak jak w przypadku fork(2).
- Jeśli podano znacznik CLONE_VM, a nie podano znacznika CLONE_VFORK to wszystkie alternatywne stosy sygnałów ustanowione przez sigaltstack(2) są czyszczone w procesie potomnym.
WARTOŚĆ ZWRACANA¶
Po pomyślnym zakończeniu, w wątku rodzica zwracany jest identyfikator wątku potomka. W wypadku błędu, w kontekście procesu wywołującego zwracane jest -1, a proces potomny nie jest tworzony i ustawiane jest errno wskazując błąd.
BŁĘDY¶
- EACCES (tylko clone3())
- W cl_args.flags podano CLONE_INTO_CGROUP, nie spełniono ograniczeń (opisanych w cgroups(7)), w odniesieniu do cl_args.cgroup, dotyczących umieszczania procesu potomnego w grupie kontrolnej w wersji 2.
- EAGAIN
- Działa już zbyt wiele procesów; zob. fork(2).
- EBUSY (tylko clone3())
- W cl_args.flags podano CLONE_INTO_CGROUP, lecz deskryptor pliku podany w cl_args.cgroup odnosi się do grupy kontrolnej w wersji 2, w której włączony jest kontroler domeny.
- EEXIST (tylko clone3())
- Jeden (lub więcej) PID podany w set_tid już istnieje w odpowiedniej przestrzeni nazw PID.
- EINVAL
- W masce flags podano jednocześnie CLONE_SIGHAND i CLONE_CLEAR_SIGHAND.
- EINVAL
- W masce flags podano CLONE_SIGHAND, lecz nie podano CLONE_VM (od Linuksa 2.6.0).
- EINVAL
- W masce flags podano CLONE_THREAD, lecz nie podano CLONE_SIGHAND (od Linuksa 2.5.35).
- EINVAL
- W masce flags podano CLONE_THREAD, lecz bieżący proces użył uprzednio unshare(2) ze znacznikiem CLONE_NEWPID lub użył setns(2) do ponownego związania się z przestrzenią nazw PID.
- EINVAL
- W masce flags podano jednocześnie CLONE_FS i CLONE_NEWNS.
- EINVAL (od Linuksa 3.9)
- W masce flags podano jednocześnie CLONE_NEWUSER i CLONE_FS.
- EINVAL
- W masce flags podano jednocześnie CLONE_NEWIPC i CLONE_SYSVSEM.
- EINVAL
- W masce flags podano jednocześnie CLONE_FS i CLONE_NEWNS.
- EINVAL
- W masce flags podano jednocześnie CLONE_FS i CLONE_NEWNS.
- EINVAL (od Linuksa 2.6.32)
- Podano CLONE_PARENT, a wywołujący jest procesem init.
- EINVAL
- Zwracane przez funkcję opakowującą clone() z glibc, gdy fn lub stack określono jako NULL.
- EINVAL
- W masce flags podano CLONE_NEWIPC, lecz jądro nie zostało skonfigurowane z opcjami CONFIG_SYSVIPC i CONFIG_IPC_NS.
- EINVAL
- W masce flags podano CLONE_NEWNET, lecz jądro nie zostało skonfigurowane z opcją CONFIG_NET_NS.
- EINVAL
- W masce flags podano CLONE_NEWPID, lecz jądro nie zostało skonfigurowane z opcją CONFIG_PID_NS.
- EINVAL
- W masce flags podano CLONE_NEWUSER, lecz jądro nie zostało skonfigurowane z opcją CONFIG_USER_NS.
- EINVAL
- W masce flags podano CLONE_NEWUTS, lecz jądro nie zostało skonfigurowane z opcją CONFIG_UTS_NS.
- EINVAL
- Stos stack nie jest wyrównany do odpowiedniej granicy na tej architekturze. Przykładowo na aarch64, stack musi być wielokrotnością 16.
- EINVAL (tylko clone3())
- W masce flags podano CLONE_DETACHED.
- EINVAL (tylko clone())
- W masce flags podano CLONE_PIDFD jednocześnie z CLONE_DETACHED.
- EINVAL
- W masce flags podano CLONE_PIDFD jednocześnie z CLONE_THREAD.
- EINVAL (tylko clone())
- W masce flags podano CLONE_PIDFD jednocześnie z CLONE_PARENT_SETTID.
- EINVAL (tylko clone3())
- set_tid_size jest większy od liczby zagnieżdżonych przestrzeni nazw PID.
- EINVAL (tylko clone3())
- Jeden z PID-ów podanych w set_tid był nieprawidłowy.
- EINVAL (tylko clone3())
- W masce flags podano CLONE_THREAD lub CLONE_PARENT, lecz w exit_signal określono sygnał.
- EINVAL (tylko AArch64, Linux 4.6 i wcześniejsze)
- stack nie był wyrównany do granicy 128-bitów.
- ENOMEM
- Za mało pamięci aby przydzielić strukturę zadania dla procesu potomnego, lub aby skopiować niezbędne fragmenty kontekstu procesu wywołującego.
- ENOSPC (od Linuksa 3.7)
- W masce flags podano CLONE_NEWPID, lecz zostałby przekroczony limit zagnieżdżenia przestrzeni nazw PID; zob. pid_namespaces(7).
- ENOSPC (od Linuksa 4.9; wcześniej EUSERS)
- W masce flags podano CLONE_NEWUSER, a wywołanie przekroczyłoby limit liczby zagnieżdżonych przestrzeni nazw użytkownika. Zob. user_namespaces(7).
- Od Linuksa 3.11 do Linuksa 4.8, diagnozowanym w tym przypadku błędem był EUSERS.
- ENOSPC (od Linuksa 4.9)
- Jedna z wartości w masce flags określiła utworzenie nowej przestrzeni nazw użytkownika, lecz uczynienie tego, spowodowałoby przekroczenie limitu zdefiniowanego przez odpowiedni plik w /proc/sys/user. Więcej informacji znajduje się w podręczniku namespaces(7).
- EOPNOTSUPP (tylko clone3())
- W cl_args.flags podano CLONE_INTO_CGROUP, lecz deskryptor pliku podany w cl_args.cgroup odnosi się do grupy kontrolnej w wersji 2, która jest w stanie domain invalid.
- EPERM
- CLONE_NEWCGROUP, CLONE_NEWIPC, CLONE_NEWNET, CLONE_NEWNS, CLONE_NEWPID lub CLONE_NEWUTS były określone przez proces nieuprzywilejowany (proces bez przywileju CAP_SYS_ADMIN).
- EPERM
- CLONE_PID został podany przez proces inny niż proces 0 (błąd ten występował jedynie do Linuksa 2.5.15).
- EPERM
- W masce flags podano CLONE_NEWUSER, lecz ani efektywny identyfikator użytkownika, ani efektywny identyfikator grupy wywołującego nie jest przypisany do przestrzeni nazw rodzica (zob. user_namespaces(7)).
- EPERM (od Linuksa 3.9)
- W masce flags podano CLONE_NEWUSER, a wywołujący znajduje się w środowisku chroot (tj. główny katalog wywołującego nie jest głównym katalogiem przestrzeni nazw montowań, w której rezyduje).
- EPERM (tylko clone3())
- set_tid_size był większy niż zero, a wywołujący nie ma przywileju CAP_SYS_ADMIN w jednej lub większej liczbie przestrzeni nazw użytkownika, które posiadają odpowiednie przestrzenie nazw PID.
- ERESTARTNOINTR (od Linuksa 2.6.17)
- Wywołanie systemowe przerwano sygnałem i zostanie ono przeładowane (widać to tylko przy śledzeniu).
- EUSERS (od Linuksa 3.11 do Linuksa 4.8)
- W masce flags podano CLONE_NEWUSER, a przekroczono by limit liczby zagnieżdżonych przestrzeni nazw użytkownika. Zob. opis błędu ENOSPC powyżej.
WERSJE¶
Funkcja opakowująca clone() z biblioteki glibc czyni pewne zmiany w pamięci, na którą wskazuje stack (zmiany wymagane do prawidłowego ustawienia stosu w stosunku do potomka) przed przywołaniem wywołania systemowego clone(). Dlatego, w przypadkach gdy clone() służy do rekurencyjnego tworzenia potomków, nie należy używać bufora w stosie rodzica, jako stosu potomka.
Na i386, nie należy wywoływać clone() za pomocą vsyscall; powinno się to robić bezpośrednio, poprzez int $0x80.
Różnice biblioteki C/jądra¶
Surowe wywołanie systemowe clone() jest bliższe fork(2) w tym zakresie, że wykonanie procesu potomnego jest kontynuowane od miejsca wywołania. Dlatego argumenty fn i arg funkcji opakowującej clone() są pominięte.
W odróżnienie od opakowania z glibc, surowe wywołanie systemowe clone() jako argument stack akceptuje NULL (a clone3() podobnie pozwala, aby cl_args.stack wynosiło NULL). W takim przypadku, potomek używa duplikatu stosu rodzica (jest to dokonywane za pomocą kopiowania-przy-zapisie, co zapewnia, że potomek otrzyma odrębne kopie stron stosu, gdy jeden z procesów zmodyfikuje stos). W tym przypadku, aby zapewnić poprawne działanie, nie powinno się podawać opcji CLONE_VM (jeśli potomek współdzieli pamięć z rodzicem ze względu na znacznik CLONE_VM, to nie zachodzi duplikacja z kopiowaniem-przy-zapisie, co prawdopodobnie doprowadzi do chaotycznych rezultatów).
Kolejność argumentów również różni się w surowym wywołaniu systemowym, występuje także zmienność argumentów w zależności od architektury, zgodnie z poniższym opisem.
Interfejsem surowego wywołania systemowego na x86-64 i niektórych innych architekturach (w tym sh, tile i alpha) jest:
long clone(unsigned long flags, void *stack, int *parent_tid, int *child_tid, unsigned long tls);
Na x86-32 i wielu innych popularnych architekturach (w tym score, ARM, ARM 64, PA-RISC, arc, Power PC, xtensa i MIPS), kolejność dwóch ostatnich argumentów jest zamieniona:
long clone(unsigned long flags, void *stack, int *parent_tid, unsigned long tls, int *child_tid);
Na architekturach cris i s390, kolejność pierwszych dwóch argumentów jest zamieniona:
long clone(void *stack, unsigned long flags, int *parent_tid, int *child_tid, unsigned long tls);
Na architekturze microblaze występuje dodatkowy argument:
long clone(unsigned long flags, void *stack, int stack_size, /* Rozmiar stosu */ int *parent_tid, int *child_tid, unsigned long tls);
blackfin, m68k i sparc¶
Konwencje przekazywania argumentów na blackfin, m68k i sparc są odmienne od powyższych opisów. Więcej szczegółów w źródle jądra (i glibc).
ia64¶
Na ia64 używany jest inny interfejs:
int __clone2(int (*fn)(void *), void *stack_base, size_t stack_size, int flags, void *arg, ... /* pid_t *parent_tid, struct user_desc *tls, pid_t *child_tid */ );
Powyższy prototyp jest do funkcji opakowującej z glibc; jeśli chodzi o samo wywołanie systemowe, jego prototyp może być opisany w sposób następujący (jest identyczny do prototypu clone() na microblaze):
long clone2(unsigned long flags, void *stack_base, int stack_size, /* Rozmiar stosu */ int *parent_tid, int *child_tid, unsigned long tls);
__clone2() działa w ten sam sposób co clone() z tym wyjątkiem, że stack_base wskazuje na najniższy adres przestrzeni stosu potomka, a stack_size określa rozmiar stosu, na który wskazuje stack_base.
STANDARDY¶
Linux.
HISTORIA¶
- clone3()
- Linux 5.3.
Linux 2.4 i wcześniejsze¶
W serii Linuksa 2.4.x, CLONE_THREAD zwykle nie czyniło rodzicem nowego wątku rodzica procesu wywołującego. Jednak od Linuksa 2.4.7 do Linuksa 2.4.18 znacznik CLONE_THREAD implikował znacznik CLONE_PARENT (jak ma to miejsce od Linuksa 2.6.0).
W Linuksie 2.4 i wcześniejszych clone() nie przyjmowało argumentów parent_tid, tls ani child_tid.
UWAGI¶
Jednym z zastosowań tych wywołań systemowych jest implementacja wątków: zarządzanie wieloma przepływami kontroli w programie, które działają równolegle we współdzielonej przestrzeni adresowej.
Wywołanie systemowe kcmp(2) może posłużyć do sprawdzenia, czy dwa procesy współdzielą różne zasoby, takie jak tablica deskryptorów pliku, operacje cofnięć semaforów Systemu V lub wirtualną przestrzeń adresową.
Uchwyty zarejestrowane przy pomocy pthread_atfork(3) nie są wykonywane przy wywołaniu klonowania.
USTERKI¶
Biblioteka GNU C w wersjach od 2.3.4 do 2.24 włącznie, zawierała funkcję opakowującą getpid(2), która przeprowadzała buforowanie PID-ów. To buforowanie zależało od obsługi opakowania clone() z glibc, jednak ograniczenia w implementacji powodowały, że w niektórych przypadkach bufor ten nie był aktualny. W szczególności, jeśli do potomka dostarczano sygnał od razu po wywołaniu clone(), to wywołanie getpid(2) w procedurze obsługi sygnału mogło zwrócić PID procesu wywołującego („rodzica”), jeśli opakowanie clone nie miało jeszcze szansy zaktualizowania bufora PID w potomku (ten opis ignoruje sytuację, gdy potomka utworzono za pomocą CLONE_THREAD; wówczas getpid(2) powinno zwracać tę samą wartość w potomku jak w procesie wywołującym clone(), ponieważ wywołujący i potomek znajdują się w tej samej grupie wątków. Problem nieaktualnego bufora nie występuje również, gdy argument flags zawiera CLONE_VM). Do dotarcia do prawdziwego PID, trzeba było czasem użyć kodu takiego jak poniższy:
#include <syscall.h> pid_t mypid; mypid = syscall(SYS_getpid);
Ze względu na kłopot z nieaktualnym buforem i inne problemy opisane w getpid(2), funkcjonalność buforowania PID-ów usunięto w glibc 2.25.
PRZYKŁADY¶
Poniższy program demonstruje użycie clone() do utworzenia procesu potomnego, który wykonuje się w oddzielnej przestrzeni nazw UTS. Potomek zmienia nazwę stacji w swojej przestrzeni nazw UTS. Rodzic i potomek wyświetlają następnie systemową nazwę stacji, przez co widać, że różni się ona w przestrzeniach nazw UTS rodzica i potomka. Przykład użycia tego programu znajduje się w podręczniku setns(2).
W programie przykładowym pamięć, która ma być użyta do stosu potomka, przydzielamy za pomocą mmap(2), zamiast malloc(3), z następujących powodów:
- •
- mmap(2) przydziela blok pamięci zaczynający się na granicy strony i będący wielokrotnością rozmiaru strony. Przydaje się to, gdy chcemy ustawić ochronę strony (stronę z ochroną PROT_NONE) na końcu stosu, za pomocą mprotect(2).
- •
- Możemy podać znacznik MAP_STACK, aby zażądać mapowania, które jest odpowiednie do stosu. W tej chwili znacznik ten nie daje efektu na Linuksie, ale istnieje i działa na niektórych innych systemach, dlatego należy go podać ze względu na przenośność.
Kod źródłowy programu¶
#define _GNU_SOURCE #include <err.h> #include <sched.h> #include <signal.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> #include <sys/types.h> #include <sys/utsname.h> #include <sys/wait.h> #include <unistd.h> static int /* Początek funkcje klonowanego potomka */ childFunc(void *arg) {
struct utsname uts;
/* Zmiana nazwy stacji w przestrzeni nazw UTS potomka. */
if (sethostname(arg, strlen(arg)) == -1)
err(EXIT_FAILURE, "sethostname");
/* Pobranie i wyświetlenie nazwy stacji. */
if (uname(&uts) == -1)
err(EXIT_FAILURE, "uname");
printf("uts.nodename u potomka: %s\n", uts.nodename);
/* Utrzymuje przestrzeń nazw otwartą przez chwilę, śpiąc.
Daje to pole do eksperymentów - do przestrzeni nazw
może np. dołączyć inny proces. */
sleep(200);
return 0; /* Potomek ulega zakończeniu */ } #define STACK_SIZE (1024 * 1024) /* Rozmiar stosu klon. potomka */ int main(int argc, char *argv[]) {
char *stack; /* Początek bufora stosu */
char *stackTop; /* Koniec bufora stosu */
pid_t pid;
struct utsname uts;
if (argc < 2) {
fprintf(stderr, "Użycie: %s <child-hostname>\n", argv[0]);
exit(EXIT_SUCCESS);
}
/* Przydzielenie pamięci na stos potomka. */
stack = mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
if (stack == MAP_FAILED)
err(EXIT_FAILURE, "mmap");
stackTop = stack + STACK_SIZE; /* Założenie: stos rośnie w dół */
/* Utworzenie potomka z własną przestrzenią nazw UTS;
potomek rozpoczyna wykonanie w childFunc(). */
pid = clone(childFunc, stackTop, CLONE_NEWUTS | SIGCHLD, argv[1]);
if (pid == -1)
err(EXIT_FAILURE, "clone");
printf("clone() zwróciło %jd\n", (intmax_t) pid);
/* Rodzic przechodzi tutaj */
sleep(1); /* Czas na zmianę nazwy stacji przez potomka */
/* Wyświetla nazwę stacji w przestrzeni nazw UTS rodzica. Będzie
inna, od nazwy stacji w przestrzeni nazw UTS potomka. */
if (uname(&uts) == -1)
err(EXIT_FAILURE, "uname");
printf("uts.nodename u rodzica: %s\n", uts.nodename);
if (waitpid(pid, NULL, 0) == -1) /* Czekanie na potomka */
err(EXIT_FAILURE, "waitpid");
printf("potomek zakończył działanie\n");
exit(EXIT_SUCCESS); }
ZOBACZ TAKŻE¶
fork(2), futex(2), getpid(2), gettid(2), kcmp(2), mmap(2), pidfd_open(2), set_thread_area(2), set_tid_address(2), setns(2), tkill(2), unshare(2), wait(2), capabilities(7), namespaces(7), pthreads(7)
TŁUMACZENIE¶
Autorami polskiego tłumaczenia niniejszej strony podręcznika są: Przemek Borys <pborys@dione.ids.pl>, Andrzej Krzysztofowicz <ankry@green.mf.pg.gda.pl> i Michał Kułach <michal.kulach@gmail.com>
Niniejsze tłumaczenie jest wolną dokumentacją. Bliższe informacje o warunkach licencji można uzyskać zapoznając się z GNU General Public License w wersji 3 lub nowszej. Nie przyjmuje się ŻADNEJ ODPOWIEDZIALNOŚCI.
Błędy w tłumaczeniu strony podręcznika prosimy zgłaszać na adres listy dyskusyjnej manpages-pl-list@lists.sourceforge.net.
2 maja 2024 r. | Linux man-pages 6.8 |