-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c61fd1c
commit f20981d
Showing
2 changed files
with
236 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<<OFFS_WIDTH; | ||
inline void setSMTEntry(unsigned long* smt, | ||
unsigned seg, | ||
unsigned limit, | ||
unsigned long paddr, | ||
unsigned short rwx) { | ||
smt[seg] = (paddr<<(OFFS_WIDTH+3)) | (limit<<3) | rwx; | ||
} | ||
|
||
void initSegment(SegDesc* sd, unsigned long* smt) { | ||
unsigned size = sd->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 <sys/stat.h> | ||
#include <fcntl.h> | ||
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'; | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
... | ||
} | ||
``` |