# SO Lista 2 Procesy i sygnały

## Zadanie 1

:::info
**Stan procesu** - wykonujący się proces zmienia swój stan; każdy proces może znajdować się w jednym z następujących stanów:
* **Running:** ta wartość stanu odpowiada dwóm stanom - proces jest wykonywany albo czeka na wykonanie (jest gotowy do wykonywania).
* **Interruptible (przerywalny):** stan zablokowany, w którym proces oczekuje na zdarzenie, takie jak koniec operacji we/wy, dostępność zasobu lub sygnał z innego procesu.
* **Uninterruptible (nieprzerywalny):** to również zablokowany stan, jednak w odróżnieniu od Interruptible proces czeka bezpośrednio na warunkach sprzętowych i dlatego nie obsługuje żadnych sygnałów.
* **Stopped:** proces został zatrzymany i może zostać wznowiony tylko przez działanie innego procesu (w stanie zatrzymania może się znaleźć np. debuggowany proces).
* **Zombie:** proces został zakończony, ale nie pogrzebany; jego struktura zadań nadal istnieje w tabeli procesów.
:::

**Akcje i zdarzenia wyzwalające zmianę stanów :**
* **Ready -> Executing:**
- zaplanowanie wykonania procesu przez schedulera (działanie podejmowane przez jądro).
* **Executing -> Ready:**
- wywłaszczenie procesu (upływu kwantu czasu w systemach z podziałem czasu, pojawienie się procesu gotowego z wyższym priorytetem w systemie z priorytetami dynamicznymi); działanie podejmowane przez jądro
* **Executing -> Zombie:**
- dobrowolne zakończenie się procesu przez exit().
- otrzymanie SIGKILLa lub SIGTERMa
- zamknięcie przez system w przypadku deadlocka
- błąd I/O (przez sterownik urządzenia)
* **Stopped <-> Ready:**
- otrzymanie sygnału SIGSTOP / SIGCONT (od procesu użytkownika)
* **Interruptible/Uninterruptible --> Ready:**
- wydarzenie I/O (wybudzane przez jądro)
sygnał (tylko w przypadku interruptible)
* **Executing -> Interruptible/Uninterruptible:**
- żadanie wykonania jakiegoś I/O
***
* **Sen przerywalny** - proces, który śpi snem przerywanym odbiera sygnały i może zostać przez nie wybudzony
* **Sen nieprzerywalny** - sygnały mogą być wysyłane do procesu, ale nie zostaną przez niego odczytane dopóki nie wystąpi zdarzenie, które wybudzi proces (sygnał nie może wybudzić procesu)
***
* **Zablokowanie sygnału** - sygnały zablokowane przez proces mogą być dostarczane, ale nie będą odbierane do czasu odblokowania sygnału
* **Zignorowanie sygnału** - proces otrzymuje informację o sygnale, ale nic z nią nie robi; proces jest wznawiany tam, gdzie został przerwany przez sygnał
Nie można zablokować ani zignorować sygnału SIGKILL.
SIGSEGV można zablokować, ale jest to nieskuteczne (w przypadku zablokowania zaraz po wysłaniu sygnału program wraca do tej samej instrukcji, która wygenerowała sygnał. W efekcie jest on wysyłany kolejny raz, a kiedy jądro wykryje taką sytuację automatycznie odblokowuje sygnał i wykonuje domyślną akcję).
SIGSEGV można zignorować (chociaż nie powinno się tego robić), ale nie umożliwi to sensownego kontynuowania programu.
## Zadanie 2





## Zadanie 3

The child inherits:
- copies of the parent's set of open file descriptors. Each file descriptor in the child refers to the same open file description as the corresponding file descriptor in the parent. This means that the two file descriptors share open file status flags, file offset, and signal-driven I/O attributes.
- copies of the parent's set of open message queue descriptors. Each file descriptor in the child refers to the same open message queue description as the corresponding file descriptor in the parent. This means that the two file descriptors share the same flags.
- The child inherits copies of the parent's set of open directory streams.
The entire virtual address space of the parent is replicated in the child, including the states of mutexes, condition variables, and other pthreads objects.
*Czemu przed wywołaniem ``fork`` należy opróżnić bufory biblioteki stdio?*
``fork`` kopiuje bufor rodzica w dziecku, więc opróżnienie tego bufora po forku sprawi, że wszystko co się tam wcześniej znajdowało wypisze się dwukrotnie.
Atrybuty przekazywane do nowego programu przez ``execve``:
- identyfikatory (PID, PPID, real user ID, real group ID, supplementary groups IDs, process group ID),
- kontrolowany terminal,
- ścieżkę roboczą (pwd) i root'a,
- blokady plików,
- maskę sygnału procesu, oczekujące symbole,
- wartości `tms_utime`, `tms_stime`, `tms_cutime`, `tms_cstime`.
*Co jądro robi w trakcie wywołania ``execve`` z konfiguracją zainstalowanych procedur obsługi sygnałów?*
Konfiguracje są resetowane do domyślnych ustawień.
## Zadanie 4

