Provided by: manpages-pl-dev_4.26.0-1_all 

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.
Linux man-pages 6.9.1 15 czerwca 2024 r. clone(2)