---
# System prepended metadata

title: Lista 10
tags: [SO2022]

---

# Lista 10
###### tags: `SO2022`

![](https://i.imgur.com/HE79aRb.png)

:::spoiler
![](https://i.imgur.com/t8qZV4s.png)


:::

## 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:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|

:heavy_check_mark:
:::

## Zadanie 1
![](https://i.imgur.com/QzLqwfw.png)

* ### Czym różni się przetwarzanie równoległe (ang. parallel) od przetwarzania współbieżnego (ang. concurrent)?
    *  **przetwarzanie równoległe** (ang. parallel):
        Wiele wątków wykonuje się w tym samym czasie
    * **przetwarzania współbieżnego** (ang. concurrent)
        Wiele wątków wykonuje się naraz, jednak tylko jeden ma dostęp do procesora
    ![](https://i.imgur.com/vQyt2Rp.png)


* ### Czym charakteryzują się procedury wielobieżne (ang. reentrant)?
    Procedury te mogą zostać bezpiecznie przerwane np. przerwaniem, w tym czasie ta procedura może zostać wywołana ponownie, a po powrocie z nowego wyołania, pierwsze wywołanie może być bezpiecznie kontynuowane.
    Takie procedury cechują:
    * Brakiem operacji na zmiennych globalnych oraz zmiennych `static`
    * Brakiem możliwości modyfikacji swojego kodu
    * Brakiem wywołań procedur niewielobieżnych

* ### Podaj przykład procedury w języku C
    * wielobieżnej, ale nie wielowątkowo-bezpiecznej (ang. MT-safe) 
        ```c=
        int tmp;

        void swap(int* x, int* y)
        {
            int s = tmp; 
            tmp = *x;
            *x = *y;
            *y = tmp;     

            tmp = s;
        }

        isr()
        {
            int x = 1, y = 2;
            swap(&x, &y);
        }
        ```

    
    * niewielobieżnej, ale wielowątkowo-bezpiecznej
        ![](https://i.imgur.com/K0cQuNw.png)

* ### Kiedy w jednowątkowym procesie uniksowym może wystąpić współbieżność?
    Taką “współbieżność” nazywamy wielozadaniowością kooperacyjną, a najlepszym przykładem są prawdopodobnie korutyny, czyli kilka współprogramów działających w obrębie jednego procesu.


## Zadanie 2
![](https://i.imgur.com/rMVSVFD.png)

* ### Precyzyjnie opisz zjawisko:
    * zakleszczenia (ang. deadlock)
        to sytuacja, w której wątek A czeka na zakończenie operacji wątka B, a wątek B czeka na zakończenie wątka A. Program nigdy nie zakończy działania, gdyż wątki czekają na siebie nawzajem.

    * uwięzienia (ang. livelock)
        to sytuacja, w której dwa wątki zatrzymują swoje działanie aby uniknąć deadlocka, aby umożliwić innym wątkom wykonanie się, jednak robią to jednocześnie, przez co nie jest możliwe wykonanie się żadnego z nich. W przeciwieństwie do deadlocka, stan wątku może ulec zmianie.

    * głodzenia (ang. starvation) 
        to sytuacja, w której wątek nie otrzymuje dostępu do zasobu, na który oczekuje, przez co nie może rozpocząć swojego działania.

## Zadanie 3
![](https://i.imgur.com/Vwoj0Rt.png)
:::spoiler
```c=
const int n = 50;
shared int tally = 0;

void total() {
for (int count = 1; count <= n; count++)
tally = tally + 1;
}

void main() { parbegin (total(), total()); }
```
:::
2 <-> k * 50

## Zadanie 4
![](https://i.imgur.com/uAJSOQK.png)


* ### Podaj procedury wątków POSIX, które pełnią podobną funkcję co `fork(2)`, `exit(3)`, `waitpid(2)`,` atexit(3)` oraz `abort(3)` na procesach.

![](https://i.imgur.com/ddQLd2p.png)


* ### Opisz semantykę podanych funkcji i powiedz gdzie się różnią od swoich odpowiedników na procesach.
    * `pthread_create(3)`
        ```c
        int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine) (void *), void *arg);
        ```
        Tworzy nowy wątek przekazuje pointer na handler wątku, atrybuty wątku, funkcję działającą w wątku, oraz jej paramtery
    * `pthread_exit(3)`
        ```c=
        void pthread_exit(void *retval);
        ```
        Kończy działanie wątku
    * `pthread_join(3)`
        ```c
        int pthread_join(pthread_t thread, void **retval);
        ```
        Oczekujemy na zakończenie działania wątku
    * `pthread_cleanup_push(3)`
        ```c
        void pthread_cleanup_push(void (*routine)(void *), void *arg);
        ```
        Dodanie wątku na stos wywołań
    * `pthread_cancel(3)`
        ```c
        int pthread_cancel(pthread_t thread);
        ```
        Zatrzymanie wątku
    * `pthread_self(3)`
        ```c
        pthread_t pthread_self(void);
        ```
        Pobranie id wątku

* ### Porównaj zachowanie wątków złączalnych (ang. joinable) i odczepionych (ang. detached)
    * Wątki złączalne (ang. joinable) 
        Domyślny rodzaj wątków. Nie zwalniają zasobów po zakończeniu rutyny.
        Aby je zwolnić należy wykonać `pthread_join(3)` lub zostaną zwlonione przy zakończeniu programu
    * Wątki odczepione (ang. detached) 
        Po zakończeniu rutyny natychmiastowo zwalniają zasoby.
        Aby włączyć ten tryb wątku należy użyć `pthread_detach(3)`
    * to wątki, które po zakończeniu rutyny natychmiastowo zwalniają 

## Zadanie 5
![](https://i.imgur.com/fgje3hq.png)

* ###  Co nieoczekiwanego może się wydarzyć w wielowątkowym procesie uniksowym gdy:
    * jeden z wątków zawoła funkcję `fork(2)` lub `execve(2)` lub `exit_group(2)`
        * `fork(2)`: kopiuje cały proces, jednak posiada tylko jeden wątek
            zatem z uwagi, że niektóre wątki mogą być w krytycznych częściach kodu otoczonych mutexami część danych może zostać uszkodzona w procesie dziecka
        * `execve(2)`: w procesie mogą zostać otwarte deskryptory używane przez inne wątki któe nie zostaną zamknięte

    * proces zadeklarował procedurę obsługi sygnału `SIGINT`, sterownik terminala wysyła do procesu `SIGINT` – w kontekście którego wątek zostanie obsłużony sygnał?
        Zostanie wybrany arbitralnie(losowo) jeden wątek i tylko do niego zostanie wysłąny sygnał

    * określono domyślną dyspozycję sygnału `SIGPIPE`, a jeden z wątków spróbuje zapisać do rury `pipe(2)`, której drugi koniec został zamknięty?
        Odbierzemy sysgnał `SIGPIPE` i wszystkie wątki zostaną zakończone

    * czytamy w wielu wątkach ze pliku zwykłego korzystając z tego samego deskryptora pliku?
        Może dojść do systuacji gdy dwa razy przeczytamy ten sam fragment pliku mimo innej chęci.

## Zadanie 6
![](https://i.imgur.com/M082Evn.png)

* ### Opowiedz jaka motywacja stała za wprowadzeniem wywołania `poll(2)` do systemów uniksowych?
    * Możliwość efektywnego odczytywania kilku deskryptorów jednocześnie
    
* ### Czemu lepiej konstruować oprogramowanie w oparciu o `poll`, niż o odpytywanie deskryptorów albo powiadamianie o zdarzeniach bazujące na sygnale `SIGIO`?
    * SIGIO nie mówi nam który deskryptor jest gotowy do odczytu
    * Odpytywanie deskryptorów marnuje cykle procesora.

* ### Na jakich plikach można oczekiwać na zdarzenia przy pomocy `poll(2)`?
 - pliki zwykłe
 - terminal (tutaj trzeba uważać bo jeżeli zamknięta zostanie połowa terminala to zwracana wartość zależy od implementacji)
 - pipe
 - sockety


* ### Czemu wszystkie deskryptory przekazywane do `poll` powinno skonfigurować się do pracy w trybie nieblokującym?
- ![](https://i.imgur.com/FbMJ455.png)
kocham ten przedmiot
- Jak poll wróci to mamy pewność, że fd X może być przeczytany raz bez blokowania. To znaczy, że na każdy read przypada 2 polle. Jeśli mamy nieblokujące fd to czytamy ile wlezie i ignorujemy EWOULDBLOCK na końcowym readzie. Jest jeszcze problem taki, że poll wróci, bo mamy klienta do zaakceptowania. Wtedy klient się rozłącza, a my blokujemy się w accept()
PS to jest od tamtych

* ### Jak zapewnić, żeby wywołania `connect(2)`, `accept(2)`, `read(2)` i `write(2)` na gnieździe sieciowym zawsze zwróciły `EWOULDBLOCK` zamiast blokować się w jądrze?
- read, write i accept: wystarczy ustawić flagę non-block na deskryptorze socketa
- Connect według manuala nie może zwrócić EWOULDBLOCK (zamiast tego  jeżeli socket jest nieblokujący zwraca EINPROGRESS)

* ### Chcemy by po wywołaniu `poll(2)` pierwsza instancja wyżej wymienionych wywołań systemowych nie zwróciła `EWOULDBLOCK`. Jaka wartość musi być wpisana przez jądro do pola `revents` struktury `pollfd` dla danego deskryptora pliku, żeby mieć tę pewność?

- read i accept: POLLIN
- write: POLLOUT
- connect: ???
## Zadanie 7
![](https://i.imgur.com/K5KZl33.png)

```c=
#include "csapp.h"
#include "rio.h"

#define THREADMAX 100
#define ITERMAX 50

/* Read whole file into process address space.
 * Make sure it ends with a newline and NUL character. */
static char *readtext(const char *path) {
  int fd = Open(path, O_RDONLY, 0);

  struct stat sb;
  Fstat(fd, &sb);

  size_t len = sb.st_size;
  char *data = Malloc(len + 2);
  Read(fd, data, len);

  Close(fd);

  if (data[len - 1] != '\n')
    data[len++] = '\n';
  data[len] = '\0';

  return data;
}

typedef struct line {
  const char *data;
  size_t length;
} line_t;

static const size_t splitlines(const char *text, line_t **linesp) {
  size_t capacity = 10;
  size_t nlines;

  line_t *lines = Malloc(capacity * sizeof(line_t));
  
  for (nlines = 0; *text; nlines++) {
    /* Grow lines array if needed. */
    if (nlines == capacity) {
      capacity *= 2;
      lines = Realloc(lines, capacity * sizeof(line_t));
    }

    const char *begin = text;

    /* Advance to the next line. */
    while (*text && *text != '\n')
      text++;

    /* Skip newline character. */
    if (*text == '\n')
      text++;

    lines[nlines].data = begin;
    lines[nlines].length = text - begin;
  }

  /* Trim lines array down. */
  *linesp = Realloc(lines, nlines * sizeof(line_t));
  return nlines;
}

static sig_atomic_t quit = false;

static void sigint_handler(int sig) {
  safe_printf("Quit requested!\n");
  quit = true;
}

static char *c_host = NULL;
static char *c_port = NULL;
static line_t *c_lines = NULL;
size_t c_nlines = 0;

static void *thread(void *data) {
  unsigned seed = (unsigned)pthread_self();
  char *buf = NULL;
  size_t buflen = 0;

  while (!quit) {
    int fd = Open_clientfd(c_host, c_port);

    for (int i = 0; i < ITERMAX && !quit; i++) {
      line_t *line = &c_lines[rand_r(&seed) % c_nlines];

      if (buflen < line->length) {
        buflen = line->length;
        buf = Realloc(buf, buflen);
      }

      Rio_writen(fd, (void *)line->data, line->length);
      Rio_readn(fd, buf, line->length);
      assert(memcmp(buf, line->data, line->length) == 0);
      usleep(1000 * (rand_r(&seed) % 50));
    }

    Close(fd);
  }

  free(buf);
  return NULL;
}

int main(int argc, char **argv) {
  if (argc != 5)
    app_error("usage: %s <textfile> <nthreads> <host> <port>\n", argv[0]);

  size_t nthreads = atol(argv[2]);
  assert(nthreads > 0 && nthreads <= THREADMAX);

  char *text = readtext(argv[1]);

  c_nlines = splitlines(text, &c_lines);
  c_host = argv[3];
  c_port = argv[4];

  Signal(SIGINT, sigint_handler);

  /* TODO: Start threads and wait for them to finish. */
  pthread_t tid[nthreads];

  for (int i = 0; i < nthreads; i++)
    Pthread_create(&tid[i], NULL, thread, NULL);

  for (int i = 0; i < nthreads; i++)
    Pthread_join(tid[i], NULL);
  /* TODO: END */

  free(text);
}
```
## Zadanie 8
![](https://i.imgur.com/t0YxuHW.png)

```c=
#include "csapp.h"
#include "rio.h"

#define LISTENQ 10

typedef struct client {
  size_t id;
  rio_t rio;
} client_t;

static size_t clientid = 1;
static client_t **clients;
static struct pollfd *fds;
static nfds_t nfds = 0;
static nfds_t maxfds = 0;

static sig_atomic_t quit = false;
static size_t nbytes = 0; /* Counts total bytes received by server */

static void initclients(int listenfd) {
  nfds = 1;
  maxfds = 1;
  fds = Malloc(sizeof(struct pollfd));
  fds[0].fd = listenfd;
  fds[0].events = POLLIN;
}

static void addclient(int connfd, const char *hostname, const char *port) {
  printf("[%ld] Connected to %s:%s\n", clientid, hostname, port);

  /* Should expand space to accommodate for new client ? */
  if (maxfds == nfds) {
    maxfds++;
    fds = Realloc(fds, sizeof(struct pollfd) * maxfds);
    clients = Realloc(clients, sizeof(client_t *) * maxfds);
    clients[maxfds - 1] = Malloc(sizeof(client_t));
  }

  int i = nfds++;
  fds[i].fd = connfd;
  fds[i].events = POLLIN; /* Wait only for input events! */
  fds[i].revents = 0;

  client_t *c = clients[i];
  c->id = clientid++;
  rio_readinitb(&c->rio, connfd);
}

static void delclient(int i) {
  assert(i > 0);
  Close(fds[i].fd);
  printf("[%ld] Disconnected!\n", clients[i]->id);

  if (i < nfds - 1) {
    fds[i] = fds[nfds - 1];

    /* Swap slots so that prefix of array comprises only active clients. */
    client_t *tmp = clients[i];
    clients[i] = clients[nfds - 1];
    clients[nfds - 1] = tmp;
  }

  nfds--;
}

static int clientread(int i) {
  char buf[MAXLINE];
  int n = Rio_readlineb(&clients[i]->rio, buf, MAXLINE);

  if (n > 0) {
    Rio_writen(fds[i].fd, buf, n);
    nbytes += n;
    printf("[%ld] Received %d (%ld total) from client\n",
           clients[i]->id, n, nbytes);
  }

  return n;
}

static void sigint_handler(int sig) {
  safe_printf("Server received quit request!\n");
  quit = true;
}

int main(int argc, char **argv) {
  if (argc != 2)
    app_error("usage: %s <port>\n", argv[0]);

  Signal(SIGINT, sigint_handler);

  int listenfd = Open_listenfd(argv[1], LISTENQ);
  initclients(listenfd);

  while (!quit) {
    int nready = Poll(fds, nfds, 500);
    if (nready == 0)
      continue;

    /* TODO: If listening descriptor ready, add new client to the pool. */
    if(fds[0].revents & POLLIN){
      char host[MAXLINE];
      char port[MAXLINE];
      socklen_t clientlen = sizeof(struct sockaddr);
      struct sockaddr clientaddr;
      int connfd;
      
      connfd = Accept(listenfd, &clientaddr, &clientlen);
      Getnameinfo(&clientaddr, clientlen, host, MAXLINE, port, MAXLINE, 0);
      addclient(connfd,host,port);
      nready--;
    }

    /* TODO: Echo a text line from each ready connected descriptor.
     * Delete a client when end-of-file condition was detected on socket. */
    int i = 1;
    while (nready > 0) {
      if(fds[i].revents & POLLIN){
        if(clientread(i) == 0)
          delclient(i--);
        nready--;
      }
      i++;
    }
  }

  printf("Server received %ld total bytes.\n", nbytes);
  return EXIT_SUCCESS;
}
```