clone, __clone2, clone3 - tworzy proces potomny
Standardowa biblioteka C (libc, -lc)
/* 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).
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.
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.
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 |
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.
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.
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.
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.
- 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.
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);
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).
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.
- clone3()
- Linux 5.3.
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.
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.
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.
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ść.
#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);
}
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 niniejszej strony podręcznika: 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.