# Ćwiczenia 0, grupa cz. 10-12, 6 października 2022
###### tags: `SO22` `ćwiczenia` `pwit`
## Deklaracje
Gotowość rozwiązania zadania należy wyrazić poprzez postawienie X w odpowiedniej kolumnie! Jeśli pożądasz zreferować dane zadanie (co najwyżej jedno!) w trakcie dyskusji oznacz je znakiem ==X== na żółtym tle.
**UWAGA: Tabelkę wolno edytować tylko wtedy, gdy jest na zielonym tle!**
:::danger
| | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| ----------------------:| ----- | --- | --- | --- | --- | --- | --- | --- |
Miriam Bouhajeb | X | X | X | X | X | | X | X | X
Kacper Chmielewski | X | X | | | X | X | | |
Jakub Gałaszewski | X | X | X | |X | | | |
Kacper Jóźwiak | | | | | | | | |
Jakub Kaczmarek | | | | | | | | |
Jarosław Kadecki | | | | | | | | |
Zuzanna Kania | X | | | | | | | X |
Julia Konefał | X | X | X | | X | | | |
Michał Mękarski | | | | | | | | |
Natalia Piasta | | | | | | | | |
Daniel Sarniak | X | X | X | X | X | X | | |
Paweł Tkocz | X | X | | | X | | | |
Miłosz Urbanik | | | | | | | | |
Tomasz Wołczański | X | | X | | | | | |
Radosław Śliwiński | X | X | X | X | X | | X | |
:::
:::info
**Uwaga:** Po rozwiązaniu zadania należy zmienić kolor nagłówka na zielony.
:::
## Zadanie 1
:::success
Autor: Jakub Gałaszewski

**przerwanie sprzętowe (hardware interrupt)** - jest to asynchroniczne przerwanie, które jest nadsyłane przez urządzenia zewnętrzne, tj. spoza procesora. Po tym przerwaniu następuje przejście do kolejnej instrukcji. Przykłady to:
- klikanie w przyciski na klawiaturze
- odczyt ruchu myszki
- odczyt z napędu cd
**wyjątek procesora (exception)** - jest to nagła zmiana control flow'u w odpowiedzi na zmianę stanu procesora. Przykłady:
- dzielenie przez zero
- page fault pamięci wirtualnej
- overflow
**pułapka (trap)** - jest to intencjonalny wyjątek, wynik jakiejś instrukcji. Jest ona synchroniczna. wywołanie jej przekazuje kontrolę do trap handler'a, który posiada uprawnienia do kernel mode'a. Również wyjątek nie oznacza błędu podczas wykonania programu kiedy wydarzenie jest niezwiązane z wykonywaniem jakiejś instrukcji.
:::
## Zadanie 2
:::success
Autor: Kacper Chmielewski


:::
## Zadanie 3
:::success
Autor: Tomasz Wołczański

Składowe pliku wykonywalnego:
* nagłówek ELF - ogólne dane o pliku, adres pierwszej instrukcji programu 
* tablica nagłówków programu - tablica przechowująca informacje o segmentach (typ, offset, adres wirtualny i fizyczny, rozmiar w pliku i rozmiar po załadowaniu do pamięci, flagi, wyrównanie) 
* sekcje - .init, .text, .rodata (read only); .data, .bss (read/write); .symtab, .debug, .strtab (nieładowane do pamięci)
* tablica nagłówków sekcji - tablica przechowująca informacje o sekcjach (nazwa, typ, adres, offset względem początku pliku, rozmiar, rozmiar rekordu, flagi, wyrównanie, dodatkowe informacje) 
Segmenty zawierają sekcje. Poleceniem `readelf` można sprawdzić przyporządkowanie sekcji do segmentów:

O tym, pod jakim adresem powinien zostać umieszczony dany segment, mówi pole p_vaddr rekordu tablicy nagłówków programu:

Adres pierwszej instrukcji programu znajduje się w nagłówku ELF:

:::
## Zadanie 4
:::success
Autor: Miriam Bouhajeb
Przestrzeń adresowa - mapa możliwej do zaadresowania przez procesor pamięci, dla danego procesu.

Jądro musi załadować adresy, które zostały explicite wyznaczone
do przekazania, typu %rsp (stos) do %r15, oraz floating point control register, który jest albo resetowany do stanu 0 (round to nearest), albo jest dziedziczony od procesu rodzica.
Auxilliary vector - tablica struktur w formacie klucz-wartość,
która przekazuje informacje między systemem operacyjnym a programem. Te informacje mówią o środowisku w którym odbywa się proces, np o zmiennych środowiskowych, UID, EUID, miejsce w pamięci gdzie interpreter został załadowany.
Funkcje jądra wywołuje się instrukcją syscall. Argumenty powinny być umieszczone w rejestrach %rdi %rsi, %rdx, %r10, %r8 i %r9.
W rejestrze %rax umieszczamy numer syscalla dla jądra. Wyników możemy spodziewać się w rejestrze %rax, a niepowodzenie sygnalizuje umieszczeniem tam wartości p.
Wywołanie systemowe - mechanizm, który pozwala na komunikowanie się między procesem a systemem operacyjnym. Pozwala on na wysyłprogramy.
:::
## Zadanie 5
:::success
Autor: Paweł Tkocz