* ```xeyes``` Generuje nam parę googly eyes ktore patrzą się na myszkę 
<<CTRL+Z>> da nam możliwość pisania dalej w terminalu ale oczy przestaną patrzeć się na myszkę bo proces zostanie wstrzymany.
* Jeśli chcemy go wznowić nie blokując sobie wiersza poleceń to możemy użyć polecenia ```bg```, które uruchamia w tle pierwszy zatrzymany proces. Możemy także użyć polecenia ```fg```, które przywróci program do stanu z pierwszego wywołania.
:::spoiler <<CTRL+Z>>
Wykonując <<CTRL+Z>> wywołujemy sygnał ```SIGTSTP``` ([Istnieje jeszcze ```SIGSTOP```](https://stackoverflow.com/questions/11886812/what-is-the-difference-between-sigstop-and-sigtstp)) Główna różnica między nimi jest taka, że ```SIGSTOP``` NIE MOŻE być zignorowany. ```SIGTSTP``` MOŻE być zignorowany.

:::
* ```kill [ID procesu]```
* ```pkill [nazwaprocesu]```
* ```xkill (po czym wybiera się okno, które zabijamy)```
:::spoiler ``` man xkill```

:::
* **sygnały oczekujące** - to sygnały, których dostarczenie jest wstrzymane do momentu wyjścia przez proces z nieprzerywalnego snu
* Plik ```/proc/pid/status ``` sprawdźimy poleceniem ```cat /proc/[pid]/status``` co zwraca wynik :


* po wywołaniu ```kill -SIGUSR1 [pid]``` okienko oczu się nie zamknęło a w wydruku ```cat /proc/[pid]/status``` zmieniło się to :

* po wywołaniu ```kill -SIGUSR2 [pid]```okienko oczu się nie zamknęło a w wydruku ```cat /proc/[pid]/status``` zmieniło się to :

* po wywołaniu ```kill -SIGHUP [pid]```okienko oczu się nie zamknęło a w wydruku ```cat /proc/[pid]/status``` zmieniło się to :

* po wywołaniu ```kill -SIGINT [pid]``` okienko oczu się nie zamknęło a w wydruku ```cat /proc/[pid]/status``` zmieniło się to :

Jako pierwszy po wybudzeniu procesu wysyłany jest sygnał ```SIGHUP```:
```
[1] + 19997 continued xeyes
[1] + 19997 hangup xeyes
```
## Zadanie 5

``sinit -> suckless init``
> **Main**
```c=
#include "config.h"
static sigset_t set;
int
main(void)
{
int sig;
size_t i;
if (getpid() != 1) // jeśli PID tego procesu != 1
return 1;
chdir("/"); // przejdź do głównego(?) folder (odpowiednik cd)
sigfillset(&set); // inicjacja zestawu odbieranych sygnałów
sigprocmask(SIG_BLOCK, &set, NULL); // === !!! ===
spawn(rcinitcmd); // opisana poniżej
while (1) {
alarm(TIMEO);
//alarm() organizuje dostarczenie sygnału
//SIGALRM do procesu wywołującego, w sekundach.
//Jeśli seconds jest równe zero, to każdy oczekujący
// alarm jest anulowany. W każdym przypadku każdy
// wcześniej ustawiony alarm() jest anulowany.
sigwait(&set, &sig); // === !!! ===
for (i = 0; i < LEN(sigmap); i++) { // obsługa obecnego sygnału sig
if (sigmap[i].sig == sig) {
sigmap[i].handler();
break;
}
}
}
/* not reachable */
return 0;
}
```
* `sigprocmask()` - używana jest do pobierania i/lub zmiany maski sygnałowej. **Maska sygnałowa** jest zbiorem sygnałów, których dostarczenie jest aktualnie zablokowane dla wywołującego
* `sigwait()` - wstrzymuje wykonywanie wątku wywołującego, aż jeden z sygnałów określonych w zestawie sygnałów stanie się oczekujący. Funkcja przyjmuje sygnał (usuwa go z listy oczekujących sygnałów), i zwraca numer sygnału w sig.
``sinit`` reaguje na 4 sygnały:
```
SIGUSR1: powers off the machine.
SIGINT: reboots the machine (or alternatively via ctrl-alt-del).
SIGCHLD, SIGALRM: reap children
```
Kod źródłowy [sinit.c](https://github.com/henrysher/sinit/blob/master/sinit.c)
> **Tablica ``sigmap``**
```c=
#define LEN(x) (sizeof (x) / sizeof *(x))
#define TIMEO 30
static void sigpoweroff(void);
static void sigreap(void);
static void sigreboot(void);
static void spawn(char *const []);
static struct {
int sig;
void (*handler)(void);
} sigmap[] = {
{ SIGUSR1, sigpoweroff }, // wyłącznie komputera
{ SIGCHLD, sigreap }, // grzebanie procesu
{ SIGALRM, sigreap }, // ^
{ SIGINT, sigreboot }, // ponowne uruchamianie komputera
};
```
> **funkcja wywoływana dla sygnału *SIGUSR1***
```c=
static void sigpoweroff(void) {
spawn(rcpoweroffcmd);
}
```
> **funkcja wywoływana dla sygnałów *SIGCHLD, SIGALRM***
```c=
static void sigreap(void) {
while (waitpid(-1, NULL, WNOHANG) > 0);
// grzebanie wszystkich zombie
alarm(TIMEO);
}
==========================
pid_t waitpid(pid_t pid, int* status, int options);
```
Wywołanie systemowe `waitpid()` wstrzymuje wykonywanie wywołującego procesu do czasu, aż dziecko określone przez argument `pid` zmieni stan. Domyślnie, waitpid() czeka tylko na zakończone dzieci, ale to zachowanie jest modyfikowalne poprzez argument `options`.
`WNOHANG` -> return immediately if no child has exited.
On success, returns the process ID of the child whose state has changed; if `WNOHANG` was specified and one or more child(ren) specified by pid exist, but have not yet changed state, then 0 is returned. On error, -1 is returned.
> **funkcja wywoływana dla sygnału *SIGINT***
```c=
static void sigreboot(void) {
spawn(rcrebootcmd);
}
```
```c=
static void spawn(char *const argv[]) {
switch (fork()) {
case 0:
sigprocmask(SIG_UNBLOCK, &set, NULL);
setsid();
execvp(argv[0], argv);
perror("execvp");
_exit(1);
case -1:
perror("fork");
}
}
```
---

---
## Zadanie 6

```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(); // czekamy na sygnal
printf("(%d) Got the signal!\n", getpid());
}
static void child(void) {
pid_t pid;
/* TODO: Spawn a child! */
//If the calling process is not already a session leader, setpgrp() sets the process group ID of the calling process to the process ID of the calling process. If setpgrp() creates a new session, then the new session has no controlling terminal.
//The setpgrp() function has no effect when the calling process is a session leader.
setpgrp();
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! */
char *argv[] = {"ps", "-o", "pid,ppid,pgrp,stat,cmd", NULL};
// execvp - sprawdza PATH, używa envp = environ sposób użycia z neta :D
execvp(argv[0], argv);// execve nie działa
}
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, pgrp;
int status;
/* TODO: Start child and grandchild, then kill child!
* Remember that you need to kill all subprocesses before quit. */
printf("(%d) Spawning child\n", getpid());
pid = spawn(child);
waitpid(pid, &status, 0);
printf("(%d) Child quit with %d\n", getpid(), status);
pgrp = -pid;
printf("(%d) Spawning ps\n", getpid());
pid = spawn(ps);
waitpid(pid, &status, 0);
printf("(%d) ps quit with %d\n", getpid(), status);
printf("(%d) Killing grandchild\n", getpid());
kill(pgrp, SIGINT);
waitpid(pgrp, &status, 0);//czeka na proces który jest liderem grupy którym jest wnuk
printf("(%d) Granchild quit with %d\n", getpid(), status);
return EXIT_SUCCESS;
}
```
## Zadanie 7

## Zadanie 8
