From f20981dd3ac03e0c1c7c61bb0f916ace75f7c702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luka=20Simi=C4=87?= Date: Mon, 21 Jun 2021 13:34:37 +0200 Subject: [PATCH] June 2021. --- md/2021/6/k-sol.md | 139 +++++++++++++++++++++++++++++++++++++++++++++ md/2021/6/k.md | 97 +++++++++++++++++++++++++++++++ 2 files changed, 236 insertions(+) create mode 100644 md/2021/6/k-sol.md create mode 100644 md/2021/6/k.md diff --git a/md/2021/6/k-sol.md b/md/2021/6/k-sol.md new file mode 100644 index 0000000..bee3fd7 --- /dev/null +++ b/md/2021/6/k-sol.md @@ -0,0 +1,139 @@ +2021/jun/Kolokvijum - Jun 2021 - Resenja.pdf +-------------------------------------------------------------------------------- +segment +- VA(32): Segment(12). Offset(20) +- PA(40) + +```cpp +const unsigned SEG_WIDTH = 12, OFFS_WIDTH = 20, + MAX_SEG_SIZE = 1U<size; + unsigned saddr = sd->startAddr; + while (size > 0) { + setSMTEntry(smt, saddr>>OFFS_WIDTH, + (size>MAX_SEG_SIZE?MAX_SEG_SIZE-1:size-1), 0, sd->rwx); + if (size < MAX_SEG_SIZE) break; + size -= MAX_SEG_SIZE; + saddr += MAX_SEG_SIZE; + } +} +``` + +-------------------------------------------------------------------------------- +semimpl +```cpp +void Semaphore::wait() { + lock(lck); + Thread* oldRunning = Thread::runningThread; + if (--val < 0) + this->blocked.put(oldRunning); + else + Scheduler::put(oldRunning); + Thread* newRunning = Thread::runningThread = Scheduler::get(); + if (oldRunning != newRunning) + Thread::yield(oldRunning, newRunning); + unlock(lck); +} + +void Semaphore::signal() { + lock(lck); + if (++val <= 0) + Scheduler::put(this->blocked.get()); + Thread* oldRunning = Thread::runningThread; + Scheduler::put(oldRunning); + Thread* newRunning = Thread::runningThread = Scheduler::get(); + if (oldRunning != newRunning) + Thread::yield(oldRunning, newRunning); + unlock(lck); +} +``` + +-------------------------------------------------------------------------------- +ioblock +```cpp +Byte* BlockIOCache::getBlock(BlkNo blk) { + // Find the requested block in the cache and return it if present: + int entry = hash(blk); + for (int i = map[entry]; i != -1; i = entries[i].next) { + if (entries[i].blkNo==blk) { + entries[i].refCounter++; + return entries[i].buf; + } + } + // The block is not in the cache, find a free slot to load it: + if (freeHead == -1) evict(); + if (freeHead == -1) return 0; // Error: cannot find space + int free = freeHead; + freeHead = entries[freeHead].next; + // Load the requested block: + entries[free].blkNo = blk; + entries[free].refCounter = 1; + entries[free].next = map[entry]; + map[entry] = free; + ioRead(dev, blk, entries[free].buf); + return entries[free].buf; +} +``` + +-------------------------------------------------------------------------------- +fsintr +```cpp +#include +#include + +class IFStream { +public: + IFStream(const char* path); + ~IFStream() { + if (fd >= 0) close(fd); + } + bool eof() const { return isEOF; } + bool err() const { return isErr; } + char getc(); +protected: + inline void fetch(); +private: + static const size_t BLOCK_SIZE = ..., CHAR_SIZE = 2; + int fd; + bool isEOF, isErr; + char buffer[(BLOCK_SIZE + CHAR_SIZE – 1)/CHAR_SIZE]; + ssize_t curPos, size; +}; + +IFStream::IFStream(const char* path) : isEOF(false), isErr(false), + curPos(-1), size(-1) { + fd = open(path, O_RDONLY); + if (fd == -1) { + isErr = isEOF = true; + return; + } + fetch(); +} + +inline void IFStream::fetch() { + size = read(fd, buffer, BLOCK_SIZE); + if (size == -1) isErr = true; + if (size <= 0) isEOF = true; + curPos = 0; +} + +char IFStream::getc() { + if (!isErr && !isEOF && curPos < size) { + char c = buffer[curPos++]; + if (curPos >= size) + if (size < BLOCK_SIZE) isEOF = true; + else fetch(); + return c; + } + return '\-1'; +} +``` diff --git a/md/2021/6/k.md b/md/2021/6/k.md new file mode 100644 index 0000000..b872b66 --- /dev/null +++ b/md/2021/6/k.md @@ -0,0 +1,97 @@ +2021/jun/Kolokvijum - Jun 2021.pdf +-------------------------------------------------------------------------------- +segment +Neki sistem ima segmentnu organizaciju memorije. Virtuelna adresa je 32-bitna, adresibilna jedinica je bajt, a maksimalna veličina fizičkog segmenta je 1 MB. Fizički adresni prostor je veličine 1 TB. Jedan ulaz u SMT-u sadrži prava pristupa *rwx* u najniža tri bita, granicu fizičkog segmenta (*limit* u opsegu od 0 do maksimalne veličine segmenta minus 1) u bitima do njih, a zatim fizičku adresu u bitima do njih; ovaj deskriptor (ulaz u SMT-u) zauzima najmanji potreban ceo broj bajtova. + +Operativni sistem alocira segmente na zahtev, tako da pri kreiranju procesa ne alocira i ne učitava nijedan fizički segment. Vrednost 0 u polju za fizičku adresu u deskriptoru segmenta u SMT-u označava da preslikavanje nije moguće (segment nije alociran ili nije učitan). + +Jedan alociran logički segment procesa opisan je deskriptorom tipa `SegDesc` u kom su, između ostalog, sledeća polja: + +- `unsigned startAddr`: početna virtuelna adresa logičkog segmenta, svakako poravnata na početak fizičkog segmenta; +- `unsigned size`: veličina logičkog segmenta u bajtovima (može biti i veća od maksimalne veličine fizičkog segmenta); +- `unsigned short rwx`: prava pristupa za ceo logički segment u tri najniža bita. + +Implementirati sledeću funkciju: +```cpp +void initSegment (SegDesc* sd, unsigned long* smt); +``` +Ovu funkciju poziva kod kernela kada inicijalizuje SMT novokreiranog procesa za svaki logički segment sa datim deskriptorom `sd`. Na već alociran SMT ukazuje `smt`. Veličine tipova su sledeće: `int` – 32 bita, `long` – 64 bita, `short` – 16 bita. + +-------------------------------------------------------------------------------- +semimpl +U Školskom jezgru implementirana je statička operacija klase `Thread` +```cpp +void Thread::yield (Thread* oldRunning, Thread* newRunning); +``` +koja obavlja promenu konteksta oduzimajući procesor (tekućoj) niti na koju pokazuje parametar `oldRunning` i predajući procesor niti na koju pokazuje parametar `newRunning`. Korišćenjem ove operacije implementirati operacije `wait` i `signal` klase `Semaphore`, s tim da se uvek, pa i kod neblokirajućih poziva obavlja promena konteksta ukoliko raspoređivač odabere neku drugu nit za izvršavanje. U redu spremnih uvek se nalazi barem neka nit, makar nit *idle* koja ne radi ništa korisno (samo troši procesorsko vreme instrukcijama bez efekta). + +-------------------------------------------------------------------------------- +ioblock +U nekom sistemu implementira se keš blokova sa blokovskih uređaja („diskova“) kodom koji je dat u nastavku. Za svaki uređaj sa datim identifikatorom pravi se jedan objekat klase `BlockIOCache`, inicijalizovan tim identifikatorom, koji predstavlja keš blokova sa tog uređaja. Keš je kapaciteta `CACHESIZE` blokova veličine `BLKSIZE`. Keš je interno organizovan kao heš mapa `map` sa `MAPSIZE` ulaza. Svaki ulaz niza `map` sadrži glavu liste keširanih blokova koji se preslikavaju u taj ulaz. Funkcija `hash` je heš funkcija koja preslikava broj bloka u ulaz u nizu `map`. Glava liste, kao i pokazivač na sledeći element u listi čuvaju se kao indeksi elementa niza `entries` koji sadrži keširane blokove; vrednost -1 označava kraj (*null*). Svaki element niza `entries` je struktura tipa `CacheEntry` u kojoj je polje `blkNo` broj bloka koji je keširan u tom elementu, polje `next` ukazuje na sledeći element liste, a polje `buf` je sadržaj samog bloka. + +Na početku složene operacije sa uređajem, kod koji koristi keš najpre traži da je potrebni blok učitan pozivom funkcije `getBlock` koja vraća pokazivač na niz bajtova u baferu – učitanom bloku. Pošto više ovakvih složenih operacija može biti ugnježdeno, blok iz keša može biti izbačen (zamenjen drugim) samo ako ga više niko ne koristi, što se realizuje brojanjem referenci u polju `refCounter` strukture `CacheEntry`. + +Član `freeHead` je glava liste slobodnih elemenata u nizu `entries` (-1 ako slobodnih nema). Funkcija `evict` izbacuje jedan keširani blok iz punog keša, ukoliko takav može da nađe, oslobađa njegov ulaz i stavlja ga u listu slobodnih. + +Implementirati funkciju `getBlock` koja treba da obezbedi da je traženi blok u kešu, odnosno učita ga ako nije. Ako nema mesta u kešu jer nijedan blok ne može da se izbaci, treba vratiti *null*. Ostale članice date klase su implementirane, a na raspolaganju je i funkcija koja učitava blok na dati uređaj: +```cpp +void ioRead(int device, BlkNo blk, Byte* buffer); + +typedef unsigned char Byte; // Unit of memory +typedef long long BlkNo; // Device block number +const unsigned BLKSIZE = ...; // Block size in Bytes +class BlockIOCache { +public: + BlockIOCache(int device); + Byte* getBlock(BlkNo blk); + ... +protected: + static int hash(BlkNo); + void evict(); +private: + static const unsigned CACHESIZE = ...; // Cache size in blocks + static const unsigned MAPSIZE = ...; // Hash map size in entries + struct CacheEntry { + BlkNo blkNo; + int next; + int refCounter; + Byte buf[BLKSIZE]; + }; + int dev; + int map[MAPSIZE]; // Hash map + CacheEntry entries[CACHESIZE]; // Cache + int freeHead; // Free entries list head +}; +``` + +-------------------------------------------------------------------------------- +fsintr +Na raspolaganju je POSIX API za fajlove: +```cpp +int open(const char* path, int flags); +int close(int fd); +ssize_t read(int fd, void *buf, size_t count); +``` +Tip `ssize_t` je označen celobrojni tip, isti kao `size_t`, samo što može da prihvati i vrednost -1. Funkcija `read` vraća stvarno učitan broj bajtova (0 ili više); ako je taj broj manji od `count`, stiglo se do kraja fajla. Funkcije `open`, `close` i `read` vraćaju -1 u slučaju greške. + +Korišćenjem ovog interfejsa realizovati apstrakciju `IFStream` kao klasu čiji je interfejs dat dole. Ova klasa apstrahuje ulazni znakovni tok vezan za fajl. Staza do fajla zadaje se parametrom konstruktora. Zatvaranje fajla treba obezbediti destruktorom. Operacija `getc` vraća jedan znak učitan sa toka. Kada se došlo do kraja fajla, operacija `eof` vraća `true`. Ukoliko je u nekoj ranijoj operaciji sa tokom, uključujući i otvaranje, došlo do greške, funkcije `err` i `eof` vraćaju `true`. Ukoliko je tok u stanju greške ili se stiglo do njegovog kraja, operacija `getc` treba da vraća `'\-1'`. Učitavanje treba da bude što je moguće efikasnije u smislu da se sa uređaja učitava najmanji mogući broj blokova za sekvencijalni pristup. Veličina bloka u bajtovima je `BLOCK_SIZE`, a veličina znakova u bajtovima je `CHAR_SIZE`. +```cpp +class IFStream { +public: + IFStream(const char* path); + ~IFStream(); + bool eof() const; + bool err() const; + char getc(); +private: + static const size_t BLOCK_SIZE = ..., CHAR_SIZE = 2; +}; +``` +Ova apstrakcija može se koristiti ovako: +```cpp +IFStream str("myfile.txt"); +while (!str.err() && !str.eof()) { + char c = str.getc(); + ... +} +```