Słowo kluczowe volatile mówi kompilatorowi, że wartość zmiennej może ulec zmianie w dowolnej chwili, niekoniecznie jako wynik wykonania kodu programu, w którym występuje ta zmienna.
Przykład użycia:
volatile int x;
Scenariusz 1:
Systemy wbudowane mają część pamięci określaną jako peryferie, która zawiera wartości rejestrów – ich wartość może zmieniać się asynchronicznie. Załóżmy, że rejestr stanu znajduje się w pamięci pod adresem 0x1234, a jego wartość wynosi 0. Załóżmy, że chcemy napisać program, który wykonuje pętle dopóki wartość tego rejestru nie ulegnie zmianie.
uint8_t * reg = (uint8_t *) 0x1234;
do {…} while (*reg == 0)
Takie rozwiązanie nie zadziała, ponieważ kompilator widzi, że wartość pod adresem wskazywanym przez reg nie jest nigdy modyfikowana w kodzie pętli, zatem pętla będzie wykonywać się w nieskończoność. Poprawne rozwiązanie miałoby postać:
volatile uint8_t * reg = (uint8_t *) 0x1234;
do {…} while (*reg == 0)
Teraz kompilator wie, że choć wartość pod adresem reg nie zmienia się w wyniku wykonania pętli, to może zmienić się asynchronicznie.
Scenariusz 2:
Zmienna globalna może być widoczna i modyfikowana przez różne wątki, w wielowątkowej aplikacji. Analogicznie jak dla przykładu powyżej, jeśli mielibyśmy zmienną globalną:
sig_atomic_t x;
Której wartość jest początkowo ustawiona na 0, a potem kod:
do {…} while (x == 0)
Oraz inny fragment kodu, który wykonuje
x++
To pętla while będzie wykonywała się w nieskończoność, jako że wartość x nie ulega zmianie w jej ciele. Może jednak ulec zmianie w wyniku działania instrukcji x++ innego wątku. Aby kompilator wziął to pod uwagę należy dodać do zmiennej x słowo kluczowe volatile.
:::
## Zadanie 6
:::success
Autor: Daniel Sarniak

**Tłumaczenie adresów** to proces odczytywania adresu fizycznego przy pomocy adresu wirtualnego.
**Adres fizyczny** adres wskazujący na miejsce w pamięci operacyjnej
**Adres wirtualny** adres niezalezny od fizycznego rozmiaru pamięci operacyjnej, odwzorowywany na adres fizyczny.
| 16 | 9 | 9 | 9 | 9 | 12 |
| --:| ----- | --- | --- | --- | --- |
| --- | VPN 1 | VPN 2 | VPN 3 | VPN 4 | VPO |
`%cr3` zawiera fizyczny adres początku poziomu 1 tablicy stron

PTE poziomu 1,2,3 zawierają fizyczne adresy bazowe tablicy stron. Każdy wpis wskazuje na 4KB tablicę stron.
PTE 4 zawiera fizyczny adres bazowy. Każdy wpis wskazuje na 4KB stronę.
Algorytm:
L1 PTE = %cr3 + index z VPN1
L2 PTE = Adres L1 PTE + index z VPN2
L3 PTE = Adres L2 PTE + index z VPN3
L4 PTE = Adres L3 PTE + index z VPN4
PTE = L4 PTE
Adres = PTE | PPO
**TLB**: hardware cache dla tablicy stron
– służy do mapowania adresów wirtualnych na adresy fizyczne bez pomocy tablicy stron. Zwykle jest ono zlokalizowane wewnątrz jednostki MMU i składa się z niewielkiej liczby pozycji - każda pozycja zawiera informacje dotyczące jednej strony.
:::
## Zadanie 7
:::success
Autor: Radosław Śliwiński

syscall brk zmienia lokalizację `program break`, który definiuje koniec segmentu danych danego procesu. Ma to efekt zwiększenia lub zmniejszenia pamięci przydzielonej danemu procesowi.

brk i sbrk manipulują rozmiarem kopca progamu (heap).
:::
## Zadanie 8
:::success
Autor: Zuzanna Kania

Zadanie polega na sprawdzeniu działania programu z polecenia.
Ważne informacje nt. funkcji systemowych `read` i `write`:
Funkcje te przyjmują trzy argumenty. Są to kolejno deskryptor pliku, bufor oraz rozmiar bufora. Jeżeli chodzi o deskryptor pliku to standardowo:
0 - stdin
1 - stdout
2 - stderr
natomiast kolejne numery przypadają plikom otwartym przez program.
Funkcje `read` i `write` zwracają liczbę przeczytanych/zapisanych bitów w przypadku braku błędu. Natomiast, gdy zakończą się błędem, zawracane jest `-1` oraz odpowiedni kod błędu.
Zmodyfikowany program:
```lang=c
#include "apue.h"
#include <stdio.h>
#include <fcntl.h>
#define BUFFSIZE 4096
int main(int argc, char *argv[]) {
int n;
char buf[BUFFSIZE];
int fd = open(argv[1], O_RDONLY);
while ((n = read(fd, buf, BUFFSIZE)) > 0)
if (write(1, buf, n) != n)
err_sys("write error");
if (n < 0)
err_sys("read error");
close(fd);
exit(0);
}
```
:::