1 PROGRAMOWANIE SYSTEMOWE [1/3]LINUX PROGRAMOWANIE SYSTEMOWE [1/3]
2 Plan wykładu Procesy Sygnały
3 Procesy Procesy SygnałyK. Haviland, D. Gray, B. Salama, UNIX: Programowanie systemowe
4 Co to jest proces? 1/2 Proces to egzemplarz wykonywanego programu. Należy odróżnić proces od wątku - każdy proces posiada własną przestrzeń adresową, natomiast wątki posiadają wspólną sekcję danych. Na kontekst procesu składają się m.in.: identyfikator procesu, identyfikator grupy procesów, identyfikator użytkownika, identyfikator grupy użytkowników środowisko katalog roboczy kod programu rejestry stos sterta deskryptory plików akcje sygnałów biblioteki współdzielone narzędzia komunikacji międzyprocesowej
5 Co to jest proces? 2/2
6 Hierarchia procesów 1/2 Nowe procesy zwykle tworzone za pomocą fork lub vfork Dobrze zdefiniowana hierarchia procesów: jeden rodzic i zero lub więcej procesów potomnych. Proces init jest korzeniem tego drzewa. Podczas życia procesu za pomocą wywołania funkcji systemowej exec mogą być tworzone inne procesy inne programy Procesy kończą się zwykle po wywołaniu exit
7 Hierarchia procesów 2/2 Ładowania systemu
8 Kontekst procesu Przestrzeń adresowakod, dane, stos, pamięć współdzielona, ... Informacje kontrolne (u-obszar, tablica procesów proc) u-obszar, tablica procesów, odwzorowania stos jądra odwzorowania translacji adresów Dane uwierzytelniające ID użytkownika i grupy (rzeczywiste i efektywne) Zmienne środowiskowe zmienna=wartość zwykle przechowywane na spodzie stosu
9 Informacje kontrolne procesuU-obszar Część przestrzeni użytkownika (powyżej stosu). Zwykle odwzorowywany w ustalony adres. Zawiera informacje niezbędne podczas wykonywania procesu. Może być wymieniany (ang. swapped) Tablica procesów Proc Zawiera informacje konieczne m.in. wtedy, gdy proces nie wykonuje się. Nie może być wymieniany (ang. swapped). Tradycyjna tabela o ustalonym rozmiarze.
10 U-obszar i tablica ProcKontekst procesu Wskaźnik do pozycji w tablicy Proc Rzeczywisty/efektywny UID argumenty, zwracane wartości lub błędy bieżącego wywołania funkcji systemowej. Tablica reakcji na sygnały Tabela deskryptorów plików Bieżący katalog i bieżący korzeń. Tablica Proc ID procesu i grupy Wskaźnik na U-obszar Stan procesu Wskaźniki na kolejki – planowania, uśpione, etc. Priorytet Informacja zarządzania pamięcią Flagi (znaczniki) Tablica odebranych i nieobsłużonych sygnałów Pozycja w tablicy procesów ma następujące pola: Pole stanu, które identyfikuje stan procesu. Pole z informacją, która pozwala jądru zlokalizować proces i jego u-obszar w pamięci głównej lub pomocniczej. Jądro używa tej informacji, żeby przełączyć kontekst do procesu, gdy przechodzi on ze stanu "gotowy do wykonania w pamięci" do stanu "wykonywany w trybie jądra" lub ze stanu "wywłaszczony" do stanu "wykonywany w trybie użytkownika". Ponadto używa tej informacji podczas wymiany (lub stronicowania) procesów między pamięcią główną i pomocniczą (między dwoma stanami "w pamięci" i dwoma stanami "poza pamięcią"). Pozycja w tablicy procesów zawiera także pole z rozmiarem procesu, aby jądro wiedziało, ile pamięci trzeba przydzielić na proces. Kilka identyfikatorów użytkownika (ID użytkownika lub UID) określa różne przywileje procesu. Pola UID np. określają zbiór procesów, które mogą wysyłać do siebie sygnały. Identyfikatory procesu (ID procesu lub PID) specyfikują wzajemne relacje między procesami. Pola te są ustawiane, gdy proces wchodzi do stanu "utworzony" w wywołaniu funkcji fork. Deskryptor zdarzenia, gdy proces jest w stanie uśpionym. Parametry szeregowania umożliwiają jądru określenie porządku, w jakim procesy przechodzą do stanów "wykonywany w trybie jądra" i "wykonywany w trybie użytkownika". Pole sygnałów wylicza sygnały wysłane do procesu i jeszcze nie obsłużone. U-obszar zawiera pola, które dokładniej charakteryzują stany procesu: Wskaźnik do tablicy procesów identyfikuje pozycję odpowiadającą temu u-obszarowi. Rzeczywisty (ang. real) i obowiązujący (ang. effective) identyfikator użytkownika określają różne przywilije przysługujące procesowi, takie jak prawa dostępu do plików. Pola liczników rejestrują czas spędzony przez proces (i jego potomków) w trybie użytkownika i jądra. Tablica wskazuje, jak proces chce reagować na sygnay. Pole terminala sterującego identyfikuje terminal, z którego proces powołano do życia (ang. login terminal), jeśli taki istnieje. Pole błędu rejestruje błędy napotkane podczas wykonania funkcji systemowej. Pole wartości przekazywanej zawiera wynik wykonania funkcji systemowej. Parametry wejścia-wyjścia opisują ilość danych do transmisji, adres źródłowej (docelowej) tablicy danych w przestrzeni adresowej użytkownika, adresy bajtowe operacji wejścia-wyjścia itd. Bieżący katalog i bieżący korzeń opisują środowisko systemu plików procesu. Tablica deskryptorów plików użytkownika rejestruje pliki otwarte przez proces. Pola graniczne określają rozmiar procesu i pliku, jaki proces może zapisać. Pola uprawnień modyfikują ustawienie praw dostępu do plików tworzonych przez proces.
11 Dane uwierzytelniające użytkownikaKażdy użytkownik posiada przypisany unikalny ID użytkownika (uid) oraz ID grupy (gid). Superużytkownik (root) ma uid == 0 i gid == 0 Każdy proces posiada zarówno rzeczywisty jak i efektywny ID. Efektywny ID => tworzenie plików i dostęp, Rzeczywisty ID => rzeczywisty właściciel procesu. Stosowany podczas wysyłania sygnałów. Efektywny lub rzeczywisty ID nadawcy musi być równy rzeczywistemu ID odbiorcy.
12 Uproszczony graf stanu procesów
13 Operacje blokowania W przypadku, gdy zasoby są niedostępne (być może zablokowane), proces ustawia flagę i wywołuje sleep() sleep umieszcza proces w kolejce zablokowanych procesów, ustawia stan asleep i wywołuje swtch() Gdy zasób jest zwalniany, wywoływana jest funkcja wakeup() Wszystkie uśpione procesy są budzone, a ich stan ustawiany na gotowy (procesy umieszczane są w kolejce procesów gotowych). W stanie wykonywania proces musi sprawdzić, czy zasób jest dostępny.
14 Szeregowanie procesówUnix może jednocześnie (współbieżnie) wykonywać wiele procesów Algorytm planowania round-robin z wywłaszczeniami Każdy proces posiada przydzielony, ustalony kwant czasu Procesy w trybie jądra mają przypisane priorytet jądra (priorytet uśpiony), który jest wyższy niż priorytety użytkownika. Priorytety: jądra 0-49, użytkownika
15 Tworzenie procesu fork exec Tworzy nowy procesKopiuje pamięć wirtualna rodzica Kopiuje katalog roboczy i deskryptory otwartych plików Zwraca procesowi rodzicielskiemu wartość PID potomka Zwraca procesowi potomnemu wartość 0 exec Nadpisuje istniejący proces nowym kodem z podanego programu
16 Tworzenie procesu Wywołanie systemowe fork klonuje aktualny procesWywołanie systemowe exec zastępuje bieżący proces Zwykle fork jest wywoływany przed exec A A A’ A B
17 Działanie fork Kod Dane Dane Stos Stos Obszar użytkownika
18 Efektywna implementacja forkKopiuj po zapisie Kod Kod Dane Dane Dane Stos Stos Stos Stos Obszar użytkownika Obszar użytkownika Obszar użytkownika Obszar użytkownika Przed zapisem do obszaru Dane Po zapisie do obszaru Dane
19 Działanie exec Kod Kod Przed Po Dane Dane Stos Obszar użytkownika
20 Kończenie procesu Wywoływana jest funkcja exitZamyka otwarte pliki Zwalnia inne zasoby Zapisuje statystyki użytkowania zasobów i status powrotu w tablicy procesów Budzi rodzica (jeśli czeka) Wywołuje swtch Proces jest w stanie zombie Rodzic gromadzi, via funkcje wait, status zakończenia procesu i statystyki użytkowania zasobów
21 Utrzymywana informacja o procesieKatalog roboczy Tablica deskryptorów plików ID procesu Liczba używana do identyfikacji procesu ID grupy procesów Liczba używana do identyfikacji zbioru procesów ID procesu rodzica ID procesu, który utworzył proces
22 Utrzymywana informacja o procesieEfektywny ID użytkownika i grupy Użytkownik i grupa, którzy mają prawo uruchomienia procesu Rzeczywisty ID użytkownika i grupy Użytkownik i grupa, którzy wywołali proces umask Domyślne prawa dostępu do nowo tworzonego pliku Zmienne środowiskowe
23 Środowisko procesu Zbiór par nazwa-wartość związanych z procesemKlucze i wartości są napisami Przekazywane procesom potomnym Nie mogą być zwracane z powrotem Typowe przykłady: PATH: gdzie szukać programów TERM: typ terminala
24 Identyfikacja procesów#include
25 Identyfikacja właścicieli procesów#include
26 Czas działania procesu#include
27 Tworzenie nowego procesu: fork 1/2#include
28 Tworzenie nowego procesu: fork 2/2test-fork.c #include
29 Tworzenie nowego procesu: vfork#include
30 Proces potomny a proces macierzysty 1/2Proces potomny dziedziczy po procesie macierzystym: rzeczywiste i efektywne identyfikatory użytkownika i grupy, deskryptory plików (i pozycje w plikach) identyfikatory dodatkowych grup, identyfikator sesji, terminal sterujący, sygnalizator ustanowienia identyfikatora użytkownika oraz sygnalizator ustanowienia identyfikatora grupy, bieżący katalog roboczy, katalog główny, maskę tworzenia plików, maskę sygnałów oraz dyspozycje obsługi sygnałów, sygnalizator zamykania przy wywołaniu funkcji exec (close-on-exec) dla wszystkich otwartych deskryptorów plików, środowisko, przyłączone segmenty pamięci wspólnej, ograniczenia zasobów systemowych.
31 Proces potomny a proces macierzysty 2/2Różnice między procesem potomnym i macierzystym: wartość powrotu z funkcji fork, różne identyfikatory procesów, inne identyfikatory procesów macierzystych - w procesie potomnym jest to identyfikator procesu macierzystego; w procesie macierzystym identyfikator procesu macierzystego nie zmienia się, w procesie potomnym wartości tms_utime, tms_cutime i tms_ustime są równe 0, potomek nie dziedziczy blokad plików, ustalonych w procesie macierzystym, w procesie potomnym jest zerowany zbiór zaległych sygnałów.
32 Uruchamianie programów: exec... 1/3#include
33 Uruchamianie programów: exec... 2/3execve nie powraca po pomyślnym wywołaniu, segmenty text, data, bss oraz segment stosu procesu wywołującego zostają nadpisane przez odpowiedniki ładowanego programu, wywoływany program dziedziczy PID procesu wywołującego i wszelkie deskryptory otwartych plików, które nie są ustawione jako close-on-exec, sygnały oczekujące na proces wywołujący zostają wyczyszczone, sygnałom, które były przechwytywane przez proces wywołujący, zostaje przypisana ich domyślna obsługa, Jeżeli plik programu wskazywany przez filename ma ustawiony bit set-uid, to efektywny identyfikator użytkownika procesu wywołującego jest ustawiany na właściciela pliku programu res=fcntl( fd, F_GETFD,0) fcntl( fd, F_SETFD,0) fcntl( fd, F_SETFD,1)
34 Uruchamianie programów: exec... 3/3#include
35 Kończenie procesu: exit#include
36 Kończenie procesu: _exit#include
37 Kończenie procesu: atexit#include
38 Synchronizacja procesów: wait#include
39 Synchronizacja procesów: waitpid 1/2#include
40 Synchronizacja procesów: waitpid 2/2Wartość pid może być: < -1 oczekiwanie na dowolny proces potomny, którego ID grupy procesów jest równy wartości bezwzględnej pid -1 oczekiwanie na dowolny proces potomny (takie samo zachowanie, jakie wykazuje wait) 0 oczekiwanie na każdy proces potomny, którego ID grupy procesu jest równe ID grupy procesu wywołującego funkcję. > 0 oczekiwanie na potomka, którego ID procesu jest równy wartości pid. Ustawienie options na WNOHANG oznacza natychmiastowy powrót z funkcji, jeśli potomek nie zakończył pracy. Funkcja zwraca wtedy wartość 0.
41 Przedwczesne zakończenie i ‘zombie’Proces potomny kończy się w czasie, gdy jego proces rodzicielski nie wykonuje funkcji wait Proces potomny staje się procesem zombie i umieszczany jest w stanie zawieszenia. Nadal zajmuje pozycję w tablicy procesów jądra, ale nie używa innych zasobów jądra. Pozycja w tablicy procesów zostanie zwolniona po wywołaniu przez rodzica funkcji wait. Proces rodzicielski kończy się, gdy jeden lub więcej procesów potomnych ciągle działa Procesy potomne (w tym potencjalne procesy zombie) są adoptowane przez proces init.
42 Sygnały Procesy SygnałyK. Haviland, D. Gray, B. Salama, UNIX: Programowanie systemowe
43 Sygnały Zdarzenia asynchroniczne i wyjątkiSygnały generowane są za pomocą funkcji systemowej kill() Operacje domyślne lub specyficzne programy obsługi napisane przez użytkownika Ustawiają bit w masce sygnału w tablicy procesów
44 Sygnały Sygnał: wiadomość, którą proces może przesłać do procesu lub grupy procesów, jeśli tylko ma odpowiednie uprawnienia (Zdarzenia asynchroniczne i wyjątki). Typ wiadomości reprezentowany jest przez nazwę symboliczną Dla każdego sygnału otrzymujący go proces może: Jawnie zignorować sygnał (bit w masce sygnałów w tablicy procesów nie jest ustawiany – brak śladu odebrania sygnału) Realizować specjalne działania za pomocą tzw. signal handler (np. funkcja użytkownika) W przeciwnym przypadku realizowane jest działanie domyślne (zwykle proces kończony) Wybrane sygnały mogą być również blokowane, tzn. ich nadejście jednorazowo jest odznaczane w masce sygnałów w tablicy procesów ale obsługa jest odkładana do momentu zdjęcia blokady.
45 Przykład sygnałów 1/3 Jeśli istnieje potomek, to wysyła sygnał SIGCHLD do rodzica (wartość 20,17 lub 18 zależna od architektury – dla i386 i ppc 17). Jeśli rodzic chce czekać na zakończenie potomka, to powiadamia system, że chce przechwycić sygnał SIGCHLD Jeśli nie zasygnalizuje oczekiwania, to sygnał SIGCHLD jest przez niego ignorowany (standardowa obsługa) W większości systemów dokładny opis obsługiwanych sygnałów wraz z ich domyślną obsługą i dodatkowymi informacjami umieszczany jest w 7 manualu signal (man 7 signal)
46 Przykład sygnałów 2/3 SIGINT Sygnał przerwania generowany zwykle, gdy użytkownik naciśnie klawisz przerwania na terminalu. SIGQUIT Sygnał przerwania generowany gdy użytkownik naciśnie na terminalu klawisz zakończenia pracy. Sygnał SIGQUIT jest podobny do sygnału SIGINT, ale dodatkowo generuje obraz pamięci. SIGKILL Bezwarunkowe zakończenie procesu (proces odbierający ten sygnał nie może go ani zignorować, ani przechwycić). SIGTSTP Sygnał wysyłany do procesu po naciśnieciu klawisza zawieszenia (na ogół CRTL+Z) lub klawisza zawieszenia z opóźnieniem (CTRL+Y). Proces zawieszony (zatrzymany), można wznowić sygnałem SIGCONT. SIGILL Sygnał ten jest generowany po wystąpieniu wykrywanej sprzętowo sytuacji wyjątkowej, spowodowanej przez niewłaściwą implementację systemu. SIGSTOP Sygnał ten zatrzymuje proces. Podobnie jak sygnał SIGKILL nie może zostać zignorowany lub przechwycony. Działanie zatrzymanego procesu można wznowić sygnałem SIGCONT.
47 Przykład sygnałów 3/3 SIGSEGV Sygnał generowany po wystąpieniu błędu sprzętowego spowodowanego przez niewłaściwą implementację systemu. Sygnał naruszenia segmentacji pojawia się na ogół wtedy, kiedy proces odnosi się do takiego adresu w pamięci, do którego nie ma dostępu. SIGALARM Sygnał budzika generowany przez f-cję unsigned int alarm(unsigned int sec); SIGCHLD Sygnał wysyłany do procesu, kiedy jego potomny proces się skończył. SIGUSR1, SIGUSR2 Sygnały definiowane przez użytkownika, których można używać do komunikacji między procesami. SIGTERM Domyślny sygnał wysyłany przez komendę kill. Wymusza zawieszenie procesu.
48 Obsługa sygnałów Wykonujący się proces q Pojawia się “SIG#” dla “p”/* kod procesu p */ . . . signal(SIG#, sig_hndlr); /* DOWOLNY KOD */ void sig_hndlr(...) { /* DOWOLNY KOD */ } Wykonujący się proces q Pojawia się “SIG#” dla “p” sig_hndlr wykonuje się w przestrzeni adre- sowej procesu p q jest blokowany q wznawia wykonywanie
49 Wysyłanie sygnałów: kill 1/2#include
50 Wysyłanie sygnałów: kill 2/2Linux pozwala procesowi wysłać sygnał do samego siebie, ale wywołanie kill(-1,sig) pod Linuksem nie powoduje wysłania sygnału do bieżącego procesu. Aby proces miał prawo wysłać sygnał do procesu pid musi on mieć uprawnienia roota albo rzeczywisty lub efektywny ID użytkownika procesu wysyłającego musi być równy rzeczywistemu ID lub zachowanemu set UID procesu otrzymującego sygnał.
51 Obsługa sygnałów: signal 1/2#include
52 Obsługa sygnałów: signal 2/2test-signal-1.c ... void (*f)(); f=signal(SIGINT,SIG_IGN); /* ignorowanie sygnału sigint*/ signal(SIGINT,f); /*przywrócenie poprzedniej reakcji na syg.*/ signal(SIGINT,SIG_DFL); /*ustaw. standardowej reakcji na syg.*/ test-signal-2.c void moja_funkcja(int s) { printf("Został przechwycony sygnał %d\n„, ); return 0; } main(){ signal(SIGINT, moja_funkcja); /* przechwycenie sygnału */ ... }
53 Obsługa sygnałów: sigaction 1/4#include
54 Obsługa sygnałów: sigaction 2/4sa_handler podaje akcję, związaną z sygnałem signum i może to być m.in SIG_DFL dla akcji domyślnej, SIG_IGN dla akcji ignorowania lub wskaźnik do funkcji obsługującej sygnał. Funkcja ta ma tylko jeden argument, w którym będzie przekazany numer sygnału. sa_sigaction podaje akcję zamiast sa_handler jeżeli w sa_flags ustawiono SA_SIGINFO. Funkcja ta otrzymuje numer sygnału jako pierwszy argument, wskaźnik do siginfo_t jako drugi argument oraz wskaźnik do ucontext_t (zrzutowany na void *) jako jej trzeci argument. sa_mask podaje maskę sygnałów, które powinny być blokowane podczas wywoływania handlera sygnałów. Dodatkowo, sygnał, który wywołał handler będzie zablokowany, chyba że użyto flagi SA_NODEFER. sa_flags podaje zbiór flag, które modyfikują zachowanie procesu obsługi sygnałów. Jest to zbiór wartości połączonych bitowym OR (np. flaga SA_RESETHAND odtwórz akcję sygnałową do stanu domyślnego po wywołaniu handlera sygnałów a SA_SIGINFO określa, że handler sygnałów pobiera 3 argumenty, a nie jeden )
55 Obsługa sygnałów: sigaction 3/4Parametr siginfo_t z sa_sigaction jest strukturą zawierającą następujące elementy: siginfo_t { int si_signo; /* Numer sygnału */ int si_errno; /* Wartość errno */ int si_code; /* Kod sygnału */ pid_t si_pid; /* Id procesu wysyłającego */ uid_t si_uid; /* Rzeczywisty ID użytkownika wysyłającego procesu */ int si_status; /* Kod zakończenia lub sygnał */ clock_t si_utime; /* Czas spędzony w przestrzeni użytkownika */ clock_t si_stime; /* Czas spędzony w przestrzeni systemu */ sigval_t si_value; /* Wartość sygnału */ int si_int; /* sygnał POSIX.1b */ void * si_ptr; /* sygnał POSIX.1b */ void * si_addr; /* Adres pamięci, który spowodował błąd */ int si_band; /* Zdarzenie grupy (band event) */ int si_fd; /* Deskryptor pliku */ }
56 Obsługa sygnałów: sigaction 4/4test-sigaction-1.c void obslugaint(int s) { printf("Tak mnie nie przerwiesz!\n"); } int main(void) { int x = 1; sigset_t iset; struct sigaction act; sigemptyset(&iset); act.sa_handler = &obslugaint; act.sa_mask = iset; act.sa_flags = 0; sigaction(SIGINT, &act, NULL); while (x != 0) { printf("Skoncze sie dopiero kiedy wprowadzisz 0\n"); scanf("%d", &x); } return 0; }
57 Blokowanie sygnałów: sigprocmask 1/2#include
58 Blokowanie sygnałów: sigprocmask 2/2test-sigprocmask.c #include
59 Zbiory sygnałów #include