# Lista 1 ###### tags: `SO2022` ## Deklaracja :::spoiler | Zadanie | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |-----------|---|---|---|---|---|---|---|---| | Deklaracja|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| :heavy_check_mark:| :( |:heavy_check_mark:|:white_check_mark:| :heavy_check_mark:| ::: ## Zadanie 1 ![](https://i.imgur.com/OHAO232.png) //:::spoiler * `ps -eo user,pid,ppid,pgid,tid,pri,stat,wchan,cmd | less` * #### Na wydruku zidentyfikuj identyfikator procesu, identyfikator grupy procesów, identyfikator rodzica oraz właściciela procesu ![](https://i.imgur.com/QstHjwM.png) - **ID procesu** - `pid` (unikalna liczba, dzięki której łatwiej jest zidentyfikować każdy proces) - **ID rodzica** - `ppid` (identyfikator procesu, który za pomocą `form()` utworył procesy dzieci) - **ID grupy procesów** - `pgid` (zbiór jednego lub większej ilości procesów, które mogą razem otrzymywać sygnały) - **Właściciel** - `user` (użytkownik, który uruchomił proces i posiada pełne uprawnienia do kontrolowania ich, np. zatrzymania lub zabijania) * #### Kto jest rodzicem procesu init? **PPID 0** - placeholder, oznaczane nim są procesy nieposiadające rodzica * #### Wskaż, które z wyświetlonych zadań są wątkami jądra. **Wątkami jądra** (czyli wątki wykonujące kod jądra, nie sa one powiązane z procesami z przestrzeni użytkownika, nie używają również pamięci) są te których `PPID=2` czyli wskazuje na `kthreadd` Dzieje się tak dlatego, że wątki jądra mogą zostać uruchomione przed wszystkim procesami z przestrzeniu użytkownika Dodatkowo ich nazwa jest opatrzona `[]` * #### Jakie jest znaczenie poszczególnych znaków w kolumnie STAT? ``` **PROCESS STATE CODES** Here are the different values that the s, stat and state output specifiers (header "STAT" or "S") will display to describe the state of a process: D uninterruptible sleep (usually IO) I Idle kernel thread R running or runnable (on run queue) S interruptible sleep (waiting for an event to complete) T stopped by job control signal t stopped by debugger during the tracing W paging (not valid since the 2.6.xx kernel) X dead (should never be seen) Z defunct ("zombie") process, terminated but not reaped by its parent For BSD formats and when the stat keyword is used, additional characters may be displayed: < high-priority (not nice to other users) N low-priority (nice to other users) L has pages locked into memory (for real-time and custom IO) s is a session leader l is multi-threaded (using CLONE_THREAD, like NPTL pthreads do) + is in the foreground process group ``` * #### Wyświetl drzewiastą reprezentację hierarchii procesów poleceniem `pstree | less` – które z zadań są wątkami? ``` init-+- {abc} - wątek abc |- x * [{abc}] - x wątków abc |- xyz - proces xyz `- x * [xyz] - x procesów xyz ``` //::: ## Zadanie 2 ![](https://i.imgur.com/P8Euudy.png) //:::spoiler * #### Jak jądro systemu reaguje na sytuację kiedy proces staje się sierotą? ``` When we die, we re-parent all our children, and try to: 1. give them to another thread in our thread group, if such a member exists 2. give it to the first ancestor process which prctl'd itself as a child_subreaper for its children (like a service manager) 3. give it to the init process (PID 1) in our pid namespace Staramy się przekazać proces do dziadka a jesli się nie uda to przekazujem do init ``` * #### W jaki sposób pogrzebać proces, który wszedł w stan zombie? Jeśli rodzic nie pogrzebie dziecka będzie ono w systemie aż do chwili kiedy rodzic umrze. Wtedy też wykona się ponowne przypisanie rodzica W momencie kiedy **Init** stanie się rodzicem proces zostanie po pewnym czasie zakończony, ponieważ **Init** co pewien czas wykonuje wywołanie systemowe `wait()` czyszcząc w ten sposób system z zombie * #### Czemu proces nie może sam siebie pogrzebać? Proces po zakończeniu zwraca status zakonczenia dziłania, jakiś proces musi go przejąć i zwolnić to miejsce w pamięci * #### Zauważ, że proces może, przy pomocy `waitpid(2)`, czekać na zmianę stanu wyłącznie swoich dzieci. Co złego mogłoby się stać, gdyby znieść to ograniczenie? * ##### Dziecko może czekać na zmianę stanu swojego rodzica: * Rodzic i dziecko mogą czekać na siebie nawzajem przez co nigdy by się nie wykonały bo powstałby deadlock * ##### Wiele procesów oczekuje na zmianę stanu jednego procesu: * Przy zakończeniu procesu każdy rodzic próbuje grzebać syna, przez co większość otrzymuje błąd //::: ## Zadanie 3 ![](https://i.imgur.com/dOjxYkt.png) //:::spoiler * #### Do czego służy system plików `proc(5)` w systemie Linux? Pseudosystem plików podający informacje o zasobach systemowych, procesach, hardware itp., udostępnia interfejs do struktur danych kernela, montowany zazwyczaj automatycznie przez system w katalogu `/proc` przykłady danych: * **/proc/[pid]** - informacje o procesie o danym pid * **/proc/[tid]** - jw. tylko info o wątku o danym tid * **/proc/self** - link symboliczny do własnego /proc/[pid] procesu * **/proc/thread-self** - link symboliczny do własnego /proc/self/task/tid wątku * #### Dla wybranego przez siebie procesuo identyfikatorze pid wydrukuj zawartość katalogu `/proc/pid`. Wyświetl plik zawierający *argumenty programu* (parametry przekazane do programu w momencie jego wywołania - argc, argv[] z pliku **/proc/[pid]/cmdline**) oraz *zmienne środowiskowe (dane postaci klucz=wartość zarządzane przez powłokę systemową, każdy proces posiada swój zestaw, który dziedziczy po rodzicu, są w pliku **/proc/[pid]/environ **)*. ```= sudo ls /proc/1 sudo cat /proc/1/cmdline sudo cat /proc/1/environ ``` * #### Podaj znaczenie następujących pól pliku `status`: * `man proc` * `Uid`: (rzeczywisty, efektywny) ID użytkownika w systemie * `Gid`: jw. tylko ID grupy * `Groups`: lista grup uzupełniających dziedziczonych po rodzicu, mówią jakie uprawnienia ma proces * `VmPeak`: Maksymalny rozmiar zajętej pamieci wirtualnej * `VmSize`: Aktualny rozmiar zajętej pamięci wirtualnej * `VmRSS`: (vitual memory resident set size) Rozmiar zbioru roboczego (pamięci wirtualnej używanej przez proces) * `Threads`: Liczba wątków w procesie zawierającym ten wątek * `voluntary_ctxt_switches`: Liczba dobrowolnych przełączeń kontekstu * `nonvoluntary_ctxt_switches`: Liczba przymusowych przełączeń kontekstu //::: ## Zadanie 4 ![](https://i.imgur.com/rMcu2O3.png) //:::spoiler * #### Znajdź pid procesu `X-serwera` `ps -e | grep tty` ![](https://i.imgur.com/yiCwIcI.png) Bardziej szczegółowa dokumentacja **pmap**: https://docs.oracle.com/cd/E86824_01/html/E54763/pmap-1.html https://man.netbsd.org/pmap.1 * #### Używając polecenia `pmap` wyświetl zawartość jego przestrzeni adresowej. Zidentyfikuj w niej poszczególne zasoby pamięciowe – tj. stos, stertę, segmenty programu, pamięć anonimową, pliki odwzorowane w pamięć. * `pmap PID | less` * `stos` - na samym dole ![](https://i.imgur.com/0bBXG5g.png) * `pamięć anonimowa` (jest nią również stos i sterta) ![](https://i.imgur.com/uRVF6rM.png) * `segmenty programu` - na samej górze ![](https://i.imgur.com/rzejcj6.png) * `pliki odwzorowane w pamięć` ![](https://i.imgur.com/EZ28l11.png) * `pmap PID -X | less` * `sterta` - na górze ![](https://i.imgur.com/GDkZo2r.png) //::: Stos [stack] oraz Sterte [heap] znajdują się na dole lecz zobaczymy tam tylko adres początku, możemy jednak wywołać sudo `less /proc/[pid]/maps` aby uzyskac dokladne przedzialy Poszczególne kolumny wydruku pmap oznaczają: - Address - adres początku - Kbytes - rozmiar - Mode - uprawnienia rwx (czytanie, pisanie, wykonywanie) - Mapping - plik na jaki zmapowana jest pamięć lub [anon]/[stack] ## Zadanie 5 ![](https://i.imgur.com/mN1tnRA.png) lsof -p PID przegladarki Kolumna Type definiuje typy plików: - REG - pliki regularne - DIR - katalogi - IPv4, IPv6 - sockety sieciowe - sock - socket nieznanej domeny - unix, ax25 - socket unixowy - Potoki to te pliki które w name mają type=STREAM - Urządzenia - kolumna device zawiera numer urządzenia ale według dokumentacji może to oprócz numeru oznaczać kilka różnych rzeczy więc z tym trzeba uważać (można jeszcze spróbować strzelić że urządzenia to te pliki które mają w nazwie /dev/...) * **diff -u before.txt after.txt | grep +** - linijki zaczynające się od + to te które pojawiły się w after, żeby znaleźć nowe połączenia trzeba znaleźć te linijki które są socketami sieciowymi ## Zadanie 6 ![](https://i.imgur.com/Hgj6bjY.png) //:::spoiler **real** - czas (prawdziwy) w sekundach od staru do końca **user** - czas w CPU-sekundach, które proces spędził w user modzie **sys** - czas w CPU-sekundach, które proces spędził w kernel modzie usr i sys mogą być dla wielu wątków zatem może być powielony ``` time find /usr ulimit -t 1 ulimit -t 1 -S ulimit -t 5 -H ./zad6 ``` The default format string is: %Uuser %Ssystem %Eelapsed %PCPU (%Xtext+%Ddata %Mmax)k %Iinputs+%Ooutputs (%Fmajor+%Rminor)pagefaults %Wswaps When the -p option is given, the (portable) output format is used: real %e user %U sys %S `gcc zad6.c -o zad6` ```c= #include<stdio.h> #include<signal.h> #include<unistd.h> void sig_handler(int signo) { if (signo == SIGXCPU) printf("received SIGXCPU\n"); } void sig_handler1(int signo) { if (signo == SIGINT) printf("received SIGINT\n"); } void sig_handler3(int signo) { if (signo == SIGTERM) printf("received SIGTERM\n"); } int main(void) { if (signal(SIGXCPU, sig_handler) == SIG_ERR) printf("\ncan't catch SIGXCPU\n"); if (signal(SIGINT, sig_handler1) == SIG_ERR) printf("\ncan't catch SIGINT\n"); if (signal(SIGTERM, sig_handler3) == SIG_ERR) printf("\ncan't catch SIGTERM\n"); // A long long wait so that we can easily issue a signal to this process int i = 0; while(1) { } return 0; } ``` //::: ## Zadanie 7 ![](https://i.imgur.com/LV9hHNX.png) //:::spoiler ```c= #include "csapp.h" static char buf[256]; #define LINE1 49 #define LINE2 33 #define LINE3 78 static void do_read(int fd) { /* TODO: Spawn a child. Read from the file descriptor in both parent and * child. Check how file cursor value has changed in both processes. */ pid_t child = Fork(); if (child == 0) { // child (new process) int before =(int)Lseek(fd,0,SEEK_CUR); Read(fd, buf, LINE1); int after =(int)Lseek(fd,0,SEEK_CUR); printf("Child (pid:%d) (lseek before:%d after: %d)\n %s\n\n", (int)getpid(), before, after, buf); } else { Wait(NULL); int before =(int)Lseek(fd,0,SEEK_CUR); Read(fd, buf, LINE2); int after =(int)Lseek(fd,0,SEEK_CUR); printf("Parent of %d (pid:%d) (lseek before:%d after: %d)\n %s\n\n", child,(int)getpid(), before, after, buf); } exit(0); } static void do_close(int fd) { /* TODO: In the child close file descriptor, in the parent wait for child to * die and check if the file descriptor is still accessible. */ pid_t child = Fork(); if (child == 0) { // child (new process) Read(fd, buf, LINE1); printf("Child (pid:%d)\n %s\n\n", (int)getpid(), buf); // Close(fd); } else { // int rc_wait = Wait(NULL); Read(fd, buf, LINE2); printf("Parent of %d (pid:%d)\n %s\n\n", child, (int)getpid(), buf); Close(fd); } exit(0); } int main(int argc, char **argv) { if (argc != 2) app_error("Usage: %s [read|close]", argv[0]); int fd = Open("test.txt", O_RDONLY, 0); if (!strcmp(argv[1], "read")) do_read(fd); if (!strcmp(argv[1], "close")) do_close(fd); app_error("Unknown variant '%s'", argv[1]); } ``` //::: ## Zadanie 8 ![](https://i.imgur.com/Cs0lDuo.png) ```c= #include "libcsapp/csapp.h" static int ndselect(int n) { // return multiple times -> 0..n-1 /* TODO: A loop is missing here that spawns processes and waits for them! */ /* ---------------------------------- impl ---------------------------------- */ pid_t children[n]; for (int i = 0; i < n; i++) { children[i] = fork(); if (children[i] == 0) { //child code return i; } else { //parent code waitpid(children[i], NULL, 0); } } /* --------------------------------- endimpl -------------------------------- */ exit(0); } static int conflict(int x1, int y1, int x2, int y2) { return x1 == x2 || y1 == y2 || x1 + y1 == x2 + y2 || x1 - y1 == x2 - y2; } static void print_line_sep(int size) { for (int i = 0; i < size; ++i) printf("+---"); printf("+\n"); } static void print_board(int size, int board[size]) { for (int i = 0; i < size; ++i) { print_line_sep(size); for (int j = 0; j < size; ++j) printf("|%s", board[i] == j ? " Q " : " "); printf("|\n"); } print_line_sep(size); printf("\n"); } int main(int argc, char **argv) { if (argc != 2) app_error("Usage: %s [SIZE]", argv[0]); int size = atoi(argv[1]); if (size < 3 || size > 9) app_error("Give board size in range from 4 to 9!"); int board[size]; /* TODO: A loop is missing here that initializes recursive algorithm. */ /* ---------------------------------- impl ---------------------------------- */ for (int i = 0; i < size; i++) { board[i] = ndselect(size); if (board[i] < 0) exit(0); for (int k = 0; k < i; k++) { if (conflict(i, board[i], k, board[k])) exit(0); } } /* --------------------------------- endimpl -------------------------------- */ print_board(size, board); return 0; } ```