# SO Lista 2 ### Zadanie 1 <!-- Gut --> ![](https://i.imgur.com/PwvO6VN.png) ![](https://i.imgur.com/PhFXRzT.png) <!-- Radek gigachad --> <b>Możliwe stany procesu:</b> <ul> <li><b>Stoppped -></b> wstrzymany, może zostać wznowiony (np. przez syscall)</li> <li><b>Zombie -></b> proces który się zakończył, ale dalej pobiera zasoby i czeka na ogarnięcie</li> <li><b>Running (który się dzieli):</b></li> <ul> <li><b>Ready -></b> załadowany do pamięci czeka na na wykonanie</li> <li><b>Executing -></b> proces który obecnie jest wykonywany</li> </ul> <li><b>Uninterruptible (sen nieprzerywalny) -></b> proces jest zablokowany i nie reaguje na żadne sygnały</li> <li><b>Interruptible (sen przerywalny)-></b> proces jest zablokowany, ale może oczekiwać na koniec jakiejś operacji, albo na jakiś sygnał</li> </ul> <b>Podaj akcje albo zdarzenia wyzwalające zmianę stanu && czyje działania mogą wywołać </b> <table> <thead><tr> <th>Stan początkowy</th> <th>Stan końcowy</th> <th>Akcja i czyje działania</th> </tr></thead> <tbody> <tr> <th>Ready</th> <th>Executing</th> <th>Wykonanie procesu które było wcześniej zaplanowane, działanie jądra</th> </tr> <tr> <th>Executing</th> <th>Zombie</th> <th>Dobrowolne , user</th> </tr> <tr> <th>Executing</th> <th>(Un)interruptible</th> <th>Wybudzenie przez jądro interruptable przez otrzymanie I/O</th> </tr> <tr> <th>Stopped</th> <th>Ready</th> <th>otrzymanie sygnału SIGSTOP lub SIGCONT (czyli użytkownik)</th> </tr> <tr> <th>(Un)interruptible</th> <th>Ready</th> <th>?</th> </tr> </tbody> </table> <b>Czy proces może zablokować lub zignorować sygnał SIGKILL lub SIGSEGV?</b> <b>Zablokowanie sygnału -></b> niedostarczenie sygnału do procesu aż do momentu odblokowania go przez SO. Dzięki temu nie tracimy informacji o sygnale, a jednocześnie nie musimy na niego reagować w niewygodnym dla nas momencie. <b>Zignorowanie sygnału -></b> sytuacja kiedy proces nie ma zdefiniowanego zachowania dla danego sygnału Sygnału SIGKILL nie da się zablokować. Ignorowanie kończy się zabiciem procesu ### Zadanie 2 <!-- Gut --> ![](https://i.imgur.com/aWzjpUf.png) <b>Linux</b> W systemie Linux proces jest tworzony tylko przy pomocy fork() <li>Deskryptor nowego procesu i userpace są tworzone i uzupełnione w większości od rodzica</li> <li>Proces otrymuje PID i mapę pamięci. Ma dostęp do współdzielonych plików z rodzicem <li>Ustawiane są rejestry i proces jest gotowy do działania</li> <b>WinNT</b> W systemie WinNT proces towrzy się wywołując procedurę CreateProcess(której argumentem jest .exe). To wywołanie wzywa procedurę w user-mode w kernel32.dll która woła NtCreateUserProcess w jądrze. Cała procedura tworzy nowy proces (nie forkuje jak Linux). Dziecko w przeciwieństwie do procesu w Linuxie nie otrzymuje kopii pamięci rodzica <b>Obsługa frok() i execve() poniżej na rysunku</b> ![](https://i.imgur.com/sj7hF67.png) <b> Dlaczego w takim przypadku mielibyśmy problemy z dodaniem do powłoki obsługi przekierowania standardowego wejścia/wyjścia odpowiednio z/do pliku albo łączenia dowolnych procesów potokami?</b> Pipe zdecydowanie ułatwia komunikację między procesami, dlatego spawn leci w chuja ### Zadanie 3 <!-- Gut --> ![](https://i.imgur.com/z4pblqF.png) #### A) ![](https://i.imgur.com/IRD6osx.png) #### B) ![](https://i.imgur.com/fSd1u8w.png) #### Czemu trzeba wyczyścić bufor? Proces dziecko odziedzicza deskryptory otwartych plików (w tym stdout) i bufor z nim związany. (po prostu żeby nie wypisywać kilka razy tego samego) #### Obsługa sygnałów Resetuje je do defaultowaych ustawień (man exe) ![](https://i.imgur.com/uey5yEH.png) ![](https://i.imgur.com/UgJ5JhM.jpg) ![](https://i.imgur.com/yv9VCUO.jpg) ### Zadanie 4 <!-- Gut --> ![](https://i.imgur.com/pCYNuoI.png) <b>Co robią poszczególne polecenia</b> <ul> <li><b>Kill (pid)</b> -> domyślnie wysyła sygnał TERM </li> <li><b>Pkill (process name)</b> ->domyślnie wysyła SIGTERM to lażdego procesu </li> <li><b>Xkill (okno z procesem)</b> -> will display a special cursor as a prompt for the user to select a window to be killed. This command does not provide any warranty that the application whose connection to the X server is closed will abort nicely, or even abort at all.</li> </ul> <b>Sygnały oczekujące -> </b> to sygnały, których dostarczenie jest wstrzymane do momentu wyjścia przez proces z nieprzerywalnego snu ![](https://i.imgur.com/UT9PrU0.png) cat /proc/pid/status kill -s SIGINT 2792 Wykona się ten który ma najmniejszy numer (u nas SIGHUP bo ma 1) ### Zadanie 5 ![](https://i.imgur.com/dtgbTRF.png) #### Co robi sinit Startuje init, blokuje wszystkie sygnały, czeka na eventy, które ma zdefiniowane i odpala odpowiednie handlery ### Jakie akcje wykonuje <ul> <li>reboot -> sigreboot</li> <li>shutdown -> sigpoweroff</li> <li>grzebanie dzieci -> sigreap</li> </ul> ### Do czego służą procedury <ul> <li>Sigprocmask -> manipuluje blokowanymi sygnałami</li> <li>Sigwait -> czeka na sygnał i daje info o nim w 2arg podanym do niego</li> </ul> ### Jak grzebie dzieci? Waitpid dopóki nie pogrzebie wszystkich dzieci które powstały jak dostał sygnał SIGCHCD ![](https://i.imgur.com/ywVPdmu.jpg) ![](https://i.imgur.com/g91SJJU.jpg) ### Zadanie 6 <!-- gut --> ![](https://i.imgur.com/9s5309q.png) ```c #include "csapp.h" static pid_t spawn(void (*fn)(void)) { pid_t pid = Fork(); if (pid == 0) { fn(); printf("(%d) I'm done!\n", getpid()); exit(EXIT_SUCCESS); } return pid; } static void grandchild(void) { printf("(%d) Waiting for signal!\n", getpid()); /* TODO: Something is missing here! */ pause(); printf("(%d) Got the signal!\n", getpid()); } static void child(void) { pid_t pid; /* TODO: Spawn a child! */ pid = spawn(grandchild); printf("(%d) Grandchild (%d) spawned!\n", getpid(), pid); } /* Runs command "ps -o pid,ppid,pgrp,stat,cmd" using execve(2). */ static void ps(void) { /* TODO: Something is missing here! */ pid_t pid; if((pid = fork()) == -1) app_error("fork failed"); else if(pid == 0){ /* child */ char *args[] = {"/bin/ps","-o","pid,ppid,pgrp,stat,cmd",NULL}; fflush(stdout); execve("/bin/ps",args,NULL); exit(0); } else /* parent */ waitpid(pid,NULL,0); } int main(void) { /* TODO: Make yourself a reaper. */ #ifdef LINUX Prctl(PR_SET_CHILD_SUBREAPER, 1); #endif printf("(%d) I'm a reaper now!\n", getpid()); pid_t pid; int status; /* TODO: Start child and grandchild, then kill child! * Remember that you need to kill all subprocesses before quit. */ pid = spawn(child); setpgid(pid,pid); //teoretycznie wyzej jest napisane ze trzeba zabic dzieciaka //ale w zadaniu jest napisane o zakonczeniu działania syna //poza tym kill na dziecko troche komplikuje sprawe z sychnronizacją //ale dziecko i tak samo z siebie robi exit() wiec nie wiem po co //go zabijac waitpid(pid,NULL,0); ps(); kill(-pid,SIGINT); wait(&status); if(WIFSIGNALED(status)) printf("Grandchild signal code: %d\n",WTERMSIG(status)); return EXIT_SUCCESS; } ``` ### Zadanie 7 ![](https://i.imgur.com/hikxndW.png) ```c #include "csapp.h" bool notified = false; static void signal_handler(int signum, siginfo_t *info, void *data) { if (signum == SIGINT) { safe_printf("(%d) Screw you guys... I'm going home!\n", getpid()); _exit(0); } else if (signum==SIGUSR1 && !notified) notified = true; } static void play(pid_t next, const sigset_t *set) { for (;;) { printf("(%d) Waiting for a ball!\n", getpid()); /* TODO: Something is missing here! */ Sigsuspend(set); usleep((300 + random() % 400) * 1000); Kill(next, SIGUSR1); printf("(%d) Passing ball to (%d)!\n", getpid(), next); } } int main(int argc, char *argv[]) { if (argc != 2) app_error("Usage: %s [CHILDREN]", argv[0]); pid_t pid=1,prev_pid = getpid(); int children = atoi(argv[1]); //siginfo_t info; if (children < 4 || children > 20) app_error("Give number of children in range from 4 to 20!"); /* Register signal handler for SIGUSR1 */ struct sigaction action = {.sa_sigaction = signal_handler}; Sigaction(SIGINT, &action,NULL); Sigaction(SIGUSR1, &action,NULL); /* TODO: Start all processes and make them wait for the ball! */ sigfillset(&action.sa_mask); sigdelset(&action.sa_mask,SIGINT); sigdelset(&action.sa_mask,SIGUSR1); Sigprocmask(SIG_BLOCK, &action.sa_mask,NULL); for (int i = 0; i < children; i++){ pid = Fork(); if (pid == 0) /* child */ play(prev_pid,&action.sa_mask); else prev_pid = pid; } /* Stary zaczyna gre podając piłke do najmłodszego */ /* teraz działa samo z siebie ale na trytytkach, wcześniej było: Kill(pid, SIGUSR1); play(pid,&action.sa_mask); */ while(!notified) Kill(pid, SIGUSR1); play(pid,&action.sa_mask); return EXIT_SUCCESS; } ``` ### Zadanie 8 ![](https://i.imgur.com/xe47tyX.png) ```c #include "csapp.h" /* First address of handled region. */ #define ADDR_START ((void *)0x10000000) /* Last address of handled region (not inclusive). */ #define ADDR_END ((void *)0x10010000) #define REG_RIP 16 static size_t pagesize; /* Maps anonymouse page with `prot` access permissions at `addr` address. */ static void mmap_page(void *addr, int prot) { Mmap(addr, pagesize, prot, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0); } /* Changes protection bits to `prot` for page at `addr` address. */ static void mprotect_page(void *addr, int prot) { Mprotect(addr, pagesize, prot); } static void sigsegv_handler(int signum, siginfo_t *info, void *data) { ucontext_t *uc = data; intptr_t rip = uc->uc_mcontext.gregs[REG_RIP]; void * page_ptr = (void *)((unsigned long)info->si_addr & ~(pagesize - 1)); /* TODO: You need to get value of instruction pointer register from `uc`. * Print all useful data from `info` and quit in such a way that a shell * reports program has been terminated with SIGSEGV. */ /* [Q] Dlaczego safe_printf() jest bezpieczny ? * bo uzywa niebuforowanego I/O (czyli tylko write()) * przez co nie zmienia globalnych struktur itd (nie to co printf()),\ * a formaty parsuje sam w pliku (patrz libcsapp/safe_printf.c) */ if(info->si_addr < ADDR_START || info->si_addr >= ADDR_END){ safe_printf("Fault at rip=%lx accessing %lx! Address not mapped - terminating\n",rip,(long)info->si_addr); _exit(139); } else if(info->si_code==SEGV_MAPERR){ safe_printf("Fault at rip=%lx ",rip); safe_printf("accessing %lx! ",(long)info->si_addr); safe_printf("Map missing page at %lx\n",(long)page_ptr); mmap_page(page_ptr,PROT_WRITE); } else if(info->si_code==SEGV_ACCERR){ safe_printf("Fault at rip=%lx ",rip); safe_printf("accessing %lx! ",(long)info->si_addr); safe_printf("Make page at %lx writable\n",(long)page_ptr); mprotect_page(page_ptr,PROT_WRITE); } } int main(int argc, char **argv) { pagesize = sysconf(_SC_PAGESIZE); /* Register signal handler for SIGSEGV */ struct sigaction action = {.sa_sigaction = sigsegv_handler, .sa_flags = SA_SIGINFO}; sigaction(SIGSEGV, &action, NULL); /* Initially all pages in the range are either not mapped or readonly! */ for (void *addr = ADDR_START; addr < ADDR_END; addr += pagesize) if (random() % 2) mmap_page(addr, PROT_READ); /* Generate lots of writes to the region. */ volatile long *array = ADDR_START; long nelems = (ADDR_END - ADDR_START) / sizeof(long); for (long i = 0; i < nelems * 2; i++) { long index = random() % nelems; array[index] = (long)&array[index]; } /* Perform off by one access - triggering a real fault! */ array[nelems] = 0xDEADC0DE; return EXIT_SUCCESS; } ```