clone(2) System Calls Manual clone(2) NAZWA clone, __clone2, clone3 - tworzy proces potomny BIBLIOTEKA Standardowa biblioteka C (libc, -lc) SKLADNIA /* Prototyp funkcji opakowujacej z glibc */ #define _GNU_SOURCE #include 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 wywolania syst. clone() */ #include /* Definicja struct clone_args */ #include /* Definicja stalych CLONE_* */ #include /* Definicja stalych SYS_* */ #include long syscall(SYS_clone3, struct clone_args *cl_args, size_t size); Uwaga: glibc nie udostepnia opakowania do clone3(), zatem wymagane jest uzycie syscall(2). OPIS Niniejsze wywolania systemowe tworza nowy proces (,,potomny"), w sposob podobny do fork(2). W przeciwienstwie do fork(2), te wywolania systemowe udostepniaja precyzyjniejsza kontrole wobec kontekstu wykonania, ktory jest dzielony miedzy procesem wywolujacym a procesem potomnym. Przykladowo, za pomoca niniejszych wywolan systemowych, wywolujacy moze kontrolowac czy oba procesy dziela wirtualna przestrzen adresowa, tablice deskryptorow pliku i tablice procedur obslugi sygnalow. Te wywolania systemowego umozliwiaja rowniez umieszczenie procesu potomnego w oddzielnych przestrzeniach nazw (zob. namespaces(7)). Prosze zauwazyc, ze w niniejszym podreczniku systemowym ,,proces wywolujacy" odpowiada zwykle ,,procesowy macierzystemu". Prosze jednak sprawdzic opisy CLONE_PARENT i CLONE_THREAD nizej. Niniejsza strona podrecznika opisuje nastepujace interfejsy: o Funkcje opakowujaca clone() z glibc i podlegle wywolanie systemowe, w oparciu o ktore dziala. Glowna czesc podrecznika opisuje funkcje opakowujaca; roznice w stosunku do surowego wywolania systemowego opisano blizej konca. o Nowsze wywolanie systemowe clone3(). W pozostalej tresci niniejszego podrecznika, pojecie ,,wywolanie clone" lub ,,wywolanie klonowania" uzywane jest przy opisywaniu szczegolow odnoszacych sie do wszystkich tych interfejsow. Funkcja opakowujaca clone() Gdy proces potomny tworzony jest za pomoca funkcji opakowujacej clone(), rozpoczyna on wykonanie od wywolania funkcji, na ktora wskazuje argument fn (rozni sie to od fork(2), gdzie proces potomny kontynuuje wykonanie od miejsca wywolania fork(2)). Argument arg jest przekazywany jako argument do funkcji fn. Gdy funkcja fn(arg) powroci, proces potomny konczy dzialanie. Liczba calkowita zwrocona przez fn jest statusem zakonczenia procesu potomnego. Proces potomny moze rowniez zakonczyc sie jawnie wolajac exit(2) lub po otrzymaniu krytycznego sygnalu. Argument stack okresla polozenie stosu uzywanego przez proces potomny. Poniewaz potomek i proces wywolujacy moga wspoldzielic pamiec, nie jest mozliwe, aby proces potomny korzystal z tego samego stosu, co proces wywolujacy. Proces wywolujacy musi wiec przydzielic obszar pamieci przeznaczony na stos potomka i przekazac wskaznik do tego obszaru w clone. Stosy rosna w dol na wszystkich procesorach, na ktorych dziala Linux (z wyjatkiem procesorow HP PA), wiec stack zazwyczaj wskazuje na najwyzszy adres obszaru pamieci zarezerwowanego na stos potomka. Prosze zauwazyc, ze clone() nie zapewnia mechanizmu, w ktorym wywolujacy moglby poinformowac jadro o wielkosci obszaru stosu. Pozostale argumenty clone() opisano ponizej. clone3() Wywolanie systemowe clone3() udostepnia nadzbior funkcjonalnosci wobec starszego interfejsu clone(). Zawiera rowniez wiele usprawnien API m.in: przestrzen na dodatkowe bity znacznikow, przejrzystszy podzial stosowania roznych argumentow oraz mozliwosc okreslenia rozmiaru przestrzeni stosu procesu potomnego. Podobnie jak fork(2), clone3() powraca zarowno 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 nastepujacej postaci: struct clone_args { u64 flags; /* Maska bitowa znacznikow */ u64 pidfd; /* Gdzie przechowywac deskryptor pliku PID (int *) */ u64 child_tid; /* Gdzie przechowywac TID p. potomnego, w pamieci p. potomnego (pid_t *) */ u64 parent_tid; /* Gdzie przechowywac TID, w pamieci procesu macierzystego (pid_t *) */ u64 exit_signal; /* Sygnal do dostarcz. przy zakonczeniu procesu potomnego */ u64 stack; /* Wskaznik do najnizszych bajtow stosu */ u64 stack_size; /* Rozmiar stosu */ u64 tls; /* Polozenie nowego TLS */ u64 set_tid; /* Wskaznik do tablicy pid_t (od Linuksa 5.5) */ u64 set_tid_size; /* Liczba elementow 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 byc zainicjowany z rozmiarem tej struktury (obecnosc argumentu size pozwala na przyszle poszerzanie struktury clone_args). Stos procesu potomnego podaje sie za pomoca cl_args.stack, ktore wskazuje na najnizsze bajty w przestrzeni stosu oraz za pomoca cl_args.stack_size, ktore okresla rozmiar stosu w bajtach. W przypadku gdy poda sie znacznik CLONE_VM (zob. nizej), stos musi byc jawnie zaalokowany i okreslony. W przeciwnym przypadku, te dwa pola mozna podac jako NULL i 0, co powoduje uzywanie przez proces potomny tej samej przestrzeni stosu, z jakiej korzysta proces macierzysty (we wlasnej wirtualnej przestrzeni adresowej procesu potomnego). Pozostale pola argumentu cl_args opisano nizej. Rownowaznosc pomiedzy argumentami clone() i clone3() W odroznieniu do starszego interfejsu clone(), ktorego argumenty sa przekazywane pojedynczo, w nowszym interfejsie clone3() argumenty sa laczone w strukturze clone_args pokazanej wyzej. Struktura pozwala na przekazanie nadzbioru informacji, przekazywanych za pomoca argumentow clone(). Ponizsza tabela ukazuje rownowaznosc pomiedzy argumentami clone() i polami w argumencie clone_args przekazywanym clone3(): clone() clone3() Uwagi pole cl_args flags & ~0xff flags Do wiekszosci znacznikow; szczegoly nizej 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. nizej aby poznac szczegoly --- set_tid_size --- cgroup Zob. CLONE_INTO_CGROUP Sygnal zakonczenia potomka Gdy proces potomny zostanie zakonczony, do rodzica moze byc wyslany sygnal. Sygnal zakonczenia jest okreslany nizszym bajtem flags (clone()) lub cl_args.exit_signal (clone3()). Jesli okreslono inny sygnal niz SIGCHLD, to proces macierzysty musi podac opcje __WALL lub __WCLONE czekajac na potomka w wait(2). Gdy sygnal nie zostanie okreslony (tj. podano zero), to proces macierzysty nie zostanie zawiadomiony o zakonczeniu pracy potomka. Tablica set_tid Domyslnie, jadro wybiera nastepny numer PID dla nowego procesu, w kazdej przestrzeni nazw PID, w ktorej jest on obecny. Przy tworzeniu procesu za pomoca clone3(), tablice set_tid (dostepna od Linuksa 5.5) mozna uzyc do wybrania konkretnych PID-ow w niektorych lub we wszystkich przestrzeniach nazw PID, w ktorych jest on obecny. Jesli PID nowo tworzonego procesu ma byc ustawiony tylko w biezacej przestrzeni nazw PID lub w nowo tworzonej przestrzeni nazw PID (jesli flags zawiera CLONE_NEWPID), to pierwszym elementem w tablicy set_tid musi byc zadany PID, a set_tid_size musi wynosic 1. Jesli PID nowo tworzonego procesu ma miec okreslona wartosc w wielu przestrzeniach nazw PID, to tablica set_tid moze zawierac wiele wpisow. Pierwszy wpis definiuje PID najbardziej zagniezdzonej przestrzeni nazw PID, a kazdy kolejny zawiera PID w odpowiadajacej przestrzeni nazw PID przodka. Liczba przestrzeni nazw PID, w ktorej PID ma byc ustawiony, jest definiowana przez set_tid_size, ktore nie moze byc wieksze od liczby aktualnie zagniezdzonych przestrzeni nazw. Aby utworzyc proces z nastepujacymi PID-ami w hierarchii przestrzeni nazw PID: Poziom zagn. PID Zadany PID Uwagi 0 31496 Najbardziej zewnetrzna p. n. PID 1 42 2 7 Najbardziej wewnetrzna p. n. PID Nalezy ustawic tablice na set_tid[0] = 7; set_tid[1] = 42; set_tid[2] = 31496; set_tid_size = 3; Jesli maja byc okreslone jedynie PID-y w dwoch najbardziej wewnetrznych przestrzeniach nazw PID, nalezy ustawic tablice na: set_tid[0] = 7; set_tid[1] = 42; set_tid_size = 2; PID w przestrzeni nazw PID poza dwoma najbardziej wewnetrznymi przestrzeniami nazw PID jest wybierany w ten sam sposob, 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 uzytkownika, w ktorych PID ma byc zmieniany. Wywolujacy moga wybrac PID wiekszy od 1 jedynie, gdy w danej przestrzeni nazw PID istnieje juz proces init (tj. proces z PID 1). W przeciwnym przypadku, wpis PID dla tej przestrzeni nazw musi wynosic 1. Maska znacznikow clone() i clone3() umozliwiaja uzycie maski bitowej znacznikow, ktore modyfikuja ich zachowanie i pozwalaja wywolujacemu na okreslenie tego, co ma byc dzielone miedzy procesem wywolujacym a potomnym. Maska bitowa -- argument flags clone() lub pole cl_args.flags przekazywane do clone3() -- jest nazywana w pozostalem czesci niniejszego podrecznika maska flags. Maske flags mozna podac jako sume bitowa (OR) zera lub wiecej z ponizszych zmiennych. Poza wskazanymi wyjatkami, znaczniki te sa dostepne (i maja takie samo zastosowanie) w clone() i clone3(). CLONE_CHILD_CLEARTID (od Linuksa 2.5.49) Czysci (zeruje) identyfikator watku potomnego w polozeniu, na ktore wskazuje child_tid (clone()) lub cl_args.child_tid (clone3()) w pamieci potomka, gdy potomek istnieje i wybudza zatrzask (mutex) pod tym adresem. Adres mozna zmienic wywolaniem systemowym set_tid_address(2). Uzywane przez biblioteki zwiazane z watkami. CLONE_CHILD_SETTID (od Linuksa 2.5.49) Przechowuje identyfikator watku potomnego w polozeniu, na ktore wskazuje child_tid (clone()) lub cl_args.child_tid (clone3()) w pamieci potomka. Operacja przechowania konczy sie przed zwroceniem kontroli przez wywolanie clone do przestrzeni uzytkownika w procesie potomnym (prosze zauwazyc, ze operacja przechowania moze nie zakonczyc sie przed powrotem przez wywolanie clone do procesu macierzystego, co ma znaczenie, jesli uzywa sie rowniez znacznika CLONE_VM). CLONE_CLEAR_SIGHAND (od Linuksa 5.5) Domyslnie, dyspozycje sygnalu w watku potomnym sa takie same jak w watku macierzystym. Przy podaniu tego znacznika, wszystkie sygnaly, ktore sa obslugiwane przez watek macierzysty (i nie ustawione na SIG_IGN) sa resetowane do swych domyslnych 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) istnial znacznik CLONE_DETACHED, ktory powodowal nieotrzymywanie przez rodzica sygnalu przy przerwaniu potomka. Ostatecznie, efekt tego znacznika zostal wlaczony do znacznika CLONE_THREAD i w momencie wydania Linuksa 2.6.0, znacznik juz nie dzialal. Poczawszy od Linuksa 2.6.2, potrzeba podawania tego znacznika razem z CLONE_THREAD zanikla. Znacznik jest wciaz zdefiniowany, lecz z reguly jest ignorowany przy wywolywaniu clone(). Pewne wyjatki opisano przy znaczniku CLONE_PIDFD. CLONE_FILES (od Linuksa 2.0) Jesli CLONE_FILES bedzie ustawione, to proces wywolujacy i proces potomny beda wspoldzielic tablice deskryptorow plikow. Dowolny deskryptor pliku utworzony przez proces wywolujacy, jak tez przez proces potomny bedzie obowiazywac rowniez w drugim procesie. Podobnie, jesli jeden z procesow zamknie deskryptor pliku lub zmieni stowarzyszone z nim znaczniki (za pomoca operacji F_SETFD fcntl(2)), bedzie to obowiazywac rowniez w drugim procesie. Jesli proces dzielacy tablice deskryptorow pliku wywola execve(2), to jego tablica deskryptorow pliku zostanie zduplikowana (przestanie byc wspoldzielona). Jesli CLONE_FILES nie zostanie ustawione, to proces potomny odziedziczy kopie wszystkich deskryptorow plikow otwartych w procesie macierzystym w chwili wywolania klonowania. Kolejne operacja otwierajace lub zamykajace deskryptory pliku przeprowadzone pozniej przez proces wywolujacy lub przez proces potomny nie beda mialy wplywu na drugi proces. Prosze jednak zauwazyc, ze zduplikowane deskryptory pliku w potomku odnosza sie tych samym opisow otwartego pliku (ODF) jak odpowiadajace im deskryptory pliku w procesie wywolujacym; beda zatem dzielic przesuniecie pliku i znaczniki statusu pliku (zob. open(2)). CLONE_FS (od Linuksa 2.0) Jesli ustawione bedzie CLONE_FS, to wywolujacy i proces potomny beda wspoldzielic informacje o systemie plikow. Informacje te obejmuja katalog glowny systemu plikow, biezacy katalog roboczy i umaske. Dowolne z wywolan chroot(2), chdir(2) lub umask(2) wykonane przez proces wywolujacy lub proces potomny bedzie wplywac rowniez na drugi proces. Jesli CLONE_FS nie zostanie ustawione, to proces potomny bedzie pracowac na kopii informacji o systemie plikow procesu wywolujacego z chwili wywolania klonowania. Wywolania chroot(2), chdir(2) lub umask(2) wykonane pozniej przez jeden z procesow nie beda miec wplywu na drugi proces. CLONE_INTO_CGROUP (od Linuksa 5.7) Domyslnie, 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 (prosze zauwazyc, ze CLONE_INTO_CGROUP dotyczy tylko grup kontrolnych w wersji 2). Aby umiescic proces potomny w innej grupie kontrolnej, wywolujacy okresla CLONE_INTO_CGROUP w cl_args.flags i przekazuje deskryptor pliku, ktory odnosi sie do grupy kontrolnej w wersji 2 w polu cl_args.cgroup (ten deskryptor pliku mozna uzyskac otwierajac katalog grupy kontrolnej v2, za pomoca znacznika O_RDONLY lub O_PATH). Prosze zauwazyc, ze obowiazuja wszystkie zwykle ograniczenia na umieszczanie procesu w grupie kontrolnej w wersji 2 (opisane w cgroups(7)). Posrod mozliwych zastosowan CLONE_INTO_CGROUP sa nastepujace: o Utworzenie procesu w grupie kontrolnej innej niz grupa kontrolna rodzica, umozliwia menedzerowi uslug bezposrednie tworzenie nowych uslug w oddzielnych grupach kontrolnych. Eliminuje sie w ten sposob narzut ksiegowania, ktory spowodowany bylby tworzeniem procesu potomnego pierwotnie w tej samej grupie kontrolnej co rodzic, a dopiero pozniej przenoszenie go do docelowej grupy kontrolnej. Co wiecej, tworzenie procesu potomnego od razu w docelowej grupie kontrolnej jest zdecydowanie tansze, niz przenoszenie procesu potomnego do docelowej grupy kontrolnej dopiero po utworzeniu. o Znacznik CLONE_INTO_CGROUP pozwala rowniez na utworzenie zamrozonego procesu potomnego, przez utworzenie go w zamrozonej grupie kontrolnej (zob. cgroups(7) aby dowiedziec sie wiecej o kontrolerze freezer). o W przypadku aplikacji korzystajacych z watkow (lub chocby implementacji watkow korzystajacych z grup kontrolnych do limitowania poszczegolnych watkow), da sie ustanowic ustalony schemat grupy kontrolnej, przed utworzeniem kazdego watku bezposrednio w jego docelowej grupie kontrolnej. CLONE_IO (od Linuksa 2.6.25) Jesli CLONE_IO jest ustawiony, to nowy proces dzieli kontekst wejscia/wyjscia z procesem wywolujacym. Jesli znacznik nie jest ustawiony, to (jak przy fork(2)) nowy proces posiada swoj kontekst wejscia/wyjscia. Kontekst wejscia/wyjscia (we/wy) jest zakresem we/wy planisty dysku (tj. tym, co planista we/wy uzywa do planowania we/wy procesu). Jesli procesy dziela ten sam kontekst we/wy, to sa traktowane jako jednosc przez planiste we/wy. Musza zatem dzielic czas dysku. W przypadku pewnych planistow we/wy, jesli dwa procesy dziela kontekst we/wy, to pozwala sie im na przeplatanie dostepu do dysku. Jesli wiele watkow korzysta z we/wy w imieniu jakiegos procesu (np. aio_read(3)), to aby uzyskac lepsza wydajnosc wejscia/wyjscia, powinny korzystac z CLONE_IO. Jesli jadra nie skonfigurowano z opcja CONFIG_BLOCK, to ten znacznik nie daje zadnego efektu. CLONE_NEWCGROUP (od Linuksa 4.6) Tworzy proces w nowej przestrzeni nazw cgroup. Jesli znacznik nie jest ustawiony (jak w przypadku fork(2)), to proces jest tworzony w tej samej przestrzeni nazw cgroup, co proces wywolujacy. Wiecej informacji o przestrzeniach nazw cgroup znajduje sie w podreczniku cgroup_namespaces(7). Jedynie proces uprzywilejowany (CAP_SYS_ADMIN) moze uzyc CLONE_NEWCGROUP. CLONE_NEWIPC (od Linuksa 2.6.19) Jesli CLONE_NEWIPC jest ustawiony, to proces jest tworzony w nowej przestrzeni nazw IPC. Jesli znacznik nie jest ustawiony (jak w przypadku fork(2)), to proces jest tworzony w tej samej przestrzeni nazw IPC, co proces wywolujacy. Wiecej informacji o przestrzeniach nazw IPC znajduje sie w podreczniku ipc_namespaces(7). Jedynie proces uprzywilejowany (CAP_SYS_ADMIN) moze uzyc CLONE_NEWIPC. Niniejszy znacznik nie moze byc podany razem z CLONE_SYSVSEM. CLONE_NEWNET (od Linuksa 2.6.24) (Implementacja tej flagi zostala ukonczona dopiero w okolicy Linuksa 2.6.29). Jest CLONE_NEWNET jest ustawiony, to proces jest tworzony w nowej przestrzeni nazw sieci. Jesli znacznik nie jest ustawiony (jak w przypadku fork(2)), to proces jest tworzony w tej samej przestrzeni nazw sieci, co proces wywolujacy. Wiecej informacji o przestrzeniach nazw sieci znajduje sie w podreczniku network_namespaces(7). Jedynie proces uprzywilejowany (CAP_SYS_ADMIN) moze uzyc CLONE_NEWNET. CLONE_NEWNS (od Linuksa 2.4.19) Jesli ustawiono CLONE_NEWNS, sklonowany potomek jest uruchamiany w nowej przestrzeni nazw montowan, inicjowanej jako kopia przestrzeni nazw rodzica. Jesli nie ustawiono CLONE_NEWNS, to potomek istnieje w tej samej przestrzeni nazw montowan, co rodzic. Wiecej informacji o przestrzeniach nazw montowan znajduje sie w podrecznikach namespaces(7) i mount_namespaces(7). Znacznik CLONE_NEWNS moze zostac uzyty jedynie przez proces uprzywilejowany (CAP_SYS_ADMIN). Zabronione jest podanie w tym samym wywolaniu klonowania zarowno 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. Jesli znacznik nie jest ustawiony (jak w przypadku fork(2)), to proces jest tworzony w tej samej przestrzeni nazw PID, co proces wywolujacy. Wiecej informacji o przestrzeniach nazw PID znajduje sie w podrecznikach namespaces(7) i pid_namespaces(7). CLONE_NEWPID moze zostac uzyty jedynie przez proces uprzywilejowany (CAP_SYS_ADMIN). Nie mozna podac tego znacznika razem z CLONE_THREAD. CLONE_NEWUSER (Flaga ta nabrala znaczenia dla clone() w Linuksie 2.6.23, biezaca semantyka clone() pojawila sie w Linuksie 3.5, a ostatnie elementy dajace pelna funkcjonalnosc przestrzeni nazw uzytkownika ukonczono w Linuksie 3.8). Jest CLONE_NEWUSER jest ustawiony, to proces jest tworzony w nowej przestrzeni nazw uzytkownika. Jesli znacznik nie jest ustawiony (jak w przypadku fork(2)), to proces jest tworzony w tej samej przestrzeni nazw uzytkownika, co proces wywolujacy. Wiecej informacji o przestrzeniach nazw uzytkownika znajduje sie w podrecznikach namespaces(7) i user_namespaces(7). Przed Linuksem 3.8, uzycie CLONE_NEWUSER wymagalo posiadania trzech przywilejow (ang. capabilities) przez wywolujacego: CAP_SYS_ADMIN, CAP_SETUID i CAP_SETGID. Od Linuksa 3.8, do utworzenia przestrzeni nazw uzytkownika nie sa wymagane przywileje. Znacznika tego nie mozna podac razem z CLONE_THREAD lub CLONE_PARENT. Ze wzgledow bezpieczenstwa, CLONE_NEWUSER nie mozna podac 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, ktorej identyfikatory sa inicjowane przez zduplikowanie identyfikatorow z przestrzeni nazw UTS procesu wywolujacego. Jesli znacznik nie jest ustawiony (jak w przypadku fork(2)), to proces jest tworzony w tej samej przestrzeni nazw UTS, co proces wywolujacy. Wiecej informacji o przestrzeniach nazw UTS znajduje sie w podreczniku uts_namespaces(7). Jedynie proces uprzywilejowany (CAP_SYS_ADMIN) moze uzyc CLONE_NEWUTS. CLONE_PARENT (od Linuksa 2.3.12) Jesli CLONE_PARENT bedzie ustawione, to rodzic nowego procesu potomnego (zwrocony przez getppid(2)) bedzie ten sam, co dla procesu wywolujacego. Jesli CLONE_PARENT nie zostanie ustawione, to (jak dla fork(2)) rodzicem potomka bedzie proces wywolujacy. Nalezy zauwazyc, ze to proces macierzysty, zwracany przez getppid(2), zostanie powiadomiony o zakonczeniu pracy przez potomka, wiec jesli CLONE_PARENT bedzie ustawione, to zawiadomiony zostanie rodzic procesu wywolujacego, a nie sam proces wywolujacy. Znacznika CLONE_PARENT nie mozna uzyc w wywolaniach 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 procesow oraz tworzeniu zombie w pierwotnej przestrzeni nazw, ktorych nie da sie dorznac (unreapable). CLONE_PARENT_SETTID (od Linuksa 2.5.49) Przechowuje identyfikator watku potomka w polozeniu, na ktore wskazuje parent_tid (clone()) lub cl_args.parent_tid (clone3()) w pamieci rodzica (w Linuksie 2.5.32-2.5.48 istnial znacznik CLONE_SETTID, ktory dzialal w ten sam sposob). Operacja przechowania konczy sie, przed zwroceniem kontroli do przestrzeni uzytkownika przez wywolanie klonowania. CLONE_PID (od Linuksa 2.0 do Linuksa 2.5.15) Jesli CLONE_PID jest ustawiony, to proces potomny jest tworzony z tym samym identyfikatorem jak proces wywolujacy. Trudno wymyslic jego przydatne zastosowanie, poza hakowaniem systemu. Od Linuksa 2.3.21, ten znacznik mogl byc podany tylko przez systemowy proces rozruchu (PID 0). Znacznik zupelnie zniknal ze zrodel jadra w Linuksie 2.5.16. Nastepnie jadro po cichu ignorowalo ten bit, gdy byl podany w masce flags. Znacznie pozniej, ten sam bit uzyto do znacznika CLONE_PIDFD. CLONE_PIDFD (od Linuksa 5.2) Jesli znacznik jest podany, to deskryptor pliku PID odnoszacego sie do procesu potomnego jest alokowany i umieszczany w okreslonym polozeniu pamieci rodzica. Na tym nowym deskryptorze pliku ustawiany jest znacznik zamknij-przy-wykonaniu. Deskryptory pliku PID mozna wykorzystac w celach opisanych w podreczniku pidfd_open(2). o Jesli korzysta sie z clone3(), to deskryptor pliku PID jest umieszczany w polozeniu, na ktore wskazuje cl_args.pidfd. o Jesli korzysta sie z clone(), to deskryptor pliku PID jest umieszczany w polozeniu, na ktore wskazuje parent_tid. Poniewaz argument parent_tid jest uzywany do zwrocenia deskryptora pliku PID, nie mozna uzyc CLONE_PIDFD razem z CLONE_PARENT_SETTID przy wywolywaniu clone(). Nie da sie obecnie korzystac z tego znacznika razem z CLONE_THREAD. Oznacza to, ze proces identyfikowany przez deskryptor pliku PID bedzie zawsze liderem grupy watkow. Jesli przestarzaly znacznik CLONE_DETACHED poda sie razem z CLONE_PIDFD przy wywolywaniu clone(), to zwracany jest blad. Blad wystepuje rowniez jesli poda sie CLONE_DETACHED przy wywolywaniu clone3(). Zwracanie bledu zapewnia, ze bit odnoszacy sie do CLONE_DETACHED moze byc w przyszlosci uzyty ponownie do nastepnych funkcji deskryptora pliku PID. CLONE_PTRACE (od Linuksa 2.2) Jesli zostanie podane CLONE_PTRACE, a proces wywolujacy bedzie sledzony, to sledzenie obejmie rowniez potomka (zobacz ptrace(2)). CLONE_SETTLS (od Linuksa 2.5.32) Deskryptor TLS (Thread Local Storage -- pamiec lokalna watku) jest ustawiony na tls. Interpretacja tls i jego skutek zalezy od architektury. Na x86, tls jest interpretowane jako struct user_desc * (zob. set_thread_area(2)). Na x86-64 jest to nowa wartosc, jaka ma byc ustawiona w bazowym rejestrze %fs (zob. argument ARCH_SET_FS do arch_prctl(2)). Na architekturach ze specjalnym rejestrem TLS, jest to nowa wartosc tego rejestru. Znacznik ten wymaga szczegolowej wiedzy i zwykle nie powinno sie go uzywac poza bibliotekami implementujacymi watkowanie. CLONE_SIGHAND (od Linuksa 2.0) Jesli CLONE_SIGHAND bedzie ustawione, to proces wywolujacy i procesy potomne beda wspoldzielic tablice programow obslugi sygnalow. Jesli proces wywolujacy lub proces potomny wywola sigaction(2), aby zmienic zachowanie towarzyszace sygnalowi, zachowanie to zostanie zmienione rowniez w drugim procesie. Jednakze, proces wywolujacy i proces potomny wciaz beda posiadac osobne maski sygnalow i zestawy sygnalow oczekujacych. Zatem jeden z nich moze zablokowac lub odblokowac niektore sygnaly za pomoca sigprocmask(2) nie wplywajac na drugi proces. Jesli CLONE_SIGHAND nie zostanie ustawione, to proces potomny odziedziczy kopie programow obslugi sygnalow od procesu wywolujacego z chwili wywolania klonowania. Wywolania sigaction(2) przeprowadzone pozniej przez jeden z procesow nie beda miec wplywu na drugi proces. Od Linuksa 2.6.0, maska flags musi rowniez zawierac CLONE_VM, jesli podano CLONE_SIGHAND. CLONE_STOPPED (od Linuksa 2.6.0) Jesli CLONE_STOPPED jest ustawione, to potomek jest poczatkowo zatrzymany (jakby otrzymal sygnal SIGSTOP) i musi byc wznowiony sygnalem SIGCONT. Znacznik byl oznaczony jako przestarzaly od Linuksa 2.6.25 i zostal zupelnie usuniety w Linuksie 2.6.38. Od tego czasu jadro po cichu ignoruje go, nie wypisujac bledu. Od Linuksa 4.6, ten sam bit sluzy znacznikowi CLONE_NEWCGROUP. CLONE_SYSVSEM (od Linuksa 2.5.10) Jesli ustawiony jest CLONE_SYSVSEM to potomek i proces wywolujacy dziela jedna liste wartosci dostosowan semaforow Systemu V (semadj; zob. semop(2)). W tym przypadku wspolna lista zbiera wartosci semadj ze wszystkich procesow dzielacych liste, a dostosowania semaforow sa wykonywane tylko gdy ostatni proces dzielacy liste zostanie zakonczony (lub przestanie dzielic liste, za pomoca unshare(2)). Jesli znacznik ten nie jest ustawiony, to potomek posiada oddzielna liste semadj, ktora poczatkowo jest pusta. CLONE_THREAD (od Linuksa 2.4.0) Jesli ustawiony jest CLONE_THREAD to potomek jest umieszczany w tej samej grupie watkow, co proces wywolujacy. Aby dalsza czesc opisu CLONE_THREAD byla bardziej przejrzysta, termin ,,watek" oznaczac bedzie tu procesy w grupie watkow. Grupy watkow zostaly dodane w Linuksie 2.4 do obslugi watkow POSIX dla zbioru procesow wspoldzielacych ten sam PID. Wewnetrznie, ten wspolny PID jest tzw. identyfikatorem grupy watkow (ang. thread group ID -- TGID) dla grupy watkow. Od Linuksa 2.4 wywolania getpid(2) zwracaja TGID wywolujacego. Watki wewnatrz grupy mozna rozroznic za pomoca ich unikatowego (w systemie) identyfikatora watku (ang. thread ID -- TID). TID nowego watku jest dostepny jako wynik funkcji zwracany do wywolujacego, a sam watek moze uzyskac swoj TID za pomoca gettid(2). Gdy wywolanie clone ma miejsce bez podania CLONE_THREAD, to wynikowy watek jest umieszczany w nowej grupie watkow, ktorej TGID jest taki sam jak TID watku. Watek ten staje sie liderem nowej grupy watkow. Nowy watek utworzony przy podaniu CLONE_THREAD ma ten sam proces macierzysty jak proces, ktory wykonal wywolanie klonowania (tj. jak CLONE_PARENT), tak wiec wywolanie getppid(2) zwroci te sama wartosc dla wszystkich watkow w grupie watkow. Gdy watek z CLONE_THREAD zostanie zakonczony, watek ktory go utworzyl nie otrzymuje sygnalu SIGCHLD (ani innego sygnalu przerwania); statusu takiego watku nie da sie rowniez pozyskac za pomoca wait(2) (taki watek jest nazywany oddzielonym -- ang. detached). Po tym, jak wszystkie watki w grupie watkow zakoncza sie, proces macierzysty grupy watkow otrzymuje sygnal SIGCHLD (lub inny sygnal przerwania). Jesli ktorys z watkow w grupie watkow wykona execve(2), to wszystkie watki poza liderem grupy watkow sa zakanczane i nowy program wykonywany jest przez lidera grupy watkow. Jesli jeden z watkow w grupie watkow tworzy potomka za pomoca fork(2), to kazdy watek w grupie moze czekac (wait(2)) na tego potomka. Od Linuksa 2.5.35, maska flags musi zawierac rowniez CLONE_SIGHAND jesli podano CLONE_THREAD (i prosze zauwazyc, ze od Linuksa 2.6.0, CLONE_SIGHAND wymaga rowniez zamieszczenia CLONE_VM). Akcje i dyspozycje sygnalow maja znaczenie dla calego procesu: jesli do watku dostarczony zostanie nieobsluzony sygnal, to dotknie on (przerwie, zatrzyma, wznowi, ustawi ignorowanie) wszystkich czlonkow grupy watkow. Kazdy watek ma swoja maske sygnalow, jak ustawiana przez sigprocmask(2). Sygnal moze byc kierowany do procesu lub kierowany do watku. Sygnal kierowany do procesu jest przeznaczony do grupy watku (tj. TGID) i jest dostarczany do dowolnie wybranego watku sposrod tych, ktore nie blokuja sygnalu. Sygnal moze byc kierowany do procesu, poniewaz zostal wygenerowany przez jadro z powodow innych niz wyjatek sprzetowy, albo poniewaz zostal wyslany za pomoca kill(2) lub sigqueue(3). Sygnal kierowany do watku jest przeznaczony (tj. dostarczany) do okreslonego watku. Sygnal moze byc kierowany do watku, poniewaz zostal wyslany za pomoca tgkill(2) lub pthread_sigqueue(3), albo poniewaz watek wykonal instrukcje jezyka maszynowego, ktora wyzwolila wyjatek sprzetowy (np. nieprawidlowy dostep do pamieci wyzwalajacy SIGSEGV lub wyjatek zmiennoprzecinkowy wyzwalajacy SIGFPE). Wywolanie do sigpending(2) zwraca sygnal, ktory jest ustawiany na sume oczekujacego sygnalu skierowanego do procesu oraz sygnalow ktore sa oczekujace dla watku wywolujacego. Jesli sygnal kierowany do procesu zostanie dostarczony do grupy watkow, a grupa ta ma zainstalowana procedure obslugi sygnalu, to jest ona wywolywana w dokladnie jednym, dowolnie wybranym czlonku grupy watkow, ktory nie zablokowal sygnalu. Jesli na zaakceptowanie tego samego sygnalu za pomoca sigwaitinfo(2) czeka wiele watkow w grupie, to jadro wybierze w sposob dowolny jeden z watkow, ktory otrzyma sygnal. CLONE_UNTRACED (od Linuksa 2.5.46) Jesli podano CLONE_UNTRACED, to proces sledzacy nie moze wymusic CLONE_PTRACE na tym procesie potomnym. CLONE_VFORK (od Linuksa 2.2) Jesli CLONE_VFORK bedzie ustawione, wykonywanie procesu wywolujacego zostanie wstrzymane do chwili, gdy potomek zwolni swoja pamiec wirtualna za pomoca execve(2) lub _exit(2) (jak w przypadku vfork(2)). Jesli CLONE_VFORK nie zostanie ustawione, wtedy zarowno proces wywolujacy, jak i potomny podlegaja po wywolaniu clone szeregowaniu zadan i aplikacja nie moze zakladac, ze ich wykonywanie bedzie sie odbywac w okreslonej kolejnosci. CLONE_VM (od Linuksa 2.0) Jesli CLONE_VM bedzie ustawione, to proces wywolujacy i potomny beda dzialac w tym samym obszarze pamieci. W szczegolnosci, zapisy do pamieci wykonywane przez proces wywolujacy lub przez proces potomny beda widoczne dla drugiego z procesow. Ponadto, dowolne mapowania pamieci i usuniecia mapowan wykonane przez jeden z tych procesow za pomoca mmap(2) lub munmap(2) beda dotyczyc rowniez drugiego procesu. Jesli CLONE_VM nie zostanie ustawione, to proces potomny bedzie dzialac w kopii obszaru pamieci procesu wywolujacego, wykonanej w chwili wywolania klonowania. Zapisy do pamieci oraz mapowania i usuniecia mapowan wykonane przez jeden z tych procesow nie beda dotyczyc drugiego z nich, tak jak w przypadku fork(2). Jesli podano znacznik CLONE_VM, a nie podano znacznika CLONE_VFORK to wszystkie alternatywne stosy sygnalow ustanowione przez sigaltstack(2) sa czyszczone w procesie potomnym. WARTOSC ZWRACANA Po pomyslnym zakonczeniu, w watku rodzica zwracany jest identyfikator watku potomka. W wypadku bledu, w kontekscie procesu wywolujacego zwracane jest -1, a proces potomny nie jest tworzony i ustawiane jest errno wskazujac blad. BLEDY EACCES (tylko clone3()) W cl_args.flags podano CLONE_INTO_CGROUP, nie spelniono ograniczen (opisanych w cgroups(7)), w odniesieniu do cl_args.cgroup, dotyczacych umieszczania procesu potomnego w grupie kontrolnej w wersji 2. EAGAIN Dziala juz zbyt wiele procesow; zob. fork(2). EBUSY (tylko clone3()) W cl_args.flags podano CLONE_INTO_CGROUP, lecz deskryptor pliku podany w cl_args.cgroup odnosi sie do grupy kontrolnej w wersji 2, w ktorej wlaczony jest kontroler domeny. EEXIST (tylko clone3()) Jeden (lub wiecej) PID podany w set_tid juz istnieje w odpowiedniej przestrzeni nazw PID. EINVAL W masce flags podano jednoczesnie 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 biezacy proces uzyl uprzednio unshare(2) ze znacznikiem CLONE_NEWPID lub uzyl setns(2) do ponownego zwiazania sie z przestrzenia nazw PID. EINVAL W masce flags podano jednoczesnie CLONE_FS i CLONE_NEWNS. EINVAL (od Linuksa 3.9) W masce flags podano jednoczesnie CLONE_NEWUSER i CLONE_FS. EINVAL W masce flags podano jednoczesnie CLONE_NEWIPC i CLONE_SYSVSEM. EINVAL W masce flags podano jednoczesnie CLONE_FS i CLONE_NEWNS. EINVAL W masce flags podano jednoczesnie CLONE_FS i CLONE_NEWNS. EINVAL (od Linuksa 2.6.32) Podano CLONE_PARENT, a wywolujacy jest procesem init. EINVAL Zwracane przez funkcje opakowujaca clone() z glibc, gdy fn lub stack okreslono jako NULL. EINVAL W masce flags podano CLONE_NEWIPC, lecz jadro nie zostalo skonfigurowane z opcjami CONFIG_SYSVIPC i CONFIG_IPC_NS. EINVAL W masce flags podano CLONE_NEWNET, lecz jadro nie zostalo skonfigurowane z opcja CONFIG_NET_NS. EINVAL W masce flags podano CLONE_NEWPID, lecz jadro nie zostalo skonfigurowane z opcja CONFIG_PID_NS. EINVAL W masce flags podano CLONE_NEWUSER, lecz jadro nie zostalo skonfigurowane z opcja CONFIG_USER_NS. EINVAL W masce flags podano CLONE_NEWUTS, lecz jadro nie zostalo skonfigurowane z opcja CONFIG_UTS_NS. EINVAL Stos stack nie jest wyrownany do odpowiedniej granicy na tej architekturze. Przykladowo na aarch64, stack musi byc wielokrotnoscia 16. EINVAL (tylko clone3()) W masce flags podano CLONE_DETACHED. EINVAL (tylko clone()) W masce flags podano CLONE_PIDFD jednoczesnie z CLONE_DETACHED. EINVAL W masce flags podano CLONE_PIDFD jednoczesnie z CLONE_THREAD. EINVAL (tylko clone()) W masce flags podano CLONE_PIDFD jednoczesnie z CLONE_PARENT_SETTID. EINVAL (tylko clone3()) set_tid_size jest wiekszy od liczby zagniezdzonych przestrzeni nazw PID. EINVAL (tylko clone3()) Jeden z PID-ow podanych w set_tid byl nieprawidlowy. EINVAL (tylko clone3()) W masce flags podano CLONE_THREAD lub CLONE_PARENT, lecz w exit_signal okreslono sygnal. EINVAL (tylko AArch64, Linux 4.6 i wczesniejsze) stack nie byl wyrownany do granicy 128-bitow. ENOMEM Za malo pamieci aby przydzielic strukture zadania dla procesu potomnego, lub aby skopiowac niezbedne fragmenty kontekstu procesu wywolujacego. ENOSPC (od Linuksa 3.7) W masce flags podano CLONE_NEWPID, lecz zostalby przekroczony limit zagniezdzenia przestrzeni nazw PID; zob. pid_namespaces(7). ENOSPC (od Linuksa 4.9; wczesniej EUSERS) W masce flags podano CLONE_NEWUSER, a wywolanie przekroczyloby limit liczby zagniezdzonych przestrzeni nazw uzytkownika. Zob. user_namespaces(7). Od Linuksa 3.11 do Linuksa 4.8, diagnozowanym w tym przypadku bledem byl EUSERS. ENOSPC (od Linuksa 4.9) Jedna z wartosci w masce flags okreslila utworzenie nowej przestrzeni nazw uzytkownika, lecz uczynienie tego, spowodowaloby przekroczenie limitu zdefiniowanego przez odpowiedni plik w /proc/sys/user. Wiecej informacji znajduje sie w podreczniku namespaces(7). EOPNOTSUPP (tylko clone3()) W cl_args.flags podano CLONE_INTO_CGROUP, lecz deskryptor pliku podany w cl_args.cgroup odnosi sie do grupy kontrolnej w wersji 2, ktora jest w stanie domain invalid. EPERM CLONE_NEWCGROUP, CLONE_NEWIPC, CLONE_NEWNET, CLONE_NEWNS, CLONE_NEWPID lub CLONE_NEWUTS byly okreslone przez proces nieuprzywilejowany (proces bez przywileju CAP_SYS_ADMIN). EPERM CLONE_PID zostal podany przez proces inny niz proces 0 (blad ten wystepowal jedynie do Linuksa 2.5.15). EPERM W masce flags podano CLONE_NEWUSER, lecz ani efektywny identyfikator uzytkownika, ani efektywny identyfikator grupy wywolujacego nie jest przypisany do przestrzeni nazw rodzica (zob. user_namespaces(7)). EPERM (od Linuksa 3.9) W masce flags podano CLONE_NEWUSER, a wywolujacy znajduje sie w srodowisku chroot (tj. glowny katalog wywolujacego nie jest glownym katalogiem przestrzeni nazw montowan, w ktorej rezyduje). EPERM (tylko clone3()) set_tid_size byl wiekszy niz zero, a wywolujacy nie ma przywileju CAP_SYS_ADMIN w jednej lub wiekszej liczbie przestrzeni nazw uzytkownika, ktore posiadaja odpowiednie przestrzenie nazw PID. ERESTARTNOINTR (od Linuksa 2.6.17) Wywolanie systemowe przerwano sygnalem i zostanie ono przeladowane (widac to tylko przy sledzeniu). EUSERS (od Linuksa 3.11 do Linuksa 4.8) W masce flags podano CLONE_NEWUSER, a przekroczono by limit liczby zagniezdzonych przestrzeni nazw uzytkownika. Zob. opis bledu ENOSPC powyzej. WERSJE Funkcja opakowujaca clone() z biblioteki glibc czyni pewne zmiany w pamieci, na ktora wskazuje stack (zmiany wymagane do prawidlowego ustawienia stosu w stosunku do potomka) przed przywolaniem wywolania systemowego clone(). Dlatego, w przypadkach gdy clone() sluzy do rekurencyjnego tworzenia potomkow, nie nalezy uzywac bufora w stosie rodzica, jako stosu potomka. Na i386, nie nalezy wywolywac clone() za pomoca vsyscall; powinno sie to robic bezposrednio, poprzez int $0x80. Roznice biblioteki C/jadra Surowe wywolanie systemowe clone() jest blizsze fork(2) w tym zakresie, ze wykonanie procesu potomnego jest kontynuowane od miejsca wywolania. Dlatego argumenty fn i arg funkcji opakowujacej clone() sa pominiete. W odroznienie od opakowania z glibc, surowe wywolanie systemowe clone() jako argument stack akceptuje NULL (a clone3() podobnie pozwala, aby cl_args.stack wynosilo NULL). W takim przypadku, potomek uzywa duplikatu stosu rodzica (jest to dokonywane za pomoca kopiowania-przy-zapisie, co zapewnia, ze potomek otrzyma odrebne kopie stron stosu, gdy jeden z procesow zmodyfikuje stos). W tym przypadku, aby zapewnic poprawne dzialanie, nie powinno sie podawac opcji CLONE_VM (jesli potomek wspoldzieli pamiec z rodzicem ze wzgledu na znacznik CLONE_VM, to nie zachodzi duplikacja z kopiowaniem-przy-zapisie, co prawdopodobnie doprowadzi do chaotycznych rezultatow). Kolejnosc argumentow rowniez rozni sie w surowym wywolaniu systemowym, wystepuje takze zmiennosc argumentow w zaleznosci od architektury, zgodnie z ponizszym opisem. Interfejsem surowego wywolania systemowego na x86-64 i niektorych 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), kolejnosc dwoch ostatnich argumentow jest zamieniona: long clone(unsigned long flags, void *stack, int *parent_tid, unsigned long tls, int *child_tid); Na architekturach cris i s390, kolejnosc pierwszych dwoch argumentow jest zamieniona: long clone(void *stack, unsigned long flags, int *parent_tid, int *child_tid, unsigned long tls); Na architekturze microblaze wystepuje 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 argumentow na blackfin, m68k i sparc sa odmienne od powyzszych opisow. Wiecej szczegolow w zrodle jadra (i glibc). ia64 Na ia64 uzywany 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 */ ); Powyzszy prototyp jest do funkcji opakowujacej z glibc; jesli chodzi o samo wywolanie systemowe, jego prototyp moze byc opisany w sposob nastepujacy (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() dziala w ten sam sposob co clone() z tym wyjatkiem, ze stack_base wskazuje na najnizszy adres przestrzeni stosu potomka, a stack_size okresla rozmiar stosu, na ktory wskazuje stack_base. STANDARDY Linux. HISTORIA clone3() Linux 5.3. Linux 2.4 i wczesniejsze W serii Linuksa 2.4.x, CLONE_THREAD zwykle nie czynilo rodzicem nowego watku rodzica procesu wywolujacego. Jednak od Linuksa 2.4.7 do Linuksa 2.4.18 znacznik CLONE_THREAD implikowal znacznik CLONE_PARENT (jak ma to miejsce od Linuksa 2.6.0). W Linuksie 2.4 i wczesniejszych clone() nie przyjmowalo argumentow parent_tid, tls ani child_tid. UWAGI Jednym z zastosowan tych wywolan systemowych jest implementacja watkow: zarzadzanie wieloma przeplywami kontroli w programie, ktore dzialaja rownolegle we wspoldzielonej przestrzeni adresowej. Wywolanie systemowe kcmp(2) moze posluzyc do sprawdzenia, czy dwa procesy wspoldziela rozne zasoby, takie jak tablica deskryptorow pliku, operacje cofniec semaforow Systemu V lub wirtualna przestrzen adresowa. Uchwyty zarejestrowane przy pomocy pthread_atfork(3) nie sa wykonywane przy wywolaniu klonowania. USTERKI Biblioteka GNU C w wersjach od 2.3.4 do 2.24 wlacznie, zawierala funkcje opakowujaca getpid(2), ktora przeprowadzala buforowanie PID-ow. To buforowanie zalezalo od obslugi opakowania clone() z glibc, jednak ograniczenia w implementacji powodowaly, ze w niektorych przypadkach bufor ten nie byl aktualny. W szczegolnosci, jesli do potomka dostarczano sygnal od razu po wywolaniu clone(), to wywolanie getpid(2) w procedurze obslugi sygnalu moglo zwrocic PID procesu wywolujacego (,,rodzica"), jesli opakowanie clone nie mialo jeszcze szansy zaktualizowania bufora PID w potomku (ten opis ignoruje sytuacje, gdy potomka utworzono za pomoca CLONE_THREAD; wowczas getpid(2) powinno zwracac te sama wartosc w potomku jak w procesie wywolujacym clone(), poniewaz wywolujacy i potomek znajduja sie w tej samej grupie watkow. Problem nieaktualnego bufora nie wystepuje rowniez, gdy argument flags zawiera CLONE_VM). Do dotarcia do prawdziwego PID, trzeba bylo czasem uzyc kodu takiego jak ponizszy: #include pid_t mypid; mypid = syscall(SYS_getpid); Ze wzgledu na klopot z nieaktualnym buforem i inne problemy opisane w getpid(2), funkcjonalnosc buforowania PID-ow usunieto w glibc 2.25. PRZYKLADY Ponizszy program demonstruje uzycie clone() do utworzenia procesu potomnego, ktory wykonuje sie w oddzielnej przestrzeni nazw UTS. Potomek zmienia nazwe stacji w swojej przestrzeni nazw UTS. Rodzic i potomek wyswietlaja nastepnie systemowa nazwe stacji, przez co widac, ze rozni sie ona w przestrzeniach nazw UTS rodzica i potomka. Przyklad uzycia tego programu znajduje sie w podreczniku setns(2). W programie przykladowym pamiec, ktora ma byc uzyta do stosu potomka, przydzielamy za pomoca mmap(2), zamiast malloc(3), z nastepujacych powodow: o mmap(2) przydziela blok pamieci zaczynajacy sie na granicy strony i bedacy wielokrotnoscia rozmiaru strony. Przydaje sie to, gdy chcemy ustawic ochrone strony (strone z ochrona PROT_NONE) na koncu stosu, za pomoca mprotect(2). o Mozemy podac znacznik MAP_STACK, aby zazadac mapowania, ktore jest odpowiednie do stosu. W tej chwili znacznik ten nie daje efektu na Linuksie, ale istnieje i dziala na niektorych innych systemach, dlatego nalezy go podac ze wzgledu na przenosnosc. Kod zrodlowy programu #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include static int /* Start function for cloned child */ childFunc(void *arg) { struct utsname uts; /* Change hostname in UTS namespace of child. */ if (sethostname(arg, strlen(arg)) == -1) err(EXIT_FAILURE, "sethostname"); /* Retrieve and display hostname. */ if (uname(&uts) == -1) err(EXIT_FAILURE, "uname"); printf("uts.nodename in child: %s\n", uts.nodename); /* Keep the namespace open for a while, by sleeping. This allows some experimentation--for example, another process might join the namespace. */ sleep(200); return 0; /* Child terminates now */ } #define STACK_SIZE (1024 * 1024) /* Stack size for cloned child */ int main(int argc, char *argv[]) { char *stack; /* Start of stack buffer */ char *stackTop; /* End of stack buffer */ pid_t pid; struct utsname uts; if (argc < 2) { fprintf(stderr, "Usage: %s \n", argv[0]); exit(EXIT_SUCCESS); } /* Allocate memory to be used for the stack of the child. */ 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; /* Assume stack grows downward */ /* Create child that has its own UTS namespace; child commences execution in childFunc(). */ pid = clone(childFunc, stackTop, CLONE_NEWUTS | SIGCHLD, argv[1]); if (pid == -1) err(EXIT_FAILURE, "clone"); printf("clone() returned %jd\n", (intmax_t) pid); /* Parent falls through to here */ sleep(1); /* Give child time to change its hostname */ /* Display hostname in parent's UTS namespace. This will be different from hostname in child's UTS namespace. */ if (uname(&uts) == -1) err(EXIT_FAILURE, "uname"); printf("uts.nodename in parent: %s\n", uts.nodename); if (waitpid(pid, NULL, 0) == -1) /* Wait for child */ err(EXIT_FAILURE, "waitpid"); printf("child has terminated\n"); exit(EXIT_SUCCESS); } ZOBACZ TAKZE 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) TLUMACZENIE Autorami polskiego tlumaczenia niniejszej strony podrecznika sa: Przemek Borys , Andrzej Krzysztofowicz i Michal Kulach Niniejsze tlumaczenie jest wolna dokumentacja. Blizsze informacje o warunkach licencji mozna uzyskac zapoznajac sie z GNU General Public License w wersji 3 lub nowszej. Nie przyjmuje sie ZADNEJ ODPOWIEDZIALNOSCI. Bledy w tlumaczeniu strony podrecznika prosimy zglaszac na adres listy dyskusyjnej . Linux man-pages 6.9.1 15 czerwca 2024 r. clone(2)