OPERATIVNI SISTEMI
(problemi i struktura)
Miroslav Hajduković
ELEKTRONSKO IZDANJE
VERZIJA 2
NOVI SAD, 2004.
PREDGOVOR
U ovoj knjizi se izlažu struktura operativnih sistema, principi njihovog
funkcionisanja, problemi vezani za pravljenje operativnih sistema, kao i načini
rešavanja ovih problema. Deo objašnjenja, navedenih u ovoj knjizi, je u tradiciji
operativnog sistema UNIX.
Podrazumeva se da čitalac ove knjige poznaje arhitekturu računara i
programski jezik C++.
Autor se zahvaljuje svom saradniku Žarku Živanovu za uloženi napor
prilikom izrade crteža, kao i za pažljivo čitanje rukopisa i ukazivanje na mnoštvo
grešaka. Na veliki broj grešaka u rukopisu su ukazali i saradnici Zorica Suvajdžin i
Predrag Rakić, pa i njima autor duguje veliku zahvalnost.
Verzija 2 ove knjiga je nastala ispravljanjem grešaka koje su uočene u tekstu
njene prve verzije, kao i pojašnjavanjem i proširenjem delova ovog teksta koji su
bili nedovoljno razumljivi studentima.
SADRŽAJ
1.
UVOD
1.1 ZADATAK OPERATIVNOG SISTEMA
1.2 POJAM DATOTEKE
OZNAČAVANJE DATOTEKA
ORGANIZACIJA DATOTEKA
ZAŠTITA DATOTEKA
1.3 POJAM PROCESA
STANJA I PRIORITET PROCESA
ULOGA PROCESA
POJAM NITI
1.4 STRUKTURA OPERATIVNOG SISTEMA
MODUL ZA RUKOVANJE PROCESOROM
MODUL ZA RUKOVANJE KONTROLERIMA
MODUL ZA RUKOVANJE RADNOM MEMORIJOM
MODUL ZA RUKOVANJE DATOTEKAMA
MODUL ZA RUKOVANJE PROCESIMA
SLOJEVITI OPERATIVNI SISTEM
SISTEMSKI POZIVI
INTERAKCIJA KORISNIKA I OPERATIVNOG SISTEMA
1.5 PITANJA
1
1
1
1
2
4
7
7
7
8
9
9
9
10
10
10
11
12
13
13
2.
KONKURENTNO PROGRAMIRANJE
2.1 SVOJSTVA KONKURENTNIH PROGRAMA
2.2 PRIMERI ŠTETNIH PREPLITANJA
2.3 SPREČAVANJE ŠTETNIH PREPLITANJA
MEĐUSOBNA ISKLJUČIVOST
KRITIČNI REGIONI
SINHRONIZACIJA
ATOMSKI REGIONI
PROPUSNICE
ISKLJUČIVI REGIONI
ATOMSKE KLASE
ISKLJUČIVE KLASE
SEMAFOR
2.4 POŽELJNE OSOBINE KONKURENTNIH PROGRAMA
2.5 PROGRAMSKI JEZICI ZA KONKURENTNO
PROGRAMIRANJE
2.6 KONKURENTNA BIBLIOTEKA COLIBRY (COncurrent
LIBraRY )
2.7 PREGLED KORISNIČKOG INTERFEJSA IZ COLIBRY.H
DATOTEKE
OZNAČAVANJE NITI
STVARANJE NITI
UNIŠTAVANJE NITI
SVOJEVOLJNO PREKLJUČIVANJE NITI
INICIJALNA NIT
SISTEMSKO VREME
ATOMSKI REGION
ATOMSKE KLASE
ISKLJUČIVI REGION
ISKLJUČIVE KLASE
2.8 PREGLED KORISNIČKOG INTERFEJSA IZ COLIB_IO.H
DATOTEKE
RUKOVANJE TERMINALOM
RUKOVANJE DISKETNOM JEDINICOM
2.9 PRIMERI SPREČAVANJA ŠTETNIH PREPLITANJA
2.10 RIZICI KONKURENTNOG PROGRAMIRANJA
2.11 NAPOMENE O KONKURENTNOJ BIBLIOTECI COLIBRY
2.12 VERZIJA 2.0 KONKURENTNE BIBLIOTEKE COLIBRY
2.13 PITANJA
15
15
15
19
19
19
20
20
20
21
21
22
23
23
24
24
25
25
26
27
28
28
29
30
31
34
35
38
38
40
41
45
45
50
51
3.
PRIMERI KONKURENTNIH PROGRAMA
3.1 PREDSTAVA VREMENA BROJEM SATI, MINUTA I
SEKUNDI
3.2 PRIKAZ I IZMENA VREMENA
3.3 KOMUNIKACIONI KANAL KAPACITETA JEDNE
PORUKE
3.4 PRVI PRIMER SIMULACIJE
3.5 DRUGI PRIMER SIMULACIJE
3.6 SEMAFORI
3.7 RAZMENA PORUKA ZASNOVANA NA MEHANIZMU
SEMAFORA
3.8 PROBLEM PET FILOZOFA
3.9 PROBLEM ČITANJA I PISANJA
3.10 UPRAVLJANJE KRETANJEM GLAVE ZA ČITANJE I
PISANJE DISKA
3.11 PARALELNO PROGRAMIRANJE
3.12 VEZIVANJE NITI U NIZ
3.13 PARALELNO SORTIRANJE
3.14 VEZIVANJE NITI U MATRICU
3.15 PARALELNO MNOŽENJE MATRICA
3.16 PARALELNO IZDVAJANJE KONTURE
3.17 VEZIVANJE NITI U DRVO
3.18 PARALELNO SABIRANJE BROJEVA
3.19 KOMUNIKACIONI KANAL KAPACITETA VIŠE PORUKA
3.20 POTPUNO MEĐUSOBNO POVEZIVANJE NITI
3.21 PARALELNO ODREĐIVANJE NAJKRAĆIH
UDALJENOSTI PAROVA ČVOROVA USMERENOG
GRAFA
3.22 PARALELNO IZRAČUNAVANJE POVRŠINE ISPOD
POLUKRUGA
3.23 PITANJA
54
54
56
58
60
63
66
67
68
72
77
83
83
86
88
91
94
97
101
102
104
105
110
117
4.
IZVEDBA KONKURENTNE BIBLIOTEKE COLIBRY
4.1 COLIBRY IZVRŠILAC
BOOL.CPP
LIMITED.CPP
LIST.CPP
EXCLUDE.CPP
DESCRIPT.CPP
TAG_LIST.CPP
READY.CPP
A_BLOCK.CPP
KERNEL.CPP
EXCLUSIV.CPP
E_BLOCK.CPP
ATOMIC.CPP
DRIVERS.CPP
MEMORY.CPP
THREAD.CPP
FAILURE.CPP
MAIN.CPP
4.2 COLIBRY ULAZNO-IZLAZNI MODUL
4.3 DATOTEKE ZAGLAVLJA COLIBRY.H I COLIB_IO.H
4.4 PITANJA
119
119
120
120
121
122
123
126
127
131
132
139
140
141
150
154
158
164
165
166
179
184
5.
SLOJEVI OPERATIVNOG SISTEMA
5.1 SLOJ ZA RUKOVANJE PROCESIMA
SISTEMSKE OPERACIJE ZA STVARANJE I UNIŠTENJE
PROCESA
ZAMENA SLIKA PROCESA
RUKOVANJE NITIMA
OSNOVA SLOJA ZA RUKOVANJE PROCESIMA
5.2 SISTEMSKI PROCESI
NULTI PROCES
PROCES DUGOROČNI RASPOREĐIVAČ
PROCESI IDENTIFIKATOR I KOMUNIKATOR
POJAM KRIPTOGRAFIJE (CRYPTOGRAPHY)
5.3 SLOJ ZA RUKOVANJE DATOTEKAMA
KONTIUNALNE DATOTEKE
RASUTE DATOTEKE
KONZISTENTNOST SISTEMA DATOTEKA
BAFERSKI PROSTOR
DESKRIPTOR DATOTEKE
IMENICI
SISTEMSKE OPERACIJE SLOJA ZA RUKOVANJE
DATOTEKAMA
SPECIJALNE DATOTEKE
STANDARDNI ULAZ I STANDARDNI IZLAZ
SPAŠAVANJE DATOTEKA
OSNOVA SLOJA ZA RUKOVANJE DATOTEKAMA
5.4 SLOJ ZA RUKOVANJE RADNOM MEMORIJOM
KONTINUALNA RADNA MEMORIJA
SEGMENTACIJA
VIRTUELNA MEMORIJA
STRANIČNA SEGMENTACIJA
OSNOVA SLOJA ZA RUKOVANJE VIRTUELNOM
MEMORIJOM
5.5 SLOJ ZA RUKOVANJE KONTROLERIMA
DRAJVERI
DRAJVERI BLOKOVSKIH UREĐAJA
BLOKOVSKI I ZNAKOVNI UREĐAJI KAO SPECIJALNE
DATOTEKE
DRAJVERI ZNAKOVNIH UREĐAJA
DRAJVER SATA
RUKOVANJE TABELOM PREKIDA
OSNOVA SLOJA ZA RUKOVANJE KONTROLERIMA
5.6 RUKOVANJE PROCESOROM
5.7 PITANJA
186
186
187
188
189
190
190
190
190
190
193
194
194
196
198
199
200
202
203
206
207
208
208
208
209
210
212
219
220
220
220
222
225
226
228
229
229
229
231
6.
7.
8.
MRTVA PETLJA
6.1 USLOVI ZA POJAVU MRTVE PETLJE
6.2 TRETIRANJE MRTVE PETLJE
6.3 PITANJA
KOMUNIKACIJA SA OPERATIVNIM SISTEMOM
7.1 PROGRAMSKI NIVO KOMUNIKACIJE SA
OPERATIVNIM SISTEMOM
7.2 INTERAKTIVNI NIVO KOMUNIKACIJE SA
OPERATIVNIM SISTEMOM
ZNAKOVNI KOMANDNI JEZICI
GRAFIČKI KOMANDNI JEZICI
7.3 PITANJA
KLASIFIKACIJA OPERATIVNIH SISTEMA
8.1 KRITERIJUM KLASIFIKACIJE OPERATIVNIH SISTEMA
8.2 OPERATIVNI SISTEMI REALNOG VREMENA
8.3 MULTIPROCESORSKI OPERATIVNI SISTEMI
8.4 DISTRIBUIRANI OPERATIVNI SISTEMI
POZIV UDALJENE OPERACIJE
PROBLEMI POZIVA UDALJENE OPERACIJE
RAZMENA PORUKA
PROBLEMI RAZMENE PORUKA
RAZLIKA KLIJENATA I SERVERA
FUNKCIJE DISTRIBUIRANOG OPERATIVNOG SISTEMA
MREŽNI OPERATIVNI SISTEMI
8.5 PITANJA
LITERATURA
235
235
235
236
237
237
237
238
243
243
244
244
244
245
245
247
248
249
250
251
252
255
255
257
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
1
1. UVOD
1.1 ZADATAK OPERATIVNOG SISTEMA
Operativni sistem je program koji objedinjuje u skladnu celinu raznorodne
delove računara i sakriva od korisnika one detalje funkcionisanja ovih delova koji
nisu bitni za korišćenje računara. Znači, operativni sistem ima dvostruku ulogu. S
jedne strane, on upravlja sastavnim delovima računara, kao što su procesor,
kontroleri i radna memorija, sa ciljem da oni budu što celishodnije upotrebljeni. S
druge strane, operativni sistem stvara za krajnjeg korisnika računara pristupačno
radno okruženje, tako što pretvara računar od mašine koja rukuje bitima, bajtima i
blokovima u mašinu koja rukuje datotekama i procesima.
1.2 POJAM DATOTEKE
Za pojam datoteke su vezani sadržaj i atributi. Sadržaj datoteke obrazuju
korisnički podaci. U atribute datoteke spada, na primer, veličina datoteke ili vreme
njenog nastanka. Atributi datoteke se čuvaju u deskriptoru datoteke.
Datoteke omogućuju korisnicima da organizuju podatke u skladu sa svojim
potrebama.
OZNAČAVANJE DATOTEKA
Svaka datoteka poseduje ime koje bira korisnik. Poželjno je da ime datoteke
ukazuje na njen konkretan sadržaj, ali i na vrstu njenog sadržaja, radi klasifikacije
datoteka po njihovom sadržaju. Zato su imena datoteka dvodelna, tako da prvi deo
imena datoteke označava njen sadržaj, a drugi deo označava vrstu njenog sadržaja,
odnosno njen tip. Ova dva dela imena datoteke obično razdvaja tačka. Tako, na
primer:
godina1.txt
može da predstavlja ime datoteke, koja sadrži podatke o studentima prve godine
studija. Na to ukazuje prvi deo imena godina1, dok drugi deo imena txt ove
datoteke govori da je datoteka tekstualna, odnosno da sadrži samo vidljive ASCII
znakove.
Rukovanje datotekom obuhvata ne samo rukovanje njenim sadržajem, nego i
rukovanje njenim imenom. Tako, na primer, stvaranje datoteke podrazumeva i
zadavanje njenog sadržaja, ali i zadavanje njenog imena. To se dešava, na primer, u
toku editiranja, kompilacije, kopiranja i slično. Takođe, izmena datoteke može da
obuhvati ne samo izmenu njenog sadržaja, nego i izmenu njenog imena, što se
dešava, na primer, u editiranju.
2
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
ORGANIZACIJA DATOTEKA
Datoteke se grupišu u skupove datoteka. Na primer, prirodno je da datoteke
sa podacima o studentima pojedinih godina studija istog odseka pripadaju jednom
skupu datoteka. Skupu datoteka pristaje naziv imenik (directory, folder), ako se
prihvati stanovište da skup datoteka sadrži imena svih datoteka koje su obuhvaćene
pomenutim skupom. Radi razlikovanja imenika, svaki od njih poseduje ime koje
bira korisnik. Za imenike su dovoljna jednodelna imena, jer nema potrebe za
klasifikacijom imenika. Tako, na primer:
odsek
može da predstavlja ime imenika, koji obuhvata datoteke sa podacima o studentima
svih godina studija istog odseka.
Razvrstavanjem datoteka u imenike nastaje hijerarhijska organizacija
datoteka, u kojoj su na višem nivou hijerarhije imenici, a na nižem nivou se nalaze
datoteke, koje pripadaju pomenutim imenicima, odnosno, čija imena su sadržana u
ovim imenicima. Ovakva hijerarhijska organizacija povlači za sobom i hijerarhijsko
označavanje datoteka. Hijerarhijsku oznaku ili putanju (path name) datoteke
obrazuju ime imenika kome datoteka pripada i ime datoteke. Delove putanje obično
razdvaja znak / (ili znak \). Tako, na primer:
odsek1/godina1.txt
predstavlja putanju datoteke, koja sadrži podatke o studentima prve godine studija
sa prvog odseka.
Hijerarhijska organizacija datoteka ima više nivoa, kada jedan imenik
obuhvata, pored datoteka, i druge imenike, odnosno sadrži, pored imena datoteka, i
imena drugih imenika. Obuhvaćeni imenici se nalaze na nižem nivou hijerarhije. Na
primer, imenik fakultet obuhvata imenike pojedinih odseka.
Na vrhu hijerarhijske organizacije datoteka se nalazi korenski imenik (root).
U slučaju više nivoa u hijerarhijskoj organizaciji datoteka, putanju datoteke
obrazuju imena imenika sa svih nivoa hijerarhije, navedena u redosledu od najvišeg
nivoa na dole, kao i ime datoteke. Na primer:
/fakultet/odsek1/godina1.txt
predstavlja putanju datoteke godina1.txt, koja pripada imeniku odsek1. Ovaj imenik
pripada imeniku fakultet, a on pripada korenskom imeniku, koji nema imena.
Na prethodno opisani način se obrazuje i putanja imenika. Tako, na primer:
/fakultet/odsek1/
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
3
predstavlja putanju imenika odsek1, koji pripada imeniku fakultet iz korenskog
imenika.
Hijerarhijska organizacija datoteka dozvoljava da postoje datoteke sa istim
imenima, odnosno, da postoje imenici sa istim imenima, pod uslovom da pripadaju
raznim imenicima. Na primer, na slici 1.2.1 je prikazana hijerarhijska organizacija
datoteka, u kojoj se nalaze datoteke sa istim imenima i imenici sa istim imenima.
fakultet1
odsek1
fakultet2
odsek2
odsek1
odsek2
...
godina1.txt
...
...
godina1.txt
...
Slika 1.2.1 Grafička predstava hijerarhijske organizacije datoteka
U hijerarhijskoj organizaciji datoteka, prikazanoj na slici 1.2.1, korenskom
imeniku pripadaju imenici fakultet1 i fakultet2. Svaki od njih sadrži imenike
odsek1 i odsek2. Pri tome, oba imenika sa imenom odsek1 sadrže datoteku
godina1.txt. Putanje omogućuju razlikovanje istoimenih imenika, odnosno
istoimenih datoteka. Tako, putanje:
/fakultet1/odsek1/
/fakultet2/odsek1/
omogućuju razlikovanje imenika sa imenom odsek1, a putanje:
/fakultet1/odsek1/godina1.txt
/fakultet2/odsek1/godina1.txt
omogućuju razlikovanje datoteka sa imenom godina1.txt.
Zahvaljujući hijerarhijskoj organizaciji datoteka, moguće je rukovanje celim
imenicima. Na primer, moguće je kopiranje celog imenika, odnosno kopiranje svih
datoteka i imenika, koji mu pripadaju.
Navođenje apsolutne putanje datoteke, sa svim prethodećim imenicima, je
potrebno kad god je moguć nesporazum, zbog datoteka sa istim imenima, odnosno,
zbog imenika sa istim imenima. Ali, ako postoji mogućnost određivanja nekog
imenika kao radnog (working directory), tada se njegova putanja podrazumeva i ne
mora se navoditi. Na primer, ako se podrazumeva da je:
4
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
/fakultet1/odsek1/
radni imenik, tada:
godina1.txt
označava datoteku, koja pripada imeniku odsek1 iz imenika fakultet1.
Radni imenik omogućuje korišćenje relativnih putanja. Na primer, ako se
podrazumeva da je:
/fakultet1/
radni imenik, tada:
odsek1/godina1.txt
označava datoteku, koja pripada imeniku fakultet1.
ZAŠTITA DATOTEKA
Datoteke su namenjene za trajno čuvanje podataka. Za uspešnu upotrebu
podataka neophodna je zaštita datoteka. Ona obezbeđuje da podaci, sadržani u
datoteci, neće biti izmenjeni bez znanja i saglasnosti njihovog vlasnika. Takođe,
ona obezbeđuje da podatke, sadržane u datoteci jednog korisnika, bez njegove
dozvole drugi korisnici ne mogu da koriste. Podaci, sadržani u datoteci, ostaju
neizmenjeni, ako se onemogući pristup datoteci radi pisanja, odnosno radi izmene
njenog sadržaja. Takođe, podaci, sadržani u datoteci, ne mogu biti korišćeni, ako se
onemogući pristup datoteci, radi čitanja, odnosno radi preuzimanja njenog
sadržaja. Na ovaj način uvedeno pravo pisanja i pravo čitanja datoteke
omogućuju da se za svakog korisnika jednostavno ustanovi koja vrsta rukovanja
datotekom mu je dozvoljena, a koja ne. Tako, korisniku, koji ne poseduje pravo
pisanja datoteke, nisu dozvoljena rukovanja datotekom, koja izazivaju izmenu
njenog sadržaja. Ili, korisniku, koji ne poseduje pravo čitanja datoteke, nisu
dozvoljena rukovanja datotekom, koja zahtevaju preuzimanje njenog sadržaja.
Za izvršne datoteke uskraćivanje prava čitanja je prestrogo, jer sprečava ne
samo neovlašteno uzimanje tuđeg izvršnog programa, nego i njegovo izvršavanje.
Zato je uputno, radi izvršnih datoteka, uvesti posebno pravo izvršavanja programa,
sadržanih u izvršnim datotekama. Zahvaljujući posedovanju ovog prava, korisnik
može da pokrene izvršavanje programa, sadržanog u izvršnoj datoteci, i onda kada
nema pravo njenog čitanja.
Pravo čitanja, pravo pisanja i pravo izvršavanja datoteke predstavljaju tri
prava pristupa datotekama, na osnovu kojih se za svakog korisnika utvrđuje koje
vrste rukovanja datotekom su mu dopuštene. Da se za svaku datoteku ne bi
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
5
evidentirala prava pristupa za svakog korisnika pojedinačno, uputno je sve
korisnike razvrstati u klase i za svaku od njih vezati pomenuta prava pristupa.
Iskustvo pokazuje da su dovoljne tri klase korisnika. Jednoj pripada vlasnik
datoteke, drugoj njegovi saradnici, a trećoj ostali korisnici. Nakon razvrstavanja
korisnika u tri klase, evidentiranje prava pristupa datotekama omogućuje matrica
zaštite (protection matrix) koja ima tri kolone, po jednu za svaku klasu korisnika, i
onoliko redova koliko ima datoteka. U preseku svakog reda i svake kolone matrice
zaštite navode se prava pristupa datoteci iz datog reda za korisnike koji pripadaju
klasi iz date kolone. Na slici 1.2.2 je prikazan primer matrice zaštite.
datoteka_1.bin
datoteka_2.bin
datoteka_n.txt
vlasnik
pisanje
čitanje
izvršavanje
čitanje
izvršavanje
...
pisanje
čitanje
-
saradnik
čitanje
izvršavanje
izvršavanje
...
čitanje
-
ostali
izvršavanje
izvršavanje
...
-
Slika 1.2.2 Matrica zaštite
U primeru matrice zaštite sa slike 1.2.2 vlasnik datoteke datoteka_1.bin ima
sva prava pristupa, njegovi saradnici nemaju pravo pisanja, a ostali korisnici imaju
samo pravo izvršavanja (pretpostavka je da je reč o izvršnoj datoteci). Ima smisla
uskratiti i vlasniku neka prava, na primer, da ne bi nehotice izmenio sadržaj
datoteke datoteka_2.bin, ili da ne bi pokušao da izvrši datoteku koja nije izvršna
(datoteka_n.txt).
Za uspeh izloženog koncepta zaštite datoteka neophodno je onemogućiti
neovlašteno menjanje matrice zaštite. Jedino vlasnik datoteke sme da zadaje i menja
prava pristupa sebi, svojim saradnicima i ostalim korisnicima. Zato je potrebno
znati za svaku datoteku ko je njen vlasnik. Takođe, potrebno je i razlikovanje
korisnika, da bi se među njima mogao prepoznati vlasnik datoteke. To se postiže
tako što svoju aktivnost svaki korisnik započinje svojim predstavljanjem. U toku
predstavljanja korisnik predočava svoje ime i navodi dokaz da je on osoba za koju
se predstavlja, za šta je, najčešće, dovoljna lozinka. Predočeno ime i navedena
lozinka se porede sa spiskom imena i za njih vezanih lozinki registrovanih
korisnika. Predstavljanje je uspešno, ako se u spisku imena i lozinki registrovanih
korisnika pronađu predočeno ime i navedena lozinka.
6
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Predstavljanje korisnika se zasniva na pretpostavci da su njihova imena
javna, ali da su im lozinke tajne. Zato je i spisak imena i lozinki registrovanih
korisnika tajan, znači, direktno nepristupačan korisnicima. Jedina dva slučaja, u
kojima ima smisla dozvoliti korisnicima posredan pristup ovom spisku, su: (1) radi
njihovog predstavljanja i (2) radi izmene njihove lozinke. Za predstavljanje
korisnika uvodi se posebna operacija, koja omogućuje samo proveru da li zadani
par ime i lozinka postoji u spisku imena i lozinki registrovanih korisnika. Slično, za
izmenu lozinki uvodi se posebna operacija, koja omogućuje samo promenu lozinke
onome ko zna postojeću lozinku. Sva druga rukovanja spiskom imena i lozinki
registrovanih korisnika, kao što su ubacivanje u ovaj spisak parova imena i lozinki,
ili njihovo izbacivanje iz ovog spiska, nalaze se u nadležnosti poverljive osobe, koja
se naziva administrator (superuser). Zaštita datoteka zavisi od odgovornosti i
poverljivosti administratora. Zbog prirode njegovog posla, administratora ima
smisla potpuno izuzeti iz zaštite datoteka, s tim da on tada svoje nadležnosti mora
vrlo oprezno da koristi.
Nakon prepoznavanja korisnika, odnosno, nakon njegovog uspešnog
predstavljanja, uz pomoć matrice zaštite moguće je ustanoviti koja prava pristupa
korisnik poseduje za svaku datoteku. Da bi se pojednostavila provera korisničkih
prava pristupa, uputno je, umesto imena korisnika, uvesti njegovu numeričku
oznaku. Radi klasifikacije korisnika zgodno je da ovu numeričku oznaku obrazuju
dva redna broja. Prvi od njih označava grupu kojoj korisnik pripada, a drugi od njih
označava člana grupe. Podrazumeva se da su svi korisnici iz iste grupe međusobno
saradnici. Prema tome, redni broj grupe i redni broj člana grupe zajedno
jednoznačno određuju vlasnika. Saradnici vlasnika su svi korisnici koji imaju isti
redni broj grupe kao i vlasnik. U ostale korisnke spadaju svi korisnici čiji redni broj
grupe je različit od rednog broja grupe vlasnika. Posebna grupa se rezerviše za
administratore.
Numerička oznaka korisnika pojednostavljuje proveru njegovog prava
pristupa datoteci. Ipak, da se takva provera ne bi obavljala prilikom svakog pristupa
datoteci, umesno je takvu proveru obaviti samo pre prvog pristupa. Zato se uvodi
posebna operacija otvaranja datoteke, koja prethodi svim drugim operacijama, kao
što su pisanje ili čitanje datoteke. Pomoću operacije otvaranja se saopštava i na
koji način korisnik namerava da koristi datoteku. Ako je njegova namera u skladu
sa njegovim pravima, otvaranje datoteke je uspešno, a pristup datoteci je dozvoljen,
ali samo u granicama iskazanih namera. Pored operacije otvaranja, potrebna je i
operacija zatvaranja datoteke, pomoću koje korisnik saopštava da završava
korišćenje datoteke. Nakon zatvaranja datoteke, pristup datoteci nije dozvoljen do
njenog narednog otvaranja.
Numerička oznaka vlasnika datoteke i prava pristupa korisnika iz pojedinih
klasa predstavljaju atribute datoteke.
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
7
1.3 POJAM PROCESA
Za pojam procesa su vezani aktivnost, slika i atributi. Aktivnost procesa
odgovara angažovanju procesora na izvršavanju korisničkog programa. Slika
procesa pokriva adresni prostor procesa sa naredbama izvršavanog programa,
stekom i podacima koji se obrađuju u toku izvršavanja programa. U atribute procesa
spadaju, na primer, stanje procesa i njegov prioritet. Atributi procesa se čuvaju u
deskriptoru procesa.
STANJA I PRIORITET PROCESA
Stanja procesa su: aktivan, čeka i spreman. Proces je aktivan kada procesor
izvršava program. On čeka kada nisu ispunjeni neophodni preduslovi za obradu
podataka. Proces je spreman kada samo zauzetost procesora onemogućuje
izvršavanje programa.
Od prioriteta procesa zavisi kada će spreman proces da postane aktivan.
Podrazumeva se da je aktivan uvek proces sa najvišim prioritetom, jasno pod
pretpostavkom da se razmatra jednoprocesorski računar, sa jednim procesorom
namenjenim za izvršavanje korisničkih programa. Ako postoji nekoliko procesa
najvišeg prioriteta, tada je interesantna ravnomerna raspodela procesorskog
vremena između aktivnog i spremnih procesa najvišeg prioriteta. Ona se postiže ako
aktivni proces prepušta procesor spremnom procesu najvišeg prioriteta čim istekne
unapred određeni vremenski interval. Ovaj interval se naziva kvantum (quantum).
Trenutke isticanja kvantuma označavaju prekidi sata. Obrada prekida sata izaziva
prevođenje aktivnog procesa u stanje spreman i preključivanje procesora na onaj od
ostalih spremnih procesa najvišeg prioriteta koji je najduže u stanju spreman.
Deskriptori spremnih procesa najvišeg prioriteta su uvezani u posebnu
spremnu listu, tako da je prvi u ovoj listi, odnosno na njenom početku deskriptor
procesa koji je najduže u stanju spreman. Sledeći u spremnoj listi je deskriptor
procesa koji je drugi po vremenu provedenom u stanju spreman i tako dalje. I
deskriptori ostalih spremnih procesa su po istom principu uvezani u spremnu listu.
Slično važi i za deskriptore procesa koji čekaju da postane moguć nastavak njihove
aktivnosti. Svakom od razloga čekanja odgovara posebna lista u koju se uvezuju
deskriptori procesa koji će nastaviti aktivnost tek kada prestane da važi razlog
čekanja. Kada se to desi, proces iz stanja čeka prelazi u stanje spreman, a njegov
deskriptor se izvezuje iz liste razloga čekanja i uvezuje u spremnu listu. Uopšteno
govoreći, svaka izmena stanja procesa uzrokuje izvezivanje deskriptora procesa iz
jedne liste i njihovo uvezivanje u drugu listu.
ULOGA PROCESA
Procesi omogućuju bolje iskorišćenje računara (procesora) i njegovu bržu
reakciju na dešavanje vanjskih događaja.
8
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Istovremeno postojanje više procesa omogućuje da se procesor preključi sa
aktivnog procesa na spreman proces čim aktivni proces pređe u stanje čeka. Na taj
način procesor ostaje iskorišćen dok god ima spremnih procesa.
Bržu reakciju na dešavanje vanjskih događaja omogućuje proces najvišeg
prioriteta. Dok ovakav hitni proces čeka da se desi vanjski događaj, aktivan je neki
drugi manje prioritetan proces. Kada se desi vanjski događaj, obrada odgovarajućeg
prekida prevodi hitni proces iz stanja čeka u stanje spreman. To dovodi do
preključivanja procesora sa prekinutog procesa na hitni proces. Hitni proces u toku
svoje aktivnosti odreaguje na dešavanje vanjskog događaja. Nakon toga on prepušta
procesor prekinutom procesu, jer se vraća u stanje čeka dok se ne desi novi vanjski
događaj, jasno, ako se u međuvremenu nisu desili novi vanjski događaji. Ako su se
takvi događaji desili, njihova dešavanja se registruju u obradama odgovarajućih
prekida. Za to je potrebno da su prekidi omogućeni u toku aktivnosti hitnog
procesa. Tada, nakon reakcije na prvi događaj, hitni proces ne prelazi u stanje čeka
dok ne odreaguje na sve u međuvremenu registrovane događaje.
POJAM NITI
Aktivnost procesa karakteriše redosled u kome se izvršavaju naredbe
programa. Ovaj redosled se naziva trag (trace) procesa. Trag procesa može da se
prikaže kao nit (thread) koja povezuje izvršene naredbe u redosledu njihovog
izvršavanja. Ako se za proces veže jedna nit, tada ona ima atribute procesa, znači
njegovo stanje i njegov prioritet. U toku aktivnosti niti izvršavaju se naredbe koje
ona povezuje.
Vezivanje za proces jedne niti je posledica pretpostavke da proces odgovara
izvršavanju sekvencijalnog programa. Za sekvencijalni program se podrazumeva da
se naredbe izvršavaju jedna za drugom u unapred zadanom redosledu, koji je
zavisan od obrađivanih podataka. Ovakve naredbe obrazuju sekvencu naredbi.
Praksa pokazuje da je korisno za procese vezati više od jedne niti. U tom
slučaju stek, prioritet i stanje se ne vezuju za proces, nego za njegove niti. Znači,
svaka od niti procesa ima svoj stek, svoj prioritet i svoje stanje, kao i svoj
deskriptor. Na primer, za editiranje teksta su važne, između ostalog, dve aktivnosti.
Prva je posvećena interakciji sa korisnikom, a druga je zadužena za periodično
smeštanje unesenog teksta na masovnu memoriju, radi sprečavanja gubljenja teksta.
Ako je za editorski proces vezana jedna nit, tada se pomenute aktivnosti odvijaju
jedna za drugom (sekvencijalno). Znači, za vreme trajanja druge aktivnosti nije
moguća interakcija sa korisnikom, što je ozbiljan nedostatak. On se može otkloniti,
ako se za editorski proces vežu dve niti raznih prioriteta. Prioritetnija, hitna nit se
pridružuje prvoj aktivnosti, a manje prioritetna, pozadinska nit se pridružuje drugoj
aktivnosti. Pod pretpostavkom da hitna nit čeka na interakciju sa korisnikom,
pozadinska nit može biti aktivna sve dok, na primer, pritisak dirke na tastraturi ne
najavi početak interakcije sa korisnikom. Tada obrada odgovarajućeg prekida
prevodi hitnu nit u stanje spremna, pa se procesor preključuje sa prekinute
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
9
pozadinske niti na hitnu nit. U toku svoje aktivnosti hitna nit obavi interakciju sa
korisnikom i vrati se u stanje čeka, što dovodi do preključivanja procesora na
prekinutu pozadinsku nit. Zahvaljujući preključivanju procesora sa pozadinske niti
na hitnu nit, u toku editiranja nema perioda bez odziva. Procesi sa više niti
odgovaraju izvršavanju konkurentnih programa u kojima istovremeno
(concurrently) postoji više relativno nezavisnih sekvenci naredbi.
1.4 STRUKTURA OPERATIVNOG SISTEMA
Unutrašnji izgled ili struktura operativnog sistema se može lakše sagledati
ako se zauzme stanovište da je zadatak operativnog sistema da upravlja fizičkim i
logičkim delovima računara. Pošto fizički delovi računara obuhvataju procesor,
kontrolere i radnu memoriju, a logički delovi računara obuhvataju datoteke i
procese, sledi da se operativni sistem može raščlaniti na module namenjene
rukovanju procesorom, kontrolerima, radnom memorijom, datotekama i procesima.
MODUL ZA RUKOVANJE PROCESOROM
Zadatak modula za rukovanje procesorom je da preključuje procesor sa jedne
niti na drugu. Pri tome ove niti mogu pripadati istom ili raznim procesima. Sa
stanovišta modula za rukovanje procesorom ključna razlika između niti koje
pripadaju istom procesu i niti koje pripadaju raznim procesima je da su prve niti u
adresnom prostoru istog procesa, dok su druge niti u adresnim prostorima raznih
procesa. Zato, u toku preključivanja procesora između niti istog procesa ne dolazi
do izmene adresnog prostora procesa, pa je ovakvo preključivanje brže (kraće) nego
preključivanje procesora između niti raznih procesa.
Modul za rukovanje procesorom ostvaruje svoj zadatak tako što uvodi
operaciju preključivanja. Pozivi te operacije dovode do preključivanja procesora.
MODUL ZA RUKOVANJE KONTROLERIMA
Zadatak modula za rukovanje kontrolerima je da upravlja raznim ulaznim i
izlaznim uređajima koji su zakačeni za kontrolere. U ovakve ulazno/izlazne uređaje
spadaju tastatura, miš, ekran, štampač, odnosno uređaji masovne memorije kao što
su diskovi, diskete ili CD ROM-ovi. Pošto upravljanje ulazno/izlaznim uređajima
zavisi od vrste uređaja, modul za rukovanje kontrolerima se sastoji od niza
komponenti, nazvanih drajveri. Svaki od drajvera je specijalizovan da opsluži
jednu klasu ulazno/izlaznih uređaja i ima zadatak da konkretan ulazno/izlazni
uređaj predstavi u apstraktnom obliku sa jednoobraznim i pravilnim načinom
korišćenja. Tako, na primer, drajver diska stvara predstavu apstraktnog diska. Za
razliku od stvarnog diska, apstraktni disk, umesto staza i sektora, sadrži niz blokova
koji su označeni rednim brojevima. Drajver diska, preslikavajući blokove u staze i
sektore, omogućuje pristup blokovima apstraktnog diska, radi ulaza (preuzimanja
podataka), odnosno radi izlaza (pohranjivanja podataka).
10
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Modul za rukovanje kontrolerima ostvaruje svoj zadatak tako što uvodi
(drajverske) operacije ulaza i izlaza. Pozivi ovih operacija dovode do prenosa
podataka na relaciji između radne memorije i ulazno/izlaznih uređaja. U okviru
ovih operacija se zaustavlja aktivnost niti za čiji račun se operacije obavljaju ako,
zbog sporosti ulazno/izlaznog uređaja, nije moguće odmah obaviti zatraženi ulaz ili
izlaz. Tada se poziva operacija preključivanja. Do nastavaka aktivnosti niti može
doći čim ulazno/izlazni uređaj javi, posredstvom mehanizma prekida, da je moguće
obavljanje zatražene operacije ulaza ili izlaza. Obradu ovakvih prekida preuzimaju
obrađivači prekida, koji, takođe, ulaze u sastav drajvera. Oni omogućuju nastavak
aktivnosti niti. Radi toga se, opet, poziva operacija preključivanja. Pošto pozivi
operacija preključivanja mogu biti posledica dešavanja prekida, trenutak
preključivanja je u opštem slučaju nepredvidiv kao što su nepredvidivi i trenuci
dešavanja prekida.
MODUL ZA RUKOVANJE RADNOM MEMORIJOM
Zadatak modula za rukovanje radnom memorijom je da vodi evidenciju o
slobodnoj radnoj memoriji radi zauzimanja zona slobodne radne memorije, odnosno
radi oslobađanja prethodno zauzetih zona radne memorije. Kada podržava virtuelnu
memoriju, ovaj modul se brine i o prebacivanju sadržaja stranica između radne i
masovne memorije. To znači da se iz modula za rukovanje radnom memorijom
pozivaju operacije ulaza i izlaza.
Modul za rukovanje radnom memorijom ostvaruje svoj zadatak tako što
uvodi operacije zauzimanja i oslobađanja. Pozivi tih operacija dovode do
zauzimanja i oslobađanja zona radne memorije.
MODUL ZA RUKOVANJE DATOTEKAMA
Zadatak modula za rukovanje datotekama je da omogući otvaranje i
zatvaranje datoteka, odnosno čitanje i pisanje njihovog sadržaja. Radi toga ovaj
modul vodi evidenciju o blokovima (masovne memorije) u kojima se nalaze
sadržaji datoteka. On se, takođe, brine i o prebacivanju delova sadržaja datoteka
između radne i masovne memorije. To znači da se iz modula za rukovanje
datotekama pozivaju operacije ulaza i izlaza. Pošto su za pomenuto prebacivanje
delova sadržaja datoteka potrebni baferi, iz modula za rukovanje datotekama se
poziva i operacija zauzimanja, radi rezervisanja dovoljno velikog baferskog
prostora.
Modul za rukovanje datotekama ostvaruje svoj zadatak tako što uvodi
operacije otvaranja, zatvaranja, čitanja i pisanja. Pozivi poslednje dve operacije
dovode do prenosa sadržaja datoteka na relaciji između radne i masovne memorije.
MODUL ZA RUKOVANJE PROCESIMA
Zadatak modula za rukovanje procesima je da omogući stvaranje i
uništavanje procesa, kao i stvaranje i uništavanje njihovih niti. Na taj način postaje
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
11
moguće da istovremeno postoji više procesa (višeprocesni režim rada,
multiprocessing) i više niti (multithreading). To je preduslov: (1) za bolje
iskorišćenje procesora, (2) za istovremenu podršku većeg broja korisnika
(višekorisnički režim rada, multiuser environment) i (3) za bržu reakciju računara
na vanjske događaje. Iz modula za rukovanje procesima se poziva operacija čitanja,
radi preuzimanja sadržaja izvršnih datoteka, koji su potrebni za svaranje slike
procesa. Pošto je za stvaranje slike procesa potrebna radna memorija, iz modula za
rukovanje procesima se pozivaju i operacije zauzimanja, odnosno oslobađanja.
Modul za rukovanje procesima ostvaruje svoj zadatak tako što uvodi
operacije stvaranja i uništavanja. Pozivi tih operacija dovode do stvaranja i
uništavanja procesa, odnosno niti.
SLOJEVITI OPERATIVNI SISTEM
Prethodno opisani moduli operativnog sistema formiraju hijerarhiju,
sastavljenu od 5 slojeva. Svaki od slojeva je predodređen da sadrži jedan od modula
operativnog sistema. Raspodelu modula po slojevima diktira pravilo koje nalaže da
se iz svakog sloja pozivaju samo operacije uvedene u nižim slojevima hijerarhije.
Primena pomenutog pravila dovodi do smeštanja modula za rukovanje procesima u
sloj na vrhu hijerarhije. U prvi niži sloj dospeva modul za rukovanje datotekama,
dok je sledeći niži sloj namenjen modulu za rukovanje radnom memorijom.
Predposlednji sloj hijerarhije sadrži modul za rukovanje kontrolerima, a u
poslednjem sloju nalazi se modul za rukovanje procesorom. Na ovaj način je
obrazovan slojeviti operativni sistem, prikazan na slici 1.4.1.
modul za rukovanje procesima
modul za rukovanje datotekama
modul za rukovanje radnom memorijom
modul za rukovanje kontrolerima
modul za rukovanje procesorom
Slika 1.4.1 Slojeviti operativni sistem
Svrha predstavljanja operativnog sistema kao hijerarhije slojeva je
motivisana željom da se zadatak operativnog sistema raščlani na više jednostavnijih
međusobno nezavisnih zadataka i zatim svaki od njih objasni zasebno. Iako je
raslojavanje operativnog sistema u hijerarhiju slojeva moguće uspešno primeniti i
prilikom pravljenja operativnog sistema, u praksi se uglavnom sreću monolitni
(monolithic) operativni sistemi. Monolitni operativni sistemi nemaju pravilnu
strukturu kao slojeviti operativni sistemi, jer se sastoje od modula čija saradnja nije
ograničena pravilima kao kod slojevitih operativnih sistema. To znači da se iz
svakog od modula monolitnih operativnih sistema mogu slobodno pozivati
operacije svih ostalih modula.
12
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
SISTEMSKI POZIVI
Slojeviti operativni sistem dozvoljava pozivanje (odnosno korišćenje)
operacija stvaranja i uništavanja (procesa) samo iz sloja, smeštenog iznad
hijerarhije njegovih slojeva. Prema tome, postojanje procesa je isključivo vezano za
ovaj, korisnički sloj.
Iako su svi procesi locirani u korisničkom sloju, oni su međusobno jasno
razdvojeni zahvaljujući činjenici da svaki od procesa poseduje zaseban adresni
prostor, koji se naziva korisnički prostor (user space). Na istom principu se
zasniva i razdvajanje procesa od operativnog sistema. Ali, razdvojenost korisničkih
prostora od adresnog prostora operativnog sistema, koji se naziva sistemski prostor
(kernel space), sprečava da se pozivi operacija operativnog sistema zasnivaju na
korišćenju poziva potprograma. Zato je neophodno uvođenje posebnog mehanizma
sistemskih poziva koji omogućuje prelazak iz korisničkog prostora u sistemski
prostor radi poziva operacija operativnog sistema. Sistemski pozivi zahtevaju
korišćenje specifičnih asemblerskih naredbi i zbog toga se sakrivaju unutar
sistemskih potprograma. Neki od sistemskih potprograma, pored sistemskih
poziva, sadrže i specifične obrade podataka, kao što je, na primer, pretvaranje
brojeva iz znakovnog u binarni oblik i obrnuto kod formatiranog ulaza ili izlaza.
Svaki od sistemskih potprograma je namenjen za pozivanje jedne od operacija
operativnog sistema, namenjenih korisničkom sloju. Ovakve operacije se nazivaju
sistemske operacije, da bi se razlikovale od ostalih (internih) operacija operativnog
sistema, namenjenih samo za korišćenje unutar operativnog sistema. Za izvršavanje
sistemskih operacija je potreban stek. Zato u sistemskom prostoru za svaki proces
postoji poseban sistemski stek, namenjeni izvršavanju sistemskih operacija koje,
posredstvom sistemskih poziva, pozivaju pojedini procesi.
Sistemski potprogrami obrazuju sistemsku biblioteku, tako da se pozivanje
sistemskih operacija svodi na pozivanje njenih potprograma. Sistemska biblioteka
se isporučuje uz operativni sistem da bi se koristila u postupku linkovanja. U toku
linkovanja njeni potprogrami se vezuju za objektni oblik korisničkog programa, radi
stvaranja izvršnog oblika korisničkog programa, koji tako postaje spreman za
saradnju sa operativnim sistemom.
Zahvaljujući sistemskim potprogramima, odnosno sistemskoj biblioteci,
operativni sistem predstavlja deo korisničkog programa, iako za njega nije direktno
linkovan. Zato se može smatrati da obavljanje sistemskih operacija predstavlja
sastavni deo izvršavanja korisničkog programa, odnosno pripada aktivnosti procesa,
vezanog za ovo izvršavanje. To, uz istovremeno postojanje više procesa i
nepredvidivost preključivanja, uzrokuje da je moguće da istovremeno postoji više
procesa, koji su započeli, a nisu završili svoju aktivnost u okviru operativnog
sistema, odnosno, čija aktivnost je zaustavljena unutar operativnog sistema. To
znači da je operativni sistem konkurentni program, jer sadrži više nezavisnih
sekvenci naredbi, vezanih za aktivnosti raznih, istovremeno postojećih procesa.
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
13
INTERAKCIJA KORISNIKA I OPERATIVNOG SISTEMA
Dok sistemska biblioteka omogućuje korišćenje operativnog sistema na
programskom nivou, komande komandnog jezika omogućuju korišćenje
operativnog sistema na interaktivnom nivou. Komande komandnog jezika
precizno opisuju rukovanja procesima i datotekama. One tako lišavaju korisnika
potrebe da poznaje mehanizme, koji se pokreću unutar operativnog sistema radi
rukovanja procesima i datotekama. Za preuzimanje i interpretiranje komandi
komandnog jezika zadužen je poseban proces iz korisničkog sloja, koji se naziva
interpreter komandnog jezika (shell). Interpreter komandnog jezika posreduje
između korisnika i operativnog sistema. U opštem slučaju u posredovanju obično
učestvuju i procesi koje stvara interpreter komandnog jezika, da bi im prepustio
izvršavanje prepoznatih komandi.
Interpreter komandnog jezika koristi operativni sistem na programskom
nivou, jer u toku svog rada poziva sistemske pozive.
1.5 PITANJA
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
Koji zadatak ima operativni sistem?
Šta obuhvata pojam datoteke?
Gde se čuvaju atributi datoteke?
Šta je imenik?
Zašto je potrebna hijerarhijska organizacija datoteka?
Šta je putanja?
Šta je korenski imenik?
Šta je radni imenik?
Šta je cilj zaštite datoteka?
Koja prava pristupa datotekama postoje?
Šta je matrica zaštite?
Ko može menjati matricu zaštite?
Zašto je potrebno predstavljanje korisnika?
U kojim slučajevima korisnici mogu da pristupe spisku imena i lozinki
registrovanih korisnika?
Od čega se sastoji numerička oznaka korisnika?
Zašto su uvedene operacije otvaranja i zatvaranja datoteke?
Šta obuhvata pojam procesa?
Gde se čuvaju atributi procesa?
U kojim stanjima može biti proces?
Šta je kvantum?
Kako se organizuju deskriptori procesa?
Šta se dešava sa deskriptorom procesa kada proces izmeni stanje?
Koju ulogu imaju procesi?
Šta je nit?
U kakvom odnosu su procesi i niti?
14
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
Šta su konkurentni programi?
Čime upravlja operativni sistem?
Od kojih modula se sastoji operativni sistem?
Koji zadatak ima modul za rukovanje procesorom?
Koji zadatak ima modul za rukovanje kontrolerima?
Koji zadatak ima modul za rukovanje radnom memorijom?
Koji zadatak ima modul za rukovanje datotekama?
Koji zadatak ima modul za rukovanje procesima?
Koje operacije podržava modul za rukovanje procesorom?
Koje operacije podržava modul za rukovanje kontrolerima?
Koje operacije podržava modul za rukovanje radnom memorijom?
Koje operacije podržava modul za rukovanje datotekama?
Koje operacije podržava modul za rukovanje procesima?
Šta su drajveri?
U čemu je razlika između slojevitog i monolitnog operativnog sistema?
Šta je korisnički prostor?
Šta je sistemski prostor?
Zašto su potrebni sistemski pozivi?
Šta sadrže sistemski potprogrami?
Od čega je sastavljena sistemska biblioteka?
Zašto operativni sistem spada u konkurentne programe?
Koji nivoi interakcije sa operativnim sistemom postoje?
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
15
2. KONKURENTNO PROGRAMIRANJE
2.1 SVOJSTVA KONKURENTNIH PROGRAMA
Za izvršavanje konkurentnih programa na jednoprocesorskim računarima je
tipično da procesor izvršava naredbe jedne niti dok je moguća njena aktivnost ili
dok se ne desi prekid. Kada se desi prekid, procesor izvrši naredbe odgovarajućeg
obrađivača prekida. Ako obrada prekida izazove preključivanje, procesor u
nastavku izvrši naredbe potprograma preključivanja i zatim produži sa izvršavanjem
naredbi druge niti. Ovakvo mešanje izvršavanja naredbi raznih niti se naziva
preplitanje (interleaving) niti. Preplitanje niti ima slučajan karakter, jer unapred
nije poznato posle izvršavanja koje naredbe će se desiti prekid i eventualno
preključivanje. To nije moguće odrediti čak ni za prekide pravilnog perioda, kao što
su prekidi sata, jer izvršavanje konkurentnog programa može započeti u bilo kom
trenutku između dva prekida sata. Prema tome, broj izvršenih naredbi konkurentnog
programa pre prvog prekida sata varira od izvršavanja do izvršavanja, pa se i
preplitanja niti konkurentnih programa razlikuju od izvršavanja do izvršavanja.
Pored međusobnog preplitanja niti, postoje i preplitanja niti i obrada prekida.
I ona imaju slučajan karakter zbog nepredvidivosti prekida.
Slučajna priroda preplitanja tera na razmišljanje o štetnom uticaju preplitanja
na izvršavanje konkurentnih programa. Pod uticajem preplitanja rezultati
izvršavanja konkurentnih programa mogu da budu stohastični, odnosno da se
menjaju od izvršavanja do izvršavanja. Štetan uticaj preplitanja na rezultate
izvršavanja konkurentnih programa se može ilustrovati na primerima.
2.2 PRIMERI ŠTETNIH PREPLITANJA
Primeri štetnih preplitanja niti se mogu pronaći u okviru operativnog sistema,
jer on predstavlja tipičan primer konkurentnog programa. Operativni sistem ima
osobine konkurentnog programa, jer (1) svaki od procesa iz korisničkog sloja
predstavlja nit operativnog sistema i jer (2) operativni sistem sadrži obrađivače
prekida. Znači, u toku aktivnosti operativnog sistema moguća su međusobna
preplitanja niti, ali i preplitanja niti i obrada prekida.
Štetna preplitanja niti i obrada prekida u okviru operativnog sistema mogu da
se ilustruju na primeru rukovanja pozicijom kursora. Poziciju kursora karakterišu
dve koordinate (x i y), a rukovanje ovom pozicijom obuhvata promenu pozicije
(change) i preuzimanje pozicije (take). Rukovanje pozicijom kursora opisuje klasa
position_type:
16
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
class position_type
{ int x,y;
public:
position_type();
void change(int new_x,int new_y);
void take(int *current_x, int *current_y); };
position_type::position_type()
{ x = 0;
y = 0; };
void position_type::change(int new_x,int new_y)
{ x = new_x;
y = new_y; };
void position_type::take(int *current_x,int *current_y)
{ *current_x = x;
*current_y = y; };
Neka u operativnom sistemu postoji objekat klase position_type:
position_type position;
i neka operacija position.change() bude na raspolaganju samo obrađivaču prekida
koji u okviru operativnog sistema registruje izmene pozicije kursora. Neka, zatim,
operacija position.take() bude na raspolaganju procesima iz korisničkog sloja.
Tada je moguće da u toku izvršavanja operacije position.take() proces bude
prekinut radi obrade prekida, koja poziva operaciju position.change(). Ako se to
desi nakon izvršavanja prvog iskaza dodele iz tela operacije position.take(), a pre
izvršavanja drugog iskaza dodele iz njenog tela, tada će rezultat izvršavanja ove
operacije biti pogrešan. Do greške dolazi, jer nakon preuzimanja apscise, a pre
preuzimanja ordinate, obrada prekida izmeni apscisu i ordinatu. Znači preuzete su
stara apscisa i nova ordinata, pa je tako dobijena pozicija u kojoj se kursor ne
nalazi. Prema tome, opisano preplitanje niti i obrade prekida, odnosno preplitanje
izvršavanja operacija position.take() i position.change(), je štetno.
Štetna međusobna preplitanja niti u okviru operativnog sistema mogu da se
ilustruju na primeru rukovanja slobodnim baferima. Rukovanje slobodnim baferima
je locirano u modulu za rukovanje datotekama i zasniva se na pretpostavci da su
slobodni baferi organizovani u listu. U pojednostavljenom slučaju, rukovanje listom
bafera obuhvata uvezivanje (link) slobodnog bafera u listu i izvezivanje (unlink)
slobodnog bafera iz liste. Rukovanje listom bafera opisuje klasa buffer_list_type:
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
17
const unsigned int BUFFER_SIZE = 512;
class buffer_list_type
{ static buffer_list_type *first;
buffer_list_type *next;
char content[BUFFER_SIZE];
public:
buffer_list_type();
void link(buffer_list_type *new_buffer);
buffer_list_type *unlink(); };
buffer_list_type *buffer_list_type::first = 0;
buffer_list_type::buffer_list_type()
{ };
void buffer_list_type::link(buffer_list_type *new_buffer)
{ new_buffer->next = first;
first = new_buffer; };
buffer_list_type *buffer_list_type::unlink()
{ buffer_list_type *unlinked;
unlinked = first;
if (first != 0)
first = first->next;
return unlinked; };
Neka u operativnom sistemu postoji objekat klase buffer_list_type:
buffer_list_type buffer_list;
neka su operacije buffer_list.link() i buffer_list.unlink() samo na raspolaganju
modulu za rukovanje datotekama i neka se pozivaju iz operacija ovog modula. Tada
je moguće da izvršavanje operacije buffer_list.unlink() bude pokrenuto u toku
aktivnosti niti nekog procesa u modulu za rukovanje datotekama. Kao rezultat
izvršavanja ove operacije na steku pomenute niti nastane primerak njene lokalne
promenljive unlinked. Izvršavanje iskaza:
unlinked = first;
smešta u ovaj primerak lokalne promenljive adresu prvog slobodnog bafera iz liste
bafera, jasno, kada takav bafer postoji. Neka, nakon izvršavanja prethodnog iskaza,
pod uticajem obrade prekida dođe do preključivanja procesora na drugi proces.
Tada, u toku aktivnosti niti ovog procesa, može doći do pokretanja još jednog
izvršavanja operacije buffer_list.unlink(). Tako na steku i ove druge niti nastaje
njen primerak lokalne promenljive unlinked. I u taj primerak dospeva adresa istog
prvog slobodnog bafer iz liste bafera, jasno, kada takav bafer postoji. Posledica
ovakvog sleda događaja je da posmatrana dva procesa, bez obzira na nastavak
njihovih aktivnosti, nezavisno jedan od drugog koriste isti bafer. To neminovno
dovodi do fatalnog ishoda. Prema tome, opisano međusobno preplitanje niti dva
18
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
razna procesa, odnosno preplitanje dva izvršavanja operacije buffer_list.unlink(),
je štetno.
Štetna međusobna preplitanja niti su moguća i za niti koje pripadaju istom
procesu. Na primer, često je potrebno ostvariti međusobnu saradnju niti istog
procesa. Ovakva saradnja se može ostvariti tako što jedna od niti šalje podatke
drugoj niti. Takva razmena podataka između niti se obično obavlja posredstvom
komunikacionog bafera. Nit koja puni ovaj bafer podacima ima ulogu proizvođača
(podataka), a nit koja prazni ovaj bafer ima ulogu potrošača (podataka). U
pojednostavljenom slučaju, rukovanje komunikacionim baferom obuhvata punjenje
(put) celog bafera, kao i pražnjenje (get) celog bafera. Rukovanje komunikacionim
baferom opisuje klasa buffer_type:
const unsigned int BUFFER_SIZE = 512;
class buffer_type
{ char content[BUFFER_SIZE];
public:
buffer_type();
void put(char *c);
void get(char *c); };
buffer_type:: buffer_type()
{ };
void buffer_type::put(char *c)
{ int i;
for (i = 0;i < BUFFER_SIZE;i++)
content[i] = *c++; };
void buffer_type::get(char *c)
{ int i;
for (i = 0;i < BUFFER_SIZE;i++)
*c++ = content[i]; };
Ako u konkurentnom programu postoji objekat klase buffer_type:
buffer_type buffer;
tada operaciju buffer.put() poziva nit proizvođača iz procesa koji odgovara
izvršavanju posmatranog konkurentnog programa. Slično, operaciju buffer.get()
poziva nit potrošača iz istog procesa. U ovoj situaciji moguće je, na primer, da se
desi prekid sata za vreme aktivnosti proizvođača u operaciji buffer.put(). Obrada
ovog prekida može izazvati preključivanje procesora na potrošač. Ako operacija
buffer.get() bude pozvana u toku aktivnosti potrošača, tada postoji mogućnost da
potrošač preuzme sadržaj delomično popunjenog bafera. Na isti način je moguće da
proizvođač višestruko puni bafer koga potrošač još nije ispraznio, odnosno da
potrošač višestruko preuzme isti sadržaj iz bafera. Prema tome, opisano preplitanje
niti istog procesa, odnosno preplitanje izvršavanja operacija buffer.put() i
buffer.get(), je štetno.
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
2.3
19
SPREČAVANJE ŠTETNIH PREPLITANJA
Konkurentni programi se zasnivaju na ideji preplitanja, ali njihova
upotrebljivost zavisi od sprečavanja štetnih preplitanja. U primerima rukovanja
pozicijom kursora, listom bafera i komunikacionim baferom, štetna preplitanja su se
javila u toku pristupanja promenljivim position, buffer_list i buffer. Pošto
promenljivoj position pristupaju razne niti i obrade prekida, može se reći da razne
niti i obrade prekida međusobno dele ovu promenljivu. Slično, promenljivim
buffer_list i buffer pristupaju razne niti, pa se može reći da one međusobno dele
ove promenljive. Zato se ovakve promenljive nazivaju deljene (shared)
promenljive, a klase, koje opisuju rukovanje deljenim promenljivim, se nazivaju
deljene klase.
MEĐUSOBNA ISKLJUČIVOST
Prethodno pomenute deljene klase su napravljene pod pretpostavkom da se
rukovanja deljenim promenljivim obavljaju sekvencijalno, odnosno da se operacije
deljenih promenljivih izvršavaju strogo jedna za drugom. To znači da novo
izvršavanje bilo koje od operacija deljene promenljive može početi tek nakon
završetka prethodno započetog izvršavanja neke od ovih operacija. Na taj način
svako od ovih izvršavanja ostavlja i zatiče deljene promenljive u konzistentnom
(predviđenom) stanju. Međutim, štetna preplitanja negiraju pomenutu pretpostavku,
jer dopuštaju da novo izvršavanje neke operacije deljene promenljive započne pre
nego što se završilo već započeto izvršavanje neke od operacija iste deljene
promenljive. Na taj način, novo izvršavanje zatiče deljenu promenljivu u delomično
izmenjenom, znači potencijalno nekonzistentnom stanju što nije moguće kod
sekvencijalnog izvršavanja operacija deljene promenljive. Problem štetnih
preplitanja ne postoji, ako se obezbedi međusobna isključivost (mutual exclusion)
izvršavanja operacija deljenih promenljivih. Međusobna isključivost izvršavanja
operacija deljenih promenljivih garantuje serijalizaciju rukovanja deljenim
promenljivim i na taj način sprečava štetna preplitanja.
KRITIČNI REGIONI
Prethodni primeri ukazuju da su štetna preplitanja vezana za pristupanje
deljenim promenljivim. Kada nema pristupanja deljenim promenljivim, odnosno,
kada se izvršavaju međusobno nezavisni delovi konkurentnog programa, tada nema
mogućnosti za ugrožavanje konzistentnosti deljenih promenljivih, pa nema ni
štetnih preplitanja. Tela operacija deljenih klasa ili delovi ovih tela, čije izvršavanje
je kritično za konzistentnost deljenih promenljivih, se nazivaju kritični regioni
(critical region), odnosno kritične sekcije (critical section). Međusobna isključivost
kritičnih regiona se ostvaruje vremenskim usklađivanjem njihovih izvršavanja.
Sprovođenje ovakvog usklađivanja se naziva sinhronizacija (synchronization).
20
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
SINHRONIZACIJA
Sinhronizacija međusobne isključivosti, koja je zadužena za ostvarenje
međusobne isključivosti, nije jedina vrsta sinhronizacije. Na primer, kod rukovanja
komunikacionim baferom potrebno je osigurati naizmenično izvršavanje operacija
buffer.put() i buffer.get(), da bi se sprečilo punjenje neispražnjenog
komunikacionog bafera, odnosno da bi se sprečilo pražnjenje nenapunjenog
komunikacionog bafera. Sinhronizacija, koja ostvaruje ovakav dodatni uslov u
pogledu izvršavanja kritičnih regiona, se naziva uslovna sinhronizacija (condition
synchronization).
ATOMSKI REGIONI
U primeru rukovanja pozicijom kursora, štetna preplitanja nastupaju kao
posledica obrada prekida. Ako se onemoguće prekidi u kritičnim regionima
odgovarajuće deljene promenljive, tada u toku izvršavanja ovih kritičnih regiona
nisu moguće ni obrade prekida, pa ni pomenuta štetna preplitanja. Zbog
neprekidnosti izvršavanja, ovakvim kritičnim regionima pristaje ime atomski
regioni. Neprekidnost atomskih regiona garantuje njihovu međusobnu isključivost.
Ali, pošto onemogućenje prekida u atomskim regionima odlaže obradu novih
prekida i produžava reakciju procesora na vanjske događaje, važno je da
izvršavanja atomskih regiona budu što kraća. Ovakav zahtev sužava primenljivost
onemogućenja prekida kao sredstva za osiguranje međusobne isključivosti kritičnih
regiona.
PROPUSNICE
U primeru rukovanja listom bafera, štetna preplitanja nastupaju kao direktna
posledica preključivanja, dok su obrade prekida, kao inicijatori ovih preključivanja,
samo posredan uzrok štetnih preplitanja. Zato, u ovom slučaju, međusobnu
isključivost kritičnih regiona nema potrebe zasnivanti na onemogućenju prekida,
nego na zaustavljanju aktivnosti niti, kada ona pokuša da uđe u kritični region
deljene promenljive kojoj već pristupa neka druga nit. Ovakav način ostvarenja
međusobne isključivosti kritičnih regiona podrazumeva da svaka deljena
promenljiva poseduje jednu propusnicu za ulazak u njene kritične regione.
Propusnica može biti slobodna (nedodeljena) ili zauzeta (dodeljena). Podrazumeva
se da bez propusnice nije moguć ulazak u kritični region deljene promenljive. Zato
propusnicu traže sve niti koje se takmiče za ulazak u neki od kritičnih regiona iste
deljene promenljive. Međutim, propusnicu dobija samo jedna od njih i ona ulazi u
kritični region. Ostale niti zaustavljaju svoju aktivnost i prelaze u stanje čeka. Svaka
od njih ostaje u tom stanju do eventualnog dobijanja tražene propusnice. To se desi
tek pošto nit, koja pristupa deljenoj promenljivoj, napusti njen kritični region i vrati
njenu propusnicu. Nit, koja tada dobije propusnicu, odmah prelazi iz stanja čeka u
stanje spremna, ali u kritični region ulazi tek kada postane aktivna (odnosno, kada
se procesor preključi na nju).
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
21
ISKLJUČIVI REGIONI
Po načinu ostvarenja međusobne isključivosti, atomski regioni se razlikuju od
kritičnih regiona, čija međusobna isključivost se zasniva na korišćenju propusnica.
Zato ove druge regione treba drugačije nazvati, na primer, isključivi regioni. Iz
istih razloga je zgodno deljene klase, odnosno deljene promenljive razvrstati na
atomske klase i njihove primerke atomske promenljive sa jedne strane, kao i na
isključive klase i njihove primerke isključive promenljive sa druge strane.
Interesantno je uočiti da kod isključivih regiona preključivanja uzrokuju
pojavu štetnih preplitanja, ali i omogućuju njihovo sprečavanje. Tako, ako nit, čiju
aktivnost je omogućilo preključivanje, pokuša da uđe u isključivi region isključive
promenljive sa zauzetom propusnicom, tada opet preključivanje dovodi do
zaustavljanja aktivnosti ove niti. Prema tome, prvo preključivanje je stvorilo uslove
za pojavu štetnog preplitanja, a drugo preključivanje je sprečilo tu pojavu.
Uslovna sinhronizacija se, takođe, zasniva na zaustavljanju aktivnosti niti,
dok se ne ispuni uslov koji je neophodan za nastavak pomenute aktivnosti.
ATOMSKE KLASE
Atomske klase omogućuju opis saradnje obrađivača prekida i pozadinskih
niti. Zato jedna operacija atomske klase obavezno opisuje obradu prekida, dok su
njene preostale operacije namenjene pozadinskim nitima. Pošto operacije atomske
klase omogućuju rukovanje atomskim promenljivim, prirodno je da tela ovih
operacija obrazuju atomske regione, radi zaštite konzistentnosti atomskih
promenljivih.
Smisao saradnje obrađivača prekida i pozadinske niti je razmena podataka
između njih. Zato, na primer, pozadinska nit zaustavlja svoju aktivnost, kada obradi
sve podatke preuzete od obrađivača prekida. Do nastavka aktivnosti pozadinske niti
dolazi, kada obrađivač prekida obezbedi nove podatke, koji nastaju kao posledica
dešavanja vanjskog događaja i pojave prekida. Zato atomska klasa treba da
omogući:
1. pozadinskoj niti da zaustavi svoju aktivnost, radi očekivanja dešavanja
zadanog događaja, a
2. obrađivaču prekida da objavi dešavanje vanjskog događaja.
Atomska klasa treba da omogući i definisanje raznih događaja. Podrazumeva
se da je svakom događaju dodeljena posebna lista događaja. U nju se uvezuje
deskriptor pozadinske niti, kada ona zaustavi svoju aktivnost, radi očekivanja
dešavanja dotičnog događaja. U listu događaja deskriptori pozadinskih niti mogu
biti uvezani u hronološkom redosledu, ali i u zadanom redosledu. Deskriptor
pozadinske niti se izvezuje iz liste događaja, kada se desi dotični događaj.
Upotreba atomskih regiona nije ograničena samo na operacije atomskih
klasa, jer se i van njih može javiti potreba za onemogućavanjem prekida.
22
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
ISKLJUČIVE KLASE
Isključive klase omogućuju opis saradnje raznih niti. Zato su sve operacije
isključivih klasa namenjene ovim nitima. Pošto operacije isključive klase
omogućuju rukovanje isključivim promenljivim, prirodno je da tela ovih operacija
obrazuju isključive regione, radi zaštite konzistentnosti isključivih promenljivih.
Rukovanje isključivom promenljivom je moguće samo ako je njena
propusnica slobodna. Do zauzimanja propusnice dolazi na ulasku u isključivi
region, a do njenog oslobađanja dolazi na izlasku iz isključivog regiona. Da bi se
znalo koja propusnica se zauzima, odnosno oslobađa, svaki isključivi region
pripada samo jednoj isključivoj promenljivoj. Ulazak u isključivi region nije
moguć, ako je propusnica zauzeta. U tom slučaju aktivnost niti se zaustavlja, a njen
deskriptor se uvezuje u ulaznu listu isključive promenljive. Ulazna lista je
pridružena propusnici isključive promenljive.
Smisao saradnje niti je u razmeni podataka između raznih niti. Zato, na
primer, jedna nit zaustavlja svoju aktivnost, da bi sačekala isporuku podataka od
druge niti. Uslov za nastavak aktivnosti prve niti je da druga nit pripremi podatke.
Zato isključiva klasa treba da omogući:
1. prvoj niti da zaustavi svoju aktivnost, radi očekivanja ispunjenja zadanog
uslova, a
2. drugoj niti da objavi ispunjenje dotičnog uslova.
Isključiva klasa treba da omogući i definisanje raznih uslova. Podrazumeva
se da je svakom uslovu dodeljena posebna lista uslova. U nju se uvezuje deskriptor
niti, kada ona zaustavi svoju aktivnost, radi očekivanja ispunjenja dotičnog uslova.
Tom prilikom se oslobađa propusnica isključive promenljive, da bi druga nit mogla
da zauzme propusnicu, pristupi ovoj isključivoj promenljivoj i objavi ispunjenje
dotičnog uslova. U listu uslova deskriptori niti mogu biti uvezani u hronološkom
redosledu, ali i u zadanom redosledu. Deskriptor niti se izvezuje iz liste uslova,
kada se ispuni dotični uslov, ali se ne uvezuje u spremnu listu sve dok propusnica
ne bude oslobođena i ponovo dodeljena ovoj niti. Dok se to ne desi, deskriptor niti
je uvezan u listu ispunjenih uslova isključive promenljive. Lista ispunjenih uslova
je pridružena propusnici isključive promenljive. Prilikom oslobađanja propusnice,
iz njene liste ispunjenih uslova se izvezuje deskriptor niti koja najduže čeka
oslobođenu propusnicu. Ako je lista ispunjenih uslova prazna, iz ulazne liste
oslobođene propusnice se izvezuje deskriptor niti koja najduže čeka dotičnu
propusnicu. Na ovaj način se daje prednost nitima koje su već započele rukovanje
isključivom promenljivom.
Upotreba isključivih regiona nije ograničena samo na operacije isključivih
klasa, jer se i van njih može javiti potreba za međusobnom isključivošću.
Međusobna isključivost se može ostvariti i bez izričitog oslanjanja na
isključive klase i njima odgovarajuće isključive promenljive, ako se uvede
samostalna propusnica. Ona se naziva semafor.
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
23
SEMAFOR
Semafor omogućuje sinhronizaciju niti. Sinhronizacija niti se zasniva na
zaustavljanju njihove aktivnosti, kao i na omogućavanju nastavljanja njihove
aktivnosti. Na istom principu se zasniva i regulacija prolaska vozila kroz raskrsnicu.
To daje ideju da se regulisanje prolaska niti kroz isključivi region može zasnovati
na principu mehanizma saobraćajnog semafora. Po analogiji sa saobraćajnim
semaforom, koji reguliše prolaz kroz raskrsnicu, prolaz kroz isključivi region bi
regulisao (softverski) semafor. Ulazak niti u isključivi region bi zavisio od stanja
semafora. Ako stanje semafora dozvoli niti da uđe u kritični region, njen zadatak bi
bio da, pri ulasku u isključivi region, prevede semafor u stanje koje onemogućuje
ulazak druge niti u isključivi region. Ako se takva nit pojavi, njena aktivnost bi se
zaustavila pred isključivim regionom, a njen deskriptor bi se uvezao u listu
semafora. Na izlasku iz isključivog regiona, prva nit bi prevodila semafor u stanje
koje omogućuje novi ulazak u isključivi region i omogućavala nastavak aktivnosti
niti koja najduže čeka na ulaz u isključivi region (ako takva niti postoji).
2.4 POŽELJNE OSOBINE KONKURENTNIH PROGRAMA
Poželjne osobine konkurentnog programa pripisuju njegovim izvršavanjima
tvrdnje koje važe za sva izvršavanja konkurentnog programa. Svaka od poželjnih
osobina uvodi ili tvrdnju isključivanja nepoželjnog (safety property) ili tvrdnju
uključivanja poželjnog (liveness property). Primeri tvrdnji isključivanja
nepoželjnog su tvrdnja da u izvršavanjima konkurentnog programa ne nastaju
pogrešni rezultati, ili tvrdnja da u izvršavanjima konkurentnog programa nema
narušavanja međusobne isključivosti kritičnih regiona. U primere tvrdnji
uključivanja poželjnog spadaju tvrdnja da izvršavanja konkurentnog programa
imaju kraj ili tvrdnja da se dese svi zatraženi ulasci u kritične regione konkurentnog
programa u toku njegovog izvršavanja.
Za konkurentni program se može reći da ima neku od poželjnih osobina samo
ako se dokaže (na neformalan ili formalan način) da važi tvrdnja koju pomenuta
osobina uvodi. Ovakvo rezonovanje o ispravnosti konkurentnog programa je
neophodno, jer slučajna priroda preplitanja može da bude uzrok
nedeterminističkog (nepredvidivog) izvršavanja konkurentnog programa. U
takvoj situaciji, ponovljena izvršavanja konkurentnog programa, u toku kojih se
obrađuju isti podaci, ne moraju da imaju isti ishod. Zbog toga, provera ispravnosti
konkurentnog programa ne može da se zasniva samo na pokazivanju da pojedina
izvršavanja konkurentnog programa imaju ispravan rezultat, jer tačan rezultat,
dobijen u jednom ili više izvršavanja, ne isključuje mogućnost postojanja
izvršavanja koja za iste ulazne podatke daju netačan rezultat.
24
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
2.5 PROGRAMSKI JEZICI ZA KONKURENTNO
PROGRAMIRANJE
Konkurentno programiranje se razlikuje od sekvencijalnog po rukovanju
nitima i deljenim promenljivima. Opisivanje ovakvih rukovanja se može zasnovati
na korišćenju posebnih konkurentnih iskaza konkurentnog programskog jezika, ali i
na pozivanju odgovarajućih potprograma iz konkurentne biblioteke. Konkurentni
programski jezik može nastati kao rezultat pravljenja potpuno novog programskog
jezika ili kao rezultat proširenja postojećeg sekvencijalnog programskog jezika
konkurentnim iskazima. U oba pristupa neizbežne su aktivnosti vezane za
definisanje sintakse i semantike programskog jezika, kao i aktivnosti vezane za
zahvate na kompajleru. Ovakve aktivnosti se izbegavaju, ako se konkurentno
programiranje zasniva na korišćenju konkurentne biblioteke. Njena dodatna
prednost je što omogućuje da se za konkurentno programiranje koristi već postojeći,
znači poznat programski jezik. Prema tome, posmatrano sa praktičnog stanovišta,
opravdano je konkurentno programiranje osloniti na konkurentnu biblioteku. Ako je
konkurentna biblioteka namenjena za objektno orijentisani programski jezik, tada
ona može da sadrži definicije klasa koje opisuju rukovanje nitima i deljenim
promenljivima. U tom slučaju, nasleđivanje ovih klasa predstavlja prirodan način za
primenu baznih koncepata konkurentnog programiranja.
2.6 KONKURENTNA BIBLIOTEKA COLIBRY
(COncurrent LIBraRY )
COLIBRY (kolibri) je konkurentna biblioteka, namenjena za programski jezik
C++. Ova biblioteka omogućuje pravljenje samostalnih (stand-alone) programa, za
čije izvršavanje nije potrebno prisustvo operativnog sistema. Radi toga, pored
rukovanja nitima i deljenim promenljivima, COLIBRY podržava i rukovanje
standardnim ulaznim uređajem (tastaturom), standardnim izlaznim uređajem
(ekranom), standardnom jedinicom masovne memorije (disketna jedinica), kao i
rukovanje vremenom. Zahvaljujući prethodno pomenutom, COLIBRY omogućuje
pravljenje i programa za rad u realnom vremenu (real-time program). Ovakve
programe posebno karakterišu zahtevi u pogledu brzine izvršavanja. Znači, za
njihovu upotrebljivost nije dovoljno da proizvode dobar rezultat, nego i da taj
rezultat proizvedu brzo, odnosno da njihovo izvršavanje ne traje duže od unapred
zadatog vremena.
COLIBRY obuhvata dve datoteke zaglavlja: COLIBRY.H i COLIB_IO.H, kao
i objektne datoteke 1.OBJ i 2.OBJ. Prva od objektnih datoteka sadrži COLIBRY
izvršilac, a druga sadrži COLIBRY ulazno-izlazni modul. Datoteke zaglavlja su
sastavni deo izvornog oblika COLIBRY programa, a objektne datoteke ulaze u
sastav izvršnog oblika COLIBRY programa.
Rukovanje nitima podržavaju:
1. klasa thread_identity, čiji primerci sadrže oznake pojedinih niti,
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
25
2. funkcija my_sequencer, koja omogućuje niti da preuzme sopstveni serijski
broj,
3. klasa thread, čiji konstruktor opisuje ponašanje pojedinih niti,
4. funkcija destroy, koja omogućuje samouništenje niti,
5. funkcije alarmed i alarm, koje omogućuju jednoj niti da pošalje alarm
drugoj niti,
6. funkcija quit, koja omogućuje uništavanje svih niti,
7. funkcija yield, koja omogućuje jednoj niti da prepusti procesor drugoj niti
svog prioriteta i
8. klasa initial, čiji konstruktor opisuje ponašanje inicijalne niti, koja se
automatski aktivira na početku izvršavanja COLIBRY programa.
Rukovanje vremenom podržavaju:
1. funkcije time_set i time_get, koje omogućuju zadavanje i preuzimanje
sistemskog vremena,
2. funkcija quantum_set, koja omogućuje zadavanje trajanja kvantuma i
3. funkcije delay i delay_till, koje omogućuju privremeno zaustavljanje
aktivnosti niti.
Rukovanje atomskim promenljivima podržavaju:
1. klasa atomic_block, čiji konstruktor i destruktor uspostavljaju i ukidaju
atomski region i
2. templejt klasa atomic, koju nasleđuju sve atomske klase, a čiji
konstruktor automatski vezuje obrađivač prekida atomske promenljive za
prekid sa zadanim brojem vektora prekida.
Rukovanje isključivim promenljvima podržavaju:
1. klasa exclusive_block, čiji konstruktor i destruktor uspostavljaju i
ukidaju isključivi region i
2. klasa exclusive, koju nasleđuju sve isključive klase, a čiji konstruktor
inicijalizuje propusnicu isključive promenljive.
Rukovanje standardnim ulazom i izlazom podržavaju:
1. funkcije ton i toff, koje omogućuju međusobnu isključivost u korišćenju
terminala,
2. objekat tout, koji omogućuje pristupanje ekranu,
3. objekat tin, koji omogućuje pristupanje tastaturi i
4. objakti disk_a i disk_b, koji omogućuju pristupanje disketnim
jedinicama.
2.7 PREGLED KORISNIČKOG INTERFEJSA IZ COLIBRY.H
DATOTEKE
OZNAČAVANJE NITI
Klasa thread_identity omogućuje označavanje niti. Deo deklaracije ove
klase, bitan za njeno korišćenje, je prikazan u nastavku:
26
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
class thread_identity
{ ...
public:
bool existent(unsigned long *sequencer = 0);
thread_identity& operator=(const thread_identity&);
};
Operacija existent omogućuje proveru da li objekat klase thread_identity
sadrži oznaku postojeće niti. Kada objekat ove klase sadrži oznaku postojeće niti,
poziv ove operacije ima vrednost true. Tada jedini parametar operacije existent
omogućuje preuzimanje serijskog broja niti. Serijski brojevi niti pokazuju u kom
redosledu su niti stvarane.
Operator = omogućuje dodelu vrednosti objekta klase thread_identity
drugom objektu iste klase.
Posebna funkcija my_sequencer omogućuje nitima da preuzmu sopstveni
serijski broj. Deklaracija ove funkcije je prikazana u nastavku:
unsigned long my_sequencer();
STVARANJE NITI
Klasa thread omogućuje stvaranje niti. Aktivnost stvarane niti opisuje
konstruktor klase koja je izvedena iz klase thread. Deo deklaracije klase thread,
bitan za njeno korišćenje, je prikazan u nastavku:
class thread
{ ...
public:
void *operator new(size_t type_size,int priority = 1,
size_t stack_size = STACK_SIZE,
thread_identity *id = 0);
virtual void exception(int vector_number);
};
Stvaranje niti nastaje kao rezultat poziva operacije operator new klase thread.
Ovu operaciju poziva operator new u toku stvaranja objekta klase koja je izvedena iz
klase thread. Prvi argument iz poziva ove operacije se nalazi u nadležnosti
kompajlera, pa se ne navodi kao argument operatora new. Ostali argumenti poziva
operacije operator new se podrazumevaju, ako se ne navedu kao argumenti new
operatora. Međutim, ako se navedu, drugi od argumenata ove operacije određuje
prioritet niti, treći određuje veličinu steka niti, odnosno broj bajta radne memorije,
potreban za smeštanje ovog steka, a četvrti omogućuje preuzimanje oznake stvarane
niti.
Prioritet niti je u rasponu od 1 do 63 (konstanta TOP_PRIORITY). Ako je
naveden broj van ovog intervala, kao prioritet služi granica intervala koja je bliža
navedenom broju. Veći broj odgovara višem prioritetu. Podrazumeva se da je među
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
27
spremnim nitima uvek aktivna nit sa najvišim prioritetom. Niti istog prioriteta su
ravnopravne i kružno ustupaju jedna drugoj procesor nakon isticanja kvantuma.
Standardna veličina steka niti je 256 bajta (konstanta STACK_SIZE).
Do stvaranja niti ne dolazi, ako već postoji 101 nit (konstanta
THREADS_NUMBER_LIMIT). Takođe, stvaranje niti je neuspešno i ako je dužina slobodne
radne memorije manja od zatražene veličine steka.
Poziv operacije operator new klase thread uvek vraća vrednost 0, bez obzira
da li je nit stvorena ili ne.
Po uspešnom stvaranju niti, za procesorsko vreme se takmiče nit stvaralac i
novostvorena nit. Aktivnost započinje novostvorena nit samo ako je prioritetnija od
niti stvaraoca. U suprotnom slučaju aktivnost nastavlja nit stvaralac.
Nije predviđeno da se nasleđuje klasa koja je izvedena iz klase thread, niti da
konstruktor ove izvedene klase sadrži lokalne promenljive. Takođe nije predviđeno
da klasa, koja je izvedena iz klase thread, sadrži operaciju operator new.
Klasa example predstavlja primer klase koja je izvedena iz klase thread.
Njena definicija je navedena u nastavku:
class example: public thread
{public:
example(); };
example::example()
{};
Primeri stvaranja niti na osnovu prethodne klase su navedeni u nastavku:
new example;
new (TOP_PRIORITY) example;
U drugom primeru je stvorena nit sa prioritetom TOP_PRIORITY.
Operacija exception predstavlja zamenu za mehanizam rukovanja izuzecima
programskog jezika C++ (try, catch, throw), jer taj mehanizam konkurentna
biblioteka COLIBRY ne podržava. Ova operacija opisuje reakciju na izuzetke,
izazvane aktivnostima niti. Kada je operacija exception potrebna, njena redefinicija
se nalazi u klasi, izvedenoj iz klase thread. Parametar ove operacije omogućuje
preuzimanje broja vektora opsluživanog izuzetka.
Pojava izuzetka u toku aktivnosti niti obavezno izaziva kraj te aktivnosti.
Tome prethodi izvršavanje operacije exception.
UNIŠTAVANJE NITI
Do uništavanja niti dolazi na kraju izvršavanja tela konstruktora koji opisuje
aktivnost niti. Međutim, kraj aktivnosti niti može da nastupi i kao rezultat njenog
samouništavanja. To omogućava funkcija destroy, čija deklaracija je navedena u
nastavku:
28
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
void destroy();
Nit poziva ovu funkciju, kada nastavak njene aktivnosi nema smisla, bilo
zbog unutrašnjih ili spoljnih razloga. Na spoljne razloge završavanja aktivnosti niti
ukazuje prijem alarma od neke druge niti. To omogućuje funkcija alarmed, čija
deklaracija je navedena u nastavku:
bool alarmed();
Poziv ove funkcije vraća vrednost true, ako je prethodno upućen alarm niti.
Slanje alarma niti omogućuje funkcija alarm, čija deklaracija je navedena u
nastavku:
void alarm(thread_identity *id);
Oznaka niti, kojoj se šalje alarm, je jedini argument poziva funkcije alarm.
Poziv ove funkcije ostavlja (naknadnom pozivu funkcije alarmed) podatak o alarmu
u deskriptoru niti kojoj se šalje alarm.
Funkcija quit omogućuje uništavanje svih niti, dovodeći tako do kraja
izvršavanje COLIBRY programa. Njena deklaracija je navedena u nastavku:
void quit();
SVOJEVOLJNO PREKLJUČIVANJE NITI
Funkcija yield omogućuje da aktivna nit svojevoljno prepusti procesor
spremnoj niti svog prioriteta. Ako takve niti nema, poziv ove funkcije nema efekta.
Deklaracija funkcije yield je navedena u nastavku:
void yield();
INICIJALNA NIT
Aktivnost inicijalne niti opisuje konstruktor klase initial, čiju deklaraciju
sadrži COLIBRY.H datoteka. Predviđeno je da definiciju ovog konstruktora sadrži
COLIBRY program. Inicijalnu nit stvara COLIBRY izvršilac na početku izvršavanja
COLIBRY programa. Pošto izvršavanje COLIBRY programa započinje aktivnošću
inicijalne niti, definicija konstruktora klase initial je obavezni deo COLIBRY
programa. Iz prethodnog sledi da ovaj konstruktor preuzima u COLIBRY programu
ulogu koju u C++ programu ima funkcija main. Zbog toga nije predviđeno da
COLIBRY program sadrži i definiciju main funkcije.
Zadatak inicijalne niti je da obavi razne inicijalizacije. Ako su ove
inicijalizacije memorijski zahtevne, tada ih treba prepustiti posebnoj niti, čije
stvaranje je u nadležnosti inicijalne niti. Na ovaj način se može izbeći prekoračenje
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
29
steka inicijalne niti (koji obuhvata 256 bajta). Prioritet inicijalne niti je jednak
konstanti TOP_PRIORITY.
SISTEMSKO VREME
COLIBRY izvršilac registruje proticanje vremena brojanjem otkucaja
(prekida) sata. Broj otkucaja predstavlja sistemsko vreme. Izmenu i preuzimanje
sistemskog vremena, odnosno broja otkucaja, omogućuju, respektivno, funkcije
time_set i time_get. Njihove deklaracije su navedene u nastavku:
void time_set(unsigned long time);
unsigned long time_get();
Funkcija quantum_set omogućuje izmenu kvantuma. Njena deklaracija je
navedena u nastavku:
void quantum_set(unsigned long quantum);
Parametar quantum omogućuje zadavanje novog kvantuma, izraženog brojem
otkucaja sata. Izmena kvantuma postaje delotvorna tek nakon preključivanja koje
usledi nakon ove izmene.
Funkcija delay omogućuje uspavljivanje aktivne niti, odnosno zaustavljanje
njene aktivnosti dok ne protekne zadani broj otkucaja sata. Njena deklaracija je
navedena u nastavku:
void delay(unsigned long duration);
Parametar duration omogućuje zadavanje broja otkucaja koji određuje
najkraći period odlaganja aktivnosti niti, pozivaoca funkcije delay. Poziv ove
funkcije sa argumentom većim od nula dovodi do aktiviranja najprioritetnije od
spremnih niti. Završavanje poziva funkcije delay i nastavljanje aktivnosti niti
njenog pozivaoca nastupa najranije nakon isticanja zadatog perioda odlaganja.
Funkcija delay_till omogućuje uspavljivanje aktivne niti, odnosno
zaustavljanje njene aktivnosti do određenog trenutka sistemskog vremena, odnosno
dok broj otkucaja ne poprimi zadanu vrednost. Njena deklaracija je navedena u
nastavku:
void delay_till(unsigned long moment);
Parametar moment omogućuje zadavanje trenutka sistemskog vremena do
koga će biti odložena aktivnost niti koja je pozvala funkciju delay_till. Ova
funkcija uvodi pojam prošlosti i budućnosti, jer njen argument može da se odnosi i
na prošli i na budući trenutak sistemskog vremena. Pošto je sistemsko vreme
izraženo brojem otkucaja sata, potrebno je odrediti koji broj otkucaja predstavlja
granicu između budućih i prošlih trenutaka. Taj, granični broj otkucaja se dobije,
30
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
kada se trenutni broj otkucaja (sadašnji trenutak) uveća polovinom najveće
vrednosti tipa unsigned long. Ako se sistemsko vreme predstavi tačkama na
kružnici, tada trenutni i granični broj otkucaja dele ovu kružnicu na dve
polukružnice. Kazaljka, koja se kreće od tačke trenutnog broja otkucaja do tačke
graničnog broja otkucaja, opisuje polukružnicu sa budućim trenucima. Preostala
polukružnica sadrži prošle trenutke.
Poziv funkcije delay_till, čiji argument određuje trenutak u budućnosti,
dovodi do aktiviranja najprioritetnije od spremnih niti. Završavanje ovog poziva i
nastavljanje aktivnosti niti pozivaoca funkcije delay_till nastupa najranije, kada
sistemsko vreme dostigne zadati trenutak u budućnosti. Pomoću funkcije
delay_till je moguće ostvariti preciznu periodičnost ponavljanja aktivnosti niti.
ATOMSKI REGION
Ulazak u atomski region je povezan sa onemogućenjem prekida, a izlazak iz
atomskog regiona je povezan sa vraćanjem omogućenosti prekida u stanje pre
njihovog onemogućavanja. Postupak ulaza u atomski region se naziva ulazni
atomski protokol, a postupak izlaza iz atomskog regiona se naziva izlazni atomski
protokol.
Za C++ je prirodno da se atomski region predstavi složenim iskazom
(compound statement ili block). U tom slučaju, ulazni i izlazni atomski protokol se
mogu ostvariti kao konstruktor i destruktor posebne lokalne promenljive, definisane
u ovakvom složenom iskazu. Položaj njene definicije određuje početak atomskog
regiona, čiji kraj se poklapa sa krajem složenog iskaza. Klasa atomic_block
omogućuje stvaranje ovakvih lokalnih promenljivih. Deo njene deklaracije, bitan za
njeno korišćenje, je prikazan u nastavku:
class atomic_block
{ ...
public:
atomic_block();
~atomic_block(); };
Primer korišćenja lokalne promenljive tipa
atomskog regiona je naveden u nastavku:
atomic_block
za ostvarenje
{atomic_block set_up;
...
};
Složeni iskaz iz prethodnog primera predstavlja atomski region, jer započinje
definicijom lokalne promenljive set_up tipa atomic_block, čiji konstruktor i
destruktor ostvaruju ulazni i izlazni atomski protokol.
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
31
ATOMSKE KLASE
Atomske klase omogućuju konzistentnu saradnju obrađivača prekida i
pozadinskih niti. Osnovna ideja ovakve saradnje je da se u obradi prekida obavi
samo neodložan deo obrade podataka, čija pojava je vezana za dešavanje vanjskih
događaja. Preostala obrada ovih podataka se prepušta pozadinskoj niti. Na taj način,
preostala obrada podataka se može obavljati pod omogućenim prekidima, što
skraćuje vreme reakcije na dešavanje vanjskih događaja.
Saradnja obrađivača prekida i pozadinskih niti podrazumeva da se aktivnost
pozadinske niti zaustavi, kada se obrade svi podaci. Nakon toga, do nastavka
aktivnosti pozadinske niti dolazi tek po prispeću novih podataka, znači posle
dešavanja vanjskog događaja i završetka obrade odgovarajućeg prekida. Prilikom
zaustavljanja, pozadinska nit prelazi u stanje čeka, a njen deskriptor se uvezuje u
listu događaja čije dešavanje se očekuje. Po dešavanju ovog događaja, u obradi
odgovarajućeg prekida, deskriptor pozadinske niti se izvezuje iz liste pomenutog
događaja i uvezuje u spremnu listu, a pozadinska nit se prevodi u stanje spremna.
Međutim, do njenog aktiviranja može doći tek nakon obrade dotičnog prekida.
Templejt (template) klasa atomic omogućuje stvaranje atomskih klasa. Deo
deklaracija ove klase, bitan za njeno korišćenje, je naveden u nastavku:
template<int VECTOR_NUMBER>
class atomic ...
{ ...
protected:
virtual void interrupt_handler();
int interrupt_vector_number() const;
class event ...
{ ...
public:
void expect();
void notify();
bool expected() const; };
class tag_event ...
{ ...
public:
bool first(tag_type *t = 0);
bool succ(tag_type *t = 0);
bool last(tag_type *t = 0);
bool attach_tag(tag_type t);
void expect(tag_type t);
void notify();
bool expected() const; };
};
Jedini parametar templejt klase atomic omogućuje zadavanje broja vektora
prekida. Prekidi sa ovim brojem vektora pripadaju atomskoj klasi koja se izvodi iz
templejt klase atomic. Pri tome se podrazumeva da svaka atomska klasa sadrži
definiciju operacije interrupt_handler. Ova operacija predstavlja obrađivač prekida
njene atomske klase. Prilikom stvaranja objekta atomske klase, za njenu vrstu
32
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
prekida se automatski vezuje njen obrađivač prekida. Zahvaljujući tome, nakon
dešavanja prekida atomske klase, uvek se poziva njen obrađivač prekida.
Podrazumeva se da su prekidi onemogućeni u toku obrade prekida. To je tačno,
kada postoji jedan nivo prekida.
Predviđeno je da su atomske promenljive uvek globalne (statične).
Operacija interrupt_vector_number omogućuje preuzimanje broja vektora
prekida atomske klase.
Klasa event omogućuje definisanje liste (vanjskog) događaja za čije
dešavanje je vezana aktivnost (pozadinskih) niti. Svakom objektu ove klase
odgovara po jedna takva lista. Operacije ove klase omogućuju zaustavljanje
aktivnosti niti do dešavanja datog događaja (expect), nastavak aktivnost niti nakon
dešavanja dotičnog događaja (notify), kao i proveru da li ima niti koje očekuju
dešavanje dotičnog događaja (expected). Dok nit čeka na dešavanje datog događaja,
njen deskriptor je uvezan u listu ovog događaja. Podrazumeva se da dešavanja
događaja dovode da nastavaka aktivosti niti u redosledu u kome su one svoju
aktivnost zaustavljale, radi očekivanja pomenutih dešavanja. U ovom redosledu su
deskriptori niti uvezani u listu dotičnog događaja.
Operacija expect omogućuje zaustavljanje aktivnosti niti dok se ne desi dati
događaj. Tome prethodi prevođenje niti u stanje čeka i uvezivanje njenog
deskriptora na kraj liste dotičnog događaja. Poziv operacije expect dovodi do
aktiviranja najprioritetnije od spremnih niti. Predviđeno je da se ova operacija
poziva samo iz atomskog regiona.
Operacija notify omogućuje objavu dešavanja nekog događaja, radi nastavka
aktivnosti niti koja najduže (ili koja jedina) očekuje dešavanje dotičnog događaja.
Ova operacija izveže deskriptor niti (kada on postoji) sa početka liste datog
događaja, a nit prevede u stanje spremna. Predviđeno je da se operacija notify
poziva samo iz obrađivača prekida, jer jedino oni opisuju reakcije na dešavanje
(vanjskih) događaja. Svaka od niti, koja je objavom dešavanja događaja prevedena
u stanje spremna, može da nastavi aktivnost tek kada se završi obrada dotičnog
događaja, odnosno, kada se izvrši odgovarajuća interrupt_handler operacija.
Ovakvih niti može biti više, jer ista obrada prekida može napraviti više poziva
operacije notify. Nakon obrade prekida, aktivnost nastavlja prekinuta nit samo ako
je prioritetnija od spremnih niti. Inače aktivnost nastavlja najprioritetnija spremna
nit, i to ona koja je najduže u stanju spremna, ako ima više spremnih niti najvišeg
prioriteta.
Operacija expected omogućuje proveru da li ima niti koje očekuju dešavanje
datog događaja ili, drugim rečima, proveru da li u listi dotičnog događaja ima
deskriptora. Predviđeno je da se ova operacija poziva samo iz atomskog regiona.
Klasa tag_event omogućuje definisanje liste (vanjskog) događaja za čije
dešavanje je vezana aktivnost (pozadinskih) niti. Svakom objektu ove klase
odgovara po jedna takva lista. Operacije ove klase omogućuju zaustavljanje
aktivnosti niti do dešavanja datog događaja (expect), nastavak aktivnost niti nakon
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
33
dešavanja dotičnog događaja (notify), kao i proveru da li ima niti koje očekuju
dešavanje dotičnog događaja (expected). Dok nit čeka na dešavanje datog događaja,
njen deskriptor je uvezan u listu ovog događaja. Za razliku od klase event, klasa
tag_event dozvoljava da dešavanja događaja dovode da nastavaka aktivnosti niti u
odabranom redosledu. Radi toga klasa tag_event predviđa dodelu posebnog
privezka svakom od deskriptora iz liste događaja (operacije attach_tag i expect).
Privesci imaju oblik neoznačenih celih brojeva i predstavljaju osnovu za
određivanje redosleda deskriptora u listi događaja. Pored toga, klasa tag_event
uvodi i operacije za pozicioniranje na tačku uvezivanja novog deskriptora u listu
događaja. Ta tačka može da bude pre prvog, pre narednog i pre poslednjeg
deskriptora u listi događaja (operacije first, succ i last, respektivno). Operacije
pozicioniranja omogućuju prolazak kroz listu događaja i pozicioniranje na tačku
uvezivanja deskriptora niti koja će sledeća da zaustavi svoju aktivnost, radi
očekivanja dešavanja dotičnog događaja. Na taj način deskriptori niti se uvezuju u
listu događaja u odabranom redosledu. To je ujedno i redosled u kome se niti
kasnije aktiviraju, jasno nakon dešavanja odgovarajućih događaja.
Operacija first omogućuje pozicioniranje pre prvog deskriptora u listi
događaja. Do pozicioniranja dolazi samo ako lista događaja nije prazna. Samo tada
poziv ove operacije vraća vrednost true, a njen parametar omogućuje preuzimanje
privezka deskriptora koji se nalazi iza tačke uvezivanja. Predviđeno je da se
operacija first poziva samo iz atomskog regiona.
Operacija succ omogućuje pozicioniranje pre narednog ili iza poslednjeg
deskriptora u listi događaja. Do pozicioniranja dolazi samo ako lista događaja nije
prazna i ako je prethodno izvršeno pozicioniranje pre nekog deskriptora u ovoj listi.
Poziv ove operacije vraća vrednost true samo kada, nakon pozicioniranja, iza tačke
uvezivanja postoji deskriptor. Tada parametar operacije succ omogućuje
preuzimanje privezka tog deskriptora. Predviđeno je da se ova operacija poziva
samo iz atomskog regiona.
Operacija last omogućuje pozicioniranje pre poslednjeg deskriptora u listi
događaja. Do pozicioniranja dolazi samo ako lista događaja nije prazna i tada poziv
ove operacije vraća vrednost true, a njen parametar omogućuje preuzimanje
privezka deskriptora koji se nalazi iza tačke uvezivanja. Predviđeno je da se
operacija last poziva samo iz atomskog regiona.
Operacija attach_tag omogućuje dodelu novog privezka deskriptoru, pre
koga se nalazi tačka uvezivanja u listu događaja. Do dodele privezka dolazi samo
ako je prethodno izvršeno pozicioniranje pre nekog deskriptora u listi događaja i
samo tada poziv ove operacije vraća vrednost true. Predviđeno je da se operacija
attach_tag poziva samo iz atomskog regiona.
Operacija expect omogućuje zaustavljanje aktivnosti niti dok se ne desi dati
događaj. Tome prethodi prevođenje niti u stanje čeka, uvezivanje njenog
deskriptora u tački uvezivanja liste dotičnog događaja i dodelu privezka ovom
deskriptoru, što omogućuje parametar ove operacije. Ako pozivu operacije expect
34
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
nije prethodilo pozicioniranje na tačku uvezivanja, podrazumeva se da se njen
deskriptor uvezuje na kraj liste odgovarajućeg događaja. Poziv ove operacije dovodi
do aktiviranja najprioritetnije od spremnih niti. Predviđeno je da se operacija expect
poziva samo iz atomskog regiona.
Operacija notify omogućuje objavu dešavanja datog događaja, radi nastavka
aktivnosti niti koja najduže (ili koja jedina) očekuje dešavanje dotičnog događaja.
Ova operacija izveže deskriptor niti (kada on postoji) sa početka liste datog
događaja, uveže ga u spremnu listu, a nit prevede u stanje spremna. Predviđeno je
da se operacija notify poziva samo iz obrađivača prekida, jer jedino oni opisuju
reakcije na dešavanje (vanjskih) događaja. Svaka od niti, koja je objavom dešavanja
događaja prevedena u stanje spremna, može da nastavi aktivnost tek kada se završi
obrada dotičnog događaja, odnosno, kada se izvrši odgovarajuća interrupt_handler
operacija. Ovakvih niti može biti više, jer ista obrada prekida može napraviti više
poziva operacije notify. Nakon obrade prekida, aktivnost nastavlja prekinuta nit
samo ako je prioritetnija od spremnih niti. Inače aktivnost nastavlja najprioritetnija
spremna nit, i to ona koja je najduže u stanju spremna, ako ima više spremnih niti
istog najvišeg prioriteta.
Operacija expected omogućuje proveru da li ima niti koje očekuju dešavanje
datog događaja ili, drugim rečima, proveru da li u listi dotičnog događaja ima
deskriptora. Predviđeno je da se ova operacija poziva samo iz atomskog regiona.
Atomski regioni se koriste u okviru operacije atomske klase samo kada to
zahtevaju razlozi očuvanja konzistentnosti. Prekomerna upotreba atomskih regiona
dovodi do produžavanja vremena reakcije na vanjske događaje.
ISKLJUČIVI REGION
Ulazak u isključivi region je povezan sa dobijanjem propusnice, a izlazak iz
isključivog regiona je povezan sa vraćanjem propusnice. Bez propusnice nit ne
može započeti aktivnost u bilo kom od isključivih regiona isključive promenljive.
Nemogućnost dobijanja propusnice povlači za sobom odlaganje pomenutog početka
aktivnosti. U tom slučaju nit prelazi u stanje čeka, a njen deskriptor se uvezuje na
kraj ulazne liste, namenjene za deskriptore niti koje čekaju da uđu u neki od
isključivih regiona isključive promenljive. Među nitima koje čekaju dobijanje
propusnice, prednost ima nit koja najduže čeka, znači nit čiji deskriptor je prvi u
ulaznoj listi isključive promenljive. Postupak ulaza u isključivi region se naziva
ulazni isključivi protokol, a postupak izlaza iz isključivog regiona se naziva
izlazni isključivi protokol.
U svakoj isključivoj promenljivoj postoji samo jedna ulazna lista.
Za C++ je prirodno da se isključivi region predstavi složenim iskazom
(compound statement ili block). U tom slučaju, ulazni i izlazni isključivi protokol se
mogu ostvariti kao konstruktor i destruktor posebne lokalne promenljive, definisane
u ovakvom složenom iskazu. Položaj njene definicije određuje početak isključivog
regiona, čiji kraj se poklapa sa krajem složenog iskaza. Klasa exclusive_block
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
35
omogućuje stvaranje ovakvih lokalnih promenljivih. Deo deklaracije ove klase,
bitan za njeno korišćenje, je naveden u nastavku:
class exclusive_block
{ ...
public:
exclusive_block(exclusive *ex);
~exclusive_block(); };
Parametar konstruktora klase exclusive_block omogućava označavanje
isključive promenljive kojoj pripada isključivi region i na čiju propusnicu se odnose
ulazni i izlazni isključivi protokol.
Primer korišćenja lokalne promenljive tipa exclusive_block za ostvarenje
isključivog regiona je naveden u nastavku:
{exclusive_block set_up(&exclusive_variable);
...
};
Složeni iskaz iz prethodnog primera predstavlja isključivi region, jer
započinje definicijom lokalne promenljive set_up tipa exclusive_block, čiji
konstruktor i destruktor ostvaruju ulazni i izlazni isključivi protokol. Argument
konstruktora lokalne promenljive set_up ukazuje da prethodni isključivi region
pripada isključivoj promenljivoj exclusive_variable. Pretpostavka je da je ova
isključiva promenljiva prethodno definisana kao objekat klase izvedene iz klase
exclusive.
ISKLJUČIVE KLASE
Isključive klase omogućuju konzistentnu saradnju raznih niti. Ova saradnja se
zasniva na međusobnoj isključivosti, kao i na uslovnoj sinhronizaciji. To znači, ako
nije ispunjen neophodan uslov za nastavak aktivnosti niti, ona zaustavlja svoju
aktivnosti, prelazeći u stanje čeka. Tom prilikom se deskriptor ove niti uvezuje u
listu uslova čije ispunjenje nit čeka. Ova nit ostaje u očekivanju ispunjenja uslova
sve dok neka druga nit, kojoj su prepušteni procesor i propusnica, ne ispuni
pomenuti uslov. Tada ta druga nit prevodi prvu nit u stanje spremna, prebacuje njen
deskriptor iz liste uslova u listu ispunjenih uslova i tako omogućuje nastavak njene
aktivnosti.
Klasa exclusive omogućuje stvaranje isključivih klasa. Deo deklaracije ove
klase, bitan za njeno korišćenje, je naveden u nastavku:
36
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
class exclusive ...
{ ...
public:
exclusive(int priority = 1);
protected:
class condition ...
{ ...
public:
void await();
void signal();
bool awaited() const; };
class tag_condition ...
{ ...
public:
bool first(tag_type *t = 0);
bool succ(tag_type *t = 0);
bool last(tag_type *t = 0);
bool attach_tag(tag_type t);
void await(tag_type t);
void signal();
bool awaited() const; };
};
Jedini parametar konstruktora klase exclusive omogućuje zadavanje
prioriteta isključivoj promenljivoj (objektu) isključive klase, koja je izvedena iz
klase exclusive. Niti, čiji prioritet je niži od prioriteta ove isključive promenljive,
dobijaju njen prioritet dok se nalaze u njenom isključivom regionu.
Klasa condition omogućuje definisanje liste uslova od čijeg ispunjenja zavisi
nastavak aktivnost niti. Svakom objektu ove klase odgovara po jedna takva lista.
Operacije ove klase omogućuju zaustavljanje aktivnosti niti do ispunjenja datog
uslova (await), nastavak aktivnosti niti nakon ispunjenja dotičnog uslova (signal),
kao i proveru da li ima niti koje očekuju ispunjenje dotičnog uslova (awaited). Dok
nit čeka na ispunjenje datog uslova, njen deskriptor je uvezan u listu ovog uslova.
Podrazumeva se da ispunjenja uslova dovode do nastavaka aktivosti niti u redosledu
u kome su one svoju aktivnost zaustavljale, radi očekivanja pomenutih ispunjenja.
U ovom redosledu su deskriptori niti uvezani u listu dotičnog uslova. Predviđeno je
da se sve operacije klase condition pozivaju samo iz isključivog regiona.
Operacija await omogućuje zaustavljanje aktivnosti niti dok se ne ispuni dati
uslov. Tome prethodi prevođenje niti u stanje čeka i uvezivanje njenog deskriptora
na kraj liste dotičnog uslova i vraćanje propusnice odgovarajuće isključive
promenljive. Poziv operacije await dovodi do aktiviranja najprioritetnije od
spremnih niti.
Operacija signal omogućuje objavu ispunjenja datog uslova, radi nastavka
aktivnosti niti koja najduže (ili koja jedina) očekuje ispunjenje dotičnog uslova.
Ova operacija izveže deskriptor signalizirane niti (kada on postoji) sa početka liste
datog uslova i uveže ga na kraj liste ispunjenih uslova. Signalizirana nit može da
nastavi aktivnost tek kada signalizirajuća nit, koja je pozvala operaciju signal, vrati
propusnicu za odgovarajuću isključivu promenljivu. Signaliziranih niti može biti
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
37
više, jer ista signalizirajuća nit može napraviti više poziva operacije signal. Kada
signalizirajuća nit vrati propusnicu, nju preuzima (kada postoji) signalizirana nit,
čiji deskriptor je prvi u listi ispunjenih uslova. Tada se ovaj deskriptor izveže iz liste
ispunjenih uslova, a signalizirana nit nastavlja aktivnost samo ako je prioritetnija od
signalizirajuće niti. Inače prelazi u stanje spremna, a aktivnost nastavlja
signalizirajuća nit.
U svakoj isključivoj promenljivoj postoji samo jedna lista ispunjenih uslova.
Operacija awaited omogućuje proveru da li ima niti koje očekuju ispunjenje
datog uslova ili, drugim rečima, proveru da li u listi dotičnog uslova ima
deskriptora.
Klasa tag_condition omogućuje definisanje liste uslova od čijeg ispunjenja
zavisi nastavak aktivnosti niti. Svakom objektu ove klase odgovara po jedna takva
lista. Operacije ove klase omogućuju zaustavljanje aktivnosti niti do ispunjenja
datog uslova (await), nastavak aktivnosti niti nakon ispunjenja dotičnog uslova
(signal), kao i proveru da li ima niti koje očekuju ispunjenje dotičnog uslova
(signaled). Dok nit čeka na ispunjenje datog uslova, njen deskriptor je uvezan u
listu ovog uslova. Za razliku od klase condition, klasa tag_condition dozvoljava da
dešavanja događaja dovode do nastavaka aktivnosti niti u odabranom redosledu.
Radi toga klasa tag_condition predviđa dodelu posebnog privezka svakom od
deskriptora iz liste uslova (operacije attach_tag i await). Privesci imaju oblik
neoznačenih celih brojeva i predstavljaju osnovu za određivanje redosleda
deskriptora u listi uslova. Pored toga, klasa tag_condition uvodi i operacije za
pozicioniranje na tačku uvezivanja novog deskriptora u listu uslova. Ta tačka može
da bude pre prvog, pre narednog i pre poslednjeg deskriptora u listi uslova
(operacije first, succ i last, respektivno). Operacije pozicioniranja omogućuju
prolazak kroz listu uslova i pozicioniranje na tačku uvezivanja deskriptora niti koja
će sledeća da zaustavi svoju aktivnost, radi očekivanja ispunjenja dotičnog uslova.
Na taj način deskriptori niti se uvezuju u listu uslova u odabranom redosledu. To je
ujedno i redosled u kome se niti kasnije aktiviraju, jasno nakon ispunjenja
odgovarajućih uslova. Predviđeno je da se sve operacije klase tag_condition
pozivaju samo iz isključivog regiona.
Operacija first omogućuje pozicioniranje pre prvog deskriptora u listi
uslova. Do pozicioniranja dolazi samo ako lista uslova nije prazna i tada poziv ove
operacije vraća vrednost true, a njen parametar omogućuje preuzimanje privezka
deskriptora koji se nalazi iza tačke uvezivanja.
Operacija succ omogućuje pozicioniranje pre narednog ili iza poslednjeg
deskriptora u listi uslova. Do pozicioniranja dolazi samo ako lista uslova nije prazna
i ako je prethodno izvršeno pozicioniranje pre nekog deskriptora u ovoj listi. Poziv
ove operacije vraća vrednost true samo kada, nakon pozicioniranja, iza tačke
uvezivanja postoji deskriptor. Tada parametar operacije succ omogućuje
preuzimanje privezka tog deskriptora.
38
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Operacija last omogućuje pozicioniranje pre poslednjeg deskriptora u listi
uslova. Do pozicioniranja dolazi samo ako lista uslova nije prazna i tada poziv ove
operacije vraća vrednost true, a njen parametar omogućuje preuzimanje privezka
deskriptora koji se nalazi iza tačke uvezivanja.
Operacija attach_tag omogućuje dodelu novog privezka deskriptoru, pre
koga se nalazi tačka uvezivanja u listu uslova. Do dodele privezka dolazi samo ako
je prethodno izvršeno pozicioniranje pre nekog deskriptora u listi uslova i samo
tada poziv ove operacije vraća vrednost true.
Operacija await omogućuje zaustavljanje aktivnosti niti dok se ne ispuni dati
uslov. Tome prethodi prevođenje niti u stanje čeka, uvezivanje njenog deskriptora u
tački uvezivanja liste dotičnog uslova, dodela privezka ovom deskriptoru, što
omogućuje parametar ove operacije, i vraćanje propusnice odgovarajuće isključive
promenljive. Ako pozivu operacije await nije prethodilo pozicioniranje na tačku
uvezivanja, podrazumeva se da se njen deskriptor uvezuje na kraj liste
odgovarajućeg uslova. Poziv ove operacije dovodi do aktiviranja najprioritetnije od
spremnih niti.
Operacija signal omogućuje objavu ispunjenja datog uslova, radi nastavka
aktivnosti niti koja najduže (ili koja jedina) očekuje ispunjenje dotičnog uslova.
Ova operacija izveže deskriptor signalizirane niti (kada on postoji) sa početka liste
datog uslova i uveže ga na kraj liste ispunjenih uslova. Signalizirana nit može da
nastavi aktivnost tek kada signalizirajuća nit, koja je pozvala operaciju signal, vrati
propusnicu za odgovarajuću isključivu promenljivu. Signaliziranih niti može biti
više, jer ista signalizirajuća nit može napraviti više poziva operacije signal. Kada
signalizirajuća nit vrati propusnicu, nju preuzima (kada postoji) signalizirana nit,
čiji deskriptor je prvi u listi ispunjenih uslova. Tada se ovaj deskriptor izveže iz liste
ispunjenih uslova, a signalizirana nit nastavlja aktivnost samo ako je prioritetnija od
signalizirajuće niti. Inače prelazi u stanje spremna, a aktivnost nastavlja
signalizirajuća nit.
Operacija awaited omogućuje proveru da li ima niti koje očekuju ispunjenje
datog uslova ili, drugim rečima, proveru da li u listi dotičnog uslova ima
deskriptora.
Isključivi regioni se koriste u okviru operacije isključive klase uvek kada to
zahtevaju razlozi očuvanja konzistentnosti.
2.8 PREGLED KORISNIČKOG INTERFEJSA IZ COLIB_IO.H
DATOTEKE
RUKOVANJE TERMINALOM
Standardni ulazni uređaj (tastatura) i standardni izlazni uređaj (ekran) zajedno
predstavljaju terminal. Terminal međusobno dele sve niti. Zbog toga je važno
ostvariti međusobnu isključivost niti u korišćenju terminala. To omogućuju
funkcije:
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
39
void ton();
void toff();
Funkcija ton omogućuje niti da označi početak vremenskog perioda u kome
je terminal dodeljen isključivo njoj na korišćenje, dok funkcija toff omogućuje niti
da označi kraj ovog vremenskog perioda. Poziv funkcije ton zaustavlja aktivnost
niti, ako je u trenutku poziva terminal već bio dodeljen drugoj niti za isključivo
korišćenje. Nastavak aktivnosti prve niti postaje moguć tek kada druga nit pozove
funkciju toff. Ako je aktivnost više niti bila zaustavljena u okviru poziva funkcije
ton, tada će nastavak njihovih aktivnosti (kada to postane moguće) biti u redosledu
u kome su te aktivnosti bile zaustavljene.
Klasa terminal_out omogućuje prikaz znakova na ekranu. Deo deklaracije
ove klase, bitan za njeno korišćenje, je naveden u nastavku:
class terminal_out
{ ...
public:
terminal_out& operator<<(int number);
terminal_out& operator<<(unsigned int number);
terminal_out& operator<<(long number);
terminal_out& operator<<(unsigned long number);
terminal_out& operator<<(double number);
terminal_out& operator<<(char character);
terminal_out& operator<<(const char *string); };
Operacije ove klase omogućuju prikazivanje označenog i neoznačenog celog
broja od 4 i 9 cifara, označenog realnog broja od 7 cifara, kao i prikazivanje znaka i
niza znakova. Prethodne operacije se izvrše samo ako terminal nije dodeljen nekoj
drugoj niti na isključivo korišćenje. Dok je terminal dodeljen nekoj drugoj niti na
isključivo korišćenje, u okviru ovih operacija se zaustavljaju aktivnosti niti koje su
ih pozvale. Zato se ove operacije nazivaju blokirajuće operacije.
Za klasu terminal_out je definisana promenljiva:
terminal_out tout;
koja omogućuje pozivanje prethodnih operacija. Tako, na primer, iskaz:
tout << "PI = " << 3.14;
omogućuje da se tekst: PI = 3.14 pojavi na ekranu terminala.
Podrazumeva se da je definisana konstanta:
const char NEW_LINE[] = "\n\r";
40
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Klasa terminal_in omogućuje preuzimanje znakova sa tastature. Deo
deklaracije ove klase, bitan za njeno korišćenje, je naveden u nastavku:
class terminal_in
{ ...
public:
bool empty();
terminal_in& operator>>(int& number);
terminal_in& operator>>(long& number);
terminal_in& operator>>(double& number);
terminal_in& operator>>(char& character); };
Prva operacija ove klase omogućuje proveru da li ima znakova za
preuzimanje. Poziv ove operacije vraća vrednost true, ako nema znakova za
preuzimanje, inače vraća vrednost false. Preostale operacije ove klase omogućuju
preuzimanje označenog celog broja od 4 i 9 cifara, označenog realnog broja od 7
cifara, kao i preuzimanje znaka. U slučaju preuzimanja znaka, kada se pritisne dirka
tastature koja ne odgovara ASCII znaku, prvo se preuzme kod koji nije veći od
nule, a zatim se preuzme poseban kod znaka, generisan u tastaturi.
Uz izuzetak operacije empty, preostale operacije klase terminal_in se izvrše
samo ako je završen unos znakova na tastaturi i samo ako terminal nije dodeljen
nekoj drugoj niti na isključivo korišćenje. Dok traje unos znakova na tastaturi i dok
je terminal dodeljen nekoj drugoj niti na isključivo korišćenje, u okviru ovih
operacija se zaustavljaju aktivnosti niti koje su ih pozvale. Zato se ove operacije
nazivaju blokirajuće operacije.
Za klasu terminal_in je definisana promenljiva:
terminal_in tin;
koja omogućuje pozivanje prethodnih operacija. Tako, na primer, iskaz:
int n;
tin >> n;
omogućuje da sa tastature se preuzme vrednost celobrojne promenljive n. Ova
vrednost može imati do 4 cifre.
RUKOVANJE DISKETNOM JEDINICOM
Klasa disk_io omogućuje preuzimanje blokova podataka sa disketne jedinice
i njihovo smeštanje na disketnu jedinicu. Deo deklaracije ove klase, bitan za njeno
korišćenje, je naveden u nastavku:
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
41
class disk_io
{ ...
public:
disk_io(char unit,unsigned blocks_number = 720);
int block_get(char *buffer,unsigned block);
int block_put(char *buffer,unsigned block); };
Konstruktor ove klase omogućuje definisanje njenog objekta koji
reprezentuje odgovarajuću disketnu jedinicu. Parametar unit omogućuje zadavanje
broja ove jedinice, a parametar blocks_number omogućuje zadavanje broja blokova
na jedinici. Podrazumeva se da je definisana konstanta:
const int DISK_BLOCK = 512;
Za klasu disk_io su definisane promenljive:
disk_io disk_a;
disk_io disk_b;
koje reprezentuju jedinice masovne memorije sa oznakama A i B. Podrazumeva se
da svaka od njih sadrži 720 blokova.
Operacija block_get omogućuje preuzimanje bloka podataka sa disketne
jedinice. Preuzeti blok se smešta u zonu radne memorije, koju određuje parametar
buffer, a čija dužina je DISK_BLOCK bajta. Podaci se preuzimaju iz bloka čiji broj
određuje parametar block. Uspešan poziv ove operacije vraća vrednost nula. U
suprotnom slučaju, poziv operacije vraća broj greške. Broj greške -1 ukazuje na
pogrešan broj dobavljanog bloka. Ostali brojevi grešaka zavise od disketne jedinice.
Operacija block_get se završava, kada je moguć pristup disketnoj jedinici i
kada se sa nje dobavi traženi blok podataka. U međuvremenu se zaustavlja
aktivnost niti pozivaoca ove operacije.
Operacija block_put omogućuje smeštanje bloka podataka na disketnu
jedinicu. Smeštani blok se nalazi u zoni radne memorije, koju određuje parametar
buffer, a čija dužina je DISK_BLOCK bajta. Podaci se smeštaju na blok čiji broj
određuje parametar block. Uspešan poziv ove operacije vraća vrednost nula. U
suprotnom slučaju, poziv operacije vraća broj greške. Broj greške -1 ukazuje na
pogrešan broj smeštanog bloka. Ostali brojevi grešaka zavise od disketne jedinice.
Operacija block_put se završava, kada je moguć pristup disketnoj jedinici i
kada se na nju smesti traženi blok podataka. U međuvremenu se zaustavlja
aktivnost niti pozivaoca ove operacije.
Operacije block_get i block_put spadaju u blokirajuće operacije.
2.9 PRIMERI SPREČAVANJA ŠTETNIH PREPLITANJA
U primeru rukovanja pozicijom kursora, nisu moguća štetna preplitanja niti i
obrada prekida, ako telo operacije take klase position_type postane atomski region.
Pošto se operacija change poziva samo iz obrada prekida, njeno telo po definiciji
42
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
obrazuje atomski region, pa je tako osigurana međusobna isključivost operacija
klase position_type. Izmenjena definicija ove klase je navedena u nastavku:
# include <colibry.h>
class position_type
{ int x,y;
public:
position_type();
void change(int new_x,int new_y);
void take(int *current_x, int *current_y); };
position_type::position_type()
{ x = 0;
y = 0; };
void position_type::change(int new_x,int new_y)
{ x = new_x;
y = new_y; };
void position_type::take(int *current_x,int *current_y)
{ atomic_block set_up;
*current_x = x;
*current_y = y; };
U primeru rukovanja slobodnim baferima, štetna međusobna preplitanja niti
mogu da se spreče, ako tela operacija klase buffer_list_type obrazuju isključive
regione. U tom slučaju je osigurana međusobna isključivost ovih operacija.
Izmenjena definicija klase buffer_list_type je navedena u nastavku:
# include <colibry.h>
const unsigned int BUFFER_SIZE = 512;
class buffer_list_type: public exclusive
{ static buffer_list_type *first;
buffer_list_type *next;
char content[BUFFER_SIZE];
public:
buffer_list_type();
void link(buffer_list_type *new_buffer);
buffer_list_type *unlink(); };
buffer_list_type *buffer_list_type::first = 0;
buffer_list_type::buffer_list_type()
{ };
void buffer_list_type::link(buffer_list_type *new_buffer)
{ exclusive_block set_up(this);
new_buffer->next = first;
first = new_buffer; };
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
43
buffer_list_type *buffer_list_type::unlink()
{ buffer_list_type *unlinked;
exclusive_block set_up(this);
unlinked = first;
if (first != 0)
first = first->next;
return unlinked; };
Operacija unlink spada u neblokirajuće operacije, jer, u situaciji, kada je lista
bafera prazna, ona ne zaustavlja aktivnost niti svog pozivaoca, nego vraća vrednost
0. Ova operacija postaje blokirajuća, ako se osloni na uslovnu sinhronizaciju,
odnosno, ako iskoristi mogućnosti koje nudi klasa condition. U nastavku je
navedena verzija klase buffer_list_type, sa blokirajućom operacijom unlink:
# include <colibry.h>
const unsigned int BUFFER_SIZE = 512;
class buffer_list_type: public exclusive
{ static buffer_list_type *first;
buffer_list_type *next;
char content[BUFFER_SIZE];
condition nonempty;
public:
buffer_list_type();
void link(buffer_list_type *new_buffer);
buffer_list_type *unlink(); };
buffer_list_type *buffer_list_type::first = 0;
buffer_list_type::buffer_list_type()
{ };
void buffer_list_type::link(buffer_list_type *new_buffer)
{ exclusive_block set_up(this);
new_buffer->next = first;
first = new_buffer;
nonempty.signal(); };
buffer_list_type *buffer_list_type::unlink()
{ buffer_list_type *unlinked;
exclusive_block set_up(this);
if (first == 0)
nonempty.await();
unlinked = first;
first = first->next;
return unlinked; };
Operacija unlink zaustavlja aktivnost niti njenog pozivaoca, ako je lista
bafera prazna. Operacija link nastavlja aktivnost pomenute niti, čim uveže novi
bafer u listu bafera.
U primeru rukovanja komunikacionim baferom, štetna međusobna preplitanja
niti mogu da se spreče, ako se operacije klase buffer_type oslone na uslovnu
44
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
sinhronizaciju. U tom slučaju je osigurano naizmenično pristupanje proizvođača i
potrošača komunikacionom baferu. Izmenjena definicija klase buffer_type je
navedena u nastavku:
# include <colibry.h>
const unsigned int BUFFER_SIZE = 512;
enum buffer_states { EMPTY,FULL };
class buffer_type: public exclusive
{ char content[BUFFER_SIZE];
buffer_states state;
condition full;
condition empty;
public:
buffer_type();
void put(char *c);
void get(char *c); };
buffer_type:: buffer_type()
{ state = EMPTY; };
void buffer_type::put(char *c)
{ int i;
exclusive_block set_up(this);
if (state == FULL)
empty.await();
for (i = 0;i < BUFFER_SIZE;i++)
content[i] = *c++;
state = FULL;
full.signal(); };
void buffer_type::get(char *c)
{ int i;
exclusive_block set_up(this);
if (state == EMPTY)
full.await();
for (i = 0;i < BUFFER_SIZE;i++)
*c++ = content[i];
state = EMPTY;
empty.signal(); };
U početku je komunikacioni bafer prazan, pa se jedino može izvršiti
operacija put(). Nakon njenog izvršavanja bafer je pun, pa se može izvršiti samo
operacija get(). Posle njenog izvršavanja bafer je opet prazan, pa se može izvršiti
samo operacija put(). Zahvaljujući izmenama stanja bafera, zaustavljanju aktivnosti
niti, kada bafer nije u potrebnom stanju, i omogućavanju nastavljanja aktivnosti niti,
kada bafer pređe u potrebno stanje, osigurano je naizmenično izvršavanja operacija
klase buffer_type.
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
45
2.10 RIZICI KONKURENTNOG PROGRAMIRANJA
Opisivanje obrada podataka je jedini cilj sekvencijalnog, a osnovni cilj
konkurentnog programiranja. Bolje iskorišćenje računara i njegovo čvršće sprezanje
sa okolinom su dodatni ciljevi konkurentnog programiranja, po kojima se ono i
razlikuje od sekvencijalnog programiranja. Od suštinske važnosti je da ostvarenje
dodatnih ciljeva ne ugrozi ostvarenje osnovnog cilja, jer je on neprikosnoven, pošto
je konkurentni program upotrebljiv jedino ako iza svakog od njegovih izvršavanja
ostaju samo ispravno obrađeni podaci.
Ostvarenja dodatnih ciljeva konkurentnog programiranja uzrokuju da se za
konkurentni program vezuje više poželjnih osobina, nego za sekvencijalni program.
Tako tipične poželjne osobine sekvencijalnog, a to znači i konkurentnog programa
obuhvataju tvrdnje uključivanja poželjnog, kao što je tvrdnja da nakon izvršavanja
programa ostaju ispravno obrađeni podaci, i tvrdnje isključivanja nepoželjnog, kao
što je tvrdnja da program ne sadrži beskonačne petlje. Tipične dodatne poželjne
osobine konkurentnog programa obuhvataju tvrdnje uključivanja poželjnog, kao što
je tvrdnja da izvršavanja programa ne narušavaju konzistentnost deljenih
promenljivih, i tvrdnje isključivanja nepoželjnog, kao što je tvrdnja da u toku
izvršavanja programa ne dolazi do trajnog zaustavljanja aktivnosti niti. To znači, u
toku pravljenja konkurentnog programa neophodno je obratiti posebnu pažnju na
način ostvarenja sinhronizacije i na proveru da li ima propusta zbog kojih se može
javiti nekonzistentnost deljenih promenljivih ili doći do trajnog zaustavljanja
aktivnosti pojedinih niti.
2.11 NAPOMENE O KONKURENTNOJ BIBLIOTECI
COLIBRY
Konkurentna biblioteka COLIBRY nudi sredstva koja, sem što pomažu
ostvarenje dodatnih ciljeva konkurentnog programiranja, omogućuju i sprečavanje
štetnih preplitanja. Da bi se štetna preplitanja sprečila i u toku izvršavanja
konkurentnih programa izbegle zagonetne greške, čija pojava ima slučajan karakter,
neophodna je pažljiva upotreba sredstava za konkurentno programiranje i
usredsređenost na otkrivanje i izbegavanje situacija bremenitih štetnim
preplitanjima. Bez opreznog i odmerenog korišćenja ovih sredstava, pravljenje
upotrebljivog COLIBRY programa nije izvesno, jer pravila primene ovih sredstava
ostavljaju dovoljno prostora za raznovrsne propuste. Otkrivanje i otklanjanje
ovakvih propusta je potpuno u nadležnosti programera. U nastavku su navedeni
tipični primeri pomenutih propusta i zatim su razmotrena neka svojstva
konkurentne biblioteke COLIBRY.
Ispravnu obradu podataka ugrožava narušavanje konzistentnosti isključivih i
atomskih promenljivih u toku izvršavanja COLIBRY programa. Do narušavanja
konzistentnosti atomskih promenljivih dolazi, ako na kraju atomskog regiona
46
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
atomska promenljiva nije u konzistentnom stanju ili ako se operacija expect pozove
pre nego je atomska promenljiva dovedena u konzistentno stanje:
{ atomic_block set_up;
// atomic region 1
some_event.expect();
// atomic region 2 };
Prethodni poziv operacije expect predstavlja (prikriveni) kraj atomskog
regiona, jer onemogućenje prekida važi samo za aktivnu nit, a operacija expect
izaziva preključivanje na novu nit, za koju prekidi nisu obavezno onemogućeni.
Jasno, na programeru je da oceni da li rukovanje atomskom promenljivom sme da
se rasporedi u više atomskih regiona, odnosno, da oceni kada je nastupio momenat
za pozivanje operacije expect.
Postojanje više nivoa prekida može, takođe, da ugrozi konzistentnost
atomskih promenljivih, ako istoj atomskoj promenljivoj pristupaju obrade prekida
raznih nivoa. U ovakvoj situaciji, punu zaštitu konzistentosti atomskih promenljivih
nudi samo primena atomskih regiona.
Do narušavanja konzistentnosti isključivih promenljivih dolazi, ako na kraju
isključivog regiona isključiva promenljiva nije u konzistentnom stanju ili ako se
operacija await pozove pre nego je isključiva promenljiva dovedena u konzistentno
stanje:
{ exclusive_block set_up(addres_of_some_exclusive_variable);
// exclusive region 1
some_condition.await();
// exclusive region 2 };
Prethodni poziv operacije await predstavlja (prikriveni) kraj isključivog
regiona, jer operacija await vraća propusnicu i izaziva preključivanje na novu nit.
Ova nit može da dobije vraćenu propusnicu i da uđe u neki od isključivih regiona
iste deljene promenljive. Jasno, na programeru je da oceni da li rukovanje
isključivom promenljivom sme da se rasporedi u više isključivih regiona, odnosno,
da oceni kada je nastupio momenat za pozivanje operacije await.
Upotrebljivost konkurentnih programa ugrožava i pojava međuzavisnosti niti,
poznata pod nazivom mrtva petlja (deadlock). Ona dovodi do trajnog zaustavljanja
aktivnosti niti, a to ima za posledicu da izvršavanje konkurentnog programa nema
kraja. Konkurentni program, u toku čijeg izvršavanja je moguća pojava mrtve
petlje, nije upotrebljiv, jer pojedina od njegovih izvršavanja, koja nemaju kraja, ne
dovode do uspešne obrade podataka. To znači da iza svakog izvršavanja ovakvog
konkurentnog programa ne ostaju ispravno obrađeni podaci. Do mrtve petlje može
da dođe, na primer, ako se iz jedne isključive klase pozivaju operacije druge
isključive klase, pod uslovom da je bar jedna od pozivanih operacija blokirajuća. U
nastavku su navedene pojednostavljene definicije ovakve dve isključive klase:
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
47
# include <colibry.h>
class passage_type: public exclusive
{ condition pass;
public:
void disable();
void enable(); };
void passage_type::disable()
{ exclusive_block set_up(this);
pass.await(); };
void passage_type::enable()
{ exclusive_block set_up(this);
pass.signal(); };
class manager_type: public exclusive
{ passage_type passage;
public:
void disable_passage();
void enable_passage(); };
void manager_type::disable_passage()
{ exclusive_block set_up(this);
passage.disable(); };
void manager_type::enable_passage()
{ exclusive_block set_up(this);
passage.enable(); };
manager_type manager;
Nit, koja pozove operaciju manager.disable_passage() i dobije propusnicu za
ulazak u isključivi region isključive promenljive manager, poziva blokirajuću
operaciju passage.disable(). Ako ova nit zatim dobije i propusnicu za ulazak u
isključivi region isključive promenljive passage, tada ona zaustavlja svoju aktivnost
u okviru poziva operacije pass.await(). Pre zaustavljanja aktivnosti ove niti, poziv
operacije pass.await() vraća propusnicu isključive promenljive kojoj pripada
objekat pass, a to je isključiva promenljiva passage. Na taj način se stvara
mogućnosti da neka druga nit dobije ovu propusnicu i pozove operaciju
pass.signal(), radi signaliziranja ispunjenosti uslova za nastavak aktivnosti prve
niti. Međutim, pošto propusnica isključive promenljive manager nije vraćena, nema
mogućnosti za izvršavanje opercije manager.enable_passage(), pa ni za pozivanje
operacije passage.enable(). To znači da pomenuto signaliziranje nije moguće, pa je
aktivnost prve niti trajno zaustavljena. Opisana situacija predstavlja tipičan primer
očekivanja ispunjenja uslova koje neće uslediti. Po analogiji, mrtvu petlju može da
izazove i očekivanje dešavanja događaja koje neće uslediti.
Mrtve petlje, koje ilustruje prethodni primer, se mogu sprečiti, ako se
blokirajuća operacija ne poziva iz isključivog regiona. U tom slučaju ne ostaje
zauzeta propusnica, pa nema zapreke ni da se objavi ispunjenje očekivanog uslova.
48
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Do mrtve petlje može da dođe i ako se iz isključivog regiona pozove funkcija
jer to dovodi do kraja aktivnost niti koja nije vratila propusnicu.
Sprečavanje mrtve petlje zahteva pažljivo programiranje i analizu programa,
radi otkrivanja mogućnosti njene pojave i načina njenog otklanjanja.
Nenamerno izazivanje konačnog, ali nepredvidivo dugog, zaustavljanja
aktivnosti procesa u toku isključivog regiona može da ima negativne posledice na
izvršavanje programa. To se, na primer, desi, kada se u isključivom regionu nalazi
atomski region sa pozivom operacije expect. Tada trajanje zaustavljanja aktivnosti
zavisi od dešavanja vanjskog događaja. Slično se desi, kada se, neposredno ili
posredno, iz isključivog regiona pozivaju potencijalno blokirajuće operacije, kao što
su operacije za rukovanje terminalom ili disketnom jedinicom, odnosno kao što su
funkcije: delay i delay_till. U oba slučaja, nit, čija aktivnost je zaustavljena,
zadržava propusnicu, koja je potrebna za pristupanje isključivoj promenljivoj čiji je
pomenuti isključivi region. Na taj način, ona privremeno onemogućuje pristupanje
ovoj isključivoj promenljivoj.
U situaciji, u kojoj je aktivnost inicijalnog i svih procesa nastalih nakon njega
trajno ili privremeno zaustavljena, o angažovanju procesora brine se COLIBRY
izvršilac.
Isključive i atomske promenljive se razlikuju po načinu sinhronizacije. Za
ispravnost COLIBRY programa veoma je važno da se ove promenljive koriste
strogo u skladu sa svojom namenom. Prema tome, pristupanje isključivim
promenljivim je vezano za isključivi region, a pristupanje atomskim promenljivim
je vezano za atomski region. Upotreba isključivog regiona nije ograničena samo na
operacije isključive klase, kao što ni upotreba atomskog regiona nije ograničena
samo na operacije atomske klase. Međutim, nije predviđeno da se isključivi region
pojavi u atomskom regionu, jer nemogućnost ulaza u isključivi region dovodi do
preključivanja procesora i do verovatnog omogućenja prekida, pa na taj način i
poništavanja atomskog regiona.
Obrada prekida je uvek najpreča, jer, radi nje se prekida aktivnost i
najprioritetnije niti. Zato završavanje svih započetih obrada prekida uvek prethodi
nastavljanju aktivnosti prekinute niti. Isto važi i za sve niti čije aktivnosti su
omogućili pozivi operacije notify. Znači, do preključivanja procesora na neku od
ovih niti dolazi tek po završavanju svih započetih obrada prekida, i to samo ako je
ova nit prioritetnija od prekinute niti. Nije predviđeno da se iz obrađivača prekida
(interrupt_handler) poziva operacija expect, kao ni bilo koja druga
operacija/funkcija koja bi zaustavila i na neodređeno vreme odložila obradu
prekida.
Definicije static polja (static data member) nisu predviđene u isključivim i
atomskim klasama, kao ni u klasama izvedenim iz klase thread, jer bi se na taj
način posredno uvele deljene promenljive. Iz istih razloga treba biti oprezan i sa
upotrebom pokazivačkih promenljivih u COLIBRY programu.
destroy,
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
49
Operacija await uvek vraća propusnicu isključive promenljive na čiji objekat
se ova operacija odnosi. Na taj način se stvara mogućnost da druga nit pristupi
pomenutoj isključivoj promenljivoj i ispuni uslov čije ispunjenje očekuje nit
pozivalac operacije await. Nakon signaliziranja ispunjenja uslova, prednost za
dobijanje propusnice date isključive promenljive imaju signalizirane niti, čiji
deskriptori se nalaze u listi ispunjenih uslova ove isključive promenljive, a ne niti
koje tek treba da uđu u isključive regione ove isključive promenljive, čiji
deskriptori se nalaze u njenoj ulaznoj listi. Objava ispunjenja uslova (signal) nema
efekta, ako pomenuto ispunjenje ne očekuje bar jedna nit. Isto važi i za objavu
dešavanja događaja (notify).
Višestruko objavljivanje ispunjenja uslova dovodi do nastavljanja (jedne za
drugom) aktivnosti više niti. Zbog toga je moguće da, na primer, nastavljanje prve
aktivnosti poništi važenje uslova čija ispunjenost je objavljena i da tako učini
bespredmetnim nastavljanje preostalih aktivnosti. U ovakvom slučaju neophodno je
ponavljati proveru ispunjenosti uslova odmah po nastavljanju aktivnosti niti i
ponovno zaustavljati njenu aktivnost, ako provera da negativan rezultat:
while (some_condition_has_not_been_fulfilled)
some_condition.await();
Niti dobijaju propusnicu za aktivnost u isključivom iskazu neke deljene
promenljive ne u redosledu njihovih prioriteta, nego u redosledu traženja pomenute
propusnice. Prema tome, nit sa najnižim prioritetom dolazi do propusnice pre
najprioritetnije niti, ako je pre nje tražila pomenutu propusnicu. U ovoj situaciji, nit
sa najnižim prioritetom ima prednost u odnosu na najprioritetniju nit, pa nastavlja
aktivnost pre najprioritetnije niti. Ali, tada srednje prioritetna nit, čija aktivnost ne
zavisi od pomenute propusnice, može da prisvoji procesor, pošto ima prednost u
odnosu na nit sa najnižim prioritetom, i da tako, na neodređeno vreme, odloži
aktivnost ne samo niti sa najnižim prioritetom, nego i najprioritetnije niti. Da bi se
izbegla prethodno opisana inverzija prioriteta (priority inversion), prioriteti su
predviđeni i za isključive promenljive. Pri tome se podrazumeva da sve niti sa
nižim prioritetom od prioriteta isključive promenljive koriste njen prioritet kao svoj
za sve vreme svoje aktivnosti u bilo kom od njenih isključivih regiona. Prema tome,
prethodno opisano prisvajanje procesora nije moguće, ako nit najnižeg prioriteta
privremeno, za vreme aktivnosti u isključivom regionu, dobije najviši prioritet. Za
to je neophodno da isključiva promenljiva, kojoj ovaj isključivi region pripada, ima
najviši prioritet.
Viši prioritet, privremeno stečen za vreme aktivnosti u isključivom regionu,
može da naruši pravednost kružnog preključivanja.
Na samom početku izvršavanja COLIBRY programa, odmah nakon stvaranja
inicijalne, a pre stvaranja bilo koje druge niti, ne postoji mogućnost za štetna
preplitanja, jer postoji samo inicijalna nit. Zato početak aktivnosti inicijalne niti,
kada još nisu stvorene druge niti, predstavlja zgodan period za izvršavanje
50
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
konstruktora dinamičkih isključivih promenljivih, jer oni nisu zaštićeni isključivim
regionom. Važno je uočiti da do aktiviranja novostvorenih niti, čiji prioritet je
jednak prioritetu inicijalne niti, dolazi čim istekne kvantum inicijalne niti, znači pre
njenog kraja. Ostale novostvorene niti se aktiviraju tek nakon završetka aktivnosti
inicijalne niti.
Veličina steka stvarane niti treba da podmiri potrebe svih istovremeno
postojećih poziva funkcija, napravljenih u toku aktivnosti niti. Nedovoljna veličina
steka može da izazove vrlo problematične greške u izvršavanju COLIBRY
programa. Nije preporučljivo da se prave rekurzivni pozivi u toku aktivnosti niti, jer
je u ovakvom slučaju teško unapred proceniti potrebnu veličinu steka.
Globalne const promenljive, koje služe za smeštanje podataka, raspoloživih
svim nitima, ne spadaju u deljene promenljive.
2.12 VERZIJA 2.0 KONKURENTNE BIBLIOTEKE COLIBRY
Verzija 2.0 konkurentne biblioteke COLIBRY je razvijena pomoću
kompajlera BORLAND C++ (verzija 3.1), namenjenog za MS-DOS operativni
sistem (verzija 4.01). Ona je, znači, namenjena za računare iz klase personalnih
(IBM PC kompatibilnih) računara, baziranih na procesoru Intel 8086. Pošto
procesori Intel 80386 i njegovi sledbenici emuliraju procesor Intel 8086, a
operativni sistem WINDOWS (2000) emulira MS-DOS operativni sistem, može se
zaključiti da se COLIBRY programi izvršavaju u emuliranom okruženju. To
sprečava da bilo kakve greške COLIBRY programa ugroze rad računara na kome se
COLIBRY program izvršava. Prema tome, sigurnost, koju nudi emulirano
okruženje, je glavni razlog što se razvoj COLIBRY programa oslanja na kompajler
BORLAND C++ (verzija 3.1).
Opis postupka instalisanja verzije 2.0 konkurentne biblioteke COLIBRY, kao
i opis postupka pripreme COLIBRY programa za izvršavanje sadrži tekst datoteka
read_me.txt. Ona se nalazi na instalacionoj disketi. Važno je uočiti da se u pripremi
izvršnog oblika COLIBRY programa obavezno prvo linkuje 1.obj datoteka, da bi
njeni konstruktori bili prvi pozvani.
Verzija 2.0 konkurentne biblioteke COLIBRY ne predviđa pozivanje C++
funkcija iz standardne biblioteke koje se oslanjaju na opertivni sistem MS-DOS, jer
su COLIBRY programi namenjeni za samostalno izvršavanje, bez oslanjanja na
operativni sistem MS-DOS. Strogo govoreći, verzija 2.0 konkurentne biblioteke
COLIBRY ne predviđa potpuno samostalno izvršavanje COLIBRY programa, jer se
klase terminal_in, terminal_out i disk_io oslanjaju na ROM (Read Only Memory)
BIOS (Basic Input/Output System). Na takvu odluku su uticali razlozi prenosivosti
(portability).
Verzija 2.0 konkurentne biblioteke COLIBRY ne predviđa pozivanje C++
funkcija exit i abort. Takođe, ona ne nudi podršku za array new i array delete
operatore (new ... [] i delete[] ... ).
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
51
U toku izvršavanja, COLIBRY program zauzima samo dva segmenta od po 64
kilobajta radne memorije (small memory model). Jedan (code) segment sadrži
procesorske naredbe, a drugi (data) segment sadrži sve vanjske promenljive,
stekove pojedinih niti i slobodnu radnu memoriju. To znači da se ne koriste stek
(stack) segment, kao ni dodatni (extra) segment.
Za upotrebljivost klase terminal_in je neophodno da COLIBRY izvršilac
opslužuje prekid tastature (broj vektora ovog prekida je 9).
COLIBRY izvršilac poziva operaciju exception aktivne niti pri pokušaju
celobrojnog deljenja nulom (broj vektora ovog prekida je 0) i pri pojavi
prekoračenja (broj vektora ovog prekida je 4).
Funkcije quantum_set, time_get, time_set, delay i delay_till su upotrebljive
samo ako COLIBRY izvršilac opslužuje prekid sata (čiji broj vektora je 8). Period
otkucaja sata iznosi oko 55 milisekundi.
U nadležnosti COLIBRY izvršioca mora da se nalazi i obrada korisničkog
(control break) prekida (čiji broj vektora je 27), da bi korisnik mogao u svakom
trenutku da završi izvršavanje COLIBRY programa istovremenim pritiskanjem
Control i Break dirki tastature.
Na početku izvršavanja COLIBRY programa, COLIBRY izvršilac opslužuje
sve prethodno pomenute prekide. On podrazumeva da su svi prekidi istog nivoa i ne
predviđa reakciju na NMI prekid (non maskable interrupt). Zato nije uputno da
interrupt_handler operacije opisuju obradu ovog prekida. Takođe, nije predviđeno
ni da se u ovim operacijama izvršava procesorska naredba int.
COLIBRY program se pokreće kao običan C++ program. Izvršavanje
COLIBRY programa se odmah završava, uz poruku “COLIBRY ERROR: MEMORY
SHORTAGE!”, ako je raspoloživa slobodna radna memorija nedovoljna za
pokrivanje njegovih minimalnih zahteva. U suprotnom slučaju, on nastavlja
izvršavanje kao samostalan program, koji se jedino delimično oslanja na BIOS.
Istovremenio pritiskanje Control i Break dirki tastature dovodi da prevremenog
kraja izvršavanja COLIBRY programa i do vraćanja računara pod upravu MS-DOS
operativnog sistema. Isto se desi i kada se završi aktivnost inicijalnog i svih procesa
nastalih iza njega, ali i kada se u izvršavanju COLIBRY programa stigne do poziva
funkcije quit.
Kraj izvršavanja COLIBRY programa nastupi i pri pojavi izuzetka u obradi
prekida, kao i pri pojavi izuzetka u kritičnom regionu (u oba slučaja izvršavanje
COLIBRY programa se završava porukom “COLIBRY ERROR: FATAL
EXCEPTION! ”.
2.13 PITANJA
1. Zašto se javlja preplitanje niti?
2. Zašto preplitanja niti imaju slučajan karakter?
3. Zašto se javljaju štetna preplitanja: (1) niti i obrada prekida, (2) niti raznih
procesa i (3) niti istog procesa (objasniti na primeru)?
52
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
Šta su deljene promenljive?
Šta je preduslov očuvanja konzistentnosti deljenih promenljivih?
Šta su kritični regioni?
Šta je sinhronizacija?
Koje vrste sinhronizacije postoje?
Šta je atomski region?
Čemu služi propusnica?
Šta je isključivi region?
Koju namenu ima atomska klasa?
Koje vrste operacija sadrži atomska klasa?
Da li tela operacija atomske klase predstavljaju kritični region?
Šta atomska klasa treba da omogući pozadinskoj niti i obrađivaču prekida?
Koje liste uvodi atomska klasa?
Koju namenu ima isključiva klasa?
Da li tela operacija isključive klase predstavljaju kritični region?
Kada se zauzima propusnica isključive promenljive?
Kada se oslobađa propusnica isključive promenljive?
Šta isključiva klasa treba da omogući nitima?
Koje liste uvodi isključiva klasa?
Šta je semafor?
Šta uvode poželjne osobine konkurentnih programa (objasniti na
primeru)?
Po čemu se konkurentno programiranje razlikuje od sekvencijalnog?
Koje prednosti ima konkurentna biblioteka u odnosu na konkurentni
programski jezik?
Kako konkurentna biblioteka COLIBRY podržava rukovanje nitima?
Kako konkurentna biblioteka COLIBRY podržava rukovanje vremenom?
Kako konkurentna biblioteka COLIBRY podržava rukovanje atomskim
promenljivima?
Kako konkurentna biblioteka COLIBRY podržava rukovanje isključivim
promenljivima?
Kako konkurentna biblioteka COLIBRY podržava rukovanje standardnim
ulazom i izlazom?
Kako se označavju niti?
Kako nit može saznati svoj serijski broj?
Kako se stvaraju niti?
Kakvu ulogu ima operacija exception?
Kako se uništavaju niti?
Šta omogućuju funkcije alarmed i alarm?
Šta omogućuje funkcija yield?
Kakvu ulogu ima inicijalna nit?
Šta je sistemsko vreme?
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
53
Kako se preuzima i menja sistemsko vreme?
Šta omogućuje funkcija quantum_set?
Kakva razlika postoji između funkcija delay i delay_till?
Kakvu ulogu ima klasa atomic_block?
Kakvu ulogu ima templejt klasa atomic?
Čemu služi parametar templejt klase atomic?
Kako se može dobiti vektor prekida koji pripada nekoj atomskoj
promenljivoj?
Kakvu ulogu ima klasa event?
Kakvu ulogu ima klasa tag_event?
Kakva razlika postoji između klase event i klase tag_event?
Kakvu ulogu ima klasa exclusive_block?
Kakvu ulogu ima klasa exclusive?
Kakvu ulogu ima klasa condition?
Kakvu ulogu ima klasa tag_condition?
Kakva razlika postoji između klase condition i klase tag_condition?
Kakva razlika postoji između templejt klase atomic i klase exclusive?
Kakvu ulogu imaju funkcije ton i toff?
Kakvu ulogu ima klasa terminal_out?
Kakvu ulogu ima klasa terminal_in?
Kakvu ulogu ima klasa disk_io?
Kakva razlika postoji između operacija change i take klase position_type?
Kakva razlika postoji između blokirajuće i neblokirajuće operacije unlink
klase buffer_list_type?
Zašto tela operacija put i get klase buffer_type predstavljaju isključive
regione?
Po kojim ciljevima se konkurentno programiranje razlikuje od
sekvencijalnog programiranja?
Zašto operacija expect predstavlja prikriveni kraj atomskog regiona?
Zašto operacija await predstavlja prikriveni kraj isključivog regiona?
Šta je mrtva petlja (objasniti na primeru)?
Zašto nije predviđeno da atomski region sadrži u sebi isključivi region?
Kako se može sprečiti inverzija prioriteta?
Zašto tela konstruktora isključivih promenljivih ne sadrže isključivi
region?
54
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
3. PRIMERI KONKURENTNIH PROGRAMA
3.1 PREDSTAVA VREMENA BROJEM SATI, MINUTA I
SEKUNDI
Proticanje vremena se prati, u računaru, brojanjem otkucaja sata. Otkucaji,
čiji period zavisi od takta procesora, nisu uvek podesni za određivanje vremena u
danu, predstavljenog brojem sati, minuta i sekundi, jer sekunda ne može uvek da se
izrazi celim brojem perioda ovakvih otkucaja. Zato je zgodno uvesti dodatni sat, čiji
otkucaji (prekidi) imaju period od tačno jedne sekunde. U ovom primeru konstanta
TIMER određuje broj vektora prekida dodatnog sata. Rukovanje ovim satom opisuje
atomska klasa day_time_type. Njena tri celobrojna polja: hour, minute i second
predstavljaju vreme u danu brojem sati, minuta i sekundi. Početni sadržaj ovih polja
određuje konstruktor atomske klase day_time_type, a periodičnu izmenu njihovog
sadržaja, sa periodom od jedne sekunde, opisuje njena operacija interrupt_handler.
Pri tome se sadržaj polja second povećava za 1 i to samo ako je manji od 59. Inače,
on postaje 0, a povećava se sadržaj polja minute za 1, opet samo ako je manji od
59. U suprotnom slučaju, on postaje 0, a povećava se sadržaj polja hour za 1, ali
samo ako je manji od 23. Inače, on postaje 0.
Atomska klasa day_time_type sadrži i operacije set i get za zadavanje i
preuzimanje sadržaja njenih polja. To su osetljive operacije, čije preplitanje sa
obradom prekida ove atomske klase dovodi do narušavanja njene konzistentnosti.
Na primer, ako se obrada prekida sata desi nakon preuzimanja sadržaja polja hour, a
pre preuzimanja sadržaja polja minute, i ako je, uz to, broj minuta pre periodične
izmene bio na granici od 59, tada preuzeto vreme kasni iza stvarnog za 60 minuta
(jer je preuzet stari sadržaj polja hour i novi sadržaj polja minute).
U prethodnom primeru naveden je samo jedan od raznovrsnih ishoda
narušavanja konzistentnosti atomske klase day_time_type. Da bi se njena
konzistentnost zaštitila, preuzimanja i zadavanja sadržaja polja ove atomske klase
moraju da budu u atomskim regionima.
Izvorna datoteka day_time.cpp sadrži definiciju atomske klase day_time_type
kao i definiciju jednog njenog objekta. U nastavku je naveden sadržaj ove datoteke:
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
55
# include <colibry.h>
const int TIMER = 0x08;
class day_time_type: public atomic<TIMER>
{ int hour;
int minute;
int second;
public:
day_time_type();
protected:
void interrupt_handler();
public:
void set(int hour,int minute,int second);
void get(int *hour,int *minute,int *second); };
day_time_type::day_time_type(): atomic<TIMER>()
{ hour = 0;
minute = 0;
second = 0; };
void day_time_type::interrupt_handler()
{ if (second < 59)
second++;
else
{ second = 0;
if (minute < 59)
minute++;
else
{ minute = 0;
if (hour < 23)
hour++;
else
hour = 0; };
};
};
void day_time_type::set(int hour,int minute,int second)
{ atomic_block set_up;
day_time_type::hour = hour;
day_time_type::minute = minute;
day_time_type::second = second; };
void day_time_type::get(int *hour,int *minute,int *second)
{ atomic_block set_up;
*hour = day_time_type::hour;
*minute = day_time_type::minute;
*second = day_time_type::second; };
day_time_type day_time;
Za podršku ispravnog korišćenja klase day_time_type uputno je predvideti
datoteku day_time.h. U nastavku je naveden sadržaj ove datoteke:
56
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
const int TIMER = 0x08;
class day_time_type: public atomic<TIMER>
{ int hour;
int minute;
int second;
public:
day_time_type();
protected:
void interrupt_handler();
public:
void set(int hour,int minute,int second);
void get(int *hour,int *minute,int *second); };
extern day_time_type day_time;
Podrazumeva se da je sadržaj datoteke day_time.h obavezni deo izvornog
COLIBRY programa u kome se nalaze pozivi operacija, deklarisanih u ovoj datoteci.
Prethodno opisani način praćenja proticanja vremena u danu, predstavljenog
brojem sati, minuta i sekundi, se može primeniti, bez suštinskih izmena, i kada ne
postoji dodatni sat, čiji otkucaji imaju period tačno jednu sekundu. U ovakvom
slučaju, jedina izmena se odnosi na operaciju interrupt_handler, u kojoj do
povećanja broja sekundi dolazi tek nakon odgovarajućeg broja otkucaja sata.
3.2 PRIKAZ I IZMENA VREMENA
Izvorna datoteka day_time.cpp nudi neophodne operacija za prikaz i izmenu
vremena, predstavljenog brojem sati, minuta i sekundi. Sem njih, za prikaz i izmenu
vremena potrebna je i posebna nit, koji na zahtev prikazuje ili menja vreme.
Izvorna datoteka opus01.cpp sadrži definiciju niti day_time_thread, u toku
čijeg izvršavanja dolazi do prikaza i izmena vremena. Do stvaranja ove niti dolazi u
toku aktivnosti inicijalne niti. U nastavku je naveden sadržaj ove datoteke:
# include <colibry.h>
# include <colib_io.h>
# include <day_time.h>
class day_time_thread: public thread
{ char c;
int hour;
int minute;
int second;
public:
day_time_thread(); };
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
57
day_time_thread::day_time_thread()
{ tout << NEW_LINE << "DAY TIME CLOCK"
<< NEW_LINE << "COMMANDS: Time, Second, Minute, Hour, Change, End"
<< NEW_LINE << "Initial time: ";
c = 'T';
do
{ switch (c)
{ case 'C' : day_time.set(hour,minute,second);
case 'T' : day_time.get(&hour,&minute,&second);
break;
case 'S' : if (second < 59)
{ second++;
break; };
second = 0;
case 'M' : if (minute < 59)
{ minute++;
break; };
minute = 0;
case 'H' : if (hour < 23)
{ hour++;
break; };
hour = 0;
break;
default : c = 'N';
};
if (c != 'N')
tout << hour << ':' << minute << ':' << second;
else
tout << " ? COMMANDS: Time, Second, Minute, Hour, Change, End";
tout << NEW_LINE << '>';
tin >> c;
}
while (c != 'E');
};
initial::initial(int,char **)
{ new day_time_thread(); };
U prethodnom programu se podrazumeva da je svaki od zahteva, koji
usmeravaju aktivnost niti day_time_thread, kodiran jednim znakom i da do ove niti
pomenuti zahtevi stižu sa tastature. Tako, znak T odgovara zahtevu za prikazom
vremena. Prikazuje se trenutno vreme. Znak S odgovara zahtevu za povećanjem
broja sekundi zadnje prikazanog vremena za 1, gde povećanje broja sekundi može
da izazove povećanje broja minuta i broja sati. Ovako izmenjeno vreme se prikazuje
odmah po izmeni, ali ne postaje trenutno vreme. Znak M odgovara zahtevu za
povećanjem broja minuta zadnje prikazanog vremena za 1, gde povećanje broja
minuta može da izazove povećanje broja sati. Ovako izmenjeno vreme se prikazuje
odmah po izmeni, ali ne postaje trenutno vreme. Znak H odgovara zahtevu za
povećanjem broja sati zadnje prikazanog vremena za 1. Ovako izmenjeno vreme se
prikazuje odmah po izmeni, ali ne postaje trenutno vreme. Znak C odgovara
zahtevu za izmenom trenutnog vremena. Pri tome zadnje prikazano vreme postaje
trenutno vreme i prikazuje se kao novo trenutno vreme. Znak E odgovara zahtevu
za završavanjem prikazivanja i menjanja vremena. Ostali znakovi se ignorišu.
58
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Sadržaj izvorne datoteke opus01.cpp predstavlja potpun COLIBRY program.
U toku njegovog izvršavanja nastanu inicijalna nit i nit čija aktivnost je posvećena
samo prikazivanju ili menjanju vremena.
3.3 KOMUNIKACIONI KANAL KAPACITETA JEDNE
PORUKE
Saradnja niti proizvođača i niti potrošača, u toku koje prva od njih prosleđuje
rezultate svoje aktivnosti drugoj niti, može da se prikaže kao razmena poruka. U
toku ove razmene proizvođač odlaže poruku u poseban pregradak iz koga tu poruku
preuzima potrošač. Takvu razmenu poruka podržava templejt klasa message_box.
Njen parametar MESSAGE određuje tip poruka koje se razmenjuju. Pošto polje content
ove klase, koje ima funkciju pregradka, može da prihvati samo jednu poruku,
neophodno je da slanja i prijemi poruka budu naizmenični. Samo tako se može
garantovati da će sve poslane poruke bile uvek ispravno primljene. Zato templejt
klasa message_box sadrži polje state koje određuje stanje polja content. Pomoću
polja state se definišu uslovi, od kojih zavise aktivnosti niti pošiljalaca i primalaca
poruka. Templejt klasa message_box sadrži i polja full i empty, koja uvode liste
pomenutih uslova. Slanje poruke omogućuje operacija send, a prijem poruke
omogućuje operacija receive. Prva od njih omogućuje smeštanje poruke u polje
content, ako je ono prazno. Inače ona zaustavlja aktivnost niti pozivaoca
(proizvođača) dok se ovo polje ne isprazni. Druga od njih omogućuje preuzimanje
poruke iz polja content, ako je ono puno. Inače ona zaustavlja aktivnost niti
pozivaoca (proizvođača) dok se ovo polje ne napuni.
Izvorna datoteka box.h sadrži definiciju templejt klase message_box. U
nastavku je naveden sadržaj ove datoteke:
template<class MESSAGE>
class message_box: public exclusive
{ enum message_box_states { EMPTY,FULL };
MESSAGE content;
message_box_states state;
condition full;
condition empty;
public:
message_box();
void send(MESSAGE message);
MESSAGE receive(); };
template<class MESSAGE>
message_box<MESSAGE>::message_box()
{ state = EMPTY; };
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
59
template<class MESSAGE>
void message_box<MESSAGE>::send(MESSAGE message)
{ exclusive_block set_up(this);
if (state == FULL)
empty.await();
content = message;
state = FULL;
full.signal(); };
template<class MESSAGE>
MESSAGE message_box<MESSAGE>::receive()
{ exclusive_block set_up(this);
if (state == EMPTY)
full.await();
state = EMPTY;
empty.signal();
return content; };
Templejt klasa message_box omogućuje uspostavljanje komunikacionog
kanala između niti pošiljaoca i niti primaoca. Njene operacije send i receive
omogućuju asinhronu razmenu poruka, jer se aktivnost pošiljaoca zaustavlja pri
slanju poruka samo kada je komunikacioni kanal pun, dok se aktivnost primaoca
zaustavlja pri prijemu poruka samo kada je ovaj kanal prazan. Ako se kapacitet
komunikacionog kanala poveća na dve ili više poruka, tada svakom prijemu mogu
da prethode dva ili više slanja. S druge strane, uz zadržavanje kapaciteta
komunikacionog kanala na jednoj poruci, razmena poruka postaje sinhrona, ako se
uvek zaustavlja aktivnost niti koja prva započne razmenu poruka, bez obzira da li se
radi o pošiljaocu ili primaocu. Aktivnost ove niti ostaje zaustavljena dok i druga nit
ne započne razmenu poruka. Pri tome se podrazumeva da pošiljalac nastavlja svoju
aktivnost tek kada primalac preuzme poruku. Prethodno dozvoljava da se u
komunikacionom kanalu ne čuva poruka, nego njena adresa. To doprinosi brzini
sinhrone razmene poruka, jer primalac može direktno preuzeti poruku od
pošiljaoca. Time se izbegava potreba da se poruka prepisuje u komunikacioni kanal,
što je neizbežno kod asinhrone razmene poruke. Iako na ovaj način primalac
pristupa lokalnoj promenljivoj pošiljaoca, u kojoj se nalazi poruka, to ne predstavlja
problem dok god mehanizam sinhrone razmene poruka osigurava međusobnu
isključivost pristupanja pomenutoj lokalnoj promenljivoj. Pošto sinhrona razmena
poruka zahteva da se pošiljalac i primalac poruke sretnu, ona se naziva i randevu
(rendezvous).
Prethodno opisana sinhrona razmena poruka se oslanja na sinhrono slanje i
sinhroni prijem poruke. Sinhrono slanje podrazumeva smeštanje adrese poruke u
pregradak, koji je inicijalno anuliran, objavljivanje da je moguće preuzimanje
poruke i čekanje da poruka bude preuzeta. Sinhroni prijem započinje proverom da li
pregradak sadrži adresu poruke. Ako ne sadrži, ako je pregradak anuliran,
neizbežno je čekanje da preuzimanje poruke postane moguće. Kada preuzimanje
poruke postane moguće, poruka se preuzima, pregradak se anulira i objavljuje se da
je poruka preuzeta.
60
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Saradnja niti (procesa), zasnovana na razmeni poruka je primamljiva, jer
eliminiše potrebu za eksplicitnim korišćenjem isključivih promenljivih. One su
sakrivene u komunikacionom kanalu, a rukovanje ovim kanalom obezbeđuje da
pošiljalac može da pristupi poruci samo pre njenog slanja, a primalac samo nakon
njenog slanja.
3.4 PRVI PRIMER SIMULACIJE
Proučavanje sistema podrazumeva izgradnju njihovih modela. Ovakvi modeli
mogu da nastanu ako se bitni elementi proučavanih sistema predstave nitima, a
međusobni odnosi pomenutih elemenata izraze kao međusobna saradnja ovih niti.
Ako su pažljivo napravljeni, ovakvi modeli verno oponašaju proučavane sisteme.
Takvo oponašanje sistema se naziva simulacija, a modeli koji omogućuju
simulaciju sistema se nazivaju simulacioni modeli.
Simulacioni modeli su veoma važni za sisteme u čijoj osnovi se nalaze
slučajne pojave, jer su simulacije nezamenljiv način proučavanja ponašanja ovakvih
sistema u stanjima u koje je proučavane sisteme praktično teško dovesti, odnosno, u
kojima je praktično teško pratiti njihovo ponašanje.
Primer sistema, za koje su razvijeni uspešni simulacioni modeli, predstavljaju
sistemi čiji elementi se nalaze u odnosu korisnika i uslužioca. Kod ovakvih sistema,
korisnik upućuje uslužiocu zahteve za uslugom, pri čemu su razmaci između pojava
susednih zahteva za uslugom, kao i dužine njihovog usluživanja, slučajne veličine.
Ovakvi simulacioni modeli omogućuju, na primer, određivanje srednjeg vremena
čekanja početka usluživanja pojedinih zahteva, ako su poznate: raspodela
verovatnoća razmaka između pojava susednih zahteva i raspodela verovatnoća
dužina njihovog usluživanja. Potrebno je samo po prvoj od ovih raspodela
generisati seriju slučajnih brojeva, koji predstavljaju razmake između pojava
susednih zahteva, a po drugoj od njih, generisati seriju slučajnih brojeva, koji
predstavljaju dužine usluživanja zahteva. Na osnovu ovih serija formira se
vremenska osa (slika 3.4.1), koja prikazuje trenutke pojava zahteva, odnosno
trenutke početaka i krajeva njihovog usluživanja.
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
pojava
1. zahteva
početak
usluživanja
1. zahteva
pojava
2. zahteva
čekanje početka
usluživanja 2. zahteva
61
vreme
kraj
usluživanja
1. zahteva
i
početak
usluživanja
2. zahteva
kraj
usluživanja
2. zahteva
Slika 3.4.1 Vremenska osa
Srednje vreme čekanja početka usluživanja pojedinih zahteva se dobije kada
se ukupnim brojem zahteva podeli suma vremena koja su protekla između pojava
pojedinih zahteva i početaka njihovog usluživanja.
U prethodnom primeru simulacionog modela, korisniku odgovara nit korisnik
koja, na osnovu zadane raspodele, određuje trenutke pojava pojedinih zahteva. Te
trenutke ona prosleđuje niti uslužiocu. Nit uslužilac na osnovu ovih trenutaka i
dužina usluživanja pojedinih zahteva (koje, takođe, diktira zadana raspodela),
određuje: trenutke početaka usluživanja pojedinih zahteva, dužine čekanja ovih
početaka i srednje vreme čekanja početka usluživanja pojedinih zahteva. Važno je
zapaziti da se nit korisnik i nit uslužilac se nalaze u odnosu proizvođač i potrošač.
Komunikacioni kanal između proizvođača i potrošača uspostavlja isključiva
promenljiva box. Kroz ovaj kanal se mogu slati poruke koje se sastoje od jednog
celog broja (int).
Funkcija stohastic omogućuje generisanje slučajnih brojeva po zadanoj
raspodeli, koju određuje generator pseudoslučajnih celih brojeva, ugrađen u
funkciju rand iz standardne biblioteke. Generisani slučajni brojevi su nenegativni i
manji od argumenta poziva ove funkcije.
U simulacionom modelu korisnika i uslužioca, izraženom pomoću
konkurentne biblioteke COLIBRY, ponašanje korisnika opisuje konstruktor user. On
koristi polje request_time koje sadrži, jedan za drugim, trenutke pojava pojedinih
zahteva. Podrazumeva se da je pojava prvog zahteva vezana za trenutak nula.
Trenutak pojave narednog zahteva nastaje dodavanjem na trenutak pojave
prethodnog zahteva razmaka između ovih trenutaka, određenog pozivom funkcije
stohastic. Trenutak pojave svakog zahteva, koji nastupi pre vremenske granice
(određene konstantom TIME_LIMIT), se šalje uslužiocu, pozivanjem operacije
box.send(). Pojava prvog zahteva, trenutak čije pojave pada iza ove vremenske
granice, izaziva slanje oznake kraja simulacije (konstanta TERMINATION).
62
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Ponašanje
uslužioca opisuje konstruktor server. On koristi polje
koje sadrži, jedan za drugim, trenutke kraja usluživanja pojedinih
zahteva. Podrazumeva se da je početna vrednost ovog polja nula. Trenutak kraja
usluživanja datog zahteva se određuje dodavanjem na trenutak početka njegovog
usluživanja dužine ovog usluživanja, koja se dobija pozivom funkcije stohastic.
Trenutak početka usluživanja narednog zahteva je jednak trenutku kraja usluživanja
prethodnog zahteva, ako trenutak pojave narednog zahteva nastupi pre trenutka
kraja usluživanja prethodnog zahteva. U tom slučaju, javlja se i čekanje na početak
usluživanja. Dužina ovog čekanja se određuje kao razlika između trenutka kraja
usluživanja prethodnog zahteva i trenutka pojave narednog zahteva. Ova razlika se
dodaje vrednosti polja mean_waiting_time. Ako trenutak pojave narednog zahteva
sledi iza trenutka kraja usluživanja prethodnog zahteva, tada nema čekanja na
početak usluživanja, a trenutak početka usluživanja narednog zahteva je jednak
trenutku njegove pojave. Trenutak pojave narednog zahteva se preuzima,
pozivanjem operacije box.receive(), i smešta u polje new_request_time. Po
preuzimanju oznake kraja simulacije (konstanta TERMINATION) izračunava se i
prikazuje srednje vreme čekanja početka usluživanja pojedinih zahteva. Pri tome,
polje request_count sadrži ukupan broj zahteva.
Uz niti user i server, obavezna je i inicijalna nit. Ona omogućuje stvaranje
niti korisnika i niti uslužioca.
Izvorna datoteka opus02.cpp sadrži definicije pomenutih niti. U nastavku je
naveden sadržaj ove datoteke:
service_end_time
#
#
#
#
include
include
include
include
<colibry.h>
<colib_io.h>
<stdlib.h>
<box.h>
message_box<int> box;
int stohastic(int a)
{ return rand()%(a); };
const int TIME_LIMIT = 500;
const int TERMINATION = -1;
class user: public thread
{ int request_time;
public:
user(); };
user::user()
{ tout << NEW_LINE << "USER-SERVER SIMULATION";
request_time = 0;
while (request_time < TIME_LIMIT)
{ box.send(request_time);
request_time += stohastic(10); };
box.send(TERMINATION);
};
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
63
class server: public thread
{ int service_end_time;
int new_request_time;
int request_count;
int mean_waiting_time;
public:
server(); };
server::server()
{ service_end_time = 0;
request_count = 0;
mean_waiting_time = 0;
while ((new_request_time = box.receive())!= TERMINATION)
{ request_count++;
if (new_request_time < service_end_time)
mean_waiting_time += service_end_time-new_request_time;
else
service_end_time = new_request_time;
service_end_time += stohastic(10); };
mean_waiting_time /= request_count;
tout << NEW_LINE << "mean waiting time = " << mean_waiting_time;
};
initial::initial(int,char **)
{ new user();
new server(); };
Sadržaj izvorne datoteke opus02.cpp predstavlja potpun COLIBRY program.
U toku njegovog izvršavanja, sem inicijalne, nastanu još dve niti. Program, sadržan
u izvornoj datoteci opus02.cpp, nije namenjen za simulaciju nekog stvarnog sistema,
nego za ilustraciju kako model za takvu simulaciju može da se izrazi pomoću
konkurentne biblioteke COLIBRY.
Način, na koji je u prethodnom programu određeno srednje vreme čekanja
početka usluživanja pojedinih zahteva, može biti iskorišćen i za određivanje drugih
interesantnih parametara ponašanja simuliranog sistema (kao što je, na primer,
iskorišćenje uslužioca).
3.5 DRUGI PRIMER SIMULACIJE
Složeniji simulacioni model korisnika i uslužioca može da ima dva uslužioca
u seriji, gde prvi od njih, nakon usluživanja, prosleđuje zahtev drugom uslužiocu na
dalje usluživanje. Ovaj simulacioni model sadrži tri niti: korisnika i prvog i drugog
uslužioca. Pri tome se u odnosu proizvođač i potrošač istovremeno nalaze korisnik i
prvi uslužilac, odnosno, prvi i drugi uslužilac. U ovom primeru simulacije,
ponašanje korisnika se ne razlikuje od ponašanja korisnika iz prvog primera
simulacije. Takođe, ni ponašanje drugog uslužioca se ne razlikuje od ponašanja
uslužioca iz prvog primera simulacije. Prvi uslužilac se razlikuje od drugog
uslužioca samo po tome što, po usluživanju, prosleđuje zahtev drugom uslužiocu na
dalje usluživanje.
64
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Ponašnje korisnika opisuje konstruktor user, ponašanje prvog uslužioca
opisuje konstruktor server1, a ponašanje drugog uslužioca opisuje konstruktor
server2.
Isključiva promenljiva box1 uspostavlja komunikacioni kanal između
korisnika i prvog uslužioca, a isključiva promenljiva box2 uspostavlja
komunikacioni kanal između prvog i drugog uslužioca.
Za konstruktor user je novo da se u njemu određuje trenutak kraja simulacije.
To znači da trajanje simulacije nije unapred određeno, nego se menja od slučaja do
slučaja.
Zadavanje suviše kratkog trajanja simulacije izaziva celobrojno deljenje
nulom pri računanju srednjeg vremena čekanja početka usluživanja pojedinih
zahteva. To se desi samo ako već trenutak pojave prvog zahteva padne iza zadane
vremenske granice, koja određuje kraj simulacije. Reakciju na takvu situaciju
opisuju operacije exception.
Izvorna datoteka opus03.cpp sadrži definicije korisnika i prvog i drugog
uslužioca. U nastavku je naveden sadržaj ove datoteke:
#
#
#
#
include
include
include
include
<colibry.h>
<colib_io.h>
<stdlib.h>
<box.h>
message_box<int> box1,box2;
int stohastic(int a)
{ return rand()%(a); };
const int TERMINATION = -1;
class user: public thread
{ int request_time;
int time_limit;
public:
user(); };
user::user()
{ tout << NEW_LINE << "USER-SERVERS SIMULATION";
request_time = 0;
do
{ tout << NEW_LINE << "simulation end time (from 0 to 999): ";
tin >> time_limit; }
while ((time_limit < 0) || (time_limit > 999));
while (request_time < time_limit)
{ box1.send(request_time);
request_time += stohastic(10); };
box1.send(TERMINATION);
};
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
class server1: public thread
{ int service_end_time;
int new_request_time;
int request_count;
int mean_waiting_time;
public:
server1();
void exception(int vector_number); };
server1::server1()
{ service_end_time = 0;
request_count = 0;
mean_waiting_time = 0;
while ((new_request_time = box1.receive()) != TERMINATION)
{ request_count++;
if (new_request_time < service_end_time)
mean_waiting_time += service_end_time-new_request_time;
else
service_end_time = new_request_time;
service_end_time += stohastic(10);
box2.send(service_end_time); };
box2.send(TERMINATION);
mean_waiting_time /= request_count;
tout << NEW_LINE << "mean waiting time for server1 = " <<
mean_waiting_time;
};
void server1::exception(int vector_number)
{ tout << NEW_LINE << "server1 exception: " << vector_number; };
class server2: public thread
{ int service_end_time;
int new_request_time;
int request_count;
int mean_waiting_time;
public:
server2();
void exception(int vector_number); };
server2::server2()
{ service_end_time = 0;
request_count = 0;
mean_waiting_time = 0;
while ((new_request_time = box2.receive())!= TERMINATION)
{ request_count++;
if (new_request_time < service_end_time)
mean_waiting_time += service_end_time-new_request_time;
else
service_end_time = new_request_time;
service_end_time += stohastic(10); };
mean_waiting_time /= request_count;
tout << NEW_LINE << "mean waiting time for server2 = " <<
mean_waiting_time;
};
void server2::exception(int vector_number)
{ tout << NEW_LINE << "server2 exception: " << vector_number; };
65
66
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
initial::initial(int,char **)
{ new user();
new server1();
new server2(); };
U prethodnom primeru, pojava izuzetka u toku aktivnosti niti uslužilaca
(uzrokovanog, na primer, celobrojnim deljenjem nulom) dovodi do pozivanja
operacije exception i do prikazivanja na ekranu poruka o vrsti izuzetka, čime se
završava aktivnost ovih niti.
Sadržaj izvorne datoteke opus03.cpp predstavlja potpun COLIBRY program.
U toku njegovog izvršavanja, sem inicijalne, nastanu još tri niti. Ni program,
sadržan u izvornoj datoteci opus03.cpp, nije namenjen za simulaciju nekog stvarnog
sistema, nego samo za ilustraciju mogućnosti konkurentne biblioteke COLIBRY.
Sem raznih uslužilaca, koji se nalaze u seriji, moguće je da postoji više
uslužilaca iste vrste, koji opslužuju jednu grupu korisnika. Uz manje izmene,
prethodni program može da podrži i ovakav slučaj simulacije.
3.6 SEMAFORI
Semafor može biti implementiran pomoću isključive klase, kao što je klasa
Njeno polje state sadrži stanje semafora. Kada semafor reguliše
međusobnu isključivost niti, tada pozitivna vrednost ovoga polja pokazuje da je
moguć ulazak u isključivi region. Podrazumevajuća početna vrednost ovoga polja je
1. Polje open uvodi listu semafora. U nju se uvezuju deskriptori niti koje čekuju na
otvaranje ulaza u isključivi region. Operacija wait se poziva na ulazu u isljučivi
region, radi provere da li je moguć ulazak u isključivi region. U okviru ove provere,
stanje semafora se umanjuje da bi se onemogućio novi ulazak u isključivi region i
ujedno registrovao broj niti koje čekaju da uđu u isključivi region. Negativna
vrednost stanja semafora pokazuje broj niti koje čekaju da uđu u isključivi region.
Ako ulazak u isključivi region nije moguć, aktivnost niti pozivaoca ove operacije se
zaustavlja, a njen deskriptor se uvezuje u listu semafora. Operacija resume se poziva
na izlazu iz isključivog regiona, radi izmene stanja semafora i omogućavanja da u
isključivi region uđe nit koja najduže čeka pred isključivim regionom (ako takva nit
postoji). U okviru ove operacije se prvo uveća stanje semafora, da bi se omogućio
novi ulazak u isključivi region i ujedno smanjio registrovani broj niti koje čekaju da
uđu u isključivi region. Zatim se objavljuje da je ispunjen uslov za novi ulazak u
isključivi region, što dovodi do izvezivanja iz liste semafora deskriptora niti koja
najduže čeka da uđe u isključivi region (ako takva nit postoji).
Izvorna datoteka sem.h sadrži definiciju klase semaphore. U nastavku je
naveden sadržaj ove datoteke:
semaphore.
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
67
class semaphore: public exclusive
{ int state;
condition open;
public:
semaphore(int value = 1);
void wait();
void resume(); };
semaphore::semaphore(int value)
{ state = value; };
void semaphore::wait()
{ exclusive_block set_up(this);
if (state-- <= 0)
open.await(); };
void semaphore::resume()
{ exclusive_block set_up(this);
state++;
open.signal(); };
Semafori i isključive klase predstavljaju dva razna pristupa sinhronizaciji.
Isključive klase su prilagođene objektno orijentisanom programiranju. One uvode
izričito označavanje deljenih promenljivih. To omogućuje automatsku proveru
ispravnog korišćenja deljenih promenljivih, jer tela operacija isključivih klasa
moraju da sadrže isključive regione. Semafori su prilagođeni procedurnom
programiranju. Oni ne omogućuju označavanje deljenih promenljivih, pa zato ne
podržavaju automatsku proveru ispravnog korišćenja deljenih promenljivih.
3.7 RAZMENA PORUKA ZASNOVANA NA MEHANIZMU
SEMAFORA
Semafori nisu podesni samo za ostvarenje međusobne isključivosti. Oni
omogućuju i uslovnu sinhronizaciju. To se može ilustrovati na primeru nove verzije
templejt klase message_box. Naizmenično slanje i prijem ispravnih poruka se može
zasnovati na korišćenju semafora empty i full, odnosno na unakrsnom pozivanju
njihovih operacija wait i resume. Ključ za uspešnu uslovnu sinhronizaciju je
inicijalizacija početnog stanja semafora full na vrednost 0, koja sprečava primaoca
poruke da primi poruku pre nego je ona poslana. Kod slanja poruke, poziv operacije
empty.wait() onemogućuje novo slanje poruke, dok prethodno poslana poruka ne
bude preuzeta, a poziv operacije full.resume() omogućuje prijem poslane poruke.
Simetrično tome, kod prijema poruke, poziv operacije full.wait() onemogućuje
novi prijem poruke, dok nova poruka ne bude poslana, a poziv operacije
empty.resume() omogućuje slanje nove poruke. Na taj način se ostvaruje
naizmenično slanje i prijem poruka.
Izvorna datoteka sembox.h sadrži definiciju nove verzije templejt klase
message_box. U nastavku je naveden sadržaj ove datoteke:
68
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
# include <sem.h>
template<class MESSAGE>
class message_box
{ MESSAGE content;
semaphore empty;
semaphore full;
public:
message_box();
void send(MESSAGE message);
MESSAGE receive(); };
template<class MESSAGE>
message_box<MESSAGE>::message_box(): full(0)
{ };
template<class MESSAGE>
void message_box<MESSAGE>::send(MESSAGE message)
{ empty.wait();
content = message;
full.resume(); };
template<class MESSAGE>
MESSAGE message_box<MESSAGE>::receive()
{ MESSAGE message;
full.wait();
message = content;
empty.resume();
return message; };
Konkurentno programiranje se u praksi često oslanja samo na mehanizam
semafora. Za to je dovoljno ponuditi biblioteku koja sadrži definiciju semafora i
operacije za rukovanje semaforom. Mehanizam semafora je jednostavan i efikasan,
ali je njegova mana u tome što ne pravi jasnu razliku između izražavanja
međusobne isključivosti i uslovne sinhronizacije. Tako međusobna isključivost
podrazumeva da su pozivi operacija wait i resume uvek upareni (da se nalaze na
početku i kraju isključivog iskaza), dok uslovna sinhronizacija podrazumeva da su
pozivi operacije wait i resume uvek raspareni. To se vidi iz prethodnog primera u
kome se poziv operacije empty.wait() nalazi na početku tela operacije send, a
njegov par, poziv operacije empty.resume() nalazi na pred kraj tela operacije
receive. Prema tome, ako se, greškom, na kraju isključivog regiona izostavi poziv
operacije resume, stvoriće se privid uslovne sinhronizacije. Ovakve greške se teško
otkrivaju kasnijim čitanjem programskog teksta, jer iz njega nije uvek jasno koja
vrsta sinhronizacije je potrebna. Zato se sredstva, koja konkurentna biblioteka
COLIBRY nudi za izražavanje međusobne isključivosti, razlikuju od sredstava, koje
ova biblioteka nudi za izražavanje uslovne sinhronizacije.
3.8 PROBLEM PET FILOZOFA
Zauzimanje više primeraka resursa iste vrste, neophodnih za aktivnost svake
niti iz neke grupe niti, predstavlja tipičan probem konkurentnog programiranja. On
se, u literaturi, ilustruje primerom problema pet filozofa (dining philosophers).
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
69
Svaki od njih provodi život razmišljajući u svojoj sobi i jedući u zajedničkoj
trpezariji. U njoj se nalazi pet stolica oko okruglog stola sa pet tanjira i pet viljuški
između njih. Pošto se, po želji filozofa, u trpezariji služe uvek špagete, svakom
filozofu su za jelo potrebne dve viljuške. Ako svi filozofi istovremeno ogladne, uđu
u trpezariju, sednu na svoje mesto za stolom i uzmu viljušku desno od sebe, tada
nastupa mrtva petlja, s kobnim ishodom po život filozofa.
Cilj rešavanja problema pet filozofa je da se svakom filozofu, kada poželi da
jede, osiguraju bez beskonačnog čekanja dve viljuške, koje stoje uz njegov tanjir.
Ponašanje svakog filozofa opisuje konstruktor philosopher. Razmišljanje
filozofa se predstavlja kao odlaganje aktivnosti niti koja reprezentuje filozofa. To
opisuje operacija thinking. Ovo odlaganje ima slučajno trajanje, što je u skladu sa
hirovitom prirodom filozofa. Operacija dining opisuje ponašanje filozofa za stolom.
Ulazni i izlani protokol, koje svaki filozof obavi pre, odnosno, posle jela, opisuju
operacije to_table i from_table. Po ulaznom protokolu, filozofi su dužni da, pre
svakog jela, provere stanja svojih suseda. Oni uzimaju viljuške samo ako njihovi
susedi ne jedu, jer su samo tada obe viljuške raspoložive. U suprotnom, ovaj
protokol zahteva od filozofa da sačekaju da obe viljuške postanu raspoložive.
Izlazni protokol predviđa da, nakon svakog jela, filozofi oslobode viljuške i da, uz
to, omoguće nastavak aktivnosti svojih suseda, ako oni čekaju samo oslobađanje
baš tih viljuški. Trajanje obroka svakog filozofa je promenljive dužine i izražava se
odlaganjem, opet u slučajnom trajanju, aktivnosti niti, odgovarajućih pojedinim
filozofima.
Klasa dining_protocol omogućuje uspostavljanje ulaznog i izlaznog
protokola. Operacije to_table i from_table, koje ona koristi, sprečavaju mrtvu
petlju, jer predviđaju da filozofi uzimaju i vraćaju odjednom obe viljuške.
Polje forks_available isključive klase dining_room_type omogućuje
očekivanje ispunjenja uslova da je viljuška raspoloživa, kao i objavljivanje
ispunjenosti ovog uslova. Klasa dining_room_type sadrži i polja referent_sequencer,
chair_state i chair_meals_number. Prvo od njih omogućuje označavanje filozofa
brojevima od 0 do 4. Drugo izražava stanja viljuški posredstvom stanja stolice
filozofa (FREE, TAKEN_BY_HUNGRY_PHILOSOPHER, TAKEN_BY_EATING_PHILOSOPHER), a treće
sadrži broj obroka koje su pojedini filozofi obavili. Klasa dining_room_type je
isključiva, radi zaštite konzistentnosti njenih polja.
Operacija show omogućuje prikazivanje svake promene stanja stolica filozofa,
a operacija set_referent_sequencer omogućuje registrovanje serijskog broja niti
koja reprezentuje filozofa 0.
Funkcija mod podržava modulo aritmetiku.
Inicijalna nit, stvara nit manager, dodjeljujući joj prioritet 2 i prosleđujući joj
kao argument broj filozofa. Nit manager registruje serijski broj niti koja reprezentuje
filozofa 0, zatim preuzima najveći predviđeni broj obroka filozofa i, na kraju, stvara
niti koje odgovaraju pojedinim filozofima.
70
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Izvorna datoteka opus04.cpp sadrži opis rešenja problema pet filozofa. U
nastavku je naveden sadržaj ove datoteke.
# include <colibry.h>
# include <colib_io.h>
# include <stdlib.h>
int mod(int a)
{ return (((a) < 0) ? ((a)+5) : (((a) > 4) ? ((a)-5) : (a))); };
int stohastic(int a)
{ return rand()%(a); };
const char FREE = 'F';
const char TAKEN_BY_HUNGRY_PHILOSOPHER = 'H';
const char TAKEN_BY_EATING_PHILOSOPHER = 'E';
class dining_room_type: public exclusive
{ unsigned long referent_sequencer;
char chair_state[5];
condition forks_available[5];
int chair_meals_number[5];
public:
dining_room_type();
void set_referent_sequencer(unsigned long sequencer);
void show();
void to_table();
void from_table(); };
dining_room_type::dining_room_type()
{ for (int i = 0;i < 5;i++)
{ chair_state[i] = FREE;
chair_meals_number[i] = 0; };
};
void dining_room_type::set_referent_sequencer(unsigned long sequencer)
{ referent_sequencer = sequencer; };
void dining_room_type::show()
{ tout << NEW_LINE;
for (int philosopher = 0;philosopher < 5;philosopher++)
tout << '(' << (char)(philosopher+'0') << ':'
<< chair_meals_number[philosopher]
<< ',' << chair_state[philosopher] << ") "; };
void dining_room_type::to_table()
{ exclusive_block set_up(this);
int philosopher = (int)(my_sequencer()-referent_sequencer);
if ((chair_state[mod(philosopher-1)] == TAKEN_BY_EATING_PHILOSOPHER) ||
(chair_state[mod(philosopher+1)] == TAKEN_BY_EATING_PHILOSOPHER))
{ chair_state[philosopher] = TAKEN_BY_HUNGRY_PHILOSOPHER;
show();
forks_available[philosopher].await(); };
chair_state[philosopher] = TAKEN_BY_EATING_PHILOSOPHER;
show();
};
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
void dining_room_type::from_table()
{ exclusive_block set_up(this);
int philosopher = (int)(my_sequencer()-referent_sequencer);
chair_state[philosopher] = FREE;
chair_meals_number[philosopher]++;
show();
if ((chair_state[mod(philosopher-1)] == TAKEN_BY_HUNGRY_PHILOSOPHER) &&
(chair_state[mod(philosopher-2)] != TAKEN_BY_EATING_PHILOSOPHER))
forks_available[mod(philosopher-1)].signal();
if ((chair_state[mod(philosopher+1)] == TAKEN_BY_HUNGRY_PHILOSOPHER) &&
(chair_state[mod(philosopher+2)] != TAKEN_BY_EATING_PHILOSOPHER))
forks_available[mod(philosopher+1)].signal(); };
dining_room_type dining_room;
class dining_protocol
{public:
dining_protocol();
~dining_protocol(); };
dining_protocol::dining_protocol()
{ dining_room.to_table(); };
dining_protocol::~dining_protocol()
{ dining_room.from_table(); };
class philosopher: public thread
{public:
philosopher(int meals_limit);
void thinking();
void dining(); };
philosopher::philosopher(int meals_limit)
{ while (meals_limit-- > 0)
{ thinking();
dining_protocol set_up;
dining(); };
quit();
};
void philosopher::thinking()
{ delay(stohastic(10)); };
void philosopher::dining()
{ delay(stohastic(10)); };
class manager: public thread
{ int meals_limit;
public:
manager(int count); };
71
72
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
manager::manager(int count)
{ tout << NEW_LINE << "DINING PHILOSOPHERS";
dining_room.set_referent_sequencer(my_sequencer()+1);
do
{ tout << NEW_LINE << "meals limit (from 1 to 999): ";
tin >> meals_limit; }
while ((meals_limit < 1) || (meals_limit > 999));
while (count-- > 0)
new philosopher(meals_limit); };
initial::initial(int,char **)
{ new (2) manager(5); };
Sadržaj izvorne datoteke opus04.cpp predstavlja potpun COLIBRY program.
U toku njegovog izvršavanja, sem inicijalne, nastane još šest niti.
Prethodno opisano rešenje problema pet filozofa dozvoljava (srećom,
praktično malo verovatno) izgladnjivanje filozofa do smrti, jer susedi bilo kog
filozofa mogu da mu neprekidno naizmenično zauzimaju viljuške. Ovo se može
otkloniti uvođenjem stanja vrlo gladan i davanjem niti, koja je u tom stanju
apsolutnog prioriteta, pri zauzimanju viljuški.
U prethodnom rešenju problema pet filozofa, usvojen je pristup zauzimanja
obe viljuške odjednom. Viljuške se mogu zauzimati i jedna za drugom, bez
opasnosti od mrtve petlje, ako se filozofi ponašaju asimetrično, odnosno ako parni
uzimaju prvo desnu, a neparni prvo levu viljušku.
3.9 PROBLEM ČITANJA I PISANJA
Problem čitanja i pisanja (readers writers problem) se može lako objasniti na
primeru kao što je rukovanje bankovnim računima. Bankovni računi pripadaju
komitentima banke i sadrže ukupan iznos novčanih sredstava svakog od komitenata.
U najjednostavnijem slučaju, rukovanje bankovnim računima se svodi: na prenos
sredstava (s jednog računa na drugi) i na proveru (stanja svih) računa. Prenos
sredstava obuhvata četiri koraka. Prvi korak sadrži čitanje stanja računa s koga se
prenose sredstva. Drugi korak obuhvata pisanje novog stanja na ovaj račun. Novo
stanje se dobije umanjivanjem pročitanog stanja za prenošeni iznos. Treći korak
sadrži čitanje stanja računa na koji se prenose sredstva. Četvrti korak obuhvata
pisanje novog stanja na ovaj račun. Novo stanje se dobije uvećanjem pročitanog
stanja za prenošeni iznos. Pri tome se podrazumeva da nisu dozvoljeni prenosi
sredstava iza kojih ostaje negativno stanje računa. Uz pretpostavku da su prenosi
sredstava mogući samo između posmatranih bankovnih računa, ukupna suma
njihovih stanja je nepromenljiva. Prema tome, provera računa se svodi na čitanja,
jedno za drugim, stanja svih računa, radi njihovog sumiranja.
Ispravnost prenosa sredstava zavisi od očuvanja konzistentnosti stanja svih
računa, za šta je neophodna međusobna isključivost raznih prenosa sredstava. U
suprotnom, moguće su razne greške. Na primer, pokušaj istovremenog obavljanja
dva ili više prenosa sredstava sa istog računa bi mogao da dovede do čitanja istog
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
73
stanja u toku prvih koraka raznih prenosa sredstava. Tada umanjenja pročitanog
stanja ne bi bila kumulativna, odnosno, samo bi poslednje od pisanja iz drugih
koraka prethodnih prenosa sredstava odredilo novo stanje posmatranog računa, a
umanjenja pročitanog stanja iz ostalih prenosa bi bila izgubljena. Sem međusobne
isključivosti raznih prenosa sredstava, neophodna je i međusobna isključivost
prenosa sredstava i provera računa. U suprotnom, provera računa bi mogla da
pročita stanje računa s kog se prenose sredstva, i to nakon obavljanja drugog koraka
prenosa sredstava, kao i da pročita stanje računa na koji se prenose sredstva, i to pre
obavljanja četvrtog koraka prenosa sredstava. Tada bi suma stanja svih računa bila
pogrešna, jer ne bi sadržala prenošena sredstva.
Bankovne račune predstavlja polje accounts isključive promenljive file.
Čitanja i pisanja ovih računa omogućuju njene operacije read i write. Konstruktor
transaction odgovara nitima koje prenose sredstva, a konstruktor auditor odgovara
nitima koje proveravaju račune. Međusobnu isključivost ovih niti obezbeđuje klasa
file_access_protocol, koja se oslanja na opercije open i close isključive
promenljive file. Parametar konstruktora klase file_access_protocol, kao i
parametar operacije open omogućuje saopštavanje namere s kojom nit pristupa
bankovnim računima. Poziv operacije open izaziva zaustavljanje aktivnosti niti, ako
ona namerava da bankovnim računima pristupa na način koji je u suprotnosti sa
njihovim stanjem, sadržanim u polju state isključive promenljive file. Pri tome,
moguća stanja bankovnih računa su: slobodni (FREE), u čitanju (READING) i u pisanju
(WRITING). Kada su bankovni računi u stanju čitanja, polje readers_number isključive
promenljive file sadrži broj niti koje bankovnim računima pristupaju samo sa
namerom čitanja.
Polje access_q isključive promenljive file omogućuje hronološki red
aktiviranja niti čija aktivnost je zaustavljena pri pozivu operacije open. Za ovo polje
je vezana lista uslova čije ispunjenje je neophodno za nastavak aktivnosti niti. U
ovu listu se uvezuju deskriptori niti u redosledu u kome će kasnije biti omogućeno
nastavljanje aktivnosti ovih niti. Kao privesci ovih deskriptora služe oznake namera
pristupanja bankovnim računima. Ispunjenja pomenutog uslova zavise od izmena
stanja bankovnih računa i objavljuju se u okviru operacije close.
Do zaustavljanja aktivnosti niti dolazi uvek, bez obzira na njenu nameru, ako
prethodno pomenuta lista uslova nije prazna, jer se jedino tako osigurava hronološki
red aktiviranja niti. Ako je ova lista prazna, tada zaustavljanja aktivnosti niti zavise
od njihovih namera i stanja bankovnih računa. Na primer, ako su bankovni računi u
stanju čitanja, obavezno se zaustavlja aktivnost niti s namerom pisanja.
Isključivoj promenljivoj file se pristupa, u toku prenosa sredstava, s
namerom pisanja (koja obuhvata i nameru čitanja), a, u toku provere računa, toj
promenljivoj se pristupa samo s namerom čitanja.
Prenose sredstava i provere računa izaziva nit manager, tako što stvara (po
slučajnom principu) odgovarajuće niti.
74
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Izvorna datoteka opus05.cpp sadrži opis rešenja problema čitanja i pisanja. U
nastavku je naveden sadržaj ove datoteke.
# include <colibry.h>
# include <colib_io.h>
# include <stdlib.h>
int stohastic(int a)
{ return rand()%(a); };
enum file_access { FREE,READING,WRITING };
const int ACCOUNTS_NUMBER = 1000;
const int INITIAL_AMOUNT = 100;
class file_type: public exclusive
{ file_access state;
int accounts[ACCOUNTS_NUMBER];
int readers_number;
tag_condition access_q;
public:
file_type();
void open(file_access access);
void close();
int read(int index);
void write(int index,int amount); };
file_type::file_type()
{ state = FREE;
for (int i = 0;i < ACCOUNTS_NUMBER;i++)
accounts[i] = INITIAL_AMOUNT;
readers_number = 0; };
void file_type::open(file_access access)
{ exclusive_block set_up(this);
if ((access_q.last() == true) ||
(state == WRITING) ||
((state == READING) && (access == WRITING)))
{ access_q.succ();
access_q.await(access); };
state = access;
if (access == READING)
readers_number++;
};
void file_type::close()
{ tag_type tag;
exclusive_block set_up(this);
if ((state == WRITING) ||
((state == READING) && (--readers_number == 0)))
if (access_q.first(&tag) == true)
{ access_q.signal();
if (tag == READING)
while ((access_q.first(&tag) == true) && (tag == READING))
access_q.signal(); }
else
state = FREE;
};
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
int file_type::read(int index)
{ exclusive_block set_up(this);
return accounts[index]; };
void file_type::write(int index,int amount)
{ exclusive_block set_up(this);
accounts[index] = amount; };
file_type file;
class file_access_protocol
{public:
file_access_protocol(file_access access);
~file_access_protocol(); };
file_access_protocol::file_access_protocol(file_access access)
{ file.open(access); };
file_access_protocol::~file_access_protocol()
{ file.close(); };
unsigned long time_of_request()
{ return time_get(); };
unsigned long start_time_of_requested_servicing()
{ delay(1);
return time_get(); };
unsigned long end_time_of_requested_servicing()
{ delay(6);
return time_get(); };
int transaction_source()
{ return stohastic(ACCOUNTS_NUMBER); };
int transaction_destination()
{ return stohastic(ACCOUNTS_NUMBER); };
int transaction_transfer()
{ return (stohastic(5)+1); };
class transaction: public thread
{ int source;
int destination;
int transfer;
int amount;
unsigned long t1;
unsigned long t2;
unsigned long t3;
public:
transaction(); };
75
76
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
transaction::transaction()
{ t1 = time_of_request();
source = transaction_source();
destination = transaction_destination();
transfer = transaction_transfer();
file_access_protocol set_up(WRITING);
t2 = start_time_of_requested_servicing();
if ((amount = file.read(source)) < transfer)
transfer = 0;
file.write(source,amount-transfer);
amount = file.read(destination);
file.write(destination,amount+transfer);
t3 = end_time_of_requested_servicing();
tout << NEW_LINE << "at " << '(' << t1 << ')'
<< " done " << '(' << t2 << ',' << t3 << ')'
<< source << " ->" << destination << ':' << transfer; };
class auditor: public thread
{ int i;
unsigned long sum;
unsigned long t1,t2,t3;
public:
auditor(); };
auditor::auditor()
{ sum = 0;
t1 = time_of_request();
file_access_protocol set_up(READING);
t2 = start_time_of_requested_servicing();
for (i = 0;i < ACCOUNTS_NUMBER;i++)
sum += file.read(i);
t3 = end_time_of_requested_servicing();
ton();
tout << NEW_LINE << "at " << '(' << t1 << ')'
<< " done " << '(' << t2 << ',' << t3 << ')'
<< " accounts sum:" << sum;
toff(); };
class manager: public thread
{ int number;
public:
manager();
void let_requested_servicing_begin(); };
manager::manager()
{ tout << NEW_LINE << "READERS AND WRITERS";
do
{ tout << NEW_LINE << "number of readers and writers (from 1 to 99): ";
tin >> number; }
while ((number < 1) || (number > 99));
while ((number--) > 0)
{ if (stohastic(2) == 0)
new transaction();
else
new auditor();
let_requested_servicing_begin(); };
};
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
77
void manager::let_requested_servicing_begin()
{ delay(1); };
initial::initial(int,char **)
{ new (2) manager(); };
U prethodnom primeru, račun, sa koga se prenose sredstva, kao i račun, na
koji se prenose sredstva, se određuju po slučajnom principu. Isto važi i za prenošeni
iznos. Za svaki prenos sredstava, kao i za svaku proveru računa, vezuje se
vremenski interval, da bi se mogle proveriti njihove međusobne isključivosti.
Odlaganja aktivnosti odgovarajućih niti obezbeđuju merljivost ovog vremenskog
intervala.
Sadržaj izvorne datoteke opus05.cpp predstavlja potpun COLIBRY program.
U toku njegovog izvršavanja, sem inicijalne, nastane više niti, čiji broj nije unapred
određen, kao ni uloga svake od njih.
Prethodno rešenje problema čitanja i pisanja ravnopravno tretira sve niti, jer
obezbeđuje da međusobno isključivi periodi njihovih aktivnosti budu u
hronološkom redu. Ali, ovakav pristup nije dobar, ako je važno da pisanja imaju
apsolutnu prednost u odnosu na čitanja, odnosno, da prenosi sredstava imaju
apsolutnu prednost u odnosu na provere računa, ili obrnuto. I jedno i drugo se može
ostvariti sa, ali i bez korišćenja liste uslova.
3.10
UPRAVLJANJE KRETANJEM GLAVE ZA ČITANJE I
PISANJE DISKA
Jedinice masovne memorije imaju oblik elektro-mehaničkih uređaja. Brzina
njihovog rada je ograničena brzinom pomeranja njihovih mehaničkih delova.
Tipičan primer ovakvih uređaja predstavlja disk. On sadrži podatke u obliku
magnetnih zapisa, raspoređenih u koncentričnim kružnim stazama, koje, na
magnetnom premazu njegovih rotirajućih ploča, opisuje njegova glava (za čitanje i
pisanje). Za pristup podacima je neophodno da glava diska, koja se pomera od
centra i ka centru rotacije ploča diska, dođe iznad prave staze. Pošto brzina
pomeranja glave diska ograničava njegovu ukupnu brzinu, važno je skratiti put koji
ona prelazi. Optimizacija kretanja glave diska je moguća kada se, zbog sporosti
diska, skupi više zahteva za čitanjem ili pisanjem blokova podataka diska. Ovakva
optimizacija se svodi na opsluživanje zahteva u redosledu staza na koje se oni
odnose, a ne u hronološkom redosledu pojave zahteva. Na taj način se izbegava da
glava diska osciluje između vanjskih i unutrašnjih staza diska, prelazeći tako
višestruko put, koji bi prešla samo jednom, ako bi, krećući se od unutrašnjih ka
vanjskim stazama, opslužila sve zahteve. Da bi se ovo ostvarilo, neophodno je
zahteve razvrstati u dva skupa, uređena u rastućem redosledu staza na koje se
zahtevi odnose. Pri tome, u jedan skup dolaze zahtevi čije staze se nalaze između
centra rotirajućih ploča diska i trenutnog položaja glave diska, a u drugi skup dolaze
zahtevi čije staze se nalaze između trenutnog položaja glave diska i oboda
78
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
rotirajućih ploča diska. Optimizacija kretanja glave diska se nalazi u nadležnosti
drajvera operativnog sistema.
Konkurentna biblioteka COLIBRY omogućuje optimizaciju kretanja glave
disketnog uređaja koji predstavlja standardnu jedinicu masovne memorije. Pri tome,
to što ova biblioteka podržava samo brojeve blokova podataka diska, a ne i fizičke
adrese staza, ne izaziva probleme, jer su blokovi na pravilan način pridruženi
stazama. Na primer, za disketni uređaj blokovi sa brojevima od 0 do 8 se nalaze na
nultoj stazi, a oni sa brojevima od 9 do 17 na prvoj stazi i tako dalje.
Optimizacija kretanja glave diska se može osloniti na klijent-server oblik
saradnje niti, jer su niti, radi kojih se blokovi podataka diska čitaju i pišu, u poziciji
klijenata. Oni traže uslugu i zatim čekaju njen rezultat. U tom slučaju, njih
opslužuje posebna nit server, koja obezbeđuje da redosled čitanja i pisanja blokova
bude optimalan sa stanovišta kretanja glave diska.
Klijent-server oblik saradnje niti podrazumeva dvosmernu komunikaciju niti.
U jednom smeru klijenti šalju zahteve za uslugom, a u drugom smeru server šalje
rezultate usluga. Za svaki od ovih smerova komunikacije potreban je poseban
komunikacioni bafer. Za smer od klijenta ka serveru potreban je komunikacioni
bafer za razmenu zahteva, a za suprotan smer komunikacioni bafer za razmenu
rezultata.
Isključiva promenljiva forwards služi kao komunikacioni bafer za razmenu
zahteva. Njena polja buffer i block sadrže elemente zahteva. Prvo od njih sadrži
adresu bafera klijenta u koji se prenosi blok podataka koga klijent čita, a drugo
polje sadrži broj bloka na koje se odnosi zahtev. Upućivanjem serveru adrese svog
bafera, klijent omogućuje pristup servera ovom baferu. Iz toga ne proizilaze
problemi dok klijent-server oblik saradnje niti osigurava međusobnu isključivost
pristupanja pomenutih niti ovoj zoni. Polja state i full isključive promenljive
forwards imaju sličnu ulogu kao i istoimena polja kod komunikacionog bafera.
Polje boundary ove isključive promenljive sadrži broj trenutno prenošenog bloka i,
na taj način, određuje trenutni položaj glave diska. Polje q iste deljene promenljive
omogućuje obrazovanje dve liste uslova, koji odgovaraju uređenim skupovima
zahteva, a polje index pokazuje koja od ovih listi odgovara zahtevima čiji brojevi
blokova su veći od ili jednaki sadržaju polja boundary. Pri tome se podrazumeva da
se u pomenute liste uslova uključuju deskriptori niti, čija aktivnost zavisi od
položaja glave diska. Kao privesci pomenutih deskriptora služe brojevi blokova.
Isključiva promenljiva backwards služi kao komunikacioni bafer za razmenu
rezultata. Njeno polje result sadrži rezultat obrade zahteva, a polja state, full i
empty imaju istu ulogu kao istoimena polja kod komunikacionog bafera.
Konstruktor server opisuje ponašanje servera. Njegova aktivnost se sastoji od
neprekidnog ponavljanja preuzimanja novog zahteva, obavljanja zahtevane
operacije i vraćanja rezultata obrade pomenutog zahteva. Pri tome, poziv funkcije
receive_request omogućuje preuzimanje novog zahteva, a poziv funkcije
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
79
send_answer omogućuje vraćanje rezultata obrade prethodnog zahteva. Aktivnost
servera se zaustavlja do pojave novog zahteva, kada nema novih zahteva.
Konstruktor client opisuje ponašanje klijenata. Oni zahtevaju usluge servera,
pozivajući funkciju read_request, u okviru čega se zaustavlja njihova aktivnost do
pojave rezultata obrade zahteva.
Konstruktor quit_caller omogućuje da kraj izvršavanja programa nastupi
čim poslednji klijent bude uslužen, jer program nema prirodan kraj zbog ciklične
prirode servera.
Zadatak inicijalne niti je da stvori nit manager koja zatim stvara niti server i
quit_caller, kao i više niti klijenata, koje zahtevaju naizmenično čitanje vanjskih i
unutrašnjih blokova. Prioriteti ovih niti su podešeni tako da se prvo postave zahtevi,
a onda usluže jedan za drugim i tako dalje. Kraj izvršavanja programa nastupa tek
kada se završi aktivnost svih klijenata, a server zaustavi svoju aktivnost, radi
očekivanja novog zahteva. Tek tada se aktivira nit najnižeg prioriteta, zadužena za
završavanje izvršavanja programa.
Izvorna datoteka opus06.cpp sadrži opis upravljanja kretanjem glave za
čitanje i pisanje diska. U nastavku je naveden sadržaj ove datoteke.
# include <colibry.h>
# include <colib_io.h>
# include <stdlib.h>
int mod(int a)
{ return (((a) > 1) ? (0) : (a)); };
enum state_type { EMPTY,FULL };
class forwards_type: public exclusive
{ state_type state;
char *buffer;
unsigned block;
unsigned boundary;
tag_condition q[2];
int index;
condition full;
public:
forwards_type();
friend int read_request(char *buffer,unsigned block);
friend void receive_request(char **buffer,unsigned *block); };
forwards_type::forwards_type()
{ state = EMPTY;
boundary = 0;
index = 0; };
forwards_type forwards;
80
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
class backwards_type: public exclusive
{ state_type state;
int result;
condition full;
condition empty;
public:
backwards_type();
friend int read_request(char *buffer,unsigned block);
friend void send_answer(int result); };
backwards_type::backwards_type()
{ state = EMPTY; };
backwards_type backwards;
int read_request(char *buffer,unsigned block)
{ int index;
tag_type tag;
int result;
{ exclusive_block set_up(&forwards);
if (forwards.state == FULL)
{ if (block < forwards.boundary)
index = mod(forwards.index+1);
else
index = forwards.index;
if (forwards.q[index].first(&tag) == true)
do
{ if (block < tag)
break;
}
while (forwards.q[index].succ(&tag) == true);
forwards.q[index].await(block);
};
forwards.buffer = buffer;
forwards.block = block;
forwards.boundary = block;
forwards.state = FULL;
forwards.full.signal();
};
{ exclusive_block set_up(&backwards);
if (backwards.state == EMPTY)
backwards.full.await();
result = backwards.result;
backwards.state = EMPTY;
backwards.empty.signal(); };
return(result);
};
void receive_request(char **buffer,unsigned *block)
{ exclusive_block set_up(&forwards);
if (forwards.state == EMPTY)
forwards.full.await();
*buffer = forwards.buffer;
*block = forwards.block;
if (forwards.q[forwards.index].first() == false)
forwards.index = mod(forwards.index+1);
forwards.state = EMPTY;
forwards.q[forwards.index].signal();
};
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
void send_answer(int result)
{ exclusive_block set_up(&backwards);
if (backwards.state == FULL)
backwards.empty.await();
backwards.result = result;
backwards.state = FULL;
backwards.full.signal();
};
class server: public thread
{ unsigned long t1;
unsigned long t2;
char *buffer;
unsigned block;
int result;
public:
server(char disk); };
server::server(char disk)
{ result = 0;
t1 = time_get();
for(;;)
{ receive_request(&buffer,&block);
if (disk == 'A')
result = disk_a.block_get(buffer,block);
else
result = disk_b.block_get(buffer,block);
t2 = time_get();
ton();
tout << NEW_LINE << "total time " << t2-t1;
toff();
send_answer(result); };
};
class client: public thread
{ char buffer[DISK_BLOCK];
int result;
int steps;
public:
client(unsigned block);
void data_processing_step(); };
client::client(unsigned block)
{ for (steps = 0;steps < 10;steps++)
{ result = read_request(buffer,block+steps);
ton();
tout << " thread " << my_sequencer() << " block " << block+steps
<< " result " << result;
toff();
data_processing_step(); };
};
void client::data_processing_step()
{ delay(1); };
81
82
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
class quit_caller: public thread
{public:
quit_caller(); };
quit_caller::quit_caller()
{ quit(); };
class manager: public thread
{ char disk;
int number;
public:
manager();
unsigned block_address_generator(int number); };
manager::manager()
{ tout << NEW_LINE << "DISK HEAD SCHEDULING";
do
{ tout << NEW_LINE << "disk (A/B): ";
tin >> disk; }
while ((disk != 'A') && (disk != 'B'));
do
{ tout << NEW_LINE << "number of disk clients (from 2 to 60): ";
tin >> number; }
while ((number < 2) || (number > 60));
new (2) server(disk);
while ((number--) > 0)
new (3) client(block_address_generator(number));
new quit_caller();
};
unsigned manager::block_address_generator(int number)
{ unsigned block_address = number*5;
if ((number%2) == 0)
block_address += 301;
else
block_address += 6;
return block_address; };
initial::initial(int,char **)
{ new (5) manager(); };
Sadržaj izvorne datoteke opus06.cpp predstavlja potpun COLIBRY program.
U toku njegovog izvršavanja, sem inicijalne, nastane više niti, čiji broj nije unapred
određen.
Predložena optimizacija kretanja glave diska predviđa usluživanje zahteva
samo u smeru kretanja glave diska od unutrašnjih ka vanjskim stazama. Pređeni put
glave diska bi se još više skratio, kada bi se zahtevi usluživali u oba smera kretanja
glave diska. To bi zahtevalo da skupovi zahteva budu uređeni na razne načine,
jedan u rastućem, a drugi u opadajućem redosledu brojeva blokova, a usluživanje bi
više čekali zahtevi vezani za krajnje staze.
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
83
3.11 PARALELNO PROGRAMIRANJE
Cilj paralelnog programiranja je da skrati vreme obrade podataka koristeći
paralelizam koga nude računari sa više procesora opšte namene. Taj cilj se ostvaruje
pronalaženjem relativno nezavisnih delova obrade podataka i zaduživanjem
posebnih niti da obavljaju pojedine od nezavisnih delova ukupne obrade podataka.
Ostvarenje pomenutog cilja podrazumeva da su aktivnosti ovih niti istovremene,
odnosno da su vezane za različite procesore. Način dekompozicije obrade podataka
u nezavisne delove zavisi od karaktera obrade podataka, ali i od oblika raspoloživog
paralelizma. Zato se paralelni programi specijalizuju za pojedine oblike
paralelizma.
Paralelno programiranje se zasniva na konkurentnom programiranju, jer
relativna nezavisnost delova obrade podataka znači da je, pre ili kasnije, neizbežna
saradnja niti koje su zadužene za nezavisne delove ukupne obrade podataka. Pored
toga, zahvaljujući konkurentnom programiranju, paralelni programi se mogu
razvijati i na jednoprocesorskom računaru, na kome, jasno, nije moguće
demonstirati skraćenje vremena obrade podataka koje paralelni programi nude.
Saradnju niti paralelnog programa je najbolje zasnovati na razmeni poruka,
jer takav oblik saradnje osigurava najveću nezavisnost niti. Međutim, razmena
poruka je ograničena oblikom paralelizma koji određuje između kojih procesora, i
njima pridruženih niti, postoji mogućnost za direktnu razmenu poruka. To znači da
se paralelni program može izvršavati na jednoprocesorskom računaru, ako se
obezbede komunikacioni kanali, saglasni sa oblikom paralelizma za koji je paralelni
program specijalizovan. Zadatak ovih komunikacionih kanala je da podrže direktnu
razmenu poruka samo između onih niti, između kojih je ta razmena moguća u
pomenutom obliku paralelizma. Na taj način se niti vezuju, na primer, u niz (array),
matricu (mesh), drvo (tree) ili potpuno međusobno povezuju.
3.12 VEZIVANJE NITI U NIZ
Vezivanje niti u niz je inspirisano idejom jednodimenzionalne sistoličke
arhitekture (array processors). Svaka nit iz ovog niza ima levo i desno od sebe i
ispod sebe po dva pregradka za poruke (slika 3.12.1).
pregradak10
pregradak11
nit1
pregradak20
pregradak30
pregradak12
nit2
pregradak21
pregradak40
pregradak31
Slika 3.12.1 Niti uvezane u niz
pregradak22
pregradak41
84
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Pregradak levo gore uspostavlja komunikacioni kanal od prethodne niti iz
niza, a pregradak levo dole ka prethodnoj niti iz niza. Slično, pregradak desno dole
uspostavlja komunikacioni kanal od sledeće niti iz niza, a pregradak desno gore ka
sledećoj niti iz niza. Za prvu nit iz niza pregradak levo gore uspostavlja
komunikacioni kanal od okruženja, a pregradak levo dole ka okruženju. Slično, za
poslednju nit iz niza pregradak desno dole uspostavlja komunikacioni kanal od
okruženja, a pregradak desno gore ka okruženju. Pored toga, za svaku nit pregradak
ispod levo uspostavlja direktan komunikacioni kanal od okruženja, a pregradak
ispod desno ka okruženju. Na ovaj način svaka nit može da razmenjuje poruke sa
svojim prethodnikom, sa svojim sledbenikom i sa okruženjem.
Templejt klasa array omogućuje uspostavljanje komunikacionih kanala koji
su potrebni za uvezivanje niti u niz. Njena dva parametra omogućuju definisanje
tipa poruke i broja niti u nizu. Elementi polja boxes1, boxes2, extra1_boxes i
extra2_boxes odgovaraju, respektivno, pregracima 1, 2, 3 i 4. Konstruktor array
omogućuje registrovanje referentnog serijskog broja, u odnosu na koji se određuju
redni brojevi niti iz niza, a operacija my_node omogućuje dobijanje rednog broja niti
iz niza. Za označavanje niti u nizu koriste se redni brojevi: 1, 2, 3 i tako dalje.
Operacije send i receive omogućuje razmenu poruka. Javno dostupan par ovih
operacija omogućuje nitima iz niza da označe izvorište/odredište poruke samo
pomoću konstanti LEFT, RIGHT i EXTRA, a bez navođenja rednog broja koji se
podrazumeva.
Izvorna datoteka array.h sadrži definiciju templejt klase array. U nastavku je
naveden sadržaj ove datoteke.
# include <box.h>
enum array_boxes { LEFT,RIGHT,EXTRA };
template<class MESSAGE,int NODES>
class array
{ unsigned long referent_sequencer;
message_box<MESSAGE> boxes1[NODES+1];
message_box<MESSAGE> boxes2[NODES+1];
message_box<MESSAGE> extra1_boxes[NODES];
message_box<MESSAGE> extra2_boxes[NODES];
protected:
array();
void send(int node,array_boxes kind,MESSAGE message);
MESSAGE receive(int node,array_boxes kind);
public:
void send(array_boxes kind,MESSAGE message);
MESSAGE receive(array_boxes kind);
int my_node(); };
template<class MESSAGE,int NODES>
array<MESSAGE,NODES>::array()
{ referent_sequencer = my_sequencer(); };
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
85
template<class MESSAGE,int NODES>
void array<MESSAGE,NODES>::send(int node,array_boxes kind,MESSAGE message)
{ message_box<MESSAGE> *destination;
switch (kind)
{ case LEFT : destination = &(boxes1[--node]);
break;
case RIGHT : destination = &(boxes2[node]);
break;
case EXTRA : destination = &(extra1_boxes[--node]); };
destination->send(message);
};
template<class MESSAGE,int NODES>
MESSAGE array<MESSAGE,NODES>::receive(int node,array_boxes kind)
{ message_box<MESSAGE> *source;
switch (kind)
{ case LEFT : source = &(boxes2[--node]);
break;
case RIGHT : source = &(boxes1[node]);
break;
case EXTRA : source = &(extra2_boxes[--node]); };
return source->receive();
};
template<class MESSAGE,int NODES>
void array<MESSAGE,NODES>::send(array_boxes kind,MESSAGE message)
{ int index = (int)(my_sequencer()-referent_sequencer);
message_box<MESSAGE> *destination;
switch (kind)
{ case LEFT : destination = &(boxes2[--index]);
break;
case RIGHT : destination = &(boxes1[index]);
break;
case EXTRA : destination = &(extra2_boxes[--index]); };
destination->send(message);
};
template<class MESSAGE,int NODES>
MESSAGE array<MESSAGE,NODES>::receive(array_boxes kind)
{ int index = (int)(my_sequencer()-referent_sequencer);
message_box<MESSAGE> *source;
switch (kind)
{ case LEFT : source = &(boxes1[--index]);
break;
case RIGHT : source = &(boxes2[index]);
break;
case EXTRA : source = &(extra1_boxes[--index]); };
return source->receive();
};
template<class MESSAGE,int NODES>
int array<MESSAGE,NODES>::my_node()
{ return (int)(my_sequencer()-referent_sequencer); };
Templejt klasa array se zasniva na asinhronoj razmeni poruka, ali se može
osloniti i na sinhronu razmenu poruka.
86
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
3.13 PARALELNO SORTIRANJE
Niti uvezane u niz su podesne, na primer, za paralelno sortiranje niza
znakova. Cilj sortiranja niza znakova je uređenje ovih znakova, na primer, u
rastućem redosledu. Jedan način sortiranja znakova u rastućem redosledu započinje
postupkom dovođenja na prvo mesto u nizu znaka koji prethodi svim preostalim
znakovima. Ovaj postupak se svodi na ponavljanje poređenja znaka, koji je na
prvom mestu u nizu, sa znakovima, koji se nalaze na preostalim mestima
nesortiranog niza. Cilj ovih poređenja je otkrivanje parova znakova čiji redosled
nije u skladu sa željenim uređenjem i eventualna zamene mesta ovih znakova.
Analognim postupkom se dovode na drugo i sva ostala mesta znakovi koji prethode
svim preostali znakovima, tako da su na kraju svi znakovi poredani u rastućem
redosledu. Postupci dovođenja željenih znakova na pojedina mesta u nizu su
međusobno nezavisni dok se ne odnose na iste znakove, pa se mogu odvijati
paralelno. Oni, znači, mogu biti predmet aktivnosti raznih niti, pod uslovom da
ovakvih niti ima koliko i sortiranih znakova, kao i da su one uvezane u niz, odnosno
da obrazuju protočnu strukturu. Tada znakovi, jedan za drugim, mogu da teku od
prve ka poslednjoj niti, tako da prva od niti može da izdvoji znak koji je na prvom
mestu u uređenju, nit iza nje znak koji je na drugom mestu u uređenju i tako dalje.
Pri tome se podrazumeva da svaka nit zadrži prvi znak koji primi i da, zatim, poredi
novopridošli sa prethodno zadržanim znakom, zadržavajući uvek znak koji je u
skladu sa željenim uređenjem, a šaljući dalje preostali znak. Posebna upravljačka nit
upućuje znakove na sortiranje, šaljući ih prvoj niti iz protočne strukture, i preuzima
ih sa sortiranja, primajući ih prvo od prve, pa od druge i na kraju od poslednje niti iz
protočne strukture.
Konstruktor klase sorter opisuje ponašanje niti, uvezanih u niz. Ove niti
stvara upravljačka nit, čiju aktivnost opisuje konstruktor klase manager. Upravljačka
nit još preuzima znakove za sortiranje, upućuje ih u protočnu strukturu, iz nje
preuzima sortirane znakove i prikazuje ih na ekranu. Podrazumeva se da sortirani
niz znakova na svom kraju sadrži razmak (space).
Izvorna datoteka opus07.cpp sadrži opis paralelnog sortiranja. U nastavku je
naveden sadržaj ove datoteke.
# include <colibry.h>
# include <colib_io.h>
# include <array.h>
const int CHARACTERS_IN_LINE = 80;
const int THREAD_NODES = CHARACTERS_IN_LINE+1;
const char SPACE = ' ';
class sorter: public thread
{ char old_character;
char new_character;
public:
sorter(array<char,THREAD_NODES> *array); };
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
87
sorter::sorter(array<char,THREAD_NODES> *array)
{ if ((old_character = array->receive(LEFT)) > SPACE)
{ while ((new_character = array->receive(LEFT)) > SPACE)
if (new_character < old_character)
{ array->send(RIGHT,old_character);
old_character = new_character; }
else
array->send(RIGHT,new_character);
array->send(RIGHT,new_character);
};
array->send(EXTRA,old_character);
};
class array_manager: public thread,public array<char,THREAD_NODES>
{ int counter;
char c;
public:
array_manager(); };
array_manager::array_manager()
{ tout << NEW_LINE << "PARALLEL SORTING"
<< NEW_LINE << "unsorted array of characters "
<< "(enter space to end input):" << NEW_LINE;
counter = CHARACTERS_IN_LINE;
do
{ tin >> c;
if (c <= SPACE)
break;
new sorter(this);
send(1,LEFT,c); }
while (--counter > 0);
new sorter(this);
send(1,LEFT,SPACE);
tout << NEW_LINE << "sorted array of characters (in ascending order)"
<< NEW_LINE;
counter = 1;
while ((c = receive(counter++,EXTRA)) > SPACE)
tout << c;
};
initial::initial(int,char **)
{ new (2) array_manager(); };
Sadržaj izvorne datoteke opus07.cpp predstavlja potpun COLIBRY program.
U toku njegovog izvršavanja, sem inicijalne, nastane više niti, čiji broj nije unapred
određen.
Dužina sortiranja, opisanog prethodnim programom, linearno zavisi od broja
sortiranih znakova, ako se svakoj od sortnih niti dodeli poseban procesor. Tada, broj
operacija svakog od procesora linearno zavisi od broja elemenata sortiranog niza.
Tako prvi procesor primi n elemenata, obavi n-1 poređenja i svom sledbeniku
pošalje n-1 elemenata. Drugi procesor primi n-1 elemenata (s kašnjenjem od jednog
prijema), obavi n-2 poređenja i svom sledbeniku pošalje n-2 elemenata. Poslednji
(n-ti) procesor samo primi 1 element (s kašnjenjem od n-1 prijema). Ako se
88
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
prethodni program izvršava na jednoprocesorskom računaru, tada je dužina
sortiranja proporcionalna kvadratu broja sortiranih znakova, jer broj poređenja koje
obavi isti procesor iznosi (n-1)+(n-2)+...+1=n(n-1)/2.
3.14 VEZIVANJE NITI U MATRICU
Vezivanje niti u matricu je inspirisano idejom dvodimenzionalne sistoličke
arhitekture (mesh). Svaka nit iz ovog niza ima levo i desno od sebe, iznad i ispod
sebe i dijagonalno od sebe po dva pregradka za poruke (slika 3.14.1).
kolona
pregradak10
pregradak20
pregradak11
pregradak21
(1,1)
pregradak30
nit1
pregradak40
pregradak12
(1,2)
pregradak31
pregradak41
nit2
pregradak50
pregradak51
pregradak60
pregradak61
pregradak22
pregradak13
pregradak23
(2,1)
pregradak32
pregradak42
nit3
red
(2,2)
pregradak33
pregradak43
nit4
pregradak52
pregradak53
pregradak62
pregradak63
Slika 3.14.1.Niti uvezane u matricu
Pregradak levo gore uspostavlja komunikacioni kanal od levog suseda iz
matrice, a pregradak levo dole ka levom susedu iz matrice. Slično, pregradak desno
dole uspostavlja komunikacioni kanal od desnog suseda iz matrice, a pregradak
desno gore ka desnom susedu iz matrice. Podrazumeva se da je nit sa levog kraja
svakog reda matrice desni sused niti sa desnog kraja istog reda i obrnuto. Pregradak
iznad levo uspostavlja komunikacioni kanal od gornjeg suseda iz matrice, a
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
89
pregradak iznad desno ka gornjem susedu iz matrice. Slično, pregradak ispod levo
uspostavlja komunikacioni kanal ka donjem susedu iz matrice, a pregradak ispod
desno od donjeg suseda iz matrice. Podrazumeva se da je nit sa gornjeg kraja svake
kolone matrice donji sused niti sa donjeg kraja iste kolone i obrnuto. Pored toga, za
svaku nit pregradak dijagonalno gore uspostavlja direktan komunikacioni kanal ka
okruženju, a pregradak dijagonalno dole od okruženja. Na ovaj način svaka nit
može da razmenjuje poruke sa svojim horizontalnim i vertikalnim susedima, kao i
sa okruženjem.
Templejt klasa mesh omogućuje uspostavljanje komunikacionih kanala koji su
potrebni za uvezivanje niti u matricu. Njena tri parametra omogućuju definisanje
tipa poruke i broja redova i kolona niti u matrici. Elementi polja above1_boxes,
above2_boxes,
before1_boxes,
before2_boxes,
extra1_boxes
i extra2_boxes
odgovaraju, respektivno, pregracima 1, 2, 3, 4, 5 i 6. Konstruktor mesh omogućuje
registrovanje referentnog serijskog broja, u odnosu na koji se određuju redni brojevi
niti iz matrice, a operacije my_row i my_column omogućuje dobijanje rednog broja
reda i kolone niti iz matrice. Za označavanje niti u nizu koriste se redni brojevi: 1,
2, 3, ... koji se izračunaju iz rednog broja reda i kolone niti. Operacije send i receive
omogućuje razmenu poruka. Javno dostupan par ovih operacija omogućuje nitima
iz matrice da označe izvorište/odredište poruke samo pomoću konstanti UPPER, DOWN,
LEFT, RIGHT i EXTRA, a bez navođenja rednog broja koji se podrazumeva.
Izvorna datoteka mesh.h sadrži definiciju templejt klase mesh. U nastavku je
naveden sadržaj ove datoteke.
# include <box.h>
enum mesh_boxes { UPPER,DOWN,LEFT,RIGHT,EXTRA };
template<class MESSAGE,int ROWS,int COLUMNS>
class mesh
{ unsigned long referent_sequencer;
message_box<MESSAGE> above1_boxes[ROWS*COLUMNS];
message_box<MESSAGE> above2_boxes[ROWS*COLUMNS];
message_box<MESSAGE> before1_boxes[ROWS*COLUMNS];
message_box<MESSAGE> before2_boxes[ROWS*COLUMNS];
message_box<MESSAGE> extra1_boxes[ROWS*COLUMNS];
message_box<MESSAGE> extra2_boxes[ROWS*COLUMNS];
protected:
mesh();
void send(int row,int column,mesh_boxes kind,MESSAGE message);
MESSAGE receive(int row,int column,mesh_boxes kind);
public:
void send(mesh_boxes kind,MESSAGE message);
MESSAGE receive(mesh_boxes kind);
int my_row();
int my_column(); };
template<class MESSAGE,int ROWS,int COLUMNS>
mesh<MESSAGE,ROWS,COLUMNS>::mesh()
{ referent_sequencer = my_sequencer(); };
90
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
template<class MESSAGE,int ROWS,int COLUMNS>
void mesh<MESSAGE,ROWS,COLUMNS>::send(int row,int column,
mesh_boxes kind,MESSAGE message)
{ int index = (row-1)*COLUMNS+column-1;
message_box<MESSAGE> *destination;
switch (kind)
{ case UPPER : destination = &(above1_boxes[index]);
break;
case DOWN : index += COLUMNS;
if (index >= ROWS*COLUMNS)
index -= ROWS*COLUMNS;
destination = &(above2_boxes[index]);
break;
case LEFT : destination = &(before1_boxes[index]);
break;
case RIGHT : if (((++index)%COLUMNS) == 0)
index -= COLUMNS;
destination = &(before2_boxes[index]);
break;
case EXTRA : destination = &(extra1_boxes[index]); };
destination->send(message);
};
template<class MESSAGE,int ROWS,int COLUMNS>
MESSAGE mesh<MESSAGE,ROWS,COLUMNS>::receive(int row,int column,
mesh_boxes kind)
{ int index = (row-1)*COLUMNS+column-1;
message_box<MESSAGE> *source;
switch (kind)
{ case UPPER : source = &(above2_boxes[index]);
break;
case DOWN : index += COLUMNS;
if (index >= ROWS*COLUMNS)
index -= ROWS*COLUMNS;
source = &(above1_boxes[index]);
break;
case LEFT : source = &(before2_boxes[index]);
break;
case RIGHT : if (((++index)%COLUMNS) == 0)
index -= COLUMNS;
source = &(before1_boxes[index]);
break;
case EXTRA : source = &(extra2_boxes[index]); };
return source->receive();
};
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
91
template<class MESSAGE,int ROWS,int COLUMNS>
void mesh<MESSAGE,ROWS,COLUMNS>::send(mesh_boxes kind,MESSAGE message)
{ int index = (int)(my_sequencer()-referent_sequencer);
message_box<MESSAGE> *destination;
switch (kind)
{ case UPPER : destination = &(above2_boxes[--index]);
break;
case DOWN : index += COLUMNS-1;
if (index >= ROWS*COLUMNS)
index -= ROWS*COLUMNS;
destination = &(above1_boxes[index]);
break;
case LEFT : destination = &(before2_boxes[--index]);
break;
case RIGHT : if ((index%COLUMNS) == 0)
index -= COLUMNS;
destination = &(before1_boxes[index]);
break;
case EXTRA : destination = &(extra2_boxes[--index]); };
destination->send(message);
};
template<class MESSAGE,int ROWS,int COLUMNS>
MESSAGE mesh<MESSAGE,ROWS,COLUMNS>::receive(mesh_boxes kind)
{ int index = (int)(my_sequencer()-referent_sequencer);
message_box<MESSAGE> *source;
switch (kind)
{ case UPPER : source = &(above1_boxes[--index]);
break;
case DOWN : index += COLUMNS-1;
if (index >= ROWS*COLUMNS)
index -= ROWS*COLUMNS;
source = &(above2_boxes[index]);
break;
case LEFT : source = &(before1_boxes[--index]);
break;
case RIGHT : if ((index%COLUMNS) == 0)
index -= COLUMNS;
source = &(before2_boxes[index]);
break;
case EXTRA : source = &(extra1_boxes[--index]); };
return source->receive();
};
template<class MESSAGE,int ROWS,int COLUMNS>
int mesh<MESSAGE,ROWS,COLUMNS>::my_row()
{ int index = (int)(my_sequencer()-referent_sequencer);
return ((index-1)/COLUMNS+1); };
template<class MESSAGE,int ROWS,int COLUMNS>
int mesh<MESSAGE,ROWS,COLUMNS>::my_column()
{ int index = (int)(my_sequencer()-referent_sequencer);
return ((index-1)%COLUMNS+1); };
3.15 PARALELNO MNOŽENJE MATRICA
Izračunavanja elemenata matrice, koja je jednaka proizvodu druge dve
matrice, su dovoljno međusobno nezavisna da mogu biti paralelna. To pokazuje
92
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
primer množenja dvodimenzionalnih matrica A i B, čiji proizvod je jednak
dvodimenzionalnoj matrici C (slika 3.15.1).
 a11
a
 21
a12  b11
×
a22  b21
b12   a11b11 + a12 b21
=
b22  a 21b11 + a 22 b21
a11b12 + a12 b22 
a 21b12 + a 22 b22 
Slika 3.15.1 Proizvod dvodimenzionalnih matrica
Ako se izračunavanju svakog od četiri elementa matrice C posveti posebna
nit, tada one, nezavisno jedna od druge, znači paralelno, mogu prvo da izračunaju
proizvode zadebljanim slovima označenih elemenata matrica A i B, jasno pod
uslovom da svaka od niti poseduje potrebne elemente. To se može postići, ako su
ove niti prostorno raspoređene kao elementi matrice C i tako komunikaciono
povezane da svaka od njih može da šalje poruke nitima desno i ispod sebe, a prima
poruke od niti levo i iznad sebe. Tada, na početku svaka nit primi s leva svoj
element matrice A, a odozgo svoj element matrice B. Pošto jedino nit (1,1)
poseduje potreban para elemenata, ostale niti moraju da razmene svoje elemente.
Tako niti (2,1) i (2,2) šalju desno raspoloživi element matrice A, a primaju s leva
potrebni element iste matrice. Slično, niti (1,2) i (2,2) šalju dole raspoloživi element
matrice B, a primaju odozgo potrebni element iste matrice. Za računanje preostalog
proizvoda, potrebno je da niti međusobno razmene raspoložive elemente matrica A
i B. Međusobna razmena elemenata matrica A i B je pravilna, pa sve niti šalju
raspoloživi element matrice A desno, a raspoloživi element matrice B dole, i zatim
primaju novi element matrice A sa leva, a novi element matrice B odozgo. Nakon
ovakve razmene, svaka od niti raspolaže elementima neophodnim za računanje
drugog proizvoda.
Konstruktor multiplier opisuje inicijalnu raspodelu elemenata matrica A i B,
kao i izračunavanje pojedinih elemenata matrice C.
Operacija input_matrix klase manager omogućuje preuzimanje elemenata
matrica A i B i njihovo upućivanje pojedinim nitima uvezanim u matricu.
Konstruktor klase manager opisuje aktivnost upravljačke niti, koja stvara niti
uvezane u matricu, isporučuje im odgovarajuće elemente matrica A i B, preuzima
elemente matrice C i prikazuje ih.
Izvorna datoteka opus08.cpp sadrži opis paralelnog množenja matrica. U
nastavku je naveden sadržaj ove datoteke.
# include <colibry.h>
# include <colib_io.h>
# include <mesh.h>
const int SQUARE_MATRIX_ORDER = 2;
const int THREAD_ROWS = SQUARE_MATRIX_ORDER;
const int THREAD_COLUMNS = SQUARE_MATRIX_ORDER;
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
class multiplier: public thread
{ int counter;
int matrix_a_element;
int matrix_b_element;
int matrix_c_element;
public:
multiplier(mesh<int,THREAD_ROWS,THREAD_COLUMNS> *mesh); };
multiplier::multiplier(mesh<int,THREAD_ROWS,THREAD_COLUMNS> *mesh)
{ counter = (THREAD_ROWS-mesh->my_row()+1)%THREAD_ROWS;
while ((counter--) > 0)
mesh->send(RIGHT,mesh->receive(LEFT));
counter = (THREAD_COLUMNS-mesh->my_column()+1)%THREAD_COLUMNS;
while ((counter--) > 0)
mesh->send(DOWN,mesh->receive(UPPER));
counter = SQUARE_MATRIX_ORDER;
matrix_c_element = 0;
while ((counter--) > 0)
{ matrix_a_element = mesh->receive(LEFT);
matrix_b_element = mesh->receive(UPPER);
matrix_c_element += matrix_a_element*matrix_b_element;
mesh->send(RIGHT,matrix_a_element);
mesh->send(DOWN,matrix_b_element); };
mesh->send(EXTRA,matrix_c_element);
};
class mesh_manager: public thread,
public mesh<int,THREAD_ROWS,THREAD_COLUMNS>
{ int row;
int column;
public:
mesh_manager();
private:
void input_matrix(char name,mesh_boxes kind); };
mesh_manager::mesh_manager()
{ tout << NEW_LINE << "PARALLEL MATRIX-BY-MATRIX MULTIPLICATION";
input_matrix('A',LEFT);
input_matrix('B',UPPER);
for (row = 1;row <= THREAD_ROWS;row++)
for (column = 1;column <= THREAD_COLUMNS;column++)
new multiplier(this);
tout << NEW_LINE << "matrix C=A*B";
for (row = 1;row <= THREAD_ROWS;row++)
{ tout << NEW_LINE;
for (column = 1;column <= THREAD_COLUMNS;column++)
tout << receive(row,column,EXTRA); };
};
93
94
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
void mesh_manager::input_matrix(char name,mesh_boxes kind)
{ int matrix_element;
tout << NEW_LINE << "matrix " << name
<< " input (elements values from -100 to 100)";
for (int row = 1;row <= THREAD_ROWS;row++)
for (int column = 1;column <= THREAD_COLUMNS;column++)
{ do
{ tout << NEW_LINE << name << '(' << row << ',' << column << "):";
tin >> matrix_element; }
while ((matrix_element < -100) || (matrix_element > 100));
send(row,column,kind,matrix_element); };
};
initial::initial(int,char **)
{ new (2) mesh_manager(); };
Sadržaj izvorne datoteke opus08.cpp predstavlja potpun COLIBRY program.
U toku njegovog izvršavanja, sem inicijalne, nastane još pet niti.
Računanje svih elemenata matrice C, opisano u prethodnom programu, traje
koliko i računanje samo jednog od ovih elemenata, ako se svakoj od niti, zaduženih
za računanje po jednog elementa matrice C, dodeli poseban procesor. Ali, ako se
prethodni program izvršava na jednoprocesorskom računaru, tada je dužina
njegovog izvršavanja proporcionalna broju elemenata matrice C.
3.16 PARALELNO IZDVAJANJE KONTURE
Konturu lika, predstavljenog crnim tačkama na crno beloj slici (slika 3.16.1):
Slika 3.16.1 Lik
obrazuju crne tačke, u čijem susedstvu je bar jedna bela tačka (slika 3.16.2):
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
95
Slika 3.16.2 Kontura
Izdvajanje konture ovakvog lika obuhvata vezane provere boje svake tačke lika i
provere boja tačaka iz njenog susedstva. Pri tome se podrazumeva da su svakoj
tački p susedne tačke koje su iznad (up: u), iznad desno (up right: ur), iznad levo
(up left: ul), ispod (down: d), ispod desno (down right: dr), ispod levo (down left:
dl), desno (right: r) i levo (left: l) u odnosu na posmatranu tačku. Ovo važi i za
rubne tačke slike, uz napomenu da se za suprotne ivice slike smatra da su spojene.
Ako se pretpostavi da crnoj tački odgovara vrednost 1, a beloj vrednost 0, tada
proveru da li je tačka p konturna opisuje logički izraz:
c = p&(~u)|p&(~ur)|p&(~ul)|p&(~d)|p&(~dr)|p&(~dl)|p&(~r)|p&(~l);
Za razne tačke, provere njihovih boja su nezavisne, pa se mogu odvijati
paralelno. One, znači, mogu biti predmet aktivnosti raznih niti. Pri tome, svakoj od
ovih niti se dodeljuje po jedna tačka slike, da bi se utvrdilo da li ona pripada konturi
lika, a niti su raspoređene u prostoru kao i tačke koje su im dodeljene, znači
uvezane u matricu. Za susedne niti je važno da budu komunikaciono povezane, radi
razmena boja tačaka koje su im dodeljene. Dovoljno je da svaka nit bude
komunikaciono povezana sa nitima iznad, ispod, levo i desno od sebe, jer tada,
posredstvom svojih vertikalnih i horizontalnih suseda, ona može razmeniti boju
svoje tačke sa bojama tačaka dodeljenih njenim dijagonalnim susedima.
Konstruktor pixel opisuje aktivnost prethodno pomenutih niti. Svaka od njih
raspolaže bojom svoje tačke (my_pixel), a preuzima boju susednih tačaka
(foreign_pixel) i izračunava da li je njena tačka konturna (contour_pixel).
Stvaranje ovih niti, uvezanih u matricu, je u nadležnosti upravljačke niti, čiju
aktivnost opisuje konstruktor klase manager. Upravljačka nit šalje nitima uvezanim u
matricu tačke slike (picture) i preuzima i prikazuje konturu.
Izvorna datoteka opus09.cpp sadrži opis paralelnog izdvajanja konture. U
nastavku je naveden sadržaj ove datoteke.
96
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
# include <colibry.h>
# include <colib_io.h>
# include <mesh.h>
const int SQUARE_MATRIX_ORDER = 8;
const int THREAD_ROWS = SQUARE_MATRIX_ORDER;
const int THREAD_COLUMNS = SQUARE_MATRIX_ORDER;
class pixel: public thread
{ char foreign_pixel;
char contour_pixel;
public:
pixel(mesh<char,THREAD_ROWS,THREAD_COLUMNS> *mesh,char my_pixel); };
pixel::pixel(mesh<char,THREAD_ROWS,THREAD_COLUMNS> *mesh,char my_pixel)
{ mesh->send(DOWN,my_pixel);
contour_pixel = my_pixel&(~(foreign_pixel = mesh->receive(UPPER)));
mesh->send(LEFT,foreign_pixel);
contour_pixel |= my_pixel&(~(mesh->receive(RIGHT)));
mesh->send(RIGHT,foreign_pixel);
contour_pixel |= my_pixel&(~(mesh->receive(LEFT)));
mesh->send(UPPER,my_pixel);
contour_pixel |= my_pixel&(~(foreign_pixel = mesh->receive(DOWN)));
mesh->send(LEFT,foreign_pixel);
contour_pixel |= my_pixel&(~(mesh->receive(RIGHT)));
mesh->send(RIGHT,foreign_pixel);
contour_pixel |= my_pixel&(~(mesh->receive(LEFT)));
mesh->send(LEFT,my_pixel);
contour_pixel |= my_pixel&(~(mesh->receive(RIGHT)));
mesh->send(RIGHT,my_pixel);
contour_pixel |= my_pixel&(~(mesh->receive(LEFT)));
mesh->send(EXTRA,contour_pixel);
};
const char picture[THREAD_ROWS][THREAD_COLUMNS] = { {
{
{
{
{
{
{
{
};
0,0,1,1,1,0,0,0
0,0,1,1,1,0,0,0
1,1,1,1,1,1,1,0
1,1,1,1,1,1,1,0
1,1,1,1,1,1,1,0
0,0,1,1,1,0,0,0
0,0,1,1,1,0,0,0
0,0,0,0,0,0,0,0
class mesh_manager: public thread,
public mesh<char,THREAD_ROWS,THREAD_COLUMNS>
{ int row;
int column;
public:
mesh_manager(); };
},
},
},
},
},
},
},
}
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
97
mesh_manager::mesh_manager()
{ tout << NEW_LINE << "PARALLEL CONTOUR FINDING";
for (row = 1;row <= THREAD_ROWS;row++)
{ tout << NEW_LINE;
for (column = 1;column <= THREAD_COLUMNS;column++)
{ tout << ((picture[row-1][column-1] == 0) ? ' ' : '.');
new pixel(this,picture[row-1][column-1]); };
};
for (row = 1;row <= THREAD_ROWS;row++)
{ tout << NEW_LINE;
for (column = 1;column <= THREAD_COLUMNS;column++)
tout << ((receive(row,column,EXTRA) == 0) ? ' ' : '.'); };
};
initial::initial(int,char **)
{ new (2) mesh_manager(); };
Sadržaj izvorne datoteke opus09.cpp predstavlja potpun COLIBRY program.
U toku njegovog izvršavanja, sem inicijalne, nastane još 65 niti.
Izdvajanje konture, opisano u prethodnom programu, traje koliko i provera za
jednu tačku da li je konturna, ako se svakoj od niti, zaduženoj za po jednu ovakvu
proveru, dodeli poseban procesor. Ali, ako prethodni program izvršava
jednoprocesorski računar, tada je vreme, potrebno za izdvajanje konture,
proporcionalno broju tačaka slike.
Pristup, primenjen u prethodnom programu za izdvajanje konture, se može
primeniti i za čišćenje slike od posledica šuma, koji (na primer, pri
telekomunikacionom prenosu slike) izaziva izmenu boja pojedinih tačaka, pa se
javljaju crne tačke na beloj pozadini i obrnuto. Pri tome je potrebno izmeniti boju
svake tačke koja je okružena tačkama suprotne boje.
3.17 VEZIVANJE NITI U DRVO
Vezivanje niti u drvo je inspirisano potrebom prilagođavanja specifičnim
paralelnim algoritmima, kao što je, na primer sabiranje niza brojeva. Svaka nit iz
ovog niza ima iznad sebe, levo i desno od sebe i ispod sebe po dva pregradka za
poruke (slika 3.17.1).
98
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
pregradak10
pregradak11
pregradak21
pregradak13
pregradak23
pregradak31
nit2
nit1
nivo
pregradak12
pregradak22
pregradak30
pregradak40
pregradak14
pregradak15
pregradak24
pregradak25
pregradak41
rang
pregradak20
pregradak32
nit3
pregradak16
pregradak26
pregradak42
Slika 3.17.1 Niti uvezane u drvo
Pregradak iznad levo uspostavlja komunikacioni kanal od prethodnika iz
drveta, a pregradak iznad desno ka prethodnku iz drveta. Pregradak levo gore
uspostavlja komunikacioni kanal ka levom sledbeniku iz drveta, a pregradak levo
dole od levog sledbenika iz drveta. Slično, pregradak desno gore uspostavlja
komunikacioni kanal ka desnom sledbeniku iz drveta, a pregradak desno dole od
desnog sledbenika iz drveta. Pregradak ispod levo uspostavlja komunikacioni kanal
od okruženja, a pregradak ispod desno ka okruženju. Za nit na vrhu drveta
pregradak iznad levo uspostavlja komunikacioni kanal od okruženja, a pregradak
iznad desno ka okruženju. Slično, za svaku nit sa najnižeg nivoa iz drveta pregradak
levo gore uspostavlja komunikacioni kanal ka okruženju, kao i pregradak desno
gore. Pregraci levo dole i desno dole uspostavljaju komunikacioni kanal od
okruženja. Na ovaj način svaka nit može da razmenjuje poruke sa svojim
prethodnikom, sa svojim sledbenicima i sa okruženjem.
Templejt klasa tree omogućuje uspostavljanje komunikacionih kanala koji su
potrebni za uvezivanje niti u drvo. Njena dva parametra omogućuju definisanje tipa
poruke i broja nivoa u drvetu (broj nivoa je ograničen na 6). Elementi polja boxes1,
boxes2, extra1_boxes i extra2_boxes odgovaraju, respektivno, pregracima 1, 2, 3 i 4.
Konstruktor tree omogućuje registrovanje referentnog serijskog broja, u odnosu na
koji se određuju redni brojevi niti iz niza, a operacije my_level i my_rank omogućuju
dobijanje rednog broja nivoa i ranga u nivou niti iz drveta. Za označavanje niti u
drvetu koriste se redni brojevi: 1, 2, 3 i tako dalje. Operacije send i receive
omogućuje razmenu poruka. Javno dostupan par ovih operacija omogućuje nitima
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
99
iz drveta da označe izvorište/odredište poruke samo pomoću konstanti UPPER, LEFT,
RIGHT i EXTRA, a bez navođenja rednog broja koji se podrazumeva.
Izvorna datoteka tree.h sadrži definiciju templejt klase tree. U nastavku je
naveden sadržaj ove datoteke.
# include <box.h>
enum tree_levels { ONE = 1,
TWO = 3,
THREE = 7,
FOUR = 15,
FIVE = 31,
SIX = 63 };
enum tree_boxes { UPPER,LEFT,RIGHT,EXTRA };
template<class MESSAGE,tree_levels LEVELS>
class tree
{ unsigned long referent_sequencer;
message_box<MESSAGE> boxes1[LEVELS*2+1];
message_box<MESSAGE> boxes2[LEVELS*2+1];
message_box<MESSAGE> extra1_boxes[LEVELS];
message_box<MESSAGE> extra2_boxes[LEVELS];
protected:
tree();
int power2(int exponent);
void send(int level,int rank,tree_boxes kind,MESSAGE message);
MESSAGE receive(int level,int rank,tree_boxes kind);
public:
void send(tree_boxes kind,MESSAGE message);
MESSAGE receive(tree_boxes kind);
int my_level();
int my_rank(); };
template<class MESSAGE,tree_levels LEVELS>
tree<MESSAGE,LEVELS>::tree()
{ referent_sequencer = my_sequencer(); };
template<class MESSAGE,tree_levels LEVELS>
int tree<MESSAGE,LEVELS>::power2(int exponent)
{ int power = 1;
if (exponent > 0)
do
{ power *= 2; }
while ((--exponent) > 0);
return power;
};
100
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
template<class MESSAGE,tree_levels LEVELS>
void tree<MESSAGE,LEVELS>::send(int level,int rank,
tree_boxes kind,MESSAGE message)
{ message_box<MESSAGE> *destination;
switch (kind)
{ case UPPER : destination = &(boxes1[power2(level-1)+rank-2]);
break;
case LEFT : destination = &(boxes2[power2(level)+(rank-1)*2-1]);
break;
case RIGHT : destination = &(boxes2[power2(level)+(rank-1)*2]);
break;
case EXTRA : destination = &(extra1_boxes[power2(level-1)+rank-2]); };
destination->send(message);
};
template<class MESSAGE,tree_levels LEVELS>
MESSAGE tree<MESSAGE,LEVELS>::receive(int level,int rank,
tree_boxes kind)
{ message_box<MESSAGE> *source;
switch (kind)
{ case UPPER : source = &(boxes2[power2(level-1)+rank-2]);
break;
case LEFT : source = &(boxes1[power2(level)+(rank-1)*2-1]);
break;
case RIGHT : source = &(boxes1[power2(level)+(rank-1)*2]);
break;
case EXTRA : source = &(extra2_boxes[power2(level-1)+rank-2]); };
return source->receive();
};
template<class MESSAGE,tree_levels LEVELS>
void tree<MESSAGE,LEVELS>::send(tree_boxes kind,MESSAGE message)
{ int index = (int)(my_sequencer()-referent_sequencer);
message_box<MESSAGE> *destination;
switch (kind)
{ case UPPER : destination = &(boxes2[--index]);
break;
case LEFT : destination = &(boxes1[index*2-1]);
break;
case RIGHT : destination = &(boxes1[index*2]);
break;
case EXTRA : destination = &(extra2_boxes[--index]); };
destination->send(message);
};
template<class MESSAGE,tree_levels LEVELS>
MESSAGE tree<MESSAGE,LEVELS>::receive(tree_boxes kind)
{ int index = (int)(my_sequencer()-referent_sequencer);
message_box<MESSAGE> *source;
switch (kind)
{ case UPPER : source = &(boxes1[--index]);
break;
case LEFT : source = &(boxes2[index*2-1]);
break;
case RIGHT : source = &(boxes2[index*2]);
break;
case EXTRA : source = &(extra1_boxes[--index]); };
return source->receive();
};
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
101
template<class MESSAGE,tree_levels LEVELS>
int tree<MESSAGE,LEVELS>::my_level()
{ int index = (int)(my_sequencer()-referent_sequencer);
int level = 1;
while (index >= power2(level))
level++;
return level; };
template<class MESSAGE,tree_levels LEVELS>
int tree<MESSAGE,LEVELS,>::my_rank()
{ int index = (int)(my_sequencer()-referent_sequencer);
int level = 1;
while (index >= power2(level))
level++;
return (index-power2(level-1)+1); };
3.18 PARALELNO SABIRANJE BROJEVA
Niti uvezane u drvo omogućuju paralelno sabiranje niza brojeva. Svaka od
niti je zadužena da sabere dva broja, dobijena od sledbenika, i da prethodniku preda
zbir. Podrazumeva se da svaka nit sa najnižeg nivoa dobije po dva broja, a da se
ukupan zbir preuzima od niti sa najvišeg nivoa. Istovremeno mogu biti aktivne sve
niti koje su dobile svoje brojeve za sabiranje.
Konstruktor adder opisuje ponašanje niti, uvezanih u drvo. Njih stvara
upravljačka nit, čiju aktivnost opisuje konstruktor klase manager. Upravljačka nit još
preuzima brojeve za sabiranje, upućuje ih nitima sa najnižeg nivoa drveta, od niti sa
najvišeg nivoa drveta preuzima zbir i prikazuje ga na ekranu.
Izvorna datoteka opus10.cpp sadrži opis paralelnog sabiranja. U nastavku je
naveden sadržaj ove datoteke.
# include <colibry.h>
# include <colib_io.h>
# include <tree.h>
class adder: public thread
{public:
adder(tree<int,THREE> *tree); };
adder::adder(tree<int,THREE> *tree)
{ tree->send(UPPER,tree->receive(LEFT)+tree->receive(RIGHT)); };
class tree_manager: public thread,public tree<int,THREE>
{ int level;
int rank;
int number;
int counter;
public:
tree_manager(); };
102
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
tree_manager::tree_manager()
{ tout << NEW_LINE << "PARALLEL SUMMATION"
<< NEW_LINE << "input " << LEVELS+1 << " integers for summation"
<< " (integers values from 1 to 100)";
level = 1;
while (LEVELS >= power2(level))
level++;
counter = 1;
for (rank = 1;rank <= ((LEVELS+1)/2);rank++)
{ do
{ tout << NEW_LINE << counter << ". integer: ";
tin >> number; }
while ((number < 1) || (number > 100));
counter++;
send(level,rank,LEFT,number);
do
{ tout << NEW_LINE << counter << ". integer: ";
tin >> number; }
while ((number < 1) || (number > 100));
counter++;
send(level,rank,RIGHT,number);
};
for (counter = 1;counter <= LEVELS;counter++)
new adder(this);
tout << NEW_LINE << "total sum:
" << receive(1,1,UPPER);
};
initial::initial(int,char **)
{ new (2) tree_manager(); };
Dužina sabiranja, opisanog prethodnim programom, linearno zavisi od broja
nivoa u drvetu koji je potreban za sabiranje brojeva, ako se svakoj od niti iz drveta
dodeli poseban procesor. Ako se prethodni program izvršava na jednoprocesorskom
računaru, tada je dužina sabiranja proporcionalna broju sabiranih brojeva.
3.19 KOMUNIKACIONI KANAL KAPACITETA VIŠE PORUKA
Pravilan karakter saradnje niti opravdava korišćenje posebnog pregradka za
svaki smer razmene poruka između bilo koje dve komunikaciono povezane niti. Ali,
ako je saradnja niti sporadična, tada postoji neizvesnost posredstvom kog pregradka
dolazi poruka, pa je moguće da nit očekuje poruku iz jednog pregradka, a da poruka
u međuvremenu pristigne u drugi pregradak. Zato je bolje, u situaciji kao što je
prethodna, da svaka nit prima sve poruke posredstvom samo jednog pregradka. Pri
tome je važno da pregradak sadrži više odeljaka da bi mogao da primi sve poruke,
upućene niti kojoj je pregradak dodeljen. U suprotnom slučaju, neizbežna je mrtva
petlja, ako se svi pregraci napune, a sve niti nastave sa slanjem poruka.
Pregradke sa više odeljaka opisuje nova verzija templejt klase message_box.
Njeno polje slots sadrži SLOTS odeljaka. Svaki od njih prima po jednu poruku. Polja
first_empty_slot i first_full_slot ukazuju na prvi prazan, odnosno na prvi pun
odeljak. Njihove vrednosti se menjaju po modulo aritmetici, zbog ciklične
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
103
organizacije odeljaka. Polja not_full i not_empty određuju uslove, od čijeg
ispunjenja zavise aktivnosti niti primalaca i pošiljalaca poruka.
Pregraci sa više odeljaka se koriste kao i pregraci sa jednim odeljkom.
Izvorna datoteka slot_box.h sadrži definiciju nove verzije templejt klase
message_box. U nastavku je naveden sadržaj ove datoteke:
template<class MESSAGE,int SLOTS>
class message_box: public exclusive
{ MESSAGE slots[SLOTS];
unsigned int first_empty_slot;
unsigned int first_full_slot;
condition not_full;
condition not_empty;
public:
message_box();
void send(MESSAGE message);
MESSAGE receive(); };
template<class MESSAGE,int SLOTS>
message_box<MESSAGE,SLOTS>::message_box()
{ first_empty_slot = 0;
first_full_slot = 0; };
template<class MESSAGE,int SLOTS>
void message_box<MESSAGE,SLOTS>::send(MESSAGE message)
{ exclusive_block set_up(this);
if ((first_empty_slot-first_full_slot) == SLOTS)
not_full.await();
slots[first_empty_slot%SLOTS] = message;
++first_empty_slot;
not_empty.signal(); };
template<class MESSAGE,int SLOTS>
MESSAGE message_box<MESSAGE,SLOTS>::receive()
{ exclusive_block set_up(this);
if (first_empty_slot == first_full_slot)
not_empty.await();
unsigned int return_index = first_full_slot%SLOTS;
++first_full_slot;
not_full.signal();
return slots[return_index]; };
Podrazumeva se da je sadržaj datoteke slot_box.h obavezni deo izvornog
COLIBRY programa u kome se koriste pregraci sa više odeljaka.
U slučaju templejt klase slot_box.h sa više pregradaka, sinhronizacija može
biti ostvarena i pomoću semafora. Primer pregradka sa više odeljaka je podesan za
ilustraciju ostvarenja još jednog oblika uslovne sinhronizacije pomoću semafora. Za
sinhronizaciju pristupa odeljcima potrebna su tri semafora. Prva dva (not_full i
not_empty) su zadužena za uslovnu sinhronizaciju, odnosno da propuštaju onoliko
niti koliko ima praznih, odnosno punih odeljaka. Treći (mutex) semafor je zadužen
za međusobnu isključivost i dozvoljava da samo po jedna nit bude aktivna na
zauzimanju i oslobađanju svog odeljka. Početno stanje semafora not_full, koji
104
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
reguliše pristup praznim odeljcima, mora biti inicijalizovano na ukupan broj
odeljaka.
Izvorna datoteka slotsbox.h sadrži definiciju nove verzije templejt klase
message_box u kojoj je sinhronizacija ostvarena pomoću semafora. U nastavku je
naveden sadržaj ove datoteke:
# include <sem.h>
template<class MESSAGE,int SLOTS>
class message_box
{ MESSAGE slots[SLOTS];
unsigned int first_empty_slot;
unsigned int first_full_slot;
semaphore not_full;
semaphore not_empty;
semaphore mutex;
public:
message_box();
void send(MESSAGE message);
MESSAGE receive(); };
template<class MESSAGE,int SLOTS>
message_box<MESSAGE,SLOTS>::message_box(): not_full(SLOTS),not_empty(0)
{ first_empty_slot = 0;
first_full_slot = 0; };
template<class MESSAGE,int SLOTS>
void message_box<MESSAGE,SLOTS>::send(MESSAGE message)
{ not_full.wait();
mutex.wait();
slots[first_empty_slot%SLOTS] = message;
++first_empty_slot;
mutex.resume();
not_empty.resume(); };
template<class MESSAGE,int SLOTS>
MESSAGE message_box<MESSAGE,SLOTS>::receive()
{ MESSAGE message;
not_empty.wait();
mutex.wait();
message = slots[first_full_slot%SLOTS];
++first_full_slot;
mutex.resume();
not_full.resume();
return message; };
3.20 POTPUNO MEĐUSOBNO POVEZIVANJE NITI
Pojedini paralelni algoritmi zahtevaju mogućnost direktnog međusobnog
komuniciranja svih niti. U tom slučaju, potrebno je potpuno međusobno povezati
sve niti. To se ostvaruje ako se uz svaku nit veže pregradak sa više odeljaka i ako se
svim nitima dozvoli da šalju poruke u pregradke preostalih niti.
Templejt klasa any2any omogućuje uspostavljanje komunikacionih kanala
koji su potrebni za potpuno međusobno povezivanje niti. Njena tri parametra
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
105
omogućuju definisanje tipa poruke, broja pregradaka i broja odeljaka u svakom
pregradku. Pojedinim pregracima odgovaraju elementi polja boxes. Konstruktor
any2any omogućuje registrovanje referentnog serijskog broja, u odnosu na koji se
određuju redni brojevi niti iz niza, a operacija my_node omogućuje dobijanje rednog
broja pregradka koji je dodeljen niti. I pregraci i niti su označene istim rednim
brojevima: 1, 2, 3 i tako dalje. Operacije send i receive omogućuju razmenu poruka.
Izvorna datoteka any2any.h sadrži definiciju templejt klase any2any. U
nastavku je naveden sadržaj ove datoteke.
# include <slot_box.h>
template<class MESSAGE,int NODES,int SLOTS>
class any2any
{ unsigned long referent_sequencer;
message_box<MESSAGE,SLOTS> boxes[NODES+1];
protected:
any2any();
public:
void send(int node,MESSAGE message);
MESSAGE receive();
int my_node(); };
template<class MESSAGE,int NODES,int SLOTS>
any2any<MESSAGE,NODES,SLOTS>::any2any()
{ referent_sequencer = my_sequencer(); };
template<class MESSAGE,int NODES,int SLOTS>
void any2any<MESSAGE,NODES,SLOTS>::send(int node,MESSAGE message)
{ boxes[node].send(message); };
template<class MESSAGE,int NODES,int SLOTS>
MESSAGE any2any<MESSAGE,NODES,SLOTS>::receive()
{ return boxes[(int)(my_sequencer()-referent_sequencer)].receive(); };
template<class MESSAGE,int NODES,int SLOTS>
int any2any<MESSAGE,NODES,SLOTS>::my_node()
{ return (int)(my_sequencer()-referent_sequencer); };
3.21 PARALELNO ODREĐIVANJE NAJKRAĆIH
UDALJENOSTI PAROVA ČVOROVA USMERENOG
GRAFA
Usmereni graf se sastoji od numerisanih čvorova, povezanih usmerenim
spojnicama raznih dužina, koje su veće od 0 (slika 3.21.1).
106
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
1
1
1
2
3
1
1
10
0
6
1
3
4
12
1
1
7
1
6
1
5
Slika 3.21.1 Usmereni graf
Određivanje najkraće udaljenosti čvorova usmerenog grafa od zadanog čvora
se svodi na sabiranje dužina spojnica, koje vode od zadanog do pojedinih od
preostalih čvorova. Za svaki od preostalih čvorova udaljenost se određuje relativno
u odnosu na prethodnika, sabiranjem udaljenosti prethodnika i dužine spojnice koja
dolazi od prethodnika. Ako od zadanog do nekog čvora vodi k spojnica, odnosno,
ako se između ovih čvorova nalazi k-1 drugih čvorova, tada se udaljenost
posmatranog čvora od zadanog čvora može odrediti tek nakon k koraka, kada se za
svaki od k-1 čvorova, koji prethode pomenutom čvoru, odrede njihove udaljenosti
od zadanog čvora. Kada od zadanog do nekog od preostalih čvorova vodi više
puteva, koji se razlikuju bar po jednoj spojnici, tada udaljenost pomenutog čvora od
zadanog čvora, određena u nekom od koraka, može biti izmenjena u nekom od
narednih koraka, ako je put preko više spojnica kraći.
Određivanje najkraće udaljenosti čvorova koji ne leže na istom putu,
posmatrano od zadanog čvora, su međusobno nezavisna, pa se mogu odvijati
paralelno. Ona, znači, mogu biti predmet aktivnosti raznih niti. Pri tome se svakoj
od ovih niti dodeljuje po jedan čvor, a niti su raspoređene u prostoru kao čvorovi
koji su im dodeljeni. Za ove niti je važno da budu međusobno komunikaciono
povezane kao i pridruženi im čvorovi, da bi mogle međusobno sarađivati, radi
slanja sledbenicima njihovih udaljenosti i radi prijema svojih udaljenosti od
prethodnika. U opštem slučaju ovo podrazumeva potpuno međusobno povezivanje
niti.
Na početku određivanja udaljenosti, zadanom čvoru se dodeli udaljenost 0, a
svim ostalim čvorovima udaljenost TOP, koja simbolizuje beskonačnu udaljenost.
Saradnja niti se odvija u koracima. Svaki od njih obuhvata dve faze. U prvoj fazi,
svaka nit šalje svojim sledbenicima njihovu udaljenost, koja je jednaka ili
izračunatoj vrednosti, ili konstanti TOP, zavisno od toga da li je nit primila ili ne
svoju udaljenost od prethodnika. U drugoj fazi, svaka nit prima od svojih
prethodnika svoju udaljenost. Primljena udaljenost zamenjuje postojeću samo ako
je manja od nje. Nakon svakog koraka sledi provera da li se izmenila ijedna od
udaljenosti. Prvi korak, u kome izostanu izmene određivanih udaljenosti, označava
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
107
kraj postupka, jer su tada određene najkraće udaljenosti čvorova usmerenog grafa
od zadanog čvora, pa nove izmene nisu moguće.
Provera kraja postupka nastupa kada sve niti završe započeti korak. Prelazak
niti na novi korak ima smisla tek nakon što se u pomenutoj proveri ustanovi da
postupak nije završen. Zato aktivnosti niti moraju biti usklađene sa pomenutom
proverom. Ovakvo usklađivanje aktivnosti niti, u kome, pre prelaska ijedne niti na
novi korak, sve niti moraju završiti započeti korak, se naziva barijerna
sinhronizacija (barrier synchronization). Barijerna sinhronizacija se uspostavlja
zahvaljujući posredovanju upravljačke niti. Radi ostvarenja barijerne sinhronizacije,
svaka od niti, po završetku započetog koraka, šalje upravljačkoj niti, u okviru jedne
poruke, broj svoga čvora i svoju važeću udaljenost. Nastavak aktivnosti niti zavisi
od broja izmenjenih udaljenosti, koji je primila upravljačka nit. Ako je ovaj broj 0,
tada niti završavaju svoju aktivnost, jer je postupak završen.
U posmatranom primeru graf ima 8 čvorova i predstavljen je nizom graph.
Njegovih prvih 8 elemenata sadrži dužine spojnica usmerenih od prvog čvora prema
svim čvorovima, njegovih drugih 8 elemenata sadrži dužine spojnica usmerenih od
drugog čvora prema svim čvorovima i tako dalje.
Klasa node sadrži polje successors za smeštanje broja sledbenika čvora, polje
links za smeštanje udaljenosti čvora od svojih sledbenika, polje predecessors za
smeštanje broja prethodnika čvora, polje message za smeštanje poruke koja se šalje
sledbenicima čvora, i polje new_distance za smeštanje nove najmanje udaljenosti
čvora od zadatog čvora. Konstruktor ove klase opisuje ponašanje niti zaduženih za
pojedine čvorove. Parametar distance omogućuje saopštavanje niti početne
udaljenosti njenog čvora.
Klasa manager sadrži polje distances za smeštanje trenutnih udaljenosti svih
čvorova od zadatog čvora, kao i polje change_counter u kome se čuva broj izmena
udaljenosti u svakom koraku. Konstruktor ove klase opisuje ponašanje upravljačke
niti. Ona na početku, svim nitima zaduženim za pojedine čvorove, šalje broj
sledbenika, udaljenosti tih sledbenika i broj prethodnika, a zatim stvori pomenute
niti i prati izmene udaljenosti, da bi na osnovu toga utvrdila kada su udaljenosti
određene.
Inicijalna nit stvara upravljačku nit.
Izvorna datoteka opus11.cpp sadrži opis paralelnog određivanja najkraće
udaljenosti čvorova usmerenog grafa od zadanog čvora. U nastavku je naveden
sadržaj ove datoteke:
# include <colibry.h>
# include <colib_io.h>
# include <any2any.h>
struct package
{ int node;
int value; };
const int THREAD_NODES = 8;
108
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
const int BOX_SLOTS = THREAD_NODES*2;
const int TOP = 127;
class node: public thread
{ int successors;
int i;
package links[THREAD_NODES-1];
int predecessors;
package message;
int new_distance;
public:
node(any2any<package,THREAD_NODES,BOX_SLOTS> *any2any,int distance); };
node::node(any2any<package,THREAD_NODES,BOX_SLOTS> *any2any,int distance)
{ successors = any2any->receive().value;
for (i = 0;i < successors;i++)
links[i] = any2any->receive();
predecessors = any2any->receive().value;
message.node = any2any->my_node();
do
{ for (i = 0;i < successors;i++)
{ message.value = (distance < TOP) ? (distance+links[i].value) :
(TOP);
any2any->send(links[i].node,message); };
for (i = 0;i < predecessors;i++)
{ new_distance = any2any->receive().value;
if (distance > new_distance)
distance = new_distance; };
message.value = distance;
any2any->send(0,message);
}
while (any2any->receive().value > 0);
};
const int graph[THREAD_NODES][THREAD_NODES] = { {
{
{
{
{
{
{
{
};
0,1,0,0,0,0,0,12
0,0,1,0,0,0,0,10
0,0,0,1,0,0,6,0
0,0,0,0,1,3,0,0
0,0,0,0,0,1,0,0
0,0,0,0,0,0,1,0
0,0,1,0,0,0,0,1
1,0,0,0,0,0,0,0
},
},
},
},
},
},
},
}
class any2any_manager: public thread,
public any2any<package,THREAD_NODES,BOX_SLOTS>
{ int i;
int distances[THREAD_NODES];
int j;
package message;
int change_counter;
public:
any2any_manager(); };
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
any2any_manager::any2any_manager()
{ tout << NEW_LINE << "PARALLEL FINDING OF SHORTEST PATH IN "
<< "DIRECTED AND WEIGHTED GRAPH"
<< NEW_LINE << "WEIGHT MATRIX:";
for (i = 0;i < THREAD_NODES;i++)
{ distances[i] = 0;
message.node = 0;
message.value = 0;
tout << NEW_LINE;
for (j = 0;j < THREAD_NODES;j++)
{ tout << graph[i][j] << ' ';
if (graph[i][j] > 0)
message.value++; };
send(i+1,message);
for (j = 0;j < THREAD_NODES;j++)
if (graph[i][j] > 0)
{ message.node = j+1;
message.value = graph[i][j];
send(i+1,message); };
};
for (i = 0;i < THREAD_NODES;i++)
{ message.node = 0;
message.value = 0;
for (j = 0;j < THREAD_NODES;j++)
if (graph[j][i] > 0)
message.value++;
send(i+1,message);
new node(this,(i == 0) ? 0 : TOP); };
tout << NEW_LINE << "DISTANCES FROM NODE 1 TO NODES:" << NEW_LINE;
for (i = 1;i < THREAD_NODES;i++)
tout << i+1;
j = 1;
do
{ change_counter = 0;
for (i = 0;i < THREAD_NODES;i++)
{ message = receive();
if (distances[message.node-1] != message.value)
{ change_counter++;
distances[message.node-1] = message.value; };
};
if (change_counter != 0)
{ tout << NEW_LINE;
for (i = 1;i < THREAD_NODES;i++)
if (distances[i] == TOP)
tout << "
";
else
tout << distances[i];
tout << "
after step" << j++; };
message.node = 0;
message.value = change_counter;
for (i = 0;i < THREAD_NODES;i++)
send(i+1,message);
}
while (change_counter > 0);
tout << " (end)";
};
initial::initial(int,char **)
{ new (2) any2any_manager(); };
109
110
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Sadržaj izvorne datoteke opus11.cpp predstavlja potpun COLIBRY program.
U toku njegovog izvršavanja, sem inicijalne, nastane još 9 niti.
Paralelno određivanje najkraće udaljenosti čvorova usmerenog grafa od
zadanog čvora, opisano u prethodnom programu, je, u najgorem slučaju,
proporcionalno proizvodu najvećeg broja spojnica, koje dolaze u neki čvor i odlaze
iz njega, i najvećeg broja koraka, ako se svakoj od niti, zaduženih za određivanje
najkraćih udaljenosti pojedinih čvorova, dodeli poseban procesor. Ali, ako
prethodni program izvršava jednoprocesorski računar, tada je vreme, potrebno za
određivanje pomenutih udaljenosti, proporcionalno proizvodu najvećeg broja
spojnica, koje dolaze u neki čvor i odlaze iz njega, najvećeg broja koraka i ukupnog
broja čvorova.
Pristup primenjen za paralelno određivanje najkraće udaljenosti čvorova
usmerenog grafa od zadanog čvora predstavlja prirodnu osnovu za paralelno
određivanje najkraće udaljenosti svih parova čvorova usmerenog grafa. Potrebno je
samo uočiti da se određivanje najkraće udaljenosti svih parova čvorova usmerenog
grafa može razložiti na nezavisna određivanja najkraćih udaljenosti njegovih
čvorova od različitih zadanih čvorova.
3.22 PARALELNO IZRAČUNAVANJE POVRŠINE ISPOD
POLUKRUGA
Površina ispod grafa funkcije na intervalu, na kome je ona neprekidna,
nenegativna i neperiodična, se određuje približno, kao površina trapeza, čija visina
se podudara sa posmatranim intervalom, a čije baze su jednake vrednostima
funkcije u krajnjim tačkama ovog intervala (slika 3.22.1).
Slika 3.22.1 Površina na intervalu
Površina ovog trapeza predstavlja traženu površinu, ako je dovoljno bliska
sumi površina trapeza, konstruisanih na isti način iznad leve i desne polovine
posmatranog intervala (slika 3.22.2).
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
111
Slika 3.22.2 Površina na dva podintervala
Ako površina prvog trapeza nije bliska sumi površina druga dva trapeza, tada
se za svaku od polovina posmatranog intervala na prethodni način određuje
površina ispod grafa krive. Postupak se završava tek kada se za svaki od
podintervala odredi površina. Suma površina svih podintervala daje traženu
površinu.
Određivanja površina za pojedine podintervale su međusobno nezavisna, pa
mogu da budu predmet aktivnosti raznih niti. Svaka od njih, pri podeli
[pod]intervala na dve polovine, odlaže podatke o jednoj polovini u skladište, a
zatim nastavlja da se bavi drugom polovinom. Iz skladišta podatke o
[pod]intervalima preuzimaju besposlene niti. Ako se na početku određivanja tražene
površine u skladište smeste podaci o posmatranom intervalu, tada je skladište
prazno po uspešnom određivanju tražene površine, pa poslednja aktivna nit, nakon
neuspešnog preuzimanja podataka iz praznog skladišta, završava izvršavanje
programa. S druge strane, neuspešan pokušaj poslednje aktivne niti da odloži
podatke u puno skladište označava neuspešno određivanje tražene površine.
Rukovanje skladištem opisuje templejt klasa pool_type. Njena tri parametra
omogućuju saopštavanje tipa podataka koji se odlažu u skladište, broja niti koje
odlažu podatke u skladište i broja odeljaka za ove podatke po svakoj od niti. Ova
templejt klasa sadrži i polja not_full i not_empty, koja određuju uslove od čije
ispunjenost zavisi aktivnost niti koje odlažu, odnosno preuzimaju podatke.
Aktivnost svake od njih se zaustavlja: pri odlaganju podataka, ako je skladište puno,
i pri preuzimanju podataka, ako je skladište prazno. Ovakvo neselektivno
zaustavljanje aktivnosti uzrokuje mrtvu petlju, ako svaka od niti proba: da odloži
podatke u puno skladište, odnosno, da preuzme podatke iz praznog skladišta. Zato,
radi sprečavanja ovakve mrtve petlje, templejt klasa pool_type sadrži polja
not_full_await_count, odnosno not_empty_await_count, sa brojem niti zaustavljenih
pri odlaganju, odnosno, pri preuzimanju podataka. Na osnovu razlike vrednosti
parametra CLIENTS i polja not_full_await_count, odnosno i polja
not_empty_await_count, moguće je ustanoviti kada poslednja nit pokuša da odloži
podatke u puno skladište, odnosno da preuzme podatke iz praznog skladišta. Tada
112
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
se poslednjoj niti, umesto zaustavljanja njene aktivnosti, vraća vrednost FALSE, koja
označava neuspešno odlaganje, odnosno, neuspešno preuzimanje podataka.
Broj slobodnih odeljaka za prijem ovakvih podataka sadrži polje
free_slots_count templejt klase pool_type. Podrazumeva se da su ovi odeljci
kružno raspoređeni (tako da iza poslednjeg odeljka sledi prvi). Indekse prvog
praznog, odnosno prvog punog odeljka sadrže polja first_empty_slot, odnosno
first_full_slot.
Odlaganje podataka u skladište omogućuje operacija insert, a preuzimanje
podataka iz skladišta operacija extract.
Izvorna datoteka pool.h sadrži definiciju templejt klase pool_type. U
nastavku je naveden sadržaj ove izvorne datoteke:
template<class ITEM,int CLIENTS,int SLOTS_PER_CLIENT>
class pool_type: public exclusive
{ ITEM slots[CLIENTS*SLOTS_PER_CLIENT];
int free_slots_count;
int first_empty_slot;
int first_full_slot;
condition not_full;
condition not_empty;
int not_full_await_count;
int not_empty_await_count;
public:
pool_type();
bool insert(ITEM item);
bool extract(ITEM *item); };
template<class ITEM,int CLIENTS,int SLOTS_PER_CLIENT>
pool_type<ITEM,CLIENTS,SLOTS_PER_CLIENT>::pool_type()
{ free_slots_count = CLIENTS*SLOTS_PER_CLIENT;
first_empty_slot = 0;
first_full_slot = 0;
not_full_await_count = 0;
not_empty_await_count = 0; };
template<class ITEM,int CLIENTS,int SLOTS_PER_CLIENT>
bool pool_type<ITEM,CLIENTS,SLOTS_PER_CLIENT>::insert(ITEM item)
{ bool r = true;
exclusive_block set_up(this);
if ((free_slots_count == 0) && (not_full_await_count == (CLIENTS-1)))
r = false;
else
{ if (free_slots_count == 0)
{ not_full_await_count++;
not_full.await();
not_full_await_count--; };
slots[first_empty_slot] = item;
if ((++first_empty_slot) == CLIENTS*SLOTS_PER_CLIENT)
first_empty_slot = 0;
free_slots_count--;
not_empty.signal();
};
return r;
};
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
113
template<class ITEM,int CLIENTS,int SLOTS_PER_CLIENT>
bool pool_type<ITEM,CLIENTS,SLOTS_PER_CLIENT>::extract(ITEM *item)
{ bool r = true;
exclusive_block set_up(this);
if ((free_slots_count == (CLIENTS*SLOTS_PER_CLIENT)) &&
(not_empty_await_count == (CLIENTS-1)))
r = false;
else
{ if (free_slots_count == CLIENTS*SLOTS_PER_CLIENT)
{ not_empty_await_count++;
not_empty.await();
not_empty_await_count--; };
*item = slots[first_full_slot];
if ((++first_full_slot) == CLIENTS*SLOTS_PER_CLIENT)
first_full_slot = 0;
free_slots_count++;
not_full.signal();
};
return r;
};
U ovom primeru površina se određuje ispod polukruga koji je prikazan na
slici 3.22.3.
0
10
20
Slika 3.22.3 Polukrug
Funkcija semicircle omogućuje izračunavanje ordinata tačaka ovog
polukruga, a funkcija square_root omogućuje računanje kvadratnog korena. On se
uvek nalazi između nule i kvadrata, odnosno između nule i jedinice, pa se računa
primenom postupka polovljenja intervala. Funkcija trapezoid omogućuje računanje
površine trapeza. Tip segment opisuje podatke koji se odlažu u skladište, odnosno
preuzimaju, iz skladišta.
Konstruktor numerical_integrator opisuje određivanje površina za pojedine
[pod]intervale, koje se akumuliraju u polju total isključive promenljive area.
Konstruktor manager određuje aktivnost upravljačke niti, koja preuzima
podatke o preciznosti određivanja površine i o granicama posmatranog intervala,
priprema podatke o ovom intervalu, odlaže ih u skladište (isključiva promenjiva
pool) i na kraju, stvara niti koje određuju površine [pod]intervala.
114
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Podatak o preciznosti određivanja površine se koristi kod računanja
kvadratnog korena za određivanje kraja iteracije, ali i kod izračunavanja površine za
utvrđivanje da li je površina trapeza sa celog [pod]intervala dovoljno bliska
površinama trapeza sa polovina [pod]intervala.
Izvorna datoteka opus12.cpp sadrži opis paralelnog izračunavanja površine
ispod polukruga. U nastavku je naveden sadržaj ove datoteke.
# include <colibry.h>
# include <colib_io.h>
# include <pool.h>
double trapezoid(double height,double base1,double base2)
{ return (height*(base1+base2)/2.); };
double absolute(double a)
{ return ((a < 0) ? (-a) : (a)); };
double square_root(double square,double approximation)
{ double upper_limit;
double lower_limit = 0.;
double old_root;
double new_root;
if (square < 1.)
upper_limit = 1.;
else
upper_limit = square;
new_root = (upper_limit+lower_limit)/2.;
do
{ old_root = new_root;
if (new_root*new_root > square)
upper_limit = new_root;
else
lower_limit = new_root;
new_root = (upper_limit+lower_limit)/2.; }
while (absolute(new_root-old_root) > approximation);
return(new_root);
};
const double RADIUS = 10.;
double semicircle(double x,double approximation)
{ return(square_root(RADIUS*RADIUS-(x-RADIUS)*(x-RADIUS),approximation)); };
class area_type: public exclusive
{ double total;
public:
area_type();
void increment(double part);
void show(); };
area_type::area_type()
{ total = 0.; };
void area_type::increment(double part)
{ exclusive_block set_up(this);
total += part; };
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
115
void area_type::show()
{ exclusive_block set_up(this);
tout << NEW_LINE << "area =" << total; };
area_type area;
struct segment
{ double begin;
double end;
double begin_f;
double end_f;
double area; };
const int THREAD_CLIENTS = 5;
const int SLOTS_PER_THREAD_CLIENT = 5;
pool_type<segment,THREAD_CLIENTS,SLOTS_PER_THREAD_CLIENT> pool;
enum numerical_integrator_states { IDLE,BUSY };
class numerical_integrator: public thread
{ numerical_integrator_states state;
segment all;
segment right;
double left_area;
public:
numerical_integrator(double approximation); };
numerical_integrator::numerical_integrator(double approximation)
{ state = IDLE;
for (;;)
{ if (state == IDLE)
{ if (pool.extract(&all) == false)
{ area.show();
quit(); };
state = BUSY;
};
right.begin = (all.begin+all.end)/2.;
right.begin_f = semicircle(right.begin,approximation);
right.area = trapezoid(all.end-right.begin,right.begin_f,all.end_f);
left_area = trapezoid(right.begin-all.begin,all.begin_f,right.begin_f);
if (absolute(all.area-(left_area+right.area)) > approximation)
{ right.end = all.end;
right.end_f = all.end_f;
if (pool.insert(right) == false)
{ tout << NEW_LINE << "POOL FULL!";
quit(); };
all.end = right.begin;
all.end_f = right.begin_f;
all.area = left_area;
}
else
{ area.increment(all.area);
state = IDLE; };
};
};
116
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
class manager: public thread
{ segment all;
int counter;
double approximation;
public:
manager(); };
manager::manager()
{ tout << NEW_LINE << "PARALLEL NUMERICAL INTEGRATION " << NEW_LINE
<< "FOR SEMICIRCLE WITH RADIUS (" << RADIUS << ") "
<< "AND WITH CENTRE (" << RADIUS << ",0.)";
do
{ tout << NEW_LINE << "approximation (from 0.001 to 10.):";
tin >> approximation; }
while ((approximation < 0.001) || (approximation > 10.));
do
{ tout << NEW_LINE << "left limit (from 0. to " << RADIUS*2. << "):";
tin >> all.begin; }
while ((all.begin < 0.) || (all.begin > RADIUS*2.));
do
{ tout << NEW_LINE << "right limit (from " << all.begin
<< " to " << RADIUS*2. << "):";
tin >> all.end; }
while ((all.end < all.begin) || (all.end > RADIUS*2.));
all.begin_f = semicircle(all.begin,approximation);
all.end_f = semicircle(all.end,approximation);
all.area = trapezoid(all.end-all.begin,all.begin_f,all.end_f);
pool.insert(all);
for (counter = THREAD_CLIENTS;counter > 0;counter--)
new numerical_integrator(approximation); };
initial::initial(int,char **)
{ new (2) manager(); };
Sadržaj izvorne datoteke opus12.cpp predstavlja potpun COLIBRY program.
U toku njegovog izvršavanja, sem inicijalne, nastane još 6 niti.
U slučaju da se svakoj od niti, zaduženoj za računanje segmenata površine,
dodeli poseban procesor, tada je dužina određivanja ukupne površine, opisana
prethodnim programom, proporcionalna broju koji se nalazi između logaritma baze
2 broja podintervala i polovine broja podintervala. Pri tome se podrazumeva da broj
procesora nije manji od najvećeg broja podintervala koji nastanu u nekom koraku.
Ali, ako prethodni program izvršava jednoprocesorski računar, tada je dužina
računanja proporcionalna broju podintervala.
Pri određivanju površine ispod grafa funkcije nije neophodno korišćenje
skladišta, ako svaka nit, pri polovljenju [pod]intervala pokuša da stvori novu nit, sa
namerom da joj prepusti određivanje površine za jednu polovinu [pod]intervala.
Podrazumeva se da se pokušaji stvaranja nove niti ponavljaju (nakon kratkotrajnih
odlaganja aktivnosti) dok se u tome ne uspe. U ovom slučaju, kraj određivanja
tražene površine nastupi kada je suma dužina [pod]intervala, čije površine su
određene, jednaka dužini polaznog intervala, ili kada su aktivnosti svih niti
zaustavljene pri pokušaju stvaranja novih niti.
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
117
3.23 PITANJA
1. Zašto telo operacije interrupt_handler atomske klase day_time_type iz
datoteke day_time.cpp ne sadrži atomski region?
2. Od kog trenutka operacija interrupt_handler atomske klase day_time_type
iz datoteke day_time.cpp preuzima obradu prekida sa brojem vektora
prekida TIMER?
3. Zašto je potrebno da klasa message_box iz datoteke box.h bude templejt
klasa?
4. Da li operacije send i receive templejt klase message_box iz datoteke box.h
dozvoljavaju ugrožavanje konzistentnosti primeraka svoje klase (objasniti
na primeru)?
5. Da li operacije send i receive templejt klase message_box iz datoteke box.h
dozvoljavaju pojavu mrtve petlje (objasniti na primeru)?
6. U čemu je razlika između asinhrone i sinhrone razmene podataka
(objasniti na primeru)?
7. Kada se inicijalizuje propusnica isključive promenljive box iz datoteke
opus02.cpp?
8. U kom redosledu se aktiviraju niti user i server iz datoteke opus02.cpp?
9. U koje liste se uvezuju deskriptori niti user i server iz datoteke
opus02.cpp?
10. Kada se izvršava operacija exception klasa server1 i server2 iz datoteke
opus03.cpp?
11. U čemu je razlika između semafora i isključivih klasa?
12. Da li su operacije send i receive templejt klase message_box iz datoteke
sembox.h međusobno isključive?
13. U koje liste se uvezuju deskriptori niti koje pozivaju operacije send i
receive templejt klase message_box iz datoteke sembox.h?
14. Zašto nema isključivog regiona u telu operacije set_referent_sequencer
isključive klase dining_room_type iz datoteke opus04.h?
15. Zašto nema isključivog regiona u telu operacije show isključive klase
dining_room_type iz datoteke opus04.h?
16. Zašto se u operaciji from_table isključive klase dining_room_type iz
datoteke opus04.h dva puta poziva operacija signal?
17. Zašto konstruktor transaction istoimene klase iz datoteke opus05.h ne
sadrži pozive funkcija ton i toff kao konstruktor klase auditor?
18. Šta sadrži lista uslova access_q isključive promenljive file iz datoteke
opus05.h?
19. Zašto su potrebne dve liste uslova q isključive promenljive forwards iz
datoteke opus06.h?
20. Zašto telo funkcije read_request iz datoteke opus06.h sadrži dva isključiva
regiona?
118
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
21. Kada se aktivira nit koju opisuje klasa quit_caller iz datoteke opus06.h?
22. Da li se templejt klasa array iz datoteke array.h može osloniti na sinhronu
razmenu poruka?
23. Da li se templejt klasa mesh iz datoteke mesh.h može osloniti na sinhronu
razmenu poruka?
24. Da li se templejt klasa tree iz datoteke tree.h može osloniti na sinhronu
razmenu poruka?
25. Zašto isključivi regioni, obrazovani pomoću operacija semafora mutex u
telima operacija send i receive templejt klase message_box iz datoteke
slotsbox.h, ne mogu da sadrže pozive operacija wait semafora not_full ili
not_empty, radi zaustavljanja aktivnosti niti? Zašto isključivi regioni iz
operacija isključivih klasa mogu da sadrže pozive operacija await
condition promenljivih?
26. Da li se templejt klasa any2any iz datoteke any2any.h može osloniti na
sinhronu razmenu poruka?
27. Da li telo operacije show isključive klase area_type iz datoteke opus11.h
mora da obrazuje isključivi region?
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
119
4. IZVEDBA KONKURENTNE BIBLIOTEKE
COLIBRY
4.1 COLIBRY IZVRŠILAC
Uloga COLIBRY izvršioca se suštinski ne razlikuje od uloge operativnog
sistema. Funkcionalna sličnost COLIBRY izvršioca sa operativnim sistemom ima za
posledicu sličnost njihovih izvedbi. Znači, struktura COLIBRY izvršioca se može
predstaviti pomoću istih slojeva kao i struktura operativnog sistema. Ključna razlika
je da COLIBRY izvršilac ne sadrži modul za rukovanje datotekama, jer ne podržava
pojam datoteke. Odnos strukture operativnog sistema i strukture COLIBRY
izvršioca je prikazan na slici 4.1.1.
modul za rukovanje procesima
failure.cpp
thread.cpp
modul za rukovanje datotekama
modul za rukovanje radnom memory.cpp
memorijom
drivers.cpp
modul za rukovanje kontrolerima
modul za rukovanje procesorom
atomic.cpp
e_block.cpp
exclusiv.cpp
kernel.cpp
a_block.cpp
ready.cpp
tag_list.cpp
descript.cpp
exclude.cpp
list.cpp
limited.cpp
bool.cpp
Slika 4.1.1. Slojevi COLIBRY izvršioca
U prvoj koloni sa slike 4.1.1. su navedeni moduli operativnog sistema, a u
drugoj koloni se nalaze imena izvornih datoteka sa odgovarajućim modulima
COLIBRY izvršioca. Tako modulu za rukovanje procesima odgovaraju datoteke
failure.cpp i thread.cpp, modulu za rukovanje radnom memorijom odgovara
datoteka memory.cpp, modulu za rukovanje kontrolerima odgovaraju datoteke
drivers.cpp i atomic.cpp, a modulu za rukovanje procesorom odgovaraju datoteke
e_block.cpp, exclusiv.cpp, kernel.cpp, a_block.cpp, ready.cpp, tag_list.cpp,
descript.cpp, exclude.cpp, list.cpp, limited.cpp i bool.cpp. U nastavku se opisuju
120
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
moduli COLIBRY izvršioca, sadržani u ovim datotekama i to u redosledu odozdo na
gore.
BOOL.CPP
Izvorna datoteka bool.cpp sadrži definiciju logičkog tipa bool i definicije
konstanti false i true, koje reprezentuju vrednosti logičkog tipa. Ove definicije su
uvedene, jer kompajler, korišćen za razvoj COLIBRY izvršioca, ne podržava logički
tip. U nastavku je naveden sadržaj datoteke bool.cpp:
typedef int bool;
const bool false = 0;
const bool true = 1;
LIMITED.CPP
Izvorna datoteka limited.cpp sadrži definiciju klase limited koja, pomoću
polja down i up, omogućuje zadavanje dozvoljenog dijapazona za neki skup
celobrojnih vrednosti, kao što su prioriteti niti, na primer. Operacije ove klase
omogućuju konverziju (conversion) celog broja u neku od vrednosti iz dozvoljenog
dijapazona, kao i proveru (in_range) da li se celi broj nalazi unutar dozvoljenog
dijapazona. U ovoj datoteci su definisane konstanta TOP_PRIORITY, koja određuje
najviši korisnički prioritet, i konstanta SYSTEM_PRIORITY, koja određuje najviši
sistemski prioritet, a predviđena je samo za isključive promenljive COLIBRY
izvršioca. Datoteka limited.cpp sadrži i definiciju promenljive priorities, koja
određuje dozvoljeni dijapazon korisničkih proriteta.
U nastavku je naveden sadržaj ove datoteke:
class limited
{ int down;
int up;
limited(const limited&);
limited& operator=(const limited&);
public:
limited(int low,int high);
int conversion(int number) const;
bool in_range(int number) const; };
limited::limited(int low,int high)
{ down = low;
up = high; };
int limited::conversion(int number) const
{ if (number < down)
number = down;
else
if (number > up)
number = up;
return number; };
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
121
bool limited::in_range(int number) const
{ return ((number >= down) && (number <= up)); };
const int TOP_PRIORITY = 63;
const int SYSTEM_PRIORITY = TOP_PRIORITY+1;
limited priorities(1,TOP_PRIORITY);
LIST.CPP
Izvorna datoteka list.cpp sadrži definiciju klase list_link koja omogućuje
obrazovanje dvosmernih listi pomoću polja left i right. Operacije ove klase
omogućuju inicijalizaciju dvosmerne liste (initiate), uvezivanje novog elementa
na kraj liste (insert), izvezivanje elementa sa početka liste (extract), proveru da li
u listi ima uvezanih elemenata (not_empty), odnosno da li je lista prazna (empty).
Za deljene promenljive, nastale na osnovu klase list_link se podrazumeva
da je na svim mestima njihovog korišćenja na odgovarajući način zaštićena njihova
konzistentnost.
U nastavku je naveden sadržaj datoteke list.cpp:
class list_link
{ list_link *left;
list_link *right;
list_link(const list_link&);
list_link& operator=(const list_link&);
public:
list_link();
void initiate();
void insert(list_link *link);
list_link *extract();
bool not_empty() const;
bool empty() const;
friend class tag_list_link; };
list_link::list_link()
{ right = this;
left = this; };
void list_link::initiate()
{ right = this;
left = this; };
void list_link::insert(list_link *link)
{ link->left = left;
link->right = this;
left->right = link;
left = link; };
list_link *list_link::extract()
{ list_link *p = right;
right->right->left = this;
right = right->right;
return p; };
122
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
bool list_link::not_empty() const
{ return (this != right); };
bool list_link::empty() const
{ return (this == right); };
Na slici 4.1.1 su prikazani uzastopni rezultati poziva operacije insert objekta
klase list_link.
this
objekat klase list_link pre
poziva operacije insert
left
right
this
link
left
right
left
right
this
left
right
objekat klase list_link nakon 1.
poziva operacije insert
link
left
right
left
right
objekat klase list_link nakon 2.
poziva operacije insert
Slika 4.1.1 Rezultati poziva operacije insert objekta klase list_link
EXCLUDE.CPP
Izvorna datoteka exclude.cpp sadrži definiciju klase exclude. Ona predstavlja
baznu klasu za hijerarhiju klasa iz koje se izvode isključive klase. Klasa exclude
nasleđuje klasu list_link, da bi omogućila stvaranje ulazne liste isključive
promenljive. Polje free klase exclude čuva stanje propusnice, vezane za
odgovarajuću isključivu promenljivu. Prioritet isključive promenljive se čuva u
polju temporary_priority, dok polje thread_priority sadrži prioritet niti koja
privremeno koristi prioritet isključive promenljive. Polje previous omogućuje
uvezivanje deljenih promenljivih u listu niti koja je dobila propusnice za ulazak u
njihove isključive regione. Deljene promenljive se uvezuju u ovu listu u redosledu
dobijanja njihovih propusnica, a izvezuju iz nje u obrnutom redosledu, jer se u
obrnutom redosledu vraćaju njihove propusnice. Oko polja signalled se obrazuje
lista ispunjenih uslova deljene promenljive.
Operacije klase exclude omogućuju (1) proveru da li je propusnica deljene
promenljve zauzeta (not_free), (2) zauzimanje ove propusnice (take) i (3) njeno
oslobađanje (release). Pozivi operacije provere da li je propusnica zauzeta i
operacije zauzimanja propusnice moraju da budu u istom atomskom regionu, inače
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
123
se može desiti da više niti, jedna za drugom, proverom ustanovi da je ista
propusnica slobodna i da zatim, jedna za drugom, zauzme istu propusnicu. U
nastavku je naveden sadržaj datoteke exclude.cpp:
class exclude: public list_link
{ bool free;
int temporary_priority;
int thread_priority;
exclude *previous;
list_link signalled;
public:
exclude(int priority = 1);
private:
bool not_free() const;
void take();
void release();
friend class kernel_type;
friend class mutex_type; };
exclude::exclude(int priority): list_link()
{ free = true;
temporary_priority = priorities.conversion(priority);
thread_priority = 0;
previous = 0; };
bool exclude::not_free() const
{ return (free == false); };
void exclude::take()
{ free = false; };
void exclude::release()
{ free = true; };
DESCRIPT.CPP
Izvorna datoteka descript.cpp sadrži definiciju klase descriptor koja
omogućuje rukovanje deskriptorima niti.
Za deskriptore niti se ne zauzima unapred prostor. Umesto toga, unapred se
zauzima prostor za pokazivače deskritpora niti. Tako se zauzme tabela od 101
pokazivača deskriptora niti umesto tabele od 101 deskriptora niti. Time se
racionalnije koristi radna memorija, koja je skromne veličine za small memory
model. Broj elemenata tabele pokazivača deskriptora određuje najveći mogući broj
istovremeno postojećih niti.
Deskriptor niti opisuje klasa descriptor, a pokazivač deskriptora niti opisuje
klasa co_descriptor. Definicije ovih klasa sadrži izvorna datoteka descript.cpp.
Tim definicijama prethode:
1. definicija tipa stack_item, koja određuje tip elemenata steka,
2.
definicija tipa tag_type, koja određuje tip privezka deskriptora (korišćenog u
klasama tag_event i tag_condition),
3. deklaracija klase co_descriptor i
4. deklaracija klase thread.
124
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Klasa descriptor nasleđuje klasu list_link, da bi bilo moguće deskriptore
niti uvezivati u liste. Ona sadrži polje interrupt_stack_top, koje pokazuje vrh steka
obrađivača prekida. Pored ovog sistemskog steka, namenjenog za obradu prekida,
svaka nit poseduje i sopstveni stek, namenjen za njenu aktivnost. Polje stack_top
sadrži pokazivač vrha steka niti. Stanje niti se čuva u polju state, njen prioritet u
polju priority, a njen serijski broj u polju sequencer. Polje co_descriptor_link je
namenjeno za adresu pokazivača deskritpora niti. Na taj način deskriptor niti sadrži
adresu sopstvenog pokazivača, što omogućuje oslobađanje pokazivača deskriptora
niti prilikom njenog uništavanja. Polje last sadrži adresu deljene promenljive, čija
propusnica je dobijena nakon svih ostalih propusnica. Ovo je potrebno kada nit
istovremeno poseduje više propusnica. Podatak da je niti upućen alarm se čuva u
polju alarming, a pokazivač na nit se nalazi u polju thread_pointer. Ovaj pokazivač
se koristi u obradi izuzetaka za pozivanje operacije exception. Polje tag je
namenjeno za smeštanje privezka deskriptora niti, kada se privezak dodeli
deskriptoru niti.
Operacija initiate klase descriptor omogućuje inicijalizaciju deskriptora
prilikom stvaranja niti.
Klasa co_descriptor nasleđuje klasu list_link, da bi bilo moguće slobodne
pokazivače deskriptora niti uvezivati u listu. Polje descriptor_link klase
co_descriptor sadrži pokazivač deskriptora niti. Operacija clear ove klase
omogućuje oslobađanje pokazivača deskriptora niti prilikom njenog uništavanja.
Za deljene promenljive, nastale na osnovu klasa descriptor i co_descriptor
se podrazumeva da je na svim mestima njihovog korišćenja na odgovarajući način
zaštićena njihova konzistentnost.
U nastavku je naveden sadržaj datoteke descript.cpp:
typedef int stack_item;
typedef unsigned long tag_type;
enum thread_states { READY,ACTIVE,WAITING };
class co_descriptor;
class thread;
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
125
class descriptor: public list_link
{ stack_item *interrupt_stack_top;
stack_item *stack_top;
thread_states state;
int priority;
unsigned long sequencer;
co_descriptor *co_descriptor_link;
exclude *last;
bool alarming;
thread *thread_pointer;
tag_type tag;
public:
descriptor();
private:
void initiate(stack_item *interrupt_stack_top,stack_item *stack_top = 0,
int priority = 0,unsigned long sequencer = 0,
co_descriptor *co_descriptor_link = 0);
friend class tag_list_link;
friend class ready_list;
friend class kernel_type;
friend class thread_identity;
friend class thread;
friend unsigned long my_sequencer();
friend bool alarmed();
friend stack_item *find_interrupt_stack_top();
friend bool inside_exclusion();
friend void destroy(); };
descriptor::descriptor(): list_link()
{ };
void descriptor::initiate(stack_item *interrupt_stack_top,
stack_item *stack_top,int priority,
unsigned long sequencer,
co_descriptor *co_descriptor_link)
{ list_link::initiate();
descriptor::interrupt_stack_top = interrupt_stack_top;
descriptor::stack_top = stack_top;
descriptor::state = ACTIVE;
descriptor::priority = priority;
descriptor::sequencer = sequencer;
descriptor::co_descriptor_link = co_descriptor_link;
descriptor::last = 0;
descriptor::alarming = false;
descriptor::tag = 0; };
class co_descriptor: public list_link
{ descriptor *descriptor_link;
public:
co_descriptor();
void clear();
friend class thread_identity;
friend class thread; };
co_descriptor::co_descriptor(): list_link()
{ };
void co_descriptor::clear()
{ descriptor_link = 0; };
126
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
TAG_LIST.CPP
Izvorna datoteka tag_list.cpp sadrži definiciju klase tag_list_link. Ova
klasa nasleđuje klasu list_link, dodajući joj operacije first, last i succ,
namenjene za pozicioniranje na tačku uvezivanja u listu, kao i operaciju attach_tag,
za dodelu privezka (deskriptoru niti). Pozicija tačke uvezivanja u listu se registuje u
polju position klase tag_list_link.
Za deljene promenljive, nastale na osnovu klase tag_list_link se
podrazumeva da je na svim mestima njihovog korišćenja na odgovarajući način
zaštićena njihova konzistentnost.
U nastavku je naveden sadržaj datoteke tag_list.cpp:
class tag_list_link: public list_link
{ tag_list_link(const tag_list_link&);
tag_list_link& operator=(const tag_list_link&);
protected:
list_link *position;
public:
tag_list_link();
bool first(tag_type *t = 0);
bool last(tag_type *t = 0);
bool succ(tag_type *t = 0);
bool attach_tag(tag_type t); };
tag_list_link::tag_list_link(): list_link()
{ position = this; }
bool tag_list_link::first(tag_type *t)
{ bool r = false;
if (not_empty())
{ position = right;
if (t != 0)
*t = ((descriptor *)position)->tag;
r = true; };
return(r);
};
bool tag_list_link::last(tag_type *t)
{ bool r = false;
if (not_empty())
{ position = left;
if (t != 0)
*t = ((descriptor *)position)->tag;
r = true; };
return(r);
};
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
127
bool tag_list_link::succ(tag_type *t)
{ bool r = false;
if (position != this)
{ position = position->right;
if (position != this)
{ if (t != 0)
*t = ((descriptor *)position)->tag;
r = true; };
};
return(r);
};
bool tag_list_link::attach_tag(tag_type t)
{ bool r = false;
if (position != this)
{ ((descriptor *)position)->tag = t;
r = true; };
return(r);
};
READY.CPP
Izvorna datoteka ready.cpp sadrži definiciju klase ready_list koja omogućuje
rukovanje spremnim nitima.
Ispunjenje zahteva da je uvek aktivna najprioritetnija nit se temelji na
pretpostavci da je uvek moguće (brzo) pronaći najprioritetniju nit među spremnim
nitima. Radi toga, svakom od prioriteta niti se dodeljuje posebna spremna lista i
podrazumeva se da se deskriptor spremne niti uvek uvezuje na kraj spremne liste
koja odgovara prioritetu dotične niti. Takođe se podrazumeva da se deskriptor
spremne niti uvek izvezuje sa početka spremne liste. Na ovaj način spremne niti
istog prioriteta se uvek aktiviraju u redosledu u kome su postajale spremne.
Sve spremne liste zajedno formiraju multi-listu. Rukovanje ovom multilistom obuhvata:
1. uvezivenje deskriptora niti na kraj odgovarajuće spremne liste,
2. izvezivanje deskriptora niti sa početka najprioritetnije neprazne spremne
liste,
3. dobijanje prioriteta najprioritetnije neprazne spremne liste i
4. poređenje prioriteta najprioritetnije neprazne spremne liste sa prioritetom
zadane niti.
Prethodno opisano rukovanje multi-listom je podržano operacijama insert, extract,
highest i higher_than klase ready_list. Polje ready ove klase uvodi multi-listu kao
niz spremnih listi. To znači da se deskriptor spremne niti uvezuje na kraj spremne
liste koju direktno indeksira prioritet ove niti. Međutim, za operaciju izvezivanja
deskriptora iz najprioritetnije neprazne spremne liste, potrebno je prvo pronaći
najprioritetniju nepraznu spremnu listu. Pregledanje više spremnih listi, od
najprioritetnije ka manje spremnim, radi pronalaženja najprioritetnije neprazne
spremne liste, se može izbeći ako se uvede stanje multi-liste. U ovom stanju svaka
spremna lista multi-liste je reprezentovana jednim bitom, tako da značajniji biti
odgovaraju prioritenijim spremnim listama. Podrazumeva se da je bit stanja multi-
128
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
liste postavljen samo ako je njemu odgovarajuća spremna lista neprazna. Uvedeno
stanje multi-liste se može iskoristiti za indeksiranje tabele index. Ova tabela je
inicijalizovana tako da njen element, koga indeksira neko stanje multi-liste, sadrži
prioritet najprioritenije neprazne spremne liste koji odgovara pomenutom stanju
multi-liste. Tabela index je potrebna samo ako procesor direktno ne omogućuje
dobijanje rednog broja najznačajnijeg postavljenog bita date reči, kao što je reč sa
stanjem multi-liste. Inicijalizacija ove tabele, za osam bitno stanje multi-liste, je
prikazana na slici 4.1.2. Svakom od prikazanih elemenata prethodi osam bitno
stanje multi-liste koje ga indeksira. Elementi koji nisu prikazani sadrže vrednosti
kao i njihovi prikazani prethodnik i sledbenik.
00000000
00000001
00000010
00000011
00000100
...
00000111
00001000
...
00001111
00010000
...
00011111
00100000
...
00111111
01000000
...
01111111
10000000
...
11111111
0
1
2
2
3
...
3
4
...
4
5
...
5
6
...
6
7
...
7
8
...
8
Slika 4.1.2 Tabela index
Osam bitno stanje multi-liste podržava 9 prioriteta, odnosno 9 spremnih listi,
i zahteva da tabela index ima 256 elemenata. Nulto stanje multi-liste odgovara
slučaju u kome su prazne spremne liste sa prioritetima od 1 do 8 i ono indeksira
posebnu nultu spremnu listu, sa prioritetom 0. Ovakav slučaj se javlja kada nema
spremnih korisničkih niti. Da bi se i u ovakvom slučaju procesor zaposlio, uvodi se
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
129
posebna nulta nit, sa prioritetom 0. Kada je nulta nit u stanju spremna, tada je njen
deskriptor uvezan u nultu spremnu listu. Nulta nit može biti još samo u stanju
aktivna. Ona u to stanje prelazi kada ne postoji neka druga nit koja može da zaposli
procesor.
Za 17 prioriteta multi-lista bi trebalo da ima šesnaest bitno stanje, a tabela
index bi tada sadržala 64536 elemenata, što je neprihvatljiva veličina za small
memory model. Ali veći broj prioriteta, na primer 65 se može ostvariti i bez
povećanja broja elemenata tabele index, ako se 65 spremnih listi multi-liste
razdvoje u devet grupa. Od ovih devet grupa, nulta grupa ima samo nultu spremnu
listu (sa prioritetom 0), dok svaka od preostalih nenultih osam grupa ima po osam
spremnih listi (sa prioritetima većim od 0). U ovom slučaju svaka od grupa ima
grupno stanje (groups), a multi-lista zbirno stanje (joint). Svaki bit iz zbirnog stanja
reprezentuje jednu od nenultih grupa i postavljen je samo ako je postavljen bar
jedan bit iz grupnog stanja odgovarajuće nenulte grupe. Podrazumeva se da
značajniji biti zbirnog stanja odgovaraju prioritetnijim nenultim grupama. Iz
prethodnog sledi da zbirno stanje ukazuje na najprioritetniju nepraznu nenultu
grupu, a grupno stanje na najprioritetniju nepraznu spremnu listu iz dotične grupe.
Prema tome, zbirno stanje indeksira tabelu index, da bi se dobio indeks elementa
niza groups. Tako dobijeno grupno stanje indeksira opet istu tabelu da bi se dobio
relativni indeks (u okviru grupe) za spremnu listu. Dodavanjem odgovarajuće
konstante (iz elementa niza base, koji ima isti indeks kao i element niza groups)
dobije se apsolutni indeks spremne liste (odnosno indeks elementa niza ready).
Pomenuti apsolutni indeks je jednak prioritetu spremne liste.
Uvezivanje deskriptora niti u odgovarajuću spremnu listu zahteva
postavljanje odgovarajućeg bita i u zbirnom i u grupnom stanju. To omogućuju
nizovi joint_mask i group_mask, respektivno. Ovi nizovi, takođe, omogućuju
obaranje bita u zbirnom i grupnom stanju kada izvezivanje deskriptora niti ostavlja
praznu spremnu listu, odnosno praznu njenu grupu.
Datoteka ready.cpp sadrži definiciju promenljive ready. Ova promenljiva
uvodi multi-listu za potrebe COLIBRY izvršioca. Pošto je ovo deljena promenljiva,
jer joj posredno pristupaju razne niti, neophodno je na svim mestima njenog
korišćenja na odgovarajući način zaštititi njenu konzistentnost.
U nastavku je naveden sadržaj datoteke ready.cpp:
const int NUMBER_IN_GROUP = 8;
const int NUMBER_OF_GROUPS = 8;
const int NUMBER_OF_STATES = 256;
130
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
class ready_list
{ unsigned char joint;
unsigned char groups[NUMBER_OF_GROUPS+1];
unsigned char joint_mask[SYSTEM_PRIORITY+1];
unsigned char group_mask[SYSTEM_PRIORITY+1];
unsigned char index[NUMBER_OF_STATES];
list_link ready[SYSTEM_PRIORITY+1];
unsigned char base[NUMBER_OF_GROUPS+1];
ready_list(const ready_list&);
ready_list& operator=(const ready_list&);
public:
ready_list();
int highest() const;
void insert(descriptor *d);
descriptor *extract();
bool higher_than(descriptor *d) const; };
ready_list::ready_list()
{ joint = 0;
groups[0] = 0;
joint_mask[0] = 0;
group_mask[0] = 0;
index[0] = 0;
base[0] = 0;
int i = 1;
int j = 1;
int index_exponent = 1;
int base_value = 0;
for (int m = 1; m <= NUMBER_OF_GROUPS; m++)
{ groups[m] = 0;
for (int n = 1; n <= index_exponent; n++)
index[i++] = m;
int group_exponent = 1;
for (int k = 1; k <= NUMBER_IN_GROUP; k++)
{ joint_mask[j] = index_exponent;
group_mask[j++] = group_exponent;
group_exponent *= 2; };
base[m] = base_value;
base_value += 8;
index_exponent *= 2;
};
};
int ready_list::highest() const
{ return (base[index[joint]]+index[groups[index[joint]]]); };
void ready_list::insert(descriptor *d)
{ d->state = READY;
joint |= joint_mask[d->priority];
groups[index[joint_mask[d->priority]]] |= group_mask[d->priority];
ready[d->priority].insert(d); };
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
131
descriptor *ready_list::extract()
{ descriptor *d = (descriptor *)ready[highest()].extract();
if (ready[d->priority].empty())
if ((groups[index[joint]] &= ~(group_mask[d->priority])) == 0)
joint &= ~(joint_mask[d->priority]);
d->state = ACTIVE;
return d; };
bool ready_list::higher_than(descriptor *d) const
{ return (highest() > d->priority); };
ready_list ready;
A_BLOCK.CPP
Izvorna datoteka a_block.cpp sadrži definiciju klase atomic_block. Ova klasa
omogućuje
stvaranje
atomskih
regiona.
Njene
privatne
operacije
disable_interrupts i restore_interrupts realizuju, respektivno, ulazni i izlazni
atomski protokol. Prva od ovih operacija u polju flags sačuva stanje
o[ne]mogućenosti prekida, da bi druga od njih mogla to stanje da vrati natrag. U
ovim operacijama se koriste asemblerske naredbe, da bi se preuzeo sadržaj status
registra procesora, da bi se onemogućili prekidi i da bi se vratio preuzeti sadržaj u
status registar procesora. U operaciji disable_interrupts se podrazumeva da se
vrednost funkcije prosleđuje kroz ax registar.
U nastavku je naveden sadržaj datoteke a_block.cpp:
class atomic_block
{ int flags;
atomic_block(const atomic_block&);
atomic_block& operator=(const atomic_block&);
int disable_interrupts();
void restore_interrupts(int flag);
public:
atomic_block();
~atomic_block(); };
int atomic_block::disable_interrupts()
{
asm
pushf
;
asm
cli
;
asm
pop
ax ; };
void atomic_block::restore_interrupts(int flag)
{
asm
push
flag ;
asm
popf
; };
atomic_block::atomic_block()
{ flags = disable_interrupts(); };
atomic_block::~atomic_block()
{ restore_interrupts(flags); };
132
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
KERNEL.CPP
Izvorna datoteka kernel.cpp sadrži definiciju klase kernel_type koja
omogućuje rukovanje procesorom.
Rukovanje procesorom se svodi na preključivanje procesora sa jedne niti na
drugu. U okviru preključivanja se prvo sačuvaju zatečeni sadržaji registara
procesora, koji su nastali u toku aktivnosti niti sa koje se procesor preključuje.
Zatim se u ove registre smeštaju prethodno sačuvani sadržaji, nastali u toku
aktivnosti niti na koju se procesor preključuje. Zato je u razmatranju preključivanja
neizbežno pominjanje registara procesora Intel 8086 i načina na koji ih upotrebljava
kompajler BORLAND C++ (verzija 3.1).
Od četrnaest registara procesora Intel 8086, u preključivanju je potrebno
sačuvati sadržaje samo registara sp (stack pointer), bp (base pointer), si (source
index), di (destination index), ip (instruction pointer) i flags. Sadržaji registara cs
(code segment) i ds (data segment) su isti za sve niti, jer sve niti dele jedini
segment naredbi i jedini segment podataka COLIBRY programa (small memory
model). I sadržaj registra ss (stack segment) je isti za sve niti, jer kompajler
podrazumeva da se svi stekovi nalaze u segmentu podataka (small memory model),
pa se pobrine da sadržaj registra ss bude jednak sadržaju registra ds. Sadržaj
registra es (extra segment) se izjednačava, u toku aktivnosti niti, sa sadržajem
registra ds, pa je i on isti za sve niti. Registre ax, bx, cx i dx (general purpose)
kompajler BORLAND C++ (verzija 3.1) koristi kao radne registre na način koji
dozvoljava da se preključivanje ne mora brinuti ni o sadržajima ova četiri registra.
Klasa kernel_type nudi operaciju stack_swap koja omogućuje preključivanje
procesora sa jedne niti na drugu. Ključni korak u ovoj operaciji je zamena važećeg
steka, koja je posledica izmene sadržaja registra sp. Na taj način u pozivu ove
operacije se koristi jedan (stari), a u povratku iz nje drugi (novi) stek. Prema tome,
povratna adresa, sačuvana u pozivu operacije stack_swap, ostaje na starom steku, a
umesto nje koristi se povratna adresa zatečena na novom steku. Kao povratna
adresa služi sadržaj registra ip, koga, u pozivu potprograma, procesor, izvršavajući
asemblersku naredbu call, smesti na stek, da bi ga, u povratku iz potprograma,
preuzeo sa steka za vreme izvršavanja asemblerske naredbe ret. Na taj način,
operaciju stack_swap pozove jedna (stara) nit, a nakon njenog izvršavanja aktivnost
nastavlja druga (nova) nit.
Neposredno nakon početka izvršavanja operacije stack_swap, na steku stare
niti nalazi se, pored povratne adrese stare niti, i sadržaj koji je zatečen u registru bp
pre početka izvršavanja operacije stack_swap. Ovaj sadržaj se smešta na stek na
samom početku izvršavanja operacije stack_swap, za šta se pobrine kompajler, jer se
registar bp koristi uvek kao pokazivač aktivnog stek frejma u kome se nalaze
argumenti poziva potprograma. Kompajler na isti način postupi i sa sadržajima
registara si i di, jer se ovi registri koriste u izvršavanju operacije stack_swap. Prema
tome, pre zamene steka, u operaciji stack_swap je potrebno na stek smestiti još samo
sadržaj registra flags. Zamena steka se obavlja izmenom sadržaja registra sp. Pri
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
133
tome se zatečeni sadržaj ovog registra smešta u polje stack_top deskriptora stare
niti (da bi bio na poznatom mestu, radi kasnijeg ponovnog preključivanja na ovu
nit), a umesto njega u registar sp dolazi sadržaj polja stack_top deskriptora nove
niti. Ako je stek nove niti popunjen za vreme nekog od ranijih izvršavanja operacije
stack_swap, tada se na njegovom vrhu nalazi sadržaj registra flags. Ispod je sadržaj
registra di, pa registra si, zatim sledi sadržaj registra bp i na kraju dolazi sadržaj
registra ip (povratna adresa). Pre povratka iz operacije stack_swap potrebno je
sadržaj sa vrha steka prebaciti u registar flags. Za prebacivanje naredna tri sadržaja
pobrine se kompajler, a do prebacivanja petog sadržaja dođe u povratku iz operacije
stack_swap.
Prvi argument poziva operacije stack_swap je adresa polja stack_top
deskriptora stare niti, u koje se smešta sadržaj zatečen u registru sp, a drugi
argument je vrednost polja stack_top deskriptora nove niti, koja se prebacuje u
registar sp. Operacija stack_swap neizbežno sadrži asemblerske naredbe, radi
pristupanja registrima procesora. Među njima je i naredba poređnja sadržaja
registara si i di, koja služi samo da navede kompajler da ove sadržaje sačuva na
steku na početku izvršavanja operacije stack_swap i da ih vrati u pomenute registre
na kraju njenog izvršavanja.
Za preključivanje je neophodno imati adresu deskriptora aktivne niti sa koje
se procesor preključuje (active), adresu deskriptora niti na koju se procesor
preključuje (pretender), kao i adresu deskriptora niti sa koje se procesor preključio
(former). Operaciju preključivanja poziva privatna operacija switch_to, koja
postavlja pokazivač deskriptora aktivne niti i pokazivač deskriptora niti sa koje se
procesor preključio.
Preključivanje je nužno vezano za raspoređivanje (scheduling), odnosno
izbor niti na koju se procesor preključuje. Ciljevi raspoređivanja kod COLIBRY
izvršioca su da uvek bude aktivna najprioritetnija nit i da se ravnomerno deli vreme
procesora između spremnih niti istog prioriteta. Do raspoređivanja dolazi: (1)
prilikom stvaranja nove niti, (2) na kraju kvantuma i (3) prilikom izlaska niti iz
isključivog regiona u kome je privremeno dobila viši prioritet, odnosno neposredno
nakon obrade prekida. Pomenuta tri slučaja raspoređivanja su podržavana,
respektivno, operacijama schedule, periodic_schedule i reschedule.
U klasi kernel_type je definisan deskriptor nulte niti (zero_thread), na koju
se procesor preključuje samo kada ne postoji druga spremna nit. Konstruktor ove
klase inicijalizuje deskriptor nulte niti i ovu nit proglašava aktivnom. Pošto nulta nit
koristi stek COLIBRY programa kao svoj stek, za nju je potrebno zauzeti samo
sistemski stek (interrupt_stack). Veličinu steka obrađivača prekida određuju
konstante INTERRUPT_STACK_ITEMS i INTERRUPT_STACK_SIZE.
U nadležnosti klase kernel_type nije samo preključivanje, nego i podrška
viših slojeva iz hijerarhijske strukture COLIBRY izvršioca. Tu podršku pružaju
preostale operacije ove klase. Tako operacija to_exclusion omogućuje registrovanje
isključive promenljive u čiji isključivi region nit ulazi i privremenu izmenu
134
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
prioriteta niti, a operacija from_exclusion poništava efekte prethodne operacije.
Operacija to_waiting omogućuje menjanje stanja niti i uvezivanje njenog
deskriptora u neku od listi, a operacija from_waiting poništava efekte prethodne
operacije. Aktivnost niti omogućava operacija start_thread.
Očekivanje dešavanja događaja omogućuju operacije expect i tag_expect, a
objavu dešavanja događaja omogućuje operacija notify.
Operacije exclusive_in i exclusive_out realizuju ulazni i izlazni isključivi
protokol, dok operacija destroy_out realizuje poseban izlazni isključivi protokol.
On je potreban kod uništavanja niti, jer tada nema signaliziranja, pa ni potrebe da se
proverava lista ispunjenih uslova, a procesor se obavezno preključuje sa uništavane
na najprioritetniju spremnu nit. Očekivanje ispunjenja uslova omogućuju operacije
await i tag_await, dok operacija signal omogućuje objavu ispunjenja uslova.
Nit koja pozove operaciju get_thread_pointer dobije sadržaj polja
thread_pointer svog deskriptora. Analogna uloga je poverena i funkcijama
my_descriptor, find_interrupt_stack_top, my_sequencer i alarmed, odnosno funkciji
inside_exclusion.
U operacijama deljene promenljive kernel se koriste atomski regioni radi
zaštite njene konzistentnosti, kao i konzistentnosti argumenata iz poziva ovih
operacija. To je neophodno da bi polja ove deljene promenljive bila konzistentna,
odnosno da bi rezultat uvezivanja u razne liste i izvezivanja iz njih bio ispravan.
Isto važi i za funkcije koje pristupaju deljenoj promenljivoj kernel. Atomski regioni
nisu korišćeni samo u operacijama koje se uvek pozivaju iz atomskih regiona i u
funkcijama koje ne ugrožavaju konzistentnost.
Prethodno pomenuto korišćenje atomskih regiona je prihvatljivo, jer su
operacije deljene promenljive kernel i funkcije koje joj pristupaju kratkotrajne i ne
dovode do značajnog odlaganja obrada prekida.
U nastavku je naveden sadržaj datoteke kernel.cpp:
const size_t INTERRUPT_STACK_ITEMS = 64;
const size_t INTERRUPT_STACK_SIZE = INTERRUPT_STACK_ITEMS*sizeof(stack_item);
const int INTERRUPT_FLAG = 0x0200;
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
class kernel_type
{ descriptor *active;
descriptor *pretender;
descriptor *former;
descriptor zero_thread;
stack_item interrupt_stack[INTERRUPT_STACK_ITEMS];
kernel_type(const kernel_type&);
kernel_type& operator=(const kernel_type&);
public:
kernel_type();
private:
void to_exclusion(exclude *ex,descriptor *d);
exclude* from_exclusion(descriptor *d);
void to_waiting(list_link *list,descriptor *d);
descriptor *from_waiting(list_link *list);
void stack_swap(stack_item **old_stack,stack_item *new_stack);
void switch_to(descriptor *d);
void schedule(descriptor *d);
public:
void periodic_schedule();
void reschedule();
void start_thread(descriptor *d);
void expect(list_link *list);
void tag_expect(tag_type t,list_link *list);
void notify(list_link *list);
void exclusive_in(exclude *ex);
void await(list_link *list);
void tag_await(tag_type t,list_link *list);
void signal(list_link *list,exclude *ex);
void exclusive_out();
void destroy_out();
thread *get_thread_pointer();
friend descriptor *my_descriptor();
friend stack_item *find_interrupt_stack_top();
friend bool inside_exclusion();
friend unsigned long my_sequencer();
friend bool alarmed(); };
kernel_type::kernel_type()
{ zero_thread.initiate(&interrupt_stack[INTERRUPT_STACK_ITEMS]);
active = &zero_thread; };
void kernel_type::to_exclusion(exclude *ex,descriptor *d)
{ ex->previous = d->last;
d->last = ex;
ex->thread_priority = d->priority;
if (d->priority < ex->temporary_priority)
d->priority = ex->temporary_priority; };
exclude *kernel_type::from_exclusion(descriptor *d)
{ exclude* ex = d->last;
d->last = ex->previous;
d->priority = ex->thread_priority;
return ex; };
void kernel_type::to_waiting(list_link *list,descriptor *d)
{ d->state = WAITING;
list->insert(d); };
135
136
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
descriptor *kernel_type::from_waiting(list_link *list)
{ descriptor *d = (descriptor *)list->extract();
d->state = ACTIVE;
return d; };
void kernel_type::stack_swap(stack_item **old_stack,stack_item *new_stack)
{
asm
pushf
;
asm
cmp
si,di
;
asm
mov
bx,old_stack
;
asm
mov
[bx],sp
;
asm
mov
sp,new_stack
;
asm
popf
; };
void kernel_type::switch_to(descriptor *d)
{ former = active;
active = d;
stack_swap(&(former->stack_top),d->stack_top); };
void kernel_type::schedule(descriptor *d)
{ if ((active->priority) < (d->priority))
{ ready.insert(active);
switch_to(d); }
else
ready.insert(d);
};
void kernel_type::periodic_schedule()
{ ready.insert(active);
pretender = ready.extract();
if (active != pretender)
switch_to(pretender); };
void kernel_type::reschedule()
{ if (ready.higher_than(active))
{ ready.insert(active);
pretender = ready.extract();
switch_to(pretender); }
};
void kernel_type::start_thread(descriptor *d)
{ atomic_block set_up;
schedule(d); };
void kernel_type::expect(list_link *list)
{ to_waiting(list,active);
pretender = ready.extract();
switch_to(pretender); };
void kernel_type::tag_expect(tag_type t,list_link *list)
{ active->tag = t;
to_waiting(list,active);
pretender = ready.extract();
switch_to(pretender); };
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
void kernel_type::notify(list_link *list)
{ if (list->not_empty())
{ pretender = from_waiting(list);
ready.insert(pretender); };
};
void kernel_type::exclusive_in(exclude *ex)
{ atomic_block set_up;
if (ex->not_free())
{ to_waiting(ex,active);
pretender = ready.extract();
switch_to(pretender); }
else
{ ex->take();
to_exclusion(ex,active); };
};
void kernel_type::await(list_link *list)
{ atomic_block set_up;
exclude *ex = from_exclusion(active);
if (ex->signalled.not_empty())
{ pretender = from_waiting(&(ex->signalled));
to_exclusion(ex,pretender);
ready.insert(pretender); }
else
if (ex->not_empty())
{ pretender = from_waiting(ex);
to_exclusion(ex,pretender);
ready.insert(pretender); }
else
ex->release();
to_waiting(list,active);
pretender = ready.extract();
switch_to(pretender);
};
void kernel_type::tag_await(tag_type t,list_link *list)
{ atomic_block set_up;
active->tag = t;
exclude *ex = from_exclusion(active);
if (ex->signalled.not_empty())
{ pretender = from_waiting(&(ex->signalled));
to_exclusion(ex,pretender);
ready.insert(pretender); }
else
if (ex->not_empty())
{ pretender = from_waiting(ex);
to_exclusion(ex,pretender);
ready.insert(pretender); }
else
ex->release();
to_waiting(list,active);
pretender = ready.extract();
switch_to(pretender);
};
137
138
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
void kernel_type::signal(list_link *list,exclude *ex)
{ atomic_block set_up;
if (list->not_empty())
{ pretender = (descriptor *)list->extract();
ex->signalled.insert(pretender); };
};
void kernel_type::exclusive_out()
{ atomic_block set_up;
exclude *ex = from_exclusion(active);
if (ex->signalled.not_empty())
{ pretender = from_waiting(&(ex->signalled));
to_exclusion(ex,pretender);
schedule(pretender); }
else
if (ex->not_empty())
{ pretender = from_waiting(ex);
to_exclusion(ex,pretender);
schedule(pretender); }
else
{ ex->release();
reschedule(); };
};
void kernel_type::destroy_out()
{ atomic_block set_up;
exclude *ex = from_exclusion(active);
if (ex->not_empty())
{ pretender = from_waiting(ex);
to_exclusion(ex,pretender); }
else
{ ex->release();
pretender = ready.extract(); };
switch_to(pretender);
};
thread *kernel_type::get_thread_pointer()
{ atomic_block set_up;
return active->thread_pointer; };
kernel_type kernel;
descriptor *my_descriptor()
{ return kernel.active; };
stack_item *find_interrupt_stack_top()
{ return kernel.active->interrupt_stack_top; };
bool inside_exclusion()
{ return (kernel.active->last != 0); };
unsigned long my_sequencer()
{ atomic_block set_up;
return kernel.active->sequencer; };
bool alarmed()
{ atomic_block set_up;
return kernel.active->alarming; };
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
139
void yield()
{ atomic_block set_up;
kernel.periodic_schedule(); };
EXCLUSIV.CPP
Izvorna datoteka exclusiv.cpp sadrži definiciju klase exclusive. Ova klasa
omogućuje stvaranje isključivih klasa. Njena definicija se razlikuje od one
navedene u opisu isključivih klasa po tome što njena unutrašnja klasa tag_condition
nasleđuje klasu tag_list_link. Polje pointer klase exclusive dele svi njeni objekti.
Ono omogućuje njihovu ispravnu inicijalizaciju, jer posredstvom ovoga polja
objekti klasa condition i tag_condition dobijaju pokazivač liste ispunjenih uslova,
bez koga njihove operacije signal ne bi mogle da deskriptore ispunjenih uslova
uvežu u ovu listu. Lista ispunjenih uslova pripada isključivoj promenljivoj koja
sadrži objekte klasa condition i tag_condition. U njenoj inicijalizaciji, njena adresa
se smesti u polje pointer. Pomenuta adresa se preuzima iz ovoga polja u
inicijalizaciji objekata klasa condition i tag_condition, radi njenog smeštanja u
polje ex ovih objekata. Prethodno opisani postupak prosleđivanja adrese isključive
promenljive objektima koje ona sadrži funkcioniše pod pretpostavkom da odmah
nakon inicijalizacije isključive promenljive slede inicijalizacije svih njenih
objekata. To je tačno za statičke isključive promenljive. Međutim, ako bi
istovremeno postojale dve ili više dinamičkih isključivih promenljivih, prethodna
pretpostavka ne mora uvek da važi, jer preključivanje može da utiče na redosled
inicijalizacija. Pošto polje pointer dele sve isključive promenljive, u ovakvoj
situaciji su moguća štetna preplitanja, pa konkurentna biblioteka COLIBRY ne
predviđa istovremeno postojanje više dinamičkih isključivih promenljivih.
U nastavku je naveden sadržaj datoteke exclusiv.cpp:
class exclusive: public exclude
{ static exclusive *pointer;
protected:
class condition: public list_link
{exclusive *ex;
public:
condition();
bool awaited() const;
void await();
void signal(); };
class tag_condition: public tag_list_link
{exclusive *ex;
public:
tag_condition();
bool awaited() const;
void await(tag_type t);
void signal(); };
public:
exclusive(int priority = 1);
friend class condition;
friend class tag_condition;
};
140
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
exclusive *exclusive::pointer = 0;
exclusive::condition::condition(): list_link()
{ ex = exclusive::pointer; };
bool exclusive::condition::awaited() const
{ return not_empty(); };
void exclusive::condition::await()
{ kernel.await(this); };
void exclusive::condition::signal()
{ kernel.signal(this,ex); };
exclusive::tag_condition::tag_condition(): tag_list_link()
{ ex = exclusive::pointer; };
bool exclusive::tag_condition::awaited() const
{ return not_empty(); };
void exclusive::tag_condition::await(tag_type t)
{ kernel.tag_await(t,position);
position = this; };
void exclusive::tag_condition::signal()
{ kernel.signal(this,ex); };
exclusive::exclusive(int priority): exclude(priority)
{ pointer = this; };
E_BLOCK.CPP
Izvorna datoteka e_block.cpp sadrži definiciju klase exclusive_block. Ova
klasa omogućuje stvaranje isključivih regiona. Njeni konstruktor i destruktor
realizuju, respektivno, ulazni i izlazni isključivi protokol.
U izvornoj datoteci e_block.cpp se nalazi i definicija klase destroy_block.
Ova klasa omogućuje stvaranje isključivog regiona koji se koristi u funkciji destroy
za uništavanje niti. Njeni konstruktor i destruktor realizuju, respektivno, ulazni i
specifičan izlazni isključivi protokol.
U nastavku je naveden sadržaj datoteke e_block.cpp:
class exclusive_block
{ exclusive_block(const exclusive_block&);
exclusive_block& operator=(const exclusive_block&);
public:
exclusive_block(exclusive *ex);
~exclusive_block(); };
exclusive_block::exclusive_block(exclusive *ex)
{ kernel.exclusive_in(ex); };
exclusive_block::~exclusive_block()
{ kernel.exclusive_out(); };
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
141
class destroy_block
{ destroy_block(const destroy_block&);
destroy_block& operator=(const destroy_block&);
public:
destroy_block(exclusive *ex);
~destroy_block(); };
destroy_block::destroy_block(exclusive *ex)
{ kernel.exclusive_in(ex); };
destroy_block::~destroy_block()
{ kernel.destroy_out(); };
ATOMIC.CPP
Izvorna datoteka atomic.cpp sadrži definiciju templejt klase atomic. Ova
templejt klasa opisuje zajednički deo obrade prekida čije opsluživanje je u
nadležnosti COLIBRY drajvera. Iz ove klase se izvode klase pojedinih drajvera.
Parametar templejt klase atomic omogućuje zadavanje broj vektora prekida koga
opslužuje dati drajver, da bi drajver raspolagao tim podatkom.
Zajednički deo obrade prekida se oslanja na BIOS, radi prenosivosti
COLIBRY programa. To znači da u obradi istog prekida mogu da učestvuju i BIOS
obrađivač prekida i COLIBRY drajver. Zato takvi prekidi pokreću zajednički deo
njihove obrade, koji eventualno poziva BIOS obrađivač prekida (što zavisi od vrste
prekida), a svakako poziva obrađivač prekida COLIBRY drajvera.
Kompajler BORLAND C++ (verzija 3.1) je doprineo komplikovanosti ovog
modula COLIBRY izvršioca, jer nije dozvolio da templejt klasa atomic bude
definisana u obliku prikazanom u opisu atomskih klasa. Umesto toga, templejt klasa
atomic je izvedena iz klase atomic_backup, Klasa atomic_backup sadrži definicije
operacija interrupt_handler i interrupt_vector_number, kao i definicije klasa event
i tag_event, od kojih klasa tag_event nasleđuje klasu tag_list_link. Konstruktor
templejt klase atomic poziva operaciju atomic_backup_initiate klase atomic_backup,
da bi adresa operacije actuator ove templejt klase dospela u element tabele prekida,
određen parametrom templejt klase atomic. To znači da prekidi, čiji broj vektora je
određen pomoću ovog parametra, izazivaju pozivanje pomenute operacije actuator.
Ova operacija prvo poziva funkciju save_working_registers, čiji osnovni zadatak je
da sačuva zatečene vrednosti radnih registara procesora, jer ih koristi zajednički deo
obrade prekida. Zatim operacija actuator poziva funkciju mediator. Njen zadatak je
da proveri da li je prekinuta aktivnost COLIBRY niti ili aktivnost BIOS operacije. U
drugom slučaju je potrebno podesiti segmentne registre procesora, tako da pokazuju
segmente COLIBRY programa. U svakom slučaju po potrebi se prvo poziva BIOS
obrađivač prekida, a onda se obavezno poziva obrađivač prekida COLIBRY
drajvera. Obrada prekida se završava na kraju funkcije mediator, što znači da nije
predviđen povratak iz ove funkcije.
Templejt klasu atomic je dodatno zakomplikovala činjenica da mehanizam
prekida poziva njenu operaciju actuator. Međutim, mehanizam prekida ne može
direktno pozvati operaciju neke klase, jer on ne podržava argumente, a
142
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
podrazumevajući argument poziva operacije svake klase je adresa objekta klase
kojoj dotična operacija pripada. Zato se u templejt klasi atomic koristi union za
preuzimanje adrese operacije actuator, koja pripada objektu klase, izvedene iz
templejt klase atomic. Ovu adresu koristi mehanizam prekida, prilikom poziva
dotične operacije actuator, koja pristupa adresi objekta svoje klase posredstvom
statičke promenljive pointer.
Parametar templejt klase atomic omogućuje obrađivaču prekida da preuzme
broj vektora prekida.
Za pozivanje BIOS obrađivača prekida neophodno je raspolagati njegovom
adresom. Ona se čuva u poljima old_address_offset i old_address_segment klase
atomic_backup. Polje bios ove klase sadrži podatak da li se poziva BIOS obrađivač
prekida, a polje vector_number sadrži podatak o broju vektora prekida, koji mora biti
iz dijapazona određenog promenljivom vector_numbers. Svi COLIBRY drajveri se
uvezuju u listu, čiji početak sadrži polje list. Ovo uvezivanje omogućuje polje
next.
Parametri operacije atomic_backup_initiate klase atomic_backup određuju
broj vektora prekida koga će opsluživati novi drajver, adresu obrađivača prekida
novog drajvera i pokazivač na eventualni stari drajver koji je opsluživao pomenutu
vrstu prekida. U ovoj operaciji se koristi tridesetdvobitni (far) pokazivač (address)
za indeksiranje tabele prekida, jer se ona nalazi u nultom segmentu, koji je različit
od segmenta podataka COLIBRY programa. Ovaj pokazivač se inicijalizuje tako da
se u njegovu manje značajnu reč smesti adresa elementa tabele prekida (određena
izrazom vector_number*4), a u značajniju reč se smesti 0 kao adresa nultog
segmenta. Iz indeksiranog elementa tabele prekida se preuzima zatečena adresa
BIOS obrađivača prekida samo ako dotičnu vrstu prekida nije opsluživao COLIBRY
drajver. Inače se adresa BIOS obrađivača prekida preuzima iz drajvera koji je
prethodno opsluživao dotičnu vrstu prekida, a čiju adresu sadrži parametar pointer.
U operaciji atomic_backup_initiate se zapamti broj vektora prekida, a za brojeve
vektora, određene konstantama CLOCK i KEYBOARD, se registruje da u obradi njihovih
prekida učestvuje i BIOS obrađivač prekida. Na kraju se novi drajver uvezuje u
listu, u manje značajnu reč indeksiranog elementa tabele prekida se smesti adresa
obrađivača prekida (new_handler), a u značajniju reč adresa segmenta naredbi
COLIBRY programa. Nju vraća funkcija code_segment.
Klasa atomic_backup nema destruktora, koji bi tabelu prekida vraćao u stanje
koje je prethodilo izvršavanju njene operacije atomic_backup_initiate. Zato
atomske promenljive ne mogu biti lokalne (dinamičke), nego samo globalne
(statičke).
Na
kraju
izvršavanja
COLIBRY
programa,
funkcija
interrupt_table_restore vraća tabelu prekida u stanje koje je prethodilo
izvršavanju COLIBRY programa, tako što prolazi kroz listu COLIBRY drajvera i iz
njih preuzima adrese BIOS obrađivača prekida.
Pokretanje zajedničkog dela obrade prekida se nalazi u nadležnosti
mehanizma prekida procesora. U slučaju Intel 8086 procesora, ovaj mehanizam
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
143
prvo smesti zatečene sadržaje registara flags, cs i ip na stek prekinute niti u
navedenom redosledu. On zatim izazove izvršavanje operacije actuator, čija adresa
je u elementu tabele prekida, koga indeksira broj vektora obrađivanog prekida.
Stanje steka prekinute niti, pre poziva operacije actuator, je prikazano u nastavku:
niže adrese
...
ip
cs
<-sp
flags
više adrese
...
Oznaka cs predstavlja adresu segmenta naredbi prekinutog programa. Stanje
prethodnog steka, nakon poziva operacije actuator, a pre izvršavanja njenog prvog
iskaza, je prikazano u nastavku:
niže adrese
1. stek frejm
...
bp
<-bp <-sp
ip
cs
flags
više adrese
...
Na steku se pojavio (zaslugom kompajlera) 1. stek frejm operacije actuator, sa
sadržajem bp registra. Nakon poziva funkcije save_working_registers, a pre
izvršavanja njene prve asemblerske naredbe, stek izgleda:
niže adrese
2. stek frejm
...
1. stek frejm
bp
bp
ip
<-bp <-sp
ip
cs
flags
više adrese
...
Na steku se pojavio (opet zaslugom kompajlera) 2. stek frejm funkcije
save_working_registers, sa povratnom adresom (oznaka ip) i sadržajem bp registra.
Ova funkcija pomera prikazana dva stek frejma za četiri stek pozicije prema nižim
adresama, da bi stvorila prostor za smeštanje sadržaja radnih registara na stek, tako
da nakon izvršavanja njenih asemblerskih naredbi, a pre povratka iz nje, stek
izgleda:
144
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
niže adrese
2. stek frejm
...
1. stek frejm
bp
bp
ip
<-bp <-sp
ax
bx
cx
dx
ip
cs
flags
više adrese
...
Oznake ax, bx, cx i dx predstavljaju sadržaje četiri radna registra, koje je u njima
zatekao prekid. Sadržaji su sačuvani, da bi, po njihovom vraćanju u ove registre na
završetku obrade prekida, mogla biti nastavljena prekinuta aktivnost niti kao da nije
bilo prekida. Nakon povratka iz funkcije save_working_registers i poziva funkcije
mediator, a pre izvršavanja njene prve asemblerske naredbe, stanje na steku izgleda:
niže adrese
3. stek frejm
...
di
si
<-sp
bp
ip
<-bp
argument
1. stek frejm
(address_of_pointer)
bp
ax
bx
cx
dx
ip
cs
flags
više adrese
...
Umesto 2. stek frejma funkcije save_working_registers, na steku se pojavio 3. stek
frejm funkcije mediator. Na počektu ove funkcije se proverava da li je prekinuto
izvršavanje BIOS operacije ili aktivnost COLIBRY niti. To se može ustanoviti
poređenjem važećeg sadržaj registra cs (koji je jednak adresi segmenta naredbi
COLIBRY programa) sa sadržajem koga je ovaj registar imao pre prekida (i koji se
nalazi u važećem steku, ali na lokaciji koja je udaljena za konstantu
OFFSET_FOR_OLD_CS_ADDRESS od lokacije steka, čiju adresu sadrži registar bp). Ako su
ovi sadržaji različiti, prekinuto je izvršavanje BIOS operacije, a ako su jednaki,
prekinuta je aktivnost niti. Pošto se obrada prekida u ova dva slučaja razlikuje, za
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
145
svaki od njih postoji posebna grana funkcije mediator. Ipak, i u jednom i u drugom
slučaju, izvršavanje funkcije mediator se završava vraćanjem u registre di, si, bp,
ax, bx, cx i dx njihovih prethodno sačuvanih sadržaja. Iza toga sledi izvršavanje
asemblerske naredbe iret, čime sa završava obrada prekida. Izričito vraćanje
prethodno sačuvanih sadržaja u navedene registre je neophodno (ne može se
prepustiti kompajleru), jer se izvršavanje funkcije mediator ne završava povratkom
iz ove funkcije.
U delu funkcije mediator, koji se aktivira samo kada je prekinuto izvršavanje
BIOS operacije, na stek se smeštaju sadržaji registara ds i es. Iza toga u registar ds
se smesti adresa segmenta podataka COLIBRY programa (DGROUP). Na taj način se
stvore neophodni uslovi za pripremu obrade prekida, za proveru da li se koristi
BIOS obrađivač prekida (radi njegovog eventualnog pozivanja), kao i za pozivanje
operacije interrupt_handler. Za pozivanje ove operacije je neophodno pripremiti
stek obrađivača prekida: (1) smeštajući adresu segmenta podataka u ss registar i (2)
smeštajući povratnu vrednost funkcije find_interrupt_stack_top u sp registar. Ova
vrednost se dobija posredstvom ax registra.
Priprema obrade prekida obuhvata povećanje brojača započetih, a
nezavršenih
poziva
funkcije
mediator
(njega
sadrži
promenljiva
mediator_calls_count) i registrovanje u promenljivoj exception_handling_possible
da se ne opslužuju izuzeci koji se dese u BIOS operacijama ili u obradama prekida.
Važno je zapaziti da istovremeno može da postoji više započetih, a nezavršenih
poziva funkcije mediator, jer BIOS obrađivač prekida izaziva prekide, pomoću
asemblerske naredbe int. Na primer, istovremeno pritiskanje control i break dirki
tastature izaziva prekid sa brojem vektora 9. U obradi ovog prekida BIOS obrađivač
prekida izaziva prekid sa brojem vektora 27.
Pozivanje BIOS obrađivača prekida omogućuje funkcija bios_handler. U njoj
se na važeći stek smešta sadržaj registra flags, radi stvaranja privida da se BIOS
obrađivač prekida poziva direktno iz mehanizma prekida. Ova funkcija sadrži i
poziv funkcije check_control_break. Ona dovodi izvršavanja COLIBRY programa
do prevremenog završavanja, što je reakcija na istovremeno pritiskanje Control i
Break dirki tastature.
Za vreme izvršavanja interrupt_handler operacije nije predviđeno
preključivanje procesora sa prekinute niti, jer je obrada prekida preča od aktiviranja
bilo koje niti. Uostalom, obrada prekida dovodi do prekidanja aktivnosti i
najprioritetnije niti. Ali, pošto se u izvršavanju interrupt_handler operacije može
omogućiti nastavak aktivnosti niti koja je prioritetnija od prekinute niti, neophodno
je, odmah nakon kraja izvršavanja interrupt_handler operacije, rasporediti procesor
na najprioritetniju od spremnih niti. Jedino tako procesor može uvek biti posvećen
najprioritetnijoj od spremnih niti. Ovo raspoređivanje procesora na najprioritetniju
od spremnih niti predstavlja dopunu izvršavanja interrupt_handler operacije. Ono
se nalazi u nadležnosti funkcije post_interrupt_handler, a vezano je samo za
poslednji od nezavršenih, a započetih poziva funkcije mediator.
146
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Deo funkcije mediator, koji se aktivira samo kada je prekinuta aktivnost niti,
ne sadrži rukovanje registrima ds, es, ss i sp, jer oni imaju ispravan sadržaj u ovom
slučaju. Priprema obrade prekida, vezana za ovaj deo funkcije mediator, pored
povećanja brojača započetih, a nezavršenih poziva funkcije mediator, predviđa i
opsluživanje izuzetaka, ali samo ako se dese u toku aktivnosti niti COLIBRY
programa.
Sve promenljive, definisane u datoteci atomic.cpp, su atomske promenljive, a
njihovu konzistentnost delom štiti automatski atomski region, koji je posledica
automatske onemogućenosti prekida u toku njihove obrade. Jedini izuzetak
predstavlja non maskable interrupt.
U nastavku je naveden sadržaj datoteke atomic.cpp:
const int TOP_VECTOR_NUMBER = 255;
limited vector_numbers(0,TOP_VECTOR_NUMBER);
unsigned code_segment()
{
asm
mov
ax,cs ; };
class atomic_backup
{ unsigned old_address_offset;
unsigned old_address_segment;
int vector_number;
bool bios;
static atomic_backup *list;
atomic_backup *next;
atomic_backup(const atomic_backup&);
atomic_backup& operator=(const atomic_backup&);
protected:
class event: public list_link
{public:
event();
bool expected() const;
void expect();
void notify(); };
class tag_event: public tag_list_link
{public:
tag_event();
bool expected() const;
void expect(tag_type t);
void notify(); };
public:
atomic_backup();
void atomic_backup_initiate(int vector_number,unsigned new_handler,
atomic_backup *pointer);
protected:
virtual void interrupt_handler();
int interrupt_vector_number() const;
friend void interrupt_table_restore();
friend void mediator(atomic_backup **address_of_pointer); };
atomic_backup *atomic_backup::list = 0;
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
atomic_backup::event::event(): list_link()
{ };
bool atomic_backup::event::expected() const
{ return not_empty(); };
void atomic_backup::event::expect()
{ kernel.expect(this); };
void atomic_backup::event::notify()
{ kernel.notify(this); };
atomic_backup::tag_event::tag_event(): tag_list_link()
{ };
bool atomic_backup::tag_event::expected() const
{ return not_empty(); };
void atomic_backup::tag_event::expect(tag_type t)
{ kernel.tag_expect(t,position);
position = this; };
void atomic_backup::tag_event::notify()
{ kernel.notify(this); };
atomic_backup::atomic_backup()
{ };
const int CLOCK = 0x08;
const int KEYBOARD = 0x09;
void atomic_backup::atomic_backup_initiate(int vector_number,
unsigned new_handler,
atomic_backup *pointer)
{ if (vector_numbers.in_range(vector_number))
{ unsigned far *address;
*((unsigned *)&(address)) = (unsigned)(vector_number*4);
*((unsigned *)&(address)+1) = 0;
if (pointer == 0)
{ old_address_offset = *address;
old_address_segment = *(address+1); }
else
{ old_address_offset = pointer->old_address_offset;
old_address_segment = pointer->old_address_segment; };
atomic_backup::vector_number = vector_number;
bios = ((vector_number == CLOCK) ||
(vector_number == KEYBOARD)) ? true : false;
next = list;
list = this;
atomic_block set_up;
*address = new_handler;
*(address+1) = code_segment(); };
};
void atomic_backup::interrupt_handler()
{ };
147
148
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
int atomic_backup::interrupt_vector_number() const
{ return vector_number; };
void interrupt_table_restore()
{ atomic_block set_up;
unsigned far *address;
while (atomic_backup::list != 0)
{ *((unsigned *)&(address)) = atomic_backup::list->vector_number*4;
*((unsigned *)&(address)+1) = 0;
*address = atomic_backup::list->old_address_offset;
*(address+1) = atomic_backup::list->old_address_segment;
atomic_backup::list = atomic_backup::list->next; };
};
unsigned mediator_calls_count = 0;
extern void clock_scheduling();
void post_interrupt_handler()
{ if ((--mediator_calls_count) == 0)
clock_scheduling(); };
void post_exception_handler()
{ --mediator_calls_count; };
const char OFFSET_FOR_OLD_CS_ADDRESS = 18;
extern void check_control_break();
void bios_handler(unsigned *address_of_bios_handler_address)
{
asm
pushf
;
asm
mov
bx,address_of_bios_handler_address ;
asm
call
dword ptr[bx]
;
check_control_break(); };
bool exception_handling_possible;
bool inside_bios_or_interrupt()
{ return (exception_handling_possible == false); };
atomic_backup *mediators_pointer;
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
void mediator(atomic_backup **address_of_pointer)
{ bios_or_colibry_interrupted:
asm
mov
cx,cs
;
asm
cmp
cx,[bp+OFFSET_FOR_OLD_CS_ADDRESS] ;
asm
je
colibry_interrupted
;
bios_interrupted:
asm
push
ds
;
asm
push
es
;
asm
mov
cx,DGROUP ;
asm
mov
ds,cx
;
mediator_calls_count++;
exception_handling_possible = false;
mediators_pointer = *address_of_pointer;
if (mediators_pointer->bios)
bios_handler(&(mediators_pointer->old_address_offset));
find_interrupt_stack_top();
asm
mov
si,ss ;
asm
mov
di,sp ;
asm
mov
cx,ds ;
asm
mov
ss,cx ;
asm
mov
sp,ax ;
mediators_pointer->interrupt_handler();
post_interrupt_handler();
asm
mov
ss,si
;
asm
mov
sp,di
;
asm
pop
es
;
asm
pop
ds
;
asm
jmp
restore ;
colibry_interrupted:
if ((mediator_calls_count++) == 0)
exception_handling_possible = true;
else
exception_handling_possible = false;
if ((*address_of_pointer)->bios)
bios_handler(&((*address_of_pointer)->old_address_offset));
(*address_of_pointer)->interrupt_handler();
post_interrupt_handler();
restore:
asm
pop
di
;
asm
pop
si
;
asm
mov
sp,[bp] ;
asm
pop
bp
;
asm
pop
ax
;
asm
pop
bx
;
asm
pop
cx
;
asm
pop
dx
;
asm
iret
; };
void save_working_registers()
{
asm
sub
sp,2
asm
push
[bp+4]
asm
mov
[bp],sp
asm
push
[bp+2]
asm
push
[bp]
asm
mov
bp,sp
asm
mov
[bp+6],ax
asm
mov
[bp+8],bx
asm
mov
[bp+10],cx
asm
mov
[bp+12],dx
;
;
;
;
;
;
;
;
;
; };
149
150
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
template<int VECTOR_NUMBER>
class atomic: public atomic_backup
{ static atomic_backup *pointer;
union
{ void (atomic::*actuator_address)();
unsigned alias_actuator_address; };
void actuator();
public:
atomic();
};
template<int VECTOR_NUMBER>
atomic_backup *atomic<VECTOR_NUMBER>::pointer = 0;
template<int VECTOR_NUMBER>
void atomic<VECTOR_NUMBER>::actuator()
{ save_working_registers();
mediator(&pointer); };
template<int VECTOR_NUMBER>
atomic<VECTOR_NUMBER>::atomic()
{ actuator_address = &atomic<VECTOR_NUMBER>::actuator;
atomic_backup_initiate(VECTOR_NUMBER,alias_actuator_address,pointer);
pointer = this; };
DRIVERS.CPP
Izvorna datoteka drivers.cpp sadrži dve klase izvedene iz templejt klase
To su klase control_break_type i clock_type. Prva od njih omogućuje
reakciju na istovremeni pritisak dirki control i break, radi izazivanja prevremenog
kraja COLIBRY programa. Druga od ovih klasa omogućuje rukovanje vremenom.
Klasa control_break_type redefiniše operaciju interrupt_handler.
Redefinisana operacija interrupt_handler omogućuje postavljanje polja pressed u
okviru obrade prekida sa brojem vektora CONTROL_BREAK. Proveru postavljenosti
ovog polja i prevremeni kraj izvršavanja COLIBRY programa omogućuje funkcija
check_control_break. Ova funkcija poziva funkciju quit, radi vraćanja računara pod
upravu MS-DOS operativnog sistema. Na osnovu klase control_break_type nastaje
drajver control_break.
Rukovanje vremenom obuhvata brojanje otkucaja sata, radi praćenja
proticanja sistemskog vremena, odbrojavanja otkucaja sata preostalih do buđenja
uspavane niti, kao i odbrojavanja otkucaja sata preostalih do kraja kvantuma
aktivne niti. Kada broj otkucaja, preostalih do buđenja uspavane niti, padne na nulu,
potrebno je probuditi sve niti za koje je nastupio trenutak buđenja. Takođe, kada
broj otkucaja, preostalih do isticanja kvantuma aktivne niti, padne na nulu, potrebno
je pokrenuti periodično raspoređivanje. Svi prethodno pobrojani poslovi se nalaze u
nadležnosti operacije interrupt_handler koju redefiniše klasa clock_type. Polje
count ove klase sadrži sistemsko vreme, polje countdown sadrži broj otkucaja do
buđenja, a polje rest broj otkucaja do isticanja kvantuma. Polje
atomic.
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
151
ukazuje kada je nastupio trenutak za periodično
raspoređivanje, a oko polja delta se formira lista deskriptora uspavanih niti. Da se
za svaku uspavanu nit ne bi proveravalo, nakon svakog otkucaja, da li je nastupilo
vreme njenog buđenja, deskriptori uspavanih niti se uvezuju u listu u hronološkom
redosledu buđenja niti. Svakom od ovih deskriptora je dodeljen privezak koji
pokazuje relativno vreme buđenja (relativni broj otkucaja do buđenja) u odnosu na
prethodnika u listi. Ovakva lista se zove delta lista. Zahvaljujući delta listi, nakon
svakog otkucaja potrebno je proveriti da li je nastupio trenutak buđenja samo za nit
koja se najranije budi, odnosno samo za prvi deskriptor iz delta liste. Pošto može da
bude više niti, čije buđenje je vezano za isti trenutak, unapred nije poznato koliko
niti treba probuditi nakon otkucaja sata. To znači da unapred nije moguće odrediti
vreme potrebno za obradu prekida sata sa brojem vektora CLOCK, jer unapred nije
poznat broj niti koje će biti probuđene u pojedinim obradama prekida sata. Ako bi
prekidi bili onemogućeni u toku cele obrade prekida sata, to bi moglo dovesti do
propuštanja registrovanja pojedinih otkucaja sata. Zato se u pojedinim delovima
obrade prekida sata, u kojima nema opasnosti od narušavanja konzistentnosti,
prekidi omogućuju. Ako se otkucaj sata desi u toku obrade prethodnog otkucaja, on
ne sme da pokrene kompletnu obradu prekida sata, nego samo deo ove obrade koji
broji otkucaje i odbrojava otkucaje preostale do isticanja kvantuma. Zato je
uvedeno polje delta_busy koje pokazuje da li je u toku obrada prekida sata. Ovo
polje štiti konzistentnost delta liste, jer obezbeđuje međusobnu isključivost raznih
pristupa ovoj listi. Kod dugačke obrade prekida sata, u toku buđenja jedne grupe
niti može da nastupi vreme buđenja naredne grupe niti. Da bi se takva situacija
otkrila, uvedena su polja old_count i difference.
Operacije interrupts_disable i interrupts_enable klase clock_type,
respektivno, onemogućuju i omogućuju prekide, a prethodno opisanu obradu
prekida sata omogućuje redefinisana operacija interrupt_handler. Na osnovu klase
clock_type nastaje drajver clock.
U nastavku je naveden sadržaj datoteke drivers.cpp:
periodic_scheduling
const char TERMINATE = 0x4c;
const char MS_DOS = 0x21;
static void dos_return()
{
asm
mov
ah,TERMINATE ;
asm
xor
al,al
;
asm
int
MS_DOS
; };
void quit()
{ interrupt_table_restore();
dos_return(); };
const int CONTROL_BREAK = 0x1b;
152
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
class control_break_type: public atomic<CONTROL_BREAK>
{ bool pressed;
public:
control_break_type();
protected:
void interrupt_handler();
friend void check_control_break(); };
control_break_type::control_break_type(): atomic<CONTROL_BREAK>()
{ atomic_block set_up;
pressed = false; };
void control_break_type::interrupt_handler()
{ pressed = true; };
control_break_type control_break;
void check_control_break()
{ if (control_break.pressed)
quit(); };
class clock_type: public atomic<CLOCK>
{ unsigned long count;
unsigned long old_count;
unsigned long difference;
unsigned long countdown;
unsigned long rest;
unsigned long quantum;
bool periodic_scheduling;
tag_event delta;
bool delta_busy;
tag_type tag;
void interrupts_disable();
void interrupts_enable();
public:
clock_type();
protected:
void interrupt_handler();
friend void time_set(unsigned long time);
friend unsigned long time_get();
friend void quantum_set(unsigned long quantum);
friend void clock_scheduling();
friend void delay(unsigned long duration); };
const unsigned long QUANTUM = 2;
void clock_type::interrupts_disable()
{
asm
cli; };
void clock_type::interrupts_enable()
{
asm
sti; };
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
clock_type::clock_type(): atomic<CLOCK>()
{ atomic_block set_up;
count = 0;
difference = 0;
countdown = 0;
rest = QUANTUM;
quantum = QUANTUM;
periodic_scheduling = false;
delta_busy = false; };
void clock_type::interrupt_handler()
{ count++;
if ((--rest) == 0)
{ rest = quantum;
periodic_scheduling = true; };
if (!delta_busy)
{ if ((countdown > 0) && ((--countdown) == 0))
{ delta.notify();
delta_busy = true;
old_count = count-difference;
interrupts_enable();
while (delta.first(&tag))
{ difference = count-old_count;
if (tag <= difference)
{ old_count += tag;
interrupts_disable();
delta.notify();
interrupts_enable(); }
else
{ countdown = tag-difference;
break; };
};
interrupts_disable();
difference = 0;
delta_busy = false;
};
};
};
clock_type clock;
void time_set(unsigned long time)
{ atomic_block set_up;
clock.count = time; };
unsigned long time_get()
{ atomic_block set_up;
return clock.count; };
void quantum_set(unsigned long quantum)
{ atomic_block set_up;
if (quantum > 0)
clock.quantum = quantum; };
153
154
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
void clock_scheduling()
{ if (!clock.delta_busy)
if (clock.periodic_scheduling)
{ clock.periodic_scheduling = false;
kernel.periodic_schedule(); }
else
kernel.reschedule();
};
void delay(unsigned long duration)
{ if (duration > 0)
{ tag_type new_tag = duration;
tag_type old_tag;
{ atomic_block set_up;
clock.old_count = clock.count;
old_tag = clock.countdown;
clock.delta_busy = true; };
if (clock.delta.first())
do
{ if (old_tag > new_tag)
{ clock.delta.attach_tag(old_tag-new_tag);
break; }
else
if (old_tag == new_tag)
{ new_tag = 0;
clock.delta.succ();
break; };
new_tag -= old_tag;
}
while (clock.delta.succ(&old_tag));
{ atomic_block set_up;
clock.delta_busy = false;
if (duration == new_tag)
clock.countdown = new_tag;
clock.difference = clock.count-clock.old_count;
if (clock.countdown > clock.difference)
{ clock.countdown -= clock.difference;
clock.difference = 0; }
else
{ clock.difference = clock.difference-clock.countdown+1;
clock.countdown = 1; };
clock.delta.expect(new_tag);
};
};
};
void delay_till(unsigned long moment)
{ unsigned long current_moment = time_get();
unsigned long difference = moment-current_moment;
if (difference <= (ULONG_MAX/2))
delay(difference); };
MEMORY.CPP
Izvorna datoteka memory.cpp sadrži definiciju klase memory_fragment koja
omogućuje rukovanje slobodnom radnom memorijom.
Na početku izvršavanja COLIBRY programa, njegov segment podataka je
izdeljen na tri dela. Prvi, početni deo ovoga segmenta, u kome su lokacije sa nižim
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
155
adresama, sadrži globalne (statičke) promenljive COLIBRY programa. Zatim sledi
neupotrebljena radna memorija. Iza nje, na drugom kraju segmenta podataka, dolazi
stek COLIBRY programa, koga koristi nulta nit.
Slobodnu radnu memoriju obrazuje celi broj jedinica sastavljenih od UNIT
bajta neupotrebljene radne memorije. Rukovanje slobodnom radnom memorijom
podrazumeva da se uvek zauzima, odnosno da se uvek oslobađa celi broj ovih
jedinica. Zauzimanja slobodne radne memorije, odnosno oslobađanja zauzete radne
memorije uzrokuju njenu iscepkanost u odsečke. Odsečci se zato uvezuju u
jednosmernu listu, uređenu u rastućem redosledu njihovih početnih adresa. Radi
uvezivanja u listu, početak svakog odsečka sadrži svoju veličinu, izraženu u
pomenutim jedinicama od po UNIT bajta, i pokazivač narednog odsečka. Veličinu
odsečka i pokazivač narednog odsečka sadrže polja size i next klase
memory_fragment.
Konstruktor klase memory_fragment opisuje obrazovanje liste odsečaka
slobodne radne memorije, sastavljene od početnog, stalnog odsečaka čija veličina je
0. Operacija initiate klase memory_fragment omogućuje dodavanje u listu slobodnih
odsečaka drugog odsečka koji obuhvata raspoloživu slobodnu radnu memoriju.
Dodavanju drugog odsečka prethodi provera da li ima dovoljno neupotrebljene
radne memorije za potrebe COLIBRY programa. Veličinu neupotrebljene radne
memorije vraća poziv funkcije coreleft. Ako je neupotrebljena radna memorija
manja od konstante MINIMAL_MEMORY, tada nema smisla nastavljanje izvršavanja
COLIBRY programa.
Zauzimanje slobodne radne memorije omogućuje operacija take klase
memory_fragment. Zauzimanju prethodi pretraživanje liste odsečaka, radi
pronalaženja prvog dovoljno velikog odsečka. Pretraživanje uvek počinje od
stalnog odsečka. Ako se pronađe dovoljno velik odsečak, traženi bajti se zauzimaju
s njegovog kraja. Ako pronađeni odsečak obuhvata baš traženi broj bajta, tada se on
isključuje iz liste i zauzimaju se svi njegovi bajti.
Oslobađanje prethodno zauzete radne memorije omogućuje funkcija free
klase memory_fragment. Za oslobađanje je neophodno u listi odsečaka pronaći
odsečak iza koga će se oslobađani odsečak uvezati u ovu listu. Pre uvezivanja
proverava se da li oslobađani odsečak može da se spoji u jedan odsečak sa svojim
prethodnikom i sa svojim sledbenikom. Odsečak se uvezuje u pomenutu listu samo
ako ovo spajanje nije moguće.
Rukovanje slobodnom radnom memorijom se ostvaruje posredstvom
prethodno opisanih operacija promenljive memory.
Operacije promenljive memory su namenjene za zauzimanje i oslobađanje
radne memorije prilikom stvaranja i uništavanja objekata pojedinih klasa. Da bi se
njihova namena ostvarila, neophodno je da se ove operacije pozivaju iz globalnih
funkcija ::operator new() i ::operator delete(), jer se na njih oslanjaju operatori
new i delete. Ali, tada razne niti mogu da pozivaju operacije promenljive memory
posredstvom prethodna dva operatora i da tako ugroze njenu konzistentnost. Pošto
156
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
promenljiva memory nije definisana kao isključiva promenljiva, za zaštitu njene
konzistentnosti je potrebno da se njene operacije pozivaju iz isključivih regiona
posebne isključive promenljive mutex. To je obezbeđeno u definicijama funkcija
operator new i operator delete.
U nastavku je naveden sadržaj datoteke memory.cpp:
class memory_fragment
{ size_t size;
memory_fragment *next;
memory_fragment(const memory_fragment&);
memory_fragment& operator=(const memory_fragment&);
public:
memory_fragment();
bool initiate();
void *take(size_t size);
void free(void *address); };
memory_fragment::memory_fragment()
{ size = 0;
next = this; };
const size_t UNIT = sizeof(memory_fragment);
const size_t STACK_ITEMS = 128;
const size_t STACK_SIZE = STACK_ITEMS*sizeof(stack_item);
const size_t MINIMAL_MEMORY = (UNIT+sizeof(descriptor)+
INTERRUPT_STACK_SIZE+STACK_SIZE)*2;
bool memory_fragment::initiate()
{ bool r = true;
size_t free_memory = coreleft();
if (free_memory < MINIMAL_MEMORY)
r = false;
else
{ size_t beginning = (size_t)malloc(free_memory);
if (beginning == 0)
r = false;
else
{ beginning += UNIT-1;
size_t rest = beginning%UNIT;
beginning -= rest;
free_memory -= UNIT-1-rest;
free_memory -= free_memory%UNIT;
next = (memory_fragment *)beginning;
next->size = free_memory;
next->next = this; };
};
return r;
};
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
void *memory_fragment::take(size_t size)
{ size += 2*UNIT-1;
size -= size%UNIT;
memory_fragment *m = 0;
memory_fragment *p = this;
while (p->next != this)
{ if ((p->next->size) < size)
p = p->next;
else
if (p->next->size == size)
{ m = p->next;
p->next = p->next->next;
break; }
else
{ p->next->size -= size;
m = (memory_fragment *)((size_t)(p->next)+(p->next->size));
break; };
};
if (m != 0)
{ m->size = size;
m++; };
return (void *)m;
};
void memory_fragment::free(void *address)
{ if (address != 0)
{ memory_fragment *a = (memory_fragment *)address;
a--;
memory_fragment *p = this;
while (p->next != this)
if (a > p->next)
p = p->next;
else
break;
if ((((size_t)p)+(p->size)) == ((size_t)a))
{ p->size += a->size;
if ((((size_t)p)+(p->size)) == ((size_t)(p->next)))
{ p->size += p->next->size;
p->next = p->next->next; };
}
else
if ((((size_t)a)+(a->size)) == ((size_t)(p->next)))
{ a->size += p->next->size;
a->next = p->next->next;
p->next = a; }
else
{ a->next = p->next;
p->next = a; };
};
};
memory_fragment memory;
class mutex_type: public exclusive
{public:
mutex_type(); };
mutex_type::mutex_type(): exclusive()
{ temporary_priority = SYSTEM_PRIORITY; };
157
158
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
mutex_type mutex;
void *operator new(size_t size)
{ exclusive_block set_up(&mutex);
return memory.take(size); };
void operator delete(void *address)
{ exclusive_block set_up(&mutex);
memory.free(address); };
THREAD.CPP
Izvorna datoteka thread.cpp sadrži definiciju klase thread koja omogućuje
stvaranje niti, kao i definiciju funkcije destroy u čijoj nadležnosti je uništavanje
niti. Prilikom stvaranja i uništavanja niti, neophodno je zauzimanje i oslobađanje
radne memorije. To se ostvaruje pozivanjem operacija take i free promenljive
memory, i to iz isključivih regiona isključive promenljive mutex. Ova isključiva
promenljiva štiti konzistentnost i promenljive threads, koja sadrži naredni
neupotrebljeni serijski broj niti (polje sequencer), broj istovremeno postojećih niti
(polje count) i listu slobodnih pokazivača deskriptora niti (polje list), u koju se
uključuju
elementi
niza
pokazivača
deskriptora
niti
(polje
co_descriptors[THREADS_NUMBER_LIMIT]).
Korišćenje isključive promenljive mutex za zaštitu konzistentnosti
promenljivih memory i threads je racionalnije od proglašavanja promenljivih memory i
threads isključivim. Kada bi promenljive memory i threads bile isključive, tada bi, na
primer, u funkciji destroy, umesto jednog, bila neophodna dva isključiva regiona, i
to jedan u drugom. Zato se, u ovom modulu COLIBRY izvršioca, međusobna
isključivost se ostvaruje samo pomoću isključive promenljive mutex.
Prilikom stvaranja niti, moguće je dobiti njen identitet. Identiteti niti su
jedinstveni, jer se sastoje od adrese pokazivača deskriptora niti (polje address klase
thread_identity) i od serijskog broja niti (polje sequencer klase thread_identity).
Dok prvi deo identiteta niti može biti isti za dve različite niti koje ne postoje
istovremeno, drugi deo identiteta niti je praktično neponovljiv. To sprečava da se
pošalje alarm pogrešnoj niti, čiji prvi deo identiteta je isti kao i prvi deo identiteta
već uništene niti, kojoj je namenjen ovakav, zakašnjeli alarm.
Stvaranje niti omogućuje primena operatora new na tip izveden iz klase
thread. Kada prepozna ovakav operator new, kompajler generiše dva poziva
funkcija. Prvi od njih poziva funkciju operator new klase thread, a drugi poziva
konstruktor stvarane niti, odnosno konstruktor tipa izvedenog iz klase thread. Za
poziv funkcije operator new kompajler generiše prvi argument. To je veličina
memorije koja je potrebna za smeštanje stvaranog objekta.
Na samom početku konstruktora stvarane niti kompajler ubaci poziv
konstruktora klase thread. Poziv konstruktora stvarane niti je uslovan i realizuje se
samo ako je poziv operacije operator new vratio vrednost različitu od nule. Razlozi
očuvanja konzistentnosti zahtevaju da izvršavanja operacije operator new i
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
159
konstruktora klase thread pripadaju istom isključivom regionu. Njegov početak se
poklapa sa početkom operacije operator new (iskaz kernel.exclusive_in(&mutex);).
Kada poziv operacije operator new vraća nulu, kraj ovog isključivog regiona se
poklapa sa krajem ove operacije (iskaz kernel.exclusive_out();), a kada poziv
operacije operator new vraća vrednost različitu od nule, tada se kraj pomenutog
isključivog regiona poklapa sa krajem konstruktora klase thread (iskaz
kernel.exclusive_out();). U operaciji operator new se zauzmu, kada je to moguće,
prvo neophodna radna memorija (za deskriptor niti, za stek obrađivača prekida i za
stek niti), a onda i pokazivač deskriptora stvarane niti. Zatim se inicijalizuju ovaj
pokazivač i deskriptor niti, uvećaju se serijski broj nove niti i broj istovremeno
postojećih niti, a na stek stvarane niti se smeste sadržaji registara i pojedinih
lokacija steka niti stvaraoca, koji su potrebni za konstruktor klase thread. U
operaciji operator new se eventualno dodeli vrednost identitetu stvarane niti. Po
završetku ove operacije, ako je njena povratna vrednost različita od nule, poziva se
konstruktor stvarane niti. Na njegovom početku se poziva konstruktor klase thread,
radi pripremanja steka stvarane niti. U okviru ove pripreme, sa steka niti stvaraoca
na stek stvarane niti se prvo prepišu argumenti poziva konstruktora stvarane niti.
Zatim se na stek stvarane niti dodaju neophodni elementi za oblikovanje stek frejma
poziva konstruktora stvarane niti. Kao povratna adresa ovog stek frejma koristi se
adresa funkcije destroy. Na kraju se prepišu, sa steka niti stvaraoca na stek stvarane
niti, vrednosti dinamičkih (lokalnih) promenljivih konstruktora stvarane niti (kada
postoje). Nakon što se na stek stvarane niti smeste sadržaji koji su potrebni za njenu
aktivnost, na ovaj stek se smeštaju sadržaji potrebni za preključivanje procesora na
stvaranu nit. Oni obuhvataju povratnu adresu konstruktora klase thread i sadržaje
registara koje na steku očekuje operacija stack_swap klase kernel_type. Izgled steka
stvarane niti je prikazan u nastavku:
niže adrese
sadržaji
potrebni za
preključivanje
stek frejm
poziva
konstruktora
stvarane niti
više adrese
...
flags
di
<-sp
si
bp
ip
...
bp
ip
...
(povratna adresa konstruktora
thread)
(lokalne promenljive)
klase
(adresa funkcije destroy)
(argumenti)
...
Stvarana nit je spremna za aktiviranje nakon pripreme njenog steka. Njeno
aktiviranje omogućuje poziv operacije kernel.start_thread(threads.d). Od ovoga
160
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
poziva se razdvajaju aktivnost niti stvaraoca i stvarane niti. U produžetku aktivnosti
niti stvaraoca nastavlja se izvršavanje konstruktora klase thread. U ovom delu
izvršavanja se izmeni stek niti stvaraoca, tako da se, na kraju izvršavanja
konstruktora klase thread, njena aktivnost ne nastavlja iza poziva ovog
konstruktora, nego iza poziva operacije operator new. Na taj način se izbegava da
nit stvaralac bude aktivna u konstruktoru stvarane niti, nego ona nastavlja svoju
aktivnost u sopstvenom konstruktoru (iza poziva operacije operator new). Ali, da
opet ne bi došlo do poziva konstruktora stvarane niti, na steku niti stvaraoca je
pripremljena 0 kao povratna vrednost poziva operacije operator new. To objašnjava
zašto poziv operacije operator new klase thread uvek vraća vrednost 0.
Stvorena nit se aktivira kada se na nju preključi procesor. Njena aktivnost
započne iza poziva konstruktora klase thread, jer je povratna adresa poziva ovog
konstruktora smeštena na stek među sadržaje koji se koriste u preključivanju. To
znači da se u aktivnosti stvorene niti ne produžava izvršavanje konstruktora klase
thread. Na kraju aktivnosti stvorene niti dolazi do izvršavanja funkcije destroy, jer
se adresa ove funkcije nalazi na steku stvorene niti na mestu povratne adrese u stek
frejmu poziva konstruktora stvarane niti. Prema tome, na kraju aktivnosti stvorene
niti, u toku izvršavanja funkcije destroy, proverava se da li se završava aktivnost
poslednje niti. Ako se završava aktivnost poslednje niti, tada se izvršavanje
COLIBRY programa završava pozivom funkcije quit. Inače se uništava nit, čija
aktivnost se završava, tako što se oslobode pokazivač njenog deskriptora i njena
radna memorija.
U nastavku je naveden sadržaj datoteke thread.cpp:
const int THREADS_NUMBER_LIMIT = 101;
class threads_support
{ unsigned long sequencer;
int count;
descriptor *d;
list_link list;
co_descriptor co_descriptors[THREADS_NUMBER_LIMIT];
public:
threads_support();
friend class thread;
friend void destroy(); };
threads_support::threads_support()
{ sequencer = 0;
count = 0;
d = 0;
for (int i = 0; i < THREADS_NUMBER_LIMIT; i++)
{ co_descriptors[i].clear();
list.insert(&co_descriptors[i]); };
};
threads_support threads;
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
void destroy()
{ destroy_block set_up(&mutex);
descriptor *d = my_descriptor();
if ((--threads.count) == 0)
quit();
d->co_descriptor_link->clear();
threads.list.insert(d->co_descriptor_link);
memory.free((void *)d); };
class thread_identity
{ void *address;
unsigned long sequencer;
thread_identity(const thread_identity&);
public:
thread_identity();
bool existent(unsigned long *sequencer = 0);
thread_identity& operator=(const thread_identity&);
private:
void try_alarm();
friend class thread;
friend void alarm(thread_identity *id); };
thread_identity::thread_identity()
{ address = 0; };
bool thread_identity::existent(unsigned long *sequencer)
{ exclusive_block set_up(&mutex);
if ((address != 0) && (sequencer != 0))
*sequencer = thread_identity::sequencer;
return (address != 0); };
thread_identity& thread_identity::operator=(const thread_identity& id)
{ exclusive_block set_up(&mutex);
address = id.address;
sequencer = id.sequencer;
return *this; };
void thread_identity::try_alarm()
{ exclusive_block set_up(&mutex);
co_descriptor *co_d = (co_descriptor *)address;
if ((co_d != 0) && (co_d->descriptor_link != 0) &&
(sequencer == co_d->descriptor_link->sequencer))
co_d->descriptor_link->alarming = true; };
void alarm(thread_identity *id)
{ if (id != 0)
id->try_alarm(); };
class thread
{public:
void *operator new(size_t type_size,int priority = 1,
size_t stack_size = STACK_SIZE,
thread_identity *id = 0);
void operator delete(void *);
thread();
virtual void exception(int vector_number); };
161
162
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
void *thread::operator new(size_t type_size,int priority,
size_t stack_size,thread_identity *id)
{ void *beginning = 0;
kernel.exclusive_in(&mutex);
if (threads.list.not_empty())
{ beginning = memory.take(sizeof(descriptor)+type_size+
INTERRUPT_STACK_SIZE+stack_size);
if (beginning != 0)
{ descriptor *d = (descriptor *)beginning;
beginning = (void *)(d+1);
threads.d = d;
co_descriptor *co_d = (co_descriptor *)threads.list.extract();
co_d->descriptor_link = d;
if (id != 0)
{ id->address = (void *)co_d;
id->sequencer = threads.sequencer; };
d->initiate((stack_item *)((size_t)beginning+type_size+
INTERRUPT_STACK_SIZE),
(stack_item *)((size_t)beginning+type_size+
INTERRUPT_STACK_SIZE+stack_size),
priorities.conversion(priority),
threads.sequencer++,co_d);
threads.count++;
stack_item temporary;
asm
mov
temporary,si ;
*(d->stack_top-1) = temporary;
asm
mov
temporary,di ;
*(d->stack_top-2) = temporary;
asm
mov
ax,[bp+2]
;
asm
mov
temporary,ax ;
*(d->stack_top-3) = temporary;
asm
mov
ax,[bp]
;
asm
mov
temporary,ax ;
*(d->stack_top-4) = temporary;
asm
mov
temporary,bp ;
*(d->stack_top-5) = temporary;
};
};
if (beginning == 0)
{ if (id != 0)
id->address = 0;
kernel.exclusive_out(); };
return beginning;
};
void thread::operator delete(void *)
{ };
thread::thread()
{
stack_item si_register = *(threads.d->stack_top-1);
stack_item di_register = *(threads.d->stack_top-2);
stack_item old_return_address = *(threads.d->stack_top-3);
stack_item old_previous_frame_pointer = *(threads.d->stack_top-4);
stack_item old_frame_pointer = *(threads.d->stack_top-5);
stack_item dummy_return_address = (stack_item)destroy;
stack_item *stack_top = threads.d->stack_top;
save_stack_pointer:
asm
mov
dx,sp ;
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
change_stack_pointer:
asm
mov
sp,stack_top ;
determine_stack_copy_1_begin_and_end:
asm
mov
bx,[bp]
;
asm
mov
cx,bx
;
asm
add
cx,2
;
asm
mov
bx,[bx]
;
asm
jmp
end_of_stack_copy_1 ;
stack_copy_1:
asm
push
[bx] ;
end_of_stack_copy_1:
asm
sub
bx,2
;
asm
cmp
bx,cx
;
asm
ja
stack_copy_1 ;
prepare_dummy_return_address:
asm
push
dummy_return_address ;
prepare_dummy_base_pointer:
asm
xor
ax,ax ;
asm
push
ax
;
save_dummy_base_pointer_address:
asm
mov
ax,sp ;
determine_stack_copy_2_begin:
asm
mov
bx,[bp]
;
asm
jmp
end_of_stack_copy_2 ;
stack_copy_2:
asm
push
[bx] ;
end_of_stack_copy_2:
asm
sub
bx,2
;
asm
cmp
bx,bp
;
asm
ja
stack_copy_2 ;
prepare_base_pointer:
asm
push
ax ;
fill_rest_of_stack:
asm
push
si
;
asm
push
di
;
asm
push
INTERRUPT_FLAG ;
store_stack_pointer:
asm
mov
stack_top,sp ;
threads.d->stack_top = stack_top;
restore_stack_pointer:
asm
mov
sp,dx ;
threads.d->thread_pointer = this;
kernel.start_thread(threads.d);
change_stack_frame:
asm
mov
si,si_register
asm
mov
di,di_register
asm
mov
ax,old_return_address
asm
mov
bx,old_previous_frame_pointer
asm
mov
sp,old_frame_pointer
asm
mov
bp,sp
asm
mov
[bp],bx
asm
mov
[bp+2],ax
asm
xor
ax,ax
asm
mov
[bp+4],ax
asm
push
si
asm
push
di
kernel.exclusive_out();
};
;
;
;
;
;
;
;
;
;
;
;
;
163
164
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
void thread::exception(int)
{ };
FAILURE.CPP
Izvorna datoteka failure.cpp sadrži dve klase izvedene iz templejt klase
To su klase divide_by_zero_exception i overflow_exception. One
omogućuju reakciju na pojavu izuzetaka u toku aktivnosti niti. Reakcija se svodi na
pozivanje funkcije exception_handler koja proverava da li je reč o fatalnom
izuzetku. U slučaju fatalnog izuzetka, završava se izvršavanje COLIBRY programa
nakon poziva eksterno definisane funkcije failure_reporter, radi ispisivanja poruke
o fatalnoj grešci. Inače se završava aktivnost niti koja je izazvala izuzetak i to nakon
poziva njene funkcije exception.
U nastavku je naveden sadržaj datoteke failure.cpp:
atomic.
enum failures { MEMORY_SHORTAGE,FATAL_EXCEPTION };
extern void failure_reporter(failures);
void exception_handler(int vector_number)
{ if (inside_bios_or_interrupt() || inside_exclusion())
{ failure_reporter(FATAL_EXCEPTION);
quit(); };
kernel.get_thread_pointer()->exception(vector_number);
post_exception_handler();
destroy();
};
const int DIVIDE_BY_ZERO = 0x00;
class divide_by_zero_exception: public atomic<DIVIDE_BY_ZERO>
{public:
divide_by_zero_exception();
void interrupt_handler(); };
divide_by_zero_exception::divide_by_zero_exception():
atomic<DIVIDE_BY_ZERO>()
{ };
void divide_by_zero_exception::interrupt_handler()
{ exception_handler(interrupt_vector_number()); };
divide_by_zero_exception divide_by_zero;
const int OVERFLOW = 0x04;
class overflow_exception: public atomic<OVERFLOW>
{public:
overflow_exception();
void interrupt_handler(); };
overflow_exception::overflow_exception(): atomic<OVERFLOW>()
{ };
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
165
void overflow_exception::interrupt_handler()
{ exception_handler(interrupt_vector_number()); };
overflow_exception overflow;
MAIN.CPP
Izvorna datoteka main.cpp uključuje sve prethodno pomenute izvorne
datoteke COLIBRY izvršioca. Pored toga, ona sadrži i deklaraciju klase initial i
definiciju funkcije main. Ova funkcija pokreće inicijalizaciju slobodne radne
memorije i stvara inicijalnu niti.
Izvršavanje COLIBRY programa započinje izvršavanjem posebne funkcije,
koju generiše kompajler. U njoj se nalaze pozivi svih konstruktora globalnih
(statičkih) promenljivih COLIBRY programa. Pozivi ovih konstruktora su navedeni
u redosledu definisanja odgovarajućih promenljivih. Iza njih se nalazi poziv
funkcije main. Prema tome, izvršavanju funkcije main prethodi izvršavanje
konstruktora svih globalnih promenljivih COLIBRY programa. U toku izvršavanja
konstruktora deljene promenljive kernel inicijalizuje se deskriptor nulte niti, a ona
postane aktivna nit. Od tog trenutka, nastavak izvršavanja COLIBRY programa,
zaključno sa izvršavanjem funkcije main, odgovara aktivnosti nulte niti. Procesor se
preključi sa nulte niti na inicijalnu nit odmah po stvaranju inicijalne niti, jer je
inicijalna nit prioritetnija. Do naknadnog preključivanja na nultu nit dolazi samo
kada nema drugih spremnih niti i tada se izvršava for iskaz sa kraja funkcije main.
U nastavku je naveden sadržaj datoteke main.cpp:
# include <limits.h>
# include <alloc.h>
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
include
include
include
include
include
include
include
include
include
include
include
include
include
include
include
include
"bool.cpp"
"limited.cpp"
"list.cpp"
"exclude.cpp"
"descript.cpp"
"tag_list.cpp"
"ready.cpp"
"a_block.cpp"
"kernel.cpp"
"exclusiv.cpp"
"e_block.cpp"
"atomic.cpp"
"drivers.cpp"
"memory.cpp"
"thread.cpp"
"failure.cpp"
class initial: public thread
{public:
initial(int argc,char *argv[]); };
166
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
void main(int argc,char *argv[])
{ if (memory.initiate() == false)
{ failure_reporter(MEMORY_SHORTAGE);
quit(); };
new (TOP_PRIORITY) initial(argc,argv);
for (;;)
;
};
4.2 COLIBRY ULAZNO-IZLAZNI MODUL
COLIBRY ulazno-izlazni modul se nalazi u izvornoj datoteci colib_io.cpp.
Datoteka colib_io.cpp sadrži klase terminal_out, terminal_in i disk_io koje
omogućuju rukovanje ekranom, tastaturom i disketnom jedinicom. Ova rukovanja
se oslanjaju na BIOS operacije. Pošto pozivi BIOS operacija zahtevaju korišćenje
specifičnih asemblerskih naredbi, definisane su posebne funkcije koje posreduju u
pozivanju ovih operacija. Ove funkcije su slične sistemskim potprogramima, koji
sadrže sistemske pozive, jer se pozivi BIOS operacija suštinski ne razlikuju od
poziva sistemskih operacija. Funkcija video_bios omogućuje prikaz znaka na
ekranu, funkcija key_bios omogućuje preuzimanje znaka sa tastature, a funkcija
disk_bios omogućuje prenos bloka podataka na relaciji radna memorija - disketna
jedinica.
Funkcija key_bios sadrži dva poziva BIOS operacija. Prvi poziv proverava da
li ima znakova za preuzimanje. Ako ih nema, ova funkcija vraća konstantu
NO_CHARACTER (posredstvom registra ax). U suprotnom slučaju, dolazi do drugog
poziva, koji vraća preuzeti znak. Na ovaj način se sprečava zaustavljanje aktivnosti
niti i ujedno zaustavljanje izvršavanja celog COLIBRY programa, ako nema
znakova za preuzimanje, jer se, u tom slučaju, izbegava drugi poziv, koji blokira
izvršavanje korisničkog programa. Ako ima znakova za preuzimanje, funkcija
key_bios vraća (posredstvom registra ax) ASCII i scan kod znaka (kao polja
strukture character_code_parts). Klasa scan_type omogućuje čuvanje i isporuku
scan koda znaka. To je važno kada se pritisne dirka tastature koja ne odgovara
ASCII znakovima (odnosno, kada ASCII kod nije veći od nule), jer je tada potrebno
sačuvati i isporučiti scan kod znaka. Pošto pritisci (i otpuštanja) dirki tastature
izazivaju prekide, potreban je drajver keyboard za opsluživanje ovih prekida. Ovaj
drajver je objekat klase keyboard_type koja je nasledila klasu atomic. Klasa
keyboard_type omogućuje očekivanje pritiska dirke i objavu dešavanja ovog
događaja (polje pressed) kao i registrovanje dešavanja prekida (polje
interrupt_happened). Očekivanje pritiska dirke omogućuje operacija expect klase
keyboard_type, a objavu dešavanja ovog događaja i registrovanje dešavanja prekida
omogućuje operacija interrupt_handler ove klase.
Isključiva promenljiva bios omogućuje osiguranje međusobne isključivosti
BIOS operacija. Iz njenog isključivog regiona se pozivaju: (1) funkcija video_bios
iz tela funkcije character_put, (2) funkcija key_bios iz tela funkcije character_get i
(3) funkcija disk_bios iz tela funkcije block_transfer. Funkcija character_get
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
167
zaustavlja aktivnost niti svog pozivaoca do pojave znaka, ako u trenutku poziva
nema znakova za preuzimanje. U funkciji block_transfer pozivu funkcije disk_bios
prethodi preračunavanje broja bloka u poziciju bloka, određenu brojem staze,
površine i sektora.
Funkciju character_put poziva funkcija failure_reporter, kada prikazuje
poruku o grešci.
Klasa terminal_type omogućuje međusobnu isključivost u korišćenju
terminala (ekrana i tastature). Njeno polje occupied pokazuje da li je terminal
zauzet, polje onefold_usage pokazuje da li je terminal zauzet za jednokratno
korišćenje terminala, odnosno samo za potrebe izvršavanja jedne operacije, a polje
sequencer čuva serijski broj niti koja je zauzela terminal. Polje available
omogućuje očekivanje oslobađanja terminala, kao i objavu da je terminal
oslobođen. Konstruktor klase terminal_type omogućuje, između ostalog,
preuzimanje svih znakova koji su pristigli sa tastature pre početka izvršavanja
COLIBRY programa. Njene operacije entry_protocol i exit_protocol omogućuju
zauzimanje terminala za jednokratno korišćenje i njegovo oslobađanje nakon
jednokratnog korišćenja. Na ove operacije se oslanja klasa embracing_protocol koja
osigurava međusobnu isključivost operacija za rukovanje terminalom.
Funkcije on i off omogućuju, respektivno, zauzimanje terminala za
višekratno korišćenje i njegovo oslobađanje nakon višekratnog korišćenja.
Operacije long_put, unsigned_long_put i double_put klase terminal_out
omogućuju pretvaranje binarnog oblika vrednosti odgovarajućeg tipa u znakovni
oblik i njegovo prikazivanje na ekranu. Prethodne operacije se pozivaju iz
isključivih regiona operacija operator<< iste klase.
Operacija empty klase terminal_in omogućuje proveru da li ima znakova za
preuzimanje. Operacije long_get i double_get klase terminal_in omogućuju
preuzimanje znakovnog oblika vrednosti odgovarajućeg tipa i njegovo pretvaranje u
binarni oblik. Prethodne operacije se pozivaju iz isključivih regiona operacija
operator>> iste klase.
Tastaturu reprezentuje promenljiva tin, ekran reprezentuje promenljiva tout,
a jedinice masovne memorije A i B reprezentuju promenljive disk_a i disk_b.
U nastavku je naveden sadržaj datoteke colib_io.cpp:
# include <colibry.h>
const
const
const
const
char
char
char
char
VIDEO = 0x10;
VIDEO_PUT = 0x0e;
COLOR = 7;
PAGE = 0;
168
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
void video_bios(char character)
{
asm
pushf
asm
cmp
si,di
asm
mov
al,character
asm
mov
ah,VIDEO_PUT
asm
mov
bl,COLOR
asm
mov
bh,PAGE
asm
int
VIDEO
asm
popf
;
;
;
;
;
;
;
; };
exclusive bios;
void character_put(char c)
{ exclusive_block set_up(&bios);
video_bios(c); };
enum failures { MEMORY_SHORTAGE,FATAL_EXCEPTION };
const char FAILURES_HEADER[] = "\n\r COLIBRY ERROR: ";
const char FAILURE1[] = "MEMORY SHORTAGE! \n\r";
const char FAILURE2[] = "FATAL EXCEPTION! \n\r";
void failure_reporter(failures kind)
{ const char *header = FAILURES_HEADER;
const char *message = (kind == MEMORY_SHORTAGE) ? FAILURE1 : FAILURE2;
while (*header != 0)
video_bios(*header++);
while (*message != 0)
video_bios(*message++);
};
const int KEYBOARD = 0x09;
class keyboard_type: public atomic<KEYBOARD>
{ event pressed;
bool interrupt_happened;
public:
keyboard_type();
protected:
void interrupt_handler();
public:
void expect(); };
keyboard_type::keyboard_type(): atomic<KEYBOARD>()
{ atomic_block set_up;
interrupt_happened = false; };
void keyboard_type::interrupt_handler()
{ interrupt_happened = true;
pressed.notify(); };
void keyboard_type::expect()
{ atomic_block set_up;
if (interrupt_happened == false)
pressed.expect();
interrupt_happened = false; };
keyboard_type keyboard;
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
const int NO_CHARACTER = -1;
const char KEY = 0x16;
const char KEY_TEST = 1;
const char KEY_GET = 0;
struct character_code_parts
{ char ascii;
char scan; };
character_code_parts key_bios()
{
asm
pushf
asm
cmp
si,di
asm
mov
ah,KEY_TEST
asm
int
KEY
asm
jnz
get
asm
mov
ax,NO_CHARACTER
asm
jmp
exit
get:
asm
mov
ah,KEY_GET
asm
int
KEY
exit:
asm
popf
;
;
;
;
;
;
;
;
;
; };
class scan_type
{ char scan_code;
bool scan_code_exists;
scan_type(const scan_type&);
scan_type& operator=(const scan_type&);
public:
scan_type();
bool get(char *c);
void put(char c); };
scan_type::scan_type()
{ scan_code_exists = false; };
bool scan_type::get(char *c)
{ bool r = scan_code_exists;
if (scan_code_exists)
{ *c = scan_code;
scan_code_exists = false; };
return r;
};
void scan_type::put(char c)
{ scan_code = c;
scan_code_exists = true; };
scan_type scan;
bool non_ascii(char c)
{ return (c <= 0); };
169
170
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
char character_get()
{ char r;
if (scan.get(&r) == false)
{ character_code_parts c;
{ exclusive_block set_up(&bios);
c = key_bios(); };
r = c.ascii;
while (r == NO_CHARACTER)
{ keyboard.expect();
{ exclusive_block set_up(&bios);
c = key_bios(); };
r = c.ascii;
};
if (non_ascii(r))
scan.put(c.scan);
};
return r;
};
const int INT_SIGNIFICANT_FIGURES_COUNT = 4;
const int LONG_SIGNIFICANT_FIGURES_COUNT = 9;
const int DOUBLE_SIGNIFICANT_FIGURES_COUNT = 7;
class terminal_type: public exclusive
{ bool occupied;
bool onefold_usage;
condition available;
unsigned long sequencer;
public:
terminal_type();
private:
void entry_protocol();
void exit_protocol();
friend class embracing_protocol;
friend void ton();
friend void toff(); };
terminal_type::terminal_type(): exclusive()
{ occupied = false;
onefold_usage = true;
while (key_bios().ascii != NO_CHARACTER)
; };
void terminal_type::entry_protocol()
{ exclusive_block set_up(this);
if (occupied)
{ if (my_sequencer() != sequencer)
{ available.await();
occupied = true;
sequencer = my_sequencer(); };
}
else
{ occupied = true;
sequencer = my_sequencer(); };
};
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
void terminal_type::exit_protocol()
{ exclusive_block set_up(this);
if ((occupied) && (my_sequencer() == sequencer) && (onefold_usage))
{ occupied = false;
available.signal(); };
};
terminal_type terminal;
class embracing_protocol
{ embracing_protocol(const embracing_protocol&);
embracing_protocol& operator=(const embracing_protocol&);
public:
embracing_protocol();
~embracing_protocol(); };
embracing_protocol::embracing_protocol()
{ terminal.entry_protocol(); };
embracing_protocol::~embracing_protocol()
{ terminal.exit_protocol(); };
void ton()
{ exclusive_block set_up(&terminal);
if ((terminal.occupied) && (my_sequencer() != terminal.sequencer))
terminal.available.await();
terminal.occupied = true;
terminal.onefold_usage = false;
terminal.sequencer = my_sequencer(); };
void toff()
{ exclusive_block set_up(&terminal);
if ((terminal.occupied) && (my_sequencer() == terminal.sequencer))
{ terminal.occupied = false;
terminal.onefold_usage = true;
terminal.available.signal(); };
};
class terminal_out
{ char string[DOUBLE_SIGNIFICANT_FIGURES_COUNT+7];
char digits[DOUBLE_SIGNIFICANT_FIGURES_COUNT+1];
void long_put(long number,int figures_count);
void unsigned_long_put(unsigned long number,int figures_count);
void double_put(double number,int figures_count);
void string_put(const char *string);
public:
terminal_out& operator<<(int number);
terminal_out& operator<<(unsigned int number);
terminal_out& operator<<(long number);
terminal_out& operator<<(unsigned long number);
terminal_out& operator<<(double number);
terminal_out& operator<<(char character);
terminal_out& operator<<(const char *string); };
171
172
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
void terminal_out::long_put(long number,int figures_count)
{ int i = figures_count+1;
string[i--] = '\0';
if (number == 0)
string[i--] = '0';
else
{ char sign = ' ';
if (number < 0)
{ sign = '-';
number *= -1; };
while (number > 0)
{ string[i--] = number%10+'0';
number /= 10; };
string[i--] = sign;
};
while (i >= 0)
string[i--] = ' ';
string_put(string);
};
void terminal_out::unsigned_long_put(unsigned long number,int figures_count)
{ int i = figures_count+1;
string[i--] = '\0';
if (number == 0)
string[i--] = '0';
else
while (number > 0)
{ string[i--] = number%10+'0';
number /= 10; };
while (i >= 0)
string[i--] = ' ';
string_put(string);
};
void terminal_out::double_put(double number,int significant_figures_count)
{ int i = 0;
if (number == 0.)
{ string[i++] = ' ';
string[i++] = '0';
string[i++] = '.';
string[i++] = '\0'; }
else
{ int exponent = 0;
int j = significant_figures_count;
double nines = 9.;
double shift = 1.;
while ((--j) > 0)
{ nines = nines*10.+9.;
shift *= 10.; };
char sign = ' ';
if (number < 0.)
{ sign = '-';
number *= -1.; };
if (number > nines)
{ exponent = significant_figures_count-1;
do
{ number /= 10.;
exponent++; }
while (number > nines); }
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
else
if (number < 1.)
{ do
{ number *= 10.;
exponent--; }
while (number < 1.);
number *= shift; }
else
while (number < shift)
{ number *= 10.;
exponent++; };
number += 0.5;
if (number > (nines+0.5))
{ number /= 10.;
exponent++; };
long significant = (long)number;
j = significant_figures_count;
digits[j--] = ' ';
while ( j >= 0)
{ digits[j--] = significant%10+'0';
significant /= 10; };
j = significant_figures_count;
if ((exponent >= significant_figures_count) || (exponent < 0))
{ while (j > 0)
if (digits[--j] == '0')
digits[j] = ' ';
else
j = 0;
string[i++] = sign;
if ((exponent < 0) && (exponent >= -significant_figures_count))
{ string[i++] = '0';
string[i++] = '.';
while (++exponent < 0)
string[i++] = '0';
while (digits[j] != ' ')
string[i++] = digits[j++]; }
else
{ string[i++] = digits[j++];
string[i++] = '.';
while (digits[j] != ' ')
string[i++] = digits[j++];
string[i++] = 'e';
if (exponent < 0)
{ string[i++] = '-';
exponent *= -1; };
if (exponent > 9)
string[i++] = exponent/10+'0';
string[i++] = exponent%10+'0';
};
}
else
{ while (j > (significant_figures_count-exponent))
if (digits[--j] == '0')
digits[j] = ' ';
else
break;
j = 0;
string[i++] = sign;
173
174
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
while (j < (significant_figures_count-exponent))
string[i++] = digits[j++];
string[i++] = '.';
while (digits[j] != ' ')
string[i++] = digits[j++]; };
string[i++] = '\0';
};
string_put(string);
};
void terminal_out::string_put(const char *string)
{ while (*string != '\0')
character_put(*string++);
};
terminal_out& terminal_out::operator<<(int number)
{ embracing_protocol set_up;
long_put(number,INT_SIGNIFICANT_FIGURES_COUNT+1);
return *this; };
terminal_out& terminal_out::operator<<(unsigned int number)
{ embracing_protocol set_up;
unsigned_long_put(number,INT_SIGNIFICANT_FIGURES_COUNT+1);
return *this; };
terminal_out& terminal_out::operator<<(long number)
{ embracing_protocol set_up;
long_put(number,LONG_SIGNIFICANT_FIGURES_COUNT+1);
return *this; };
terminal_out& terminal_out::operator<<(unsigned long number)
{ embracing_protocol set_up;
unsigned_long_put(number,LONG_SIGNIFICANT_FIGURES_COUNT+1);
return *this; };
terminal_out& terminal_out::operator<<(double number)
{ embracing_protocol set_up;
double_put(number,DOUBLE_SIGNIFICANT_FIGURES_COUNT);
return *this; };
terminal_out& terminal_out::operator<<(char character)
{ embracing_protocol set_up;
character_put(character);
return *this; };
terminal_out& terminal_out::operator<<(const char *string)
{ embracing_protocol set_up;
string_put(string);
return *this; };
class terminal_in
{ long long_get(int figures_count);
double double_get(int figures_count);
public:
bool empty();
terminal_in& operator>>(int& number);
terminal_in& operator>>(long& number);
terminal_in& operator>>(double& number);
terminal_in& operator>>(char& character); };
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
bool terminal_in::empty()
{
asm
pushf
asm
cmp
si,di
asm
mov
ah,KEY_TEST
asm
int
KEY
asm
mov
ax,false
asm
jnz
exit
asm
mov
ax,true
exit:
asm
popf
;
;
;
;
;
;
;
; };
long terminal_in::long_get(int figures_count)
{ long r;
bool deleted;
int deleting_count;
do
{ deleted = false;
deleting_count = 0;
r = 0;
char sign = ' ';
char c = character_get();
if ((c == '-') || (c == '+'))
{ sign = c;
deleting_count++;
character_put(c);
c = character_get(); };
int count = figures_count;
while ((c >= '0') && (c <= '9'))
{ character_put(c);
r = r*10+(c-'0');
c = (--count > 0) ? character_get() : ' '; };
switch (c)
{ case '\b': deleting_count += figures_count-count;
while (deleting_count-- > 0)
{ character_put(c);
character_put(' ');
character_put(c); };
deleted = true;
break;
case '\r': character_put(' ');
break;
default:
if (non_ascii(c))
{ character_get();
character_put(' '); }
else
if (count > 0)
character_put(c);
break;
};
if (sign == '-')
r *= -1;
}
while (deleted);
return(r);
};
175
176
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
double terminal_in::double_get(int figures_count)
{ double r;
bool deleted;
int deleting_count;
do
{ deleted = false;
int count = figures_count;
deleting_count = 0;
r = 0.;
double f = 10.;
char sign = ' ';
char c = character_get();
if ((c == '-') || (c == '+'))
{ sign = c;
deleting_count++;
character_put(c);
c = character_get(); };
while ((c >= '0') && (c <= '9'))
{ character_put(c);
r = r*10.+(c-'0');
c = (--count > 0) ? character_get() : ' '; };
if (c == '.')
{ character_put(c);
deleting_count++;
c = character_get();
while ((c >= '0') && (c <= '9'))
{ character_put(c);
r = r+(c-'0')/f;
f = f*10.;
c = (--count > 0) ? character_get() : ' '; };
};
if (c == 'e')
{ character_put(c);
deleting_count++;
char exponent_sign = ' ';
c = character_get();
if ((c == '-') || (c == '+'))
{ exponent_sign = c;
deleting_count++;
character_put(c);
c = character_get(); };
if ((c >= '0') && (c <= '9'))
{ int exponent = c-'0';
if (exponent_sign == '-')
while (exponent-- > 0)
r /= 10.;
else
while (exponent-- > 0)
r *= 10.; };
};
switch (c)
{ case '\b': deleting_count += figures_count-count;
while (deleting_count-- > 0)
{ character_put(c);
character_put(' ');
character_put(c); };
deleted = true;
break;
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
case '\r': character_put(' ');
break;
default:
if (non_ascii(c))
{ character_get();
character_put(' '); }
else
if (count > 0)
character_put(c);
break;
};
if (sign == '-')
r *= -1.;
}
while (deleted);
return(r);
};
terminal_in& terminal_in::operator>>(int& number)
{ embracing_protocol set_up;
number = (int)long_get(INT_SIGNIFICANT_FIGURES_COUNT);
return *this; };
terminal_in& terminal_in::operator>>(long& number)
{ embracing_protocol set_up;
number = long_get(LONG_SIGNIFICANT_FIGURES_COUNT);
return *this; };
terminal_in& terminal_in::operator>>(double& number)
{ embracing_protocol set_up;
number = double_get(DOUBLE_SIGNIFICANT_FIGURES_COUNT);
return *this; };
terminal_in& terminal_in::operator>>(char& character)
{ embracing_protocol set_up;
character = character_get();
character_put(character);
return *this; };
terminal_in tin;
terminal_out tout;
const char ONE_BLOCK_TRANSFER = 1;
const char DISK = 0x13;
177
178
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
int disk_bios(char operation,char *buffer,char unit,
char cylinder,char surface,char sector)
{
asm
pushf
;
asm
cmp
si,di
;
asm
mov
ah,operation
;
asm
mov
al,ONE_BLOCK_TRANSFER ;
asm
mov
bx,buffer
;
asm
push
ds
;
asm
pop
es
;
asm
mov
ch,cylinder
;
asm
mov
cl,sector
;
asm
mov
dh,surface
;
asm
mov
dl,unit
;
asm
int
DISK
;
asm
mov
al,ah
;
asm
xor
ah,ah
;
asm
popf
; };
const unsigned SECTORS_PER_TRACK = 9;
const unsigned SECTOR_INTERLEAVING = 5;
unsigned interleaved(unsigned block)
{ return (block%SECTORS_PER_TRACK)*SECTOR_INTERLEAVING; };
const char SURFACES_PER_CYLINDER = 2;
const char RETRY = 3;
const int OK = 0;
int block_transfer(char direction,char *buffer,char unit,unsigned block)
{ char surface = (char)(block/SECTORS_PER_TRACK);
char cylinder = surface/SURFACES_PER_CYLINDER;
surface = surface%SURFACES_PER_CYLINDER;
char sector = (char)(interleaved(block)%SECTORS_PER_TRACK+1);
exclusive_block set_up(&bios);
int r;
for (char i = 0; i < RETRY; i++)
if ((r = disk_bios(direction,buffer,unit,cylinder,surface,sector)) == OK)
break;
return r;
};
const int DISK_BLOCK = 512;
class disk_io
{ char unit;
unsigned blocks_number;
disk_io(const disk_io&);
disk_io& operator=(const disk_io&);
public:
disk_io(char unit,unsigned blocks_number = 720);
int block_get(char *buffer,unsigned block);
int block_put(char *buffer,unsigned block); };
disk_io::disk_io(char unit,unsigned blocks_number)
{ disk_io::unit = unit;
disk_io::blocks_number = blocks_number; };
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
179
const char DISK_READ = 2;
const char DISK_WRITE = 3;
const int ERROR = -1;
int disk_io::block_get(char *buffer,unsigned block)
{ int r;
if (block < blocks_number)
r = block_transfer(DISK_READ,buffer,unit,block);
else
r = ERROR;
return r; };
int disk_io::block_put(char *buffer,unsigned block)
{ int r;
if (block < blocks_number)
r = block_transfer(DISK_WRITE,buffer,unit,block);
else
r = ERROR;
return r; };
disk_io disk_a(0);
disk_io disk_b(1);
4.3 DATOTEKE ZAGLAVLJA COLIBRY.H I COLIB_IO.H
Datoteke zaglavlja COLIBRY.H i COLIB_IO.H sadrže deklaracije klasa,
funkcija i promenljivih koje predstavljaju korisnički interfejs COLIBRY izvršioca i
COLIBRY ulazno-izlaznog modula. Pošto se COLIBRY izvršilac i COLIBRY
ulazno-izlazni modul linkuju u izvršni oblik COLIBRY programa, oni nisu zaštićeni
od grešaka korisničkog programa, pa je zato važno da se COLIBRY program
izvršava u emuliranom okruženju, koje sprečava da pomenute greške ugroze rad
računara na kome se COLIBRY program izvršava.
U nastavku je naveden sadržaj datoteke colibry.h:
#ifndef _SIZE_T
#define _SIZE_T
typedef unsigned size_t;
#endif
typedef int bool;
const bool false = 0;
const bool true = 1;
const int TOP_PRIORITY = 63;
180
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
class list_link
{ list_link *left;
list_link *right;
list_link(const list_link&);
list_link& operator=(const list_link&);
public:
list_link();
void initiate();
void insert(list_link *link);
list_link *extract();
bool not_empty() const;
bool empty() const; };
typedef unsigned long tag_type;
class tag_list_link: public list_link
{ tag_list_link(const tag_list_link&);
tag_list_link& operator=(const tag_list_link&);
protected:
list_link *position;
public:
tag_list_link();
bool first(tag_type *t = 0);
bool last(tag_type *t = 0);
bool succ(tag_type *t = 0);
bool attach_tag(tag_type t); };
class exclude: public list_link
{ bool free;
int temporary_priority;
int thread_priority;
exclude *previous;
protected:
list_link signalled;
public:
exclude(int priority = 1);
private:
bool not_free() const;
void take();
void release(); };
typedef int stack_item;
class atomic_block
{ int flags;
atomic_block(const atomic_block&);
atomic_block& operator=(const atomic_block&);
int disable_interrupts();
void restore_interrupts(int flag);
public:
atomic_block();
~atomic_block(); };
extern unsigned long my_sequencer();
extern bool alarmed();
extern void yield();
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
class exclusive: public exclude
{ static exclusive *pointer;
protected:
class condition: public list_link
{exclusive *ex;
public:
condition();
bool awaited() const;
void await();
void signal(); };
class tag_condition: public tag_list_link
{exclusive *ex;
public:
tag_condition();
bool awaited() const;
void await(tag_type t);
void signal(); };
public:
exclusive(int priority = 1);
};
class exclusive_block
{ exclusive_block(const exclusive_block&);
exclusive_block& operator=(const exclusive_block&);
public:
exclusive_block(exclusive *ex);
~exclusive_block(); };
class atomic_backup
{ unsigned old_address_offset;
unsigned old_address_segment;
int vector_number;
bool bios;
static atomic_backup *list;
atomic_backup *next;
atomic_backup(const atomic_backup&);
atomic_backup& operator=(const atomic_backup&);
protected:
class event: public list_link
{public:
event();
bool expected() const;
void expect();
void notify(); };
class tag_event: public tag_list_link
{public:
tag_event();
bool expected() const;
void expect(tag_type t);
void notify(); };
public:
atomic_backup();
void atomic_backup_initiate(int vector_number,unsigned new_handler,
atomic_backup *pointer);
protected:
virtual void interrupt_handler();
int interrupt_vector_number() const; };
181
182
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
extern void mediator(atomic_backup **address_of_pointer);
extern void save_working_registers();
template<int VECTOR_NUMBER>
class atomic: public atomic_backup
{ static atomic_backup *pointer;
union
{ void (atomic::*actuator_address)();
unsigned alias_actuator_address; };
void actuator();
public:
atomic();
};
template<int VECTOR_NUMBER>
atomic_backup *atomic<VECTOR_NUMBER>::pointer = 0;
template<int VECTOR_NUMBER>
void atomic<VECTOR_NUMBER>::actuator()
{ save_working_registers();
mediator(&pointer); };
template<int VECTOR_NUMBER>
atomic<VECTOR_NUMBER>::atomic()
{ actuator_address = &atomic<VECTOR_NUMBER>::actuator;
atomic_backup_initiate(VECTOR_NUMBER,alias_actuator_address,pointer);
pointer = this; };
extern
extern
extern
extern
void quit();
void time_set(unsigned long time);
unsigned long time_get();
void quantum_set(unsigned long quantum);
const size_t STACK_ITEMS = 128;
const size_t STACK_SIZE = STACK_ITEMS*sizeof(stack_item);
const int THREADS_NUMBER_LIMIT = 101;
extern void destroy();
class thread_identity
{ void *address;
unsigned long sequencer;
thread_identity(const thread_identity&);
public:
thread_identity();
bool existent(unsigned long *sequencer = 0);
thread_identity& operator=(const thread_identity&);
private:
void try_alarm(); };
extern void alarm(thread_identity *id);
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
class thread
{public:
void *operator new(size_t type_size,int priority = 1,
size_t stack_size = STACK_SIZE,
thread_identity *id = 0);
void operator delete(void *);
thread();
virtual void exception(int vector_number); };
extern void delay(unsigned long duration);
extern void delay_till(unsigned long moment);
class initial: public thread
{public:
initial(int argc,char *argv[]); };
U nastavku je naveden sadržaj datoteke colib_io.h:
const int INT_SIGNIFICANT_FIGURES_COUNT = 4;
const int LONG_SIGNIFICANT_FIGURES_COUNT = 9;
const int DOUBLE_SIGNIFICANT_FIGURES_COUNT = 7;
extern void ton();
extern void toff();
const char NEW_LINE[] = "\n\r";
class terminal_out
{ char string[DOUBLE_SIGNIFICANT_FIGURES_COUNT+7];
char digits[DOUBLE_SIGNIFICANT_FIGURES_COUNT+1];
void long_put(long number,int figures_count);
void unsigned_long_put(unsigned long number,int figures_count);
void double_put(double number,int figures_count);
void string_put(const char *string);
public:
terminal_out& operator<<(int number);
terminal_out& operator<<(unsigned int number);
terminal_out& operator<<(long number);
terminal_out& operator<<(unsigned long number);
terminal_out& operator<<(double number);
terminal_out& operator<<(char character);
terminal_out& operator<<(const char *string); };
class terminal_in
{ long long_get(int figures_count);
double double_get(int figures_count);
public:
bool empty();
terminal_in& operator>>(int& number);
terminal_in& operator>>(long& number);
terminal_in& operator>>(double& number);
terminal_in& operator>>(char& character); };
extern terminal_in tin;
extern terminal_out tout;
const int DISK_BLOCK = 512;
183
184
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
class disk_io
{ char unit;
unsigned blocks_number;
disk_io(const disk_io&);
disk_io& operator=(const disk_io&);
public:
disk_io(char unit,unsigned blocks_number = 720);
int block_get(char *buffer,unsigned block);
int block_put(char *buffer,unsigned block); };
extern disk_io disk_a;
extern disk_io disk_b;
4.4 PITANJA
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
Kakvu ulogu ima klasa exclude?
Kakvu ulogu ima klasa descriptor?
Kakvu ulogu ima klasa co_descriptor?
Kakvu ulogu ima klasa tag_list_link?
Kakvu ulogu ima tabela index?
Kako klasa atomic_block ostvaruje atomske regione?
Kakvu namenu ima klasa kernel?
Da li klasa kernel spada u atomske klase?
Šta se dešava u toku preključivanja procesora sa jedne niti na drugu nit?
Koje vrste raspoređivanja podržava klasa kernel?
Zašto tela operacija expect, tag_expect i notify klase kernel ne sadrže atomske
regione?
Šta je namena klase exclusive?
Kakav problem može da nastane stvaranjem više dinamičkih isključivih
promenljivih?
Kako klasa exclusive_block ostvaruje isključive regione?
Zašto je klasa atomic_block templejt klasa?
Zašto mehanizam prekida ne može da poziva operacije bilo koje klase?
Šta je zadatak operacije actuator templejt klase atomic?
Šta je zadatak funkcije mediator?
Zašto se funkcija mediator ne završava kao obične funkcije?
Zašto se u operaciji interrupt_handler atomske klase contorol_break_type ne
poziva funkcija quit, nego je to prepušteno operaciji check_control_break?
Zašto su uvedene operacije interrupt_disable i interrupt_enable atomske
klase clock_type?
Kakvu ulogu ima polje delta_busy atomske klase clock_type?
Kakvu ulogu ima funkcija clock_scheduling?
Šta je namena isključive promenljive mutex?
U kom trenutku se razdvajaju aktivnosti stvarane niti i niti stvaraoca?
Na koji način stvarana nit izbegava da izvršava konstruktor niti stvaraoca?
Kako se sprečava slanje alarma nepostojećoj niti?
Zašto telo operacije operator new klase thread i telo konstruktora ove klase
obrazuju jedan isključivi region?
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
29.
30.
31.
32.
Šta sadrži datoteka failure.cpp?
Kakvu namenu ima beskonačna petlja na kraju funkcije main?
Zašto je potrebna isključiva promenljiva bios?
Zašto je potrebna isključiva klasa embracing_protocol?
185
186
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
5. SLOJEVI OPERATIVNOG SISTEMA
5.1 SLOJ ZA RUKOVANJE PROCESIMA
Osnovni zadaci sloja za rukovanje procesima su stvaranje i uništenje procesa.
Stvaranje procesa obuhvata stvaranje njegove slike i njegovog deskriptora, kao i
pokretanje njegove aktivnosti. Uništenje procesa obuhvata zaustavljanje njegove
aktivnosti, kao i uništenje njegove slike i njegovog deskriptora. Pored sistemskih
operacija stvaranja i uništenja procesa, potrebne su i sistemske operacije za izmenu
atributa deskriptora procesa, na primer, za izmenu radnog imenika procesa.
Slika procesa obuhvata niz lokacija radne memorije sa uzastopnim (logičkim)
adresama. Ona sadrži izvršavane mašinske naredbe, promenljive i stek. Slika
procesa je prikazana na slici 5.1.1.
najniža adresa
mašinske
naredbe
promenljive
↓
...
↑
slobodna
radna memorija
procesa
stek
najviša adresa
Slika 5.1.1 Grafička predstava slike procesa
Podrazumeva se da slika procesa započinje od lokacije radne memorije sa
najnižom adresom, od koje započinju mašinske naredbe, a završava na lokaciji
radne memorije sa najvišom adresom, na kojoj započinje stek. Pri tome se
podrazumeva da se stek širi (puni) u smeru nižih adresa. Iza mašinskih naredbi
dolaze statičke promenljive (i to prvo inicijalizovane, pa neinicijalizovane). Između
ovih promenljivih i steka se nalazi slobodna radna memorija procesa. Ona je na
raspolaganju procesu za stvaranje dinamičkih promenljivih, ali i za širenje
(punjenje) steka. Svi dinamički zahtevi za zauzimanjem radne memorije,
postavljeni u toku aktivnosti procesa, se zadovoljavaju samo na račun slobodne
radne memorije procesa. Ovakva organizacija slike procesa uslovljava da proces
prvo ugrozi svoju aktivnost, kada njegovi zahtevi za radnom memorijom nadmaše
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
187
njegovu raspoloživu slobodnu radnu memoriju. Na primer, ako ne postoji način da
se automatski ustanovi, prilikom širenja steka, da su zahtevi za radnom memorijom
nadmašili slobodnu radnu memoriju procesa, tada dolazi do preklapanja steka i
promenljivih, sa fatalnim ishodom po aktivnost procesa.
Pored slike, za aktivnost procesa je važan i deskriptor procesa, koji sadrži
atribute procesa. Ovi atributi karakterišu aktivnost procesa. Oni obuhvataju:
1. stanje procesa (spreman, aktivan, čeka),
2. sadržaje procesorskih registara (zatečene u njima pre poslednjeg
preključivanja procesora sa procesa),
3. numeričku oznaku vlasnika procesa,
4. oznaku procesa stvaraoca,
5. trenutak pokretanja aktivnosti procesa,
6. ukupno trajanje aktivnosti procesa (odnosno, ukupno vreme angažovanja
procesora),
7. podatke o slici procesa (njenom položaju u radnoj memoriji i njenoj
veličini),
8. podatke o datotekama koje proces koristi,
9. podatak o radnom imeniku procesa i
10. razne podatke neophodne za upravljanje aktivnošću procesa (poput
prioriteta procesa ili položaja sistemskog steka procesa, koga koristi
operativni sistem u toku obavljanja sistemskih operacija).
SISTEMSKE OPERACIJE ZA STVARANJE I UNIŠTENJE
PROCESA
Za stvaranje procesa potrebno je pristupiti odgovarajućoj izvršnoj datoteci sa
inicijalnom slikom procesa, koja, između ostalog, sadrži mašinske naredbe i
početne vrednosti (inicijalizovanih) statičkih promenljivih programa, ali i podatak o
veličini (pojedinih delova) slike procesa. Takođe, potrebno je zauzeti deskriptor
procesa, kao i dovoljno veliku zonu radne memorije za sliku procesa. Sve to, kao i
pravljenje slike procesa na osnovu njegove inicijalne slike, odnosno popunjavanje
atributa njegovog deskriptora, spada u nadležnost sistemske operacije stvaranja
procesa. Ovu operaciju poziva proces stvaralac i ona se obavlja u toku njegove
aktivnosti. U okviru poziva sistemske operacije stvaranja procesa kao argument se
navodi putanja odgovarajuće izvršne datoteke. Svi atributi deskriptora stvaranog
procesa ne moraju biti navođeni u okviru poziva sistemske operacije stvaranja
procesa, jer se jedan njihov deo nasleđuje iz deskriptora procesa stvaraoca (na
primer, numerička oznaka vlasnika procesa, podatak o radnom imeniku procesa ili
njegov prioritet), a jedan deo nastaje u toku stvaranja procesa (na primer, podaci o
slici procesa). Kada se, u okviru stvaranja procesa, stigne do pokretanja njegove
aktivnosti, moguće je preključivanje procesora sa procesa stvaraoca na stvarani
proces. To se desi, ako je prioritet stvaranog procesa viši od prioriteta procesa
188
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
stvaraoca. U tom slučaju, proces stvaralac dospeva među spremne procese. Inače
tamo dospeva stvarani proces.
Za uništenje procesa potrebno je osloboditi njegov deskriptor i zonu radne
memorije sa njegovom slikom. Ovo spada u nadležnost sistemske operacije
uništenja procesa. Nju automatski poziva proces na kraju svoje aktivnosti, čime
izaziva svoje samouništenje. Uništenje procesa se završava preključivanjem
procesora sa uništavanog na neki od spremnih procesa. U okviru poziva sistemske
operacije uništenja procesa uputno je predvideti argument, posredstvom koga
uništavani proces može da saopšti svom stvaraocu svoje završno stanje, odnosno
informaciju da li je aktivnost uništavanog procesa bila uspešna ili ne. Jasno, da bi
proces stvaralac mogao iskoristiti ovakvu povratnu informaciju od stvorenog
procesa, on mora da, u okviru poziva sistemske operacije stvaranja procesa,
posebnim argumentom zatraži zaustavljanje svoje aktivnosti i tako omogući
preključivanje procesora na stvarani proces. U ovom slučaju, proces stvaralac ne
dospeva među spremne procese, nego među procese u stanju čekanja, jer čeka kraj
aktivnosti stvorenog procesa. Takođe, u ovom slučaju sistemska operacija uništenja
stvorenog procesa ima i zadatak da prevede proces stvaralac među spremne procese
i tako omogući nastavak njegove aktivnosti.
Sistemska operacija uništenja procesa se automatski poziva, kada se desi
nepopravljiv prekid (izuzetak) u toku aktivnosti procesa.
ZAMENA SLIKA PROCESA
Najveći mogući broj slika procesa, koje mogu da istovremeno postoje u
radnoj memoriji, se naziva stepen multiprogramiranja (degree of
multiprogramming). Što je stepen multiprogramiranja viši, to je i veća verovatnoća
da je procesor zaposlen, jer je veća verovatnoća da postoji spreman proces. Stepen
multiprogramiranja zavisi od veličine radne memorije. Kada broj istovremeno
postojećih procesa dostigne stepen multiprogramiranja, stvaranje novih procesa
postaje problematično. U ovoj situaciji moguće rešenje je da se slika nekog od
(manje prioritetnih) postojećih procesa privremeno izbaci u masovnu memoriju i
tako oslobodi prostor u radnoj memoriji za sliku novog procesa. Predloženi pristup
podrazumeva da je broj deskriptora procesa veći od stepena multiprogramiranja i da
su svi deskriptori stalno prisutni u radnoj memoriji, što ne predstavlja problem, jer
deskriptori procesa ne zauzimaju mnogo radne memorije. Prethodno opisani način
oslobađanja prostora za sliku procesa u radnoj memoriji uzrokuje da, uz sliku
procesa u radnoj memoriji, obavezno postoji i njena kopija u masovnoj memoriji.
Pošto se, u toku aktivnosti procesa, menja samo deo njegove slike u radnoj
memoriji, jer se menjaju samo vrednosti njegovih promenljivih i njegov stek,
prilikom izbacivanja slike procesa potrebno je samo njen izmenjeni deo prebacivati
u kopiju slike u masovnoj memoriji. Ali, pri vraćanju slike u radnu memoriju,
prebacuje se cela njena kopija, da bi se u radnoj memoriji obnovila cela slika
procesa. Do vraćanja slike procesa u radnu memoriju dolazi, kada se tamo oslobodi
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
189
prostor, pa je vraćanje slike jednog procesa vezano za uništavanje drugog procesa,
kada se oslobađa prostor u radnoj memoriji.
Podaci o kopiji slike procesa (koja nastaje istovremeno sa slikom procesa, ili
prilikom njenog prvog izbacivanja, a nestaje istovremeno sa slikom procesa) se
čuvaju u deskriptoru procesa, zajedno sa podacima o slici procesa. Prilikom
izbacivanja slike procesa, u deskriptoru procesa se naznačava da se njegova slika ne
nalazi više u radnoj memoriji. Da procesi, čije su slike izbačene van radne
memorije, ne bi bili dugo zapostavljeni, uputno je periodično vršiti zamenu slika
procesa (swapping), znači izbacivati sliku jednog procesa, radi ubacivanja slike
drugog procesa. Za to je potrebna posebna operacija za zamenu slika procesa. Ona
ne spada obavezno u sistemske operacije. U nadležnosti ove operacije je
dugoročno raspoređivanje (long term scheduling), u okviru koga se odabira
proces, čija slika se izbacuje, kao i proces, čija slika se ubacuje. Važno je uočiti da
se dugoročno raspoređivanje razlikuje od običnog ili kratkoročnog
raspoređivanja (short term scheduling), koje među spremnim procesima odabira
proces na koga se preključuje procesor.
RUKOVANJE NITIMA
Rukovanje nitima može, ali i ne mora, biti u nadležnosti sloja za rukovanje
procesima. Kada je rukovanje nitima povereno sloju za rukovanje procesima, tada
operativni sistem nudi sistemske operacije za rukovanje nitima, koje omogućuju
stvaranje, uništavanje i sinhronizaciju niti. U ovom slučaju, deskriptori i sistemski
stek niti se nalaze u sistemskom prostoru, dok se sopstveni stek niti nalazi u
korisničkom prostoru (unutar slike procesa).
U slučaju kada rukovanje nitima nije u nadležnosti operativnog sistema,
brigu o nitima poptuno preuzima konkurentna biblioteka. Pošto ona pripada slici
procesa, u ovom slučaju rukovanje nitima se potpuno odvija u korisničkom
prostoru, u kome se nalaze i deskriptori niti, kao i stekovi niti.
Osnovna prednost rukovanja nitima van operativnog sistema je efikasnost, jer
su pozivi potprograma konkurentne biblioteke brži od poziva sistemskih operacija.
Ali, kada operativni sistem ne rukuje nitima, tada poziv blokirajuće sistemske
operacije iz jedne niti dovodi do zaustavljanja aktivnosti procesa kome ta nit
pripada, jer operativni sistem pripisuje sve pozive sistemskih operacija samo
procesima, pošto ne registruje postojanje niti. Na taj način se sprečava
konkurentnost unutar procesa, jer zaustavljanje aktivnosti procesa sprečava
aktivnost njegovih spremnih niti. Ova mana rukovanja nitima van operativnog
sistema ozbiljno umanjuje praktičnu vrednost ovakvog pristupa.
Sa stanovišta ostalih slojeva operativnog sistema nema suštinske razlike
između procesa i niti, pa se u nastavku izlaganja pominju samo procesi.
190
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
OSNOVA SLOJA ZA RUKOVANJE PROCESIMA
Sloj za rukovanje procesima se oslanja na sloj za rukovanje datotekama, radi
pristupa sadržaju izvršne datoteke, ali i radi rukovanja kopijama slika procesa, jer se
za njih rezerviše posebna datoteka. Pored toga, sloj za rukovanje procesima se
oslanja i na sloj za rukovanje radnom memorijom, radi zauzimanja i oslobađanja
zona radne memorije, potrebnih za smeštanje slika procesa. Na kraju, sloj za
rukovanje procesima se oslanja i na sloj za rukovanje procesorom, jer do
preključivanja dolazi prilikom stvaranja i uništenja procesa.
5.2 SISTEMSKI PROCESI
Za obavljanje pojedinih zadataka operativnog sistema prirodno je koristiti
procese. Ovakvi procesi se nazivaju sistemski procesi (daemon), jer su u službi
operativnog sistema.
NULTI PROCES
Tipičan primer sistemskog procesa je nulti ili beskonačni (idle) proces, na
koga se procesor preključuje, kada ne postoji drugi spreman proces. Znači,
beskonačan proces ima zadatak da zaposli procesor, kada nema mogućnosti za
korisnu upotrebu procesora. U toku aktivnosti beskonačnog procesa izvršava se
beskonačna petlja, što znači da je beskonačan proces uvek ili spreman ili aktivan
(on ne prelazi u stanje čekanja). Njegov prioritet je niži od prioriteta svih ostalih
procesa, a on postoji za sve vreme aktivnosti operativnog sistema.
PROCES DUGOROČNI RASPOREĐIVAČ
Drugi primer sistemskog procesa je proces dugoročni raspoređivač
(swapper), koji se brine o zameni slika procesa (jasno, kada za to ima potrebe). On
se periodično aktivira, radi pozivanja operacije za zamenu slika procesa. Nakon
toga, ovaj proces se uspava, odnosno, njegova aktivnost se zaustavlja, dok ne
nastupi trenutak za njegovo novo aktiviranje. Da bi se proces uspavao, odnosno da
bi se njegova aktivnost zaustavila do nastupanja zadanog trenutka, neophodna je
odgovarajuća sistemska operacija. Ona pripada sloju za rukovanje kontrolerima, jer
proticanje vremena registruje drajver sata. I proces dugoročni raspoređivač postoji
za sve vreme aktivnosti operativnog sistema (jasno, ako ima potrebe za dugoročnim
raspoređivanjem).
PROCESI IDENTIFIKATOR I KOMUNIKATOR
U sistemske procese spadaju i procesi identifikatori (login process), koji
podržavaju predstavljanje korisnika. Svaki proces identifikator opslužuje po jedan
terminal, da bi posredstvom svog terminala stupio u interakciju sa korisnikom u
toku njegovog predstavljanja, radi preuzimanja njegovog imena (koje se prikazuje
na ekranu) i njegove lozinke (koja se ne prikazuje na ekranu). Po preuzimanju
imena i lozinke, proces identifikator proverava njihovu ispravnost i, ako je
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
191
prepoznao korisnika, tada stvara proces komunikator, koji nastavlja interakciju sa
korisnikom. Pri tome, proces identifikator prepušta svoj terminal stvorenom procesu
komunikatoru i zaustavlja svoju aktivnost. Ona se nastavlja tek nakon završetka
aktivnosti procesa komunikatora. Tada proces identifikator opet preuzima
opsluživanje svog terminala, da bi podržao novo predstavljanje korisnika. Prema
tome, i procesi identifikatori postoje za sve vreme aktivnosti operativnog sistema.
Iz funkcije procesa komunikatora sledi da on ostvaruje interaktivni nivo
komunikacije korisnika i operativnog sistema. Ipak, proces komunikator ne spada
nužno u sistemske procese, jer njemu ne mora da odgovara samo interpreter
komandnog jezika, nego i drugi programi, poput tekst editora, na primer. U svakom
slučaju, proces komunikator prihvata sve komande, koje dolaze sa terminala i tretira
ih kao komande prepoznatog korisnika, koga je prepoznao proces identifikator. U
skladu s tim, proces komunikator koristi prava prepoznatog korisnika za pristupe
datotekama, jer je ovaj korisnik vlasnik procesa komunikatora.
Za proveru ispravnosti imena i lozinke korisnika, neophodno je raspolagati
spiskom imena i lozinki registrovanih korisnika. Ovaj spisak se čuva u posebnoj
datoteci lozinki (password file). Svaki slog ove datoteke sadrži:
1. ime i lozinku korisnika,
2. numeričku oznaku korisnika,
3. putanju korisničkog imenika i
4. putanju izvršne datoteke.
Proces identifikator u toku stvaranja procesa komunikatora koristi putanju
izvršne datoteke sa inicijalnom slikom korisničkog procesa komunikatora. Tom
prilikom on upotrebi numeričku oznaku prepoznatog korisnika kao numeričku
oznaku vlasnika stvaranog procesa komunikatora, a putanju imenika prepoznatog
korisnika upotrebi kao (početni) radni imenik stvaranog procesa komunikatora.
Slogovi datoteke lozinki se, zbog jednoznačnosti, međusobno razlikuju
obavezno po imenima i po numeričkim oznakama korisnika. Međutim, oni se mogu
razlikovati po putanjama korisničkih (početnih radnih) imenika, pa i po putanjama
izvršnih datoteka (sa inicijalnim slikama korisničkih procesa komunikatora).
Zahvaljujući tome, svaki korisnik može da ima poseban (sopstveni) radni imenik,
ali i poseban (sopstveni) proces komunikator. Za procese komunikatore, koji su
prilagođeni posebnim potrebama pojedinih korisnika (znači, kojima ne odgovara
standardni interpreter komandnog jezika), se ne smatra da su sistemski procesi, jer
oni ne predstavljaju sastavni deo operativnog sistema, iako obavljaju ulogu sloja za
spregu sa korisnikom. Zbog ovakvih procesa komunikatora, moguće je zauzeti
stanovište da sloj za spregu sa korisnikom i nije deo operativnog sistema, nego da
pripada višem (korisničkom) sloju, kome, između ostalog, pripadaju i sistemski
programi, kao što su tekst editor ili kompajler.
Za proces komunikator (kao i za svaki drugi proces) prava pristupa
datotekama se određuju na osnovu numeričke oznake njegovog vlasnika. Procesi,
koje stvara proces komunikator (radi izvršavanja pojedinih komandi korisnika)
192
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
imaju ista prava kao i proces komunikator, jer se podrazumeva da stvoreni procesi
nasleđuju numeričku oznaku vlasnika procesa stvaraoca. Prema tome, dok je u
interakciji sa procesom komunikatorom, njegov vlasnik nema mogućnosti za
narušavanje zaštite datoteka. Ali, ako, umesto vlasnika, u interakciju sa procesom
komunikatorom stupi neki drugi korisnik, zaštita datoteka se narušava, jer ovaj
drugi korisnik dobija priliku da uživa tuđa prava pristupa datotekama (koja
pripadaju vlasniku procesa komunikatora). Zato, nakon predstavljanja, korisnik ne
sme prepustiti svoj terminal drugom korisniku, pre nego što završi aktivnost svog
procesa komunikatora. Radi toga, među komandama procesa komunikatora,
obavezno postoji posebna komanda, namenjena baš procesu komunikatoru, a koja
izaziva njegovo uništenje. Znači, svaki korisnik započinje rad prijavom, u toku koje
se predstavi i pokrene aktivnost svog procesa komunikatora, a završi rad odjavom,
u toku koje okonča aktivnost svog procesa komunikatora.
Za zaštitu datoteka ključno je onemogućiti neovlaštene pristupe datoteci
lozinki. Prirodno je da njen vlasnik bude administrator i da jedino sebi dodeli pravo
čitanja i pisanja ove datoteke. Pošto su procesi identifikatori sistemski procesi, koji
nastaju pri pokretanju operativnog sistema, nema prepreke da njihov vlasnik bude
administrator. Iako na taj način procesi identifikatori dobijaju i pravo čitanja i pravo
pisanja datoteke lozinki, to pravo korisnici ne mogu da zloupotrebe, jer,
posredstvom procesa identifikatora, jedino mogu proveriti da li su registrovani u
datoteci lozinki. Pri tome je bitno da svoja prava proces identifikator ne prenosi na
proces komunikator. Zato proces komunikator ne nasleđuje numeričku oznaku
vlasnika procesa identifikatora, koji je njegov stvaralac.
Administrator bez problema pristupa datoteci lozinki, radi izmene njenog
sadržaja, jer je on vlasnik svih procesa, nastalih radi obavljanja njegovih komandi.
Pošto korisnici nemaju načina da pristupe datoteci lozinki, javlja se problem kako
omogućiti korisniku da sam izmeni svoju lozinku. Taj problem se može rešiti po
uzoru na proces identifikator, koga koriste svi korisnici, iako nisu njegovi vlasnici.
Prema tome, ako je administrator vlasnik izvršne datoteke sa inicijalnom slikom
procesa za izmenu lozinki, dovoljno je naznačiti da on treba da bude vlasnik i
procesa nastalog na osnovu ove izvršne datoteke. U tom slučaju, nema smetnje da
korisnik, koji izazove stvaranje procesa za izmenu lozinke, pristupi datoteci lozinki.
Pri tome, proces za izmenu lozinki dozvoljava korisniku samo da izmeni sopstvenu
lozinku, tako što od korisnika primi njegovo ime, važeću i novu lozinku, pa, ako su
ime i važeća lozinka ispravni, on važeću lozinku zameni novom.
Datoteka lozinki je dodatno zaštićena, ako su lozinke u njoj u kriptovanom
(encrypted) obliku, jer tada administrator može da posmatra sadržaj datoteke lozinki
na ekranu, ili da ga štampa, bez straha da to može biti zloupotrebljeno. Da bi se
sprečilo pogađanje tuđih lozinki, proces identifikator (ili proces za izmenu lozinki)
treba da reaguje na više uzastopnih neuspešnih pokušaja predstavljanja,
obaveštavajući o tome administratora, ili odbijajući neko vreme da prihvati nove
pokušaje predstavljanja. Takođe, korisnici moraju biti oprezni da sami ne odaju
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
193
svoju lozinku lažnom procesu identifikatoru. To se može desiti, ako se njihov
prethodnik ne odjavi, nego ostavi svoj proces da opslužuje terminal, oponašajući
proces identifikator. Ovakvi procesi se nazivaju trojanski konji (trojan horse).
Na kraju, važno je uočiti da, zbog istovremenog postojanja više procesa i
nepredvidivosti preključivanja, postoji nezanemarljiva mogućnost da više procesa
istovremeno pokuša da pristupi datoteci lozinki. Ako su to samo procesi
identifikatori, to i nije problematično, jer oni samo preuzimaju njen sadržaj. Ali,
ako ovoj datoteci (ili bilo kojoj drugoj) istovremeno pokušaju pristupiti procesi koji
menjaju njen sadržaj, ili procesi koji preuzimaju i/ili menjaju njen sadržaj, rezultat
pristupa je nepredvidiv, znači zavisan od redosleda pristupa i različit od rezultata
potpuno sekvencijalnog pristupa. Nepredvidivost rezultata pristupa je posledica
činjenice da preključivanja mogu da učine vidljivim samo delimično izmenjen
sadržaj datoteke, što je nemoguće u slučaju potpuno sekvencijalnog pristupa (kada
su vidljive samo celovite izmene sadržaja datoteke). Vidljivost delimično
izmenjenog sadržaja datoteke uzrokuje da ukupna izmena može da bude posledica
delimičnih izmena, napravljenih u toku aktivnosti raznih procesa, što je
neprihvatljivo. Iz istog razloga moguće je preuzimanje dela novog (izmenjenog) i
dela starog (neizmenjenog) sadržaja datoteke, što je, takođe, neprihvatljivo. Zato je
potrebno sinhronizovati procese koji pristupaju sadržaju iste datoteke.
POJAM KRIPTOGRAFIJE (CRYPTOGRAPHY)
Cilj kriptovanja teksta, poput, na primer, lozinke, je da tekst nakon
kriptovanja postane nerazumljiv (nečitljiv) za neupućenu osobu. Kriptovanje menja
tekst po unapred dogovorenom algoritmu kriptovanja, uz korišćenje zadatog ključa
kriptovanja. Tako, na primer, ako algoritam kriptovanja vrši zamenu znakova teksta
dvocifrenim brojevima, tada ključ kriptovanja ima oblik niza dvocifrenih brojeva,
koji korespondiraju znakovima. Pretpostavka je da je redosled znakova unapred
zadan. U ovom primeru ključeva kriptovanja ima koliko i različitih nizova
dvocifrenih brojeva koji korespondiraju znakovima. Da bi kriptovani tekst postao
razumljiv, potrebno ga je dekriptovati (decrypt), odnosno vratiti u prvobitni oblik.
To omogućuju algoritam dekriptovanja i ključ dekriptovanja. Ako algoritam
dekriptovanja direktno i jednoznačno sledi iz algoritma kriptovanja, a ključ
dekriptovanja iz ključa kriptovanja, tada je reč o simetričnoj kriptografiji
(symmetric-key cryptography, secret-key cryptography). Tako, iz prethodno
pomenutog primera algoritma kriptovanja sledi da se algoritam dekriptovanja
sastoji od zamene dvocifrenih brojeva odgovarajućim znakovima, a da ključ
dekriptovanja ima oblik niza znakova koji korespondiraju dvocifrenim brojevima.
Pretpostavka je da je redosled dvocifrenih brojeva unapred zadan.
Osobina simetrične kriptografije je da poznavanje ključa kriptovanja
omogućuje i dekriptovanje. Zato, kod simetrične kriptografije, ključ kriptovanja
predstavlja tajnu, pa njega, na primer, u slučaju kriptovanja i dekriptovanja lozinke
zna samo administrator.
194
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Algoritmi kriptovanja i dekriptovanja ne predstavljaju tajnu, jer je praksa
pokazala da se takva tajna ne može sačuvati. Prema tome, tajnost kriptovanja teksta
se zasniva na činjenici da komplikovanost algoritama kriptovanja i dekriptovanja,
velika dužina ključeva kriptovanja i dekriptovanja, kao i veliki broj ovih ključeva
čine praktično neizvodljivim pokušaj da se dekriptuje kriptovani tekst probanjem,
jednog po jednog, svih mogućih ključeva dekriptovanja.
Simterična kriptografija nije podesna za kriptovanje poruka, jer tada ključ
kriptovanja mora znati svaki pošiljalac poruke, što ga dovodi u poziciju da može da
dekriptuje poruke drugih pošiljalaca. To nije moguće u asimetričnoj kriptografiji
(public-key cryptography), jer je njena osobina da se iz ključa kriptovanja ne može
odrediti ključ dekriptovanja, pa poznavanje ključa kriptovanja ne omogućuje
dekriptovanje. Ovakav ključ kriptovanja se zove javni ključ (public key), jer je on
dostupan svima. Njemu odgovarajući ključ dekriptovanja je privatan (tajan), jer je
dostupan samo osobama ovlašćenim za dekriptovanje. Zato se on naziva privatni
ključ (private key). Prema tome, svaki pošiljalac poruke raspolaže javnim ključem,
da bi mogao da kriptuje poruke, dok privatni ključ poseduje samo primalac poruka,
da bi jedini mogao da dekriptuje poruke. Asimetrična kriptografija se temelji na
korišćenju jednostavnih algoritama kriptovanja kojima odgovaraju komplikovani
algoritmi dekriptovanja. Zato je asimterična kriptografija mnogo sporija od
simetrične.
5.3 SLOJ ZA RUKOVANJE DATOTEKAMA
Zadatak sloja za rukovanje datotekama je da ozbezbedi punu slobodu
rukovanja podacima, sadržanim u datotekama. Ovakva sloboda podrazumeva
mogućnost proizvoljnog dodavanja podataka u datoteku, ili njihovog izbacivanja iz
datoteke, kao i direktnog pristupa svakom podatku u datoteci. Punu slobodu
rukovanja podacima nudi predstava sadržaja datoteke kao niza bajta, pod uslovom
da se ovaj niz može, po potrebi, produžavati ili skraćivati, kao i da se bajtima iz
niza može pristupati u proizvoljnom redosledu, korišćenjem rednog broja bajta za
njegovu identifikaciju. Ovakva predstava datoteke dozvoljava da se iznad nje
izgrade različiti pogledi na datoteku, na primer, da se datoteka posmatra kao skup
slogova iste ili različite dužine, koji se identifikuju pomoću posebnih indeksa.
Oblikovanje ovakvih specijalizovanih pogleda na datoteke izlazi van okvira
operativnog sistema, koji sadržaj datoteke predstavlja kao niz bajta.
KONTIUNALNE DATOTEKE
Sadržaji datoteka se nalaze u blokovima masovne memorije. Za bilo kakvo
rukovanje ovim sadržajem neophodno je da on dospe u radnu memoriju. Zato je
rukovanje bajtima sadržaja datoteke neraskidivo povezano sa prebacivanjem
blokova sa ovim bajtima između radne i masovne memorije. Pri tome se bajti
prebacuju iz radne u masovnu memoriju radi njihovog trajnog čuvanja, a iz
masovne u radnu memoriju radi obrade. Da bi ovakvo prebacivanje bilo moguće,
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
195
neophodno je da sloj za rukovanje datotekama uspostavi preslikavanje rednih
brojeva bajta u redne brojeve njima odgovarajućih blokova. Ovakvo preslikavanje
se najlakše uspostavlja, ako se sadržaj datoteke nalazi u susednim blokovima (čiji
redni brojevi su uzastopni). Ovakve datoteke se nazivaju kontinualne (contiguous).
Kod kontinualnih datoteka redni broj bloka sa traženim bajtom se određuje kao
količnik rednog broja bajta i veličine bloka, izražene kao broj bajta koje sadrži
svaki blok. Pri tome, ostatak deljenja ukazuje na relativni položaj bajta u bloku.
Kontinualne datoteke zahtevaju od sloja za rukovanje datotekama da za
svaku datoteku vodi evidenciju ne samo o njenom imenu, nego i o rednom broju
početnog bloka datoteke, kao i o dužini datoteke. Ova dužina može biti izražena
brojem bajta, ali i brojem blokova, koga obavezno prati podatak o popunjenosti
poslednjeg bloka. Pojava da poslednji blok datoteke nije popunjen do kraja se
naziva interna fragmentacija (internal fragmentation). Ova pojava je važna, jer
nepopunjeni poslednji blok datoteke predstavlja neupotrebljen (i potencijalno
neupotrebljiv) deo masovne memorije.
Sloj za rukovanje datotekama obavezno vodi i evidenciju slobodnih blokova
masovne memorije. Za potrebe kontinualnih datoteka bitno je da ova evidencija
olakša pronalaženje dovoljno dugačkih nizova susednih blokova. Zato je podesna
evidencija u obliku niza bita (bit map), u kome svaki bit odgovara jednom bloku i
pokazuje da li je on zauzet (0) ili slobodan (1). U slučaju ovakve evidencije,
pronalaženje dovoljno dugačkih nizova susednih blokova se svodi na pronalaženje
dovoljno dugačkog niza jedinica u nizu bita koji odslikava zauzetost masovne
memorije. Ovakvo pronalaženje je neizbežno pri stvaranju kontinualnih datoteka,
čija veličina se zadaje prilikom njihovog stvaranja i unapred mora biti poznata.
Rukovanje slobodnim blokovima masovne memorije zahteva sinhronizaciju
(međusobnu isključivost procesa), da bi se, na primer, izbeglo da više procesa,
nezavisno jedan od drugog, zauzme iste slobodne blokove masovne memorije.
Pojava iscepkanosti slobodnih blokova masovne memorije u kratke nizove
susednih blokova otežava rukovanje kontinualnim datotakama. Ta pojava se zove
eksterna fragmentacija (external fragmentation). Ona nastaje kao rezultat
višestrukog stvaranja i uništenja datoteka u slučajnom redosledu, pa nakon
uništavanja datoteka ostaju nizovi slobodnih susednih blokova, međusobno
razdvojenih blokovima postojećih datoteka. Problem eksterne fragmentacije se
povećava, kada se, prilikom traženja dovoljno dugačkog niza susednih blokova,
pronađe niz duži od potrebnog, jer se tada zauzima (allocation) samo deo blokova u
pronađenom nizu. To dovodi do daljeg drobljenja (skraćenja) nizova slobodnih
susednih blokova, jer preostaju sve kraći nizovi slobodnih susednih blokova.
Eksterna fragmentacija je problematična, jer posredno izaziva
neupotrebljivost slobodnih blokova masovne memorije. Na primer, eksterna
fragmentacija onemogućuje stvaranje kontinualne datoteke, čija dužina je jednaka
sumi slobodnih blokova, kada oni ne obrazuju niz susednih blokova. Problem
eksterne fragmentacije se može rešiti sabijanjem (compaction) datoteka, tako da
196
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
svi slobodni blokovi budu potisnuti iza datoteka i da tako obrazuju niz susednih
blokova. Mana ovog postupka je njegova dugotrajnost.
U opštem slučaju produženje kontinualne datoteke je komplikovano, jer
zahteva stvaranje nove, veće kontinualne datoteke, prepisivanje sadržaja
produžavane datoteke u novu datoteku i uništavanje produžavane datoteke. Sve ovo
je potrebno, jer se ne može očekivati da se neposredno iza produžavane datoteke
uvek nađe dovoljno dugačak niz susednih slobodnih blokova.
RASUTE DATOTEKE
Upotrebnu vrednost kontinualnih datoteka značajno smanjuju problem
eksterne fragmentacije, potreba da se unapred zna veličina kontinualnih datoteka i
teškoće sa njihovim produžavanjem. Zato se umesto kontinualnih koriste rasute
(noncontiguous) datoteke, čiji sadržaj je smešten (rasut) u nesusednim blokovima
masovne memorije. Kod rasutih datoteka redni brojevi bajta se preslikavaju u redne
brojeve blokova pomoću posebne tabele pristupa (file allocation table). Njeni
elementi sadrže redne brojeve blokova. Indekse ovih elemenata određuje količnik
rednog broja bajta i veličine bloka. Iz prethodnog sledi da dužnu rasutih datoteka
ograničava veličina tabele pristupa. Zato se veličina tabele pristupa dimenzionira
tako da zadovolji najveće praktične zahteve u pogledu dužine rasutih datoteka.
Tabele pristupa se čuvaju u blokovima masovne memorije (kao, uostalom, i
sadržaji datoteka). Radi manjeg zauzeća, važno je da se u blokovima masovne
memorije ne čuva uvek cela tabela pristupa, nego samo njen neophodan (stvarno
korišćen) deo. Zato se tabela pristupa deli u odsečke. Početni odsečak, sa p
elemenata tabele pristupa je uvek prisutan. On nije veći od bloka masovne
memorije. Dodatni odsečci su prisutni samo kad su neophodni. Svaki dodatni
odsečak je jednak bloku masovne memorije i može da sadrži b elemenata tabele
pristupa (b > p). Prema tome, tabela pristupa svake rasute datoteke zauzima jedan
blok (ili njegov deo), u kome se nalazi početni odsečak ove tabele sa p njenih
elemenata. Za tabelu pristupa se, po potrebi, zauzima još jedan blok sa dodatnim
odsečkom, u kome se nalazi narednih b njenih elemenata. Kada zatreba još dodatnih
odsečaka, za tabelu pristupa se zauzima poseban blok prvog stepena indirekcije. On
sadrži do b rednih brojeva blokova sa dodatnim odsečcima. U svakom od njih se
nalazi b novih elemenata tabele pristupa. Na kraju, po potrebi, za ovu tabelu se
zauzima poseban blok drugog stepena indirekcije. On sadrži do b rednih brojeva
blokova prvog stepena indirekcije. Svaki od njih sadrži do b rednih brojeva blokova
sa dodatnim odsečcima, a u svakom od njih se nalazi b novih elemenata tabele
pristupa. Prema tome, ukupno ima 1+b+b2 dodatnih odsečaka, svaki sa b elemenata
tabele pristupa.
Slika 5.2.1 sadrži grafički prikaz bitnih elemenata organizacije rasute
datoteke.
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
deskriptor
rasute datoteke
tabela pristupa
p 0
p-1
p
...
b
p+b-1
p+b
...
b
...
b
p+b+b2-1
p+b+b2
...
...
blok drugog
stepena
indirekcije
b
...
...
blokovi prvog
stepena
indirekcije
b
p+b+b2+b3-1
Slika 5.2.1 Grafički prikaz bitnih elemenata organizacije rasute datoteke
197
198
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Podaci, kao što su sadržaj početnog odsečka tabele pristupa i redni broj
njenog prvog dodatnog odsečka, kao i redni brojevi blokova prvog i drugog stepena
indirekcije, obrazuju deskriptor rasute datoteke. Pored toga, ovaj deskriptor
obuhvata i dužinu rasute datoteke, jer i kod nje poslednji blok njenog sadržaja ne
mora biti popunjen do kraja.
Ideja, korišćena za organizaciju tabele pristupa, može da se upotrebi i za
organizaciju evidencije slobodnih blokova masovne memorije. U tom slučaju ova
evidencija ima oblik liste slobodnih blokova. Svaki od njih, pored rednog broja
narednog slobodnog bloka iz ove liste, sadrži do b-1 rednih brojeva slobodnih
blokova, koji nisu uvezani u listu. Ovakva evidencija slobodnih blokova je
usklađena sa tabelom pristupa, jer omogućava, pri uništenju datoteke, da blokovi,
koji odgovaraju dodatnim odsečcima, budu direktno uključeni u listu slobodnih
blokova. Alternativna organizacija evidencije slobodnih blokova je u obliku niza
bita. Ovakva organizacija ima za posledicu dugotrajnije uključivanje slobodnih
blokova u evidenciju, ali njena prednost je da olakšava pronalaženje susednih
slobodnih blokova.
Evidencija slobodnih blokova masovne memorije u obliku liste slobodnih
blokova je prikazana na slici 5.2.2.
b-1 rednih brojeva slobodnih blokova,
koji nisu vezani u listu
...
b-1 rednih brojeva slobodnih blokova,
koji nisu vezani u listu
...
...
Slika 5.2.2 Grafička predstava evidencije slobodnih blokova u obliku liste
KONZISTENTNOST SISTEMA DATOTEKA
Iza rukovanja datotekama krije se rukovanje blokovima masovne memorije, u
kojima se nalaze i sadržaj i deskriptor i, eventualno, dodatni odsečci tabele pristupa
svake rasute datoteke. Rukovanje ovim blokovima usložnjava činjenica da se
međusobno zavisni podaci nalaze u raznim blokovima. Pošto se blokovi modifikuju
u radnoj memoriji, a trajno čuvaju u masovnoj memoriji, prirodno je da u pojedinim
trenucima postoji razlika između blokova u masovnoj memoriji i njihovih kopija u
radnoj memoriji. Probleme izaziva gubitak kopija blokova u radnoj memoriji, na
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
199
primer, zbog nestanka napajanja, pre nego su sve kopije blokova, sa međusobno
zavisnim podacima, prebačene u masovnu memoriju. Tako, na primer, produženje
rasute datoteke zahteva (1) izmenu evidencije slobodnih blokova, radi isključivanja
pronađenog slobodnog bloka iz ove evidencije, kao i (2) izmenu tabele pristupa
produžavane rasute datoteke, radi smeštanja rednog broja novog bloka u element
ove tabele. Izmena evidencije slobodnih blokova dovodi do promene jedne od
kopija njenih blokova u radnoj memoriji. Isti efekat ima i izmena tabele pristupa
produžavane rasute datoteke. Ako obe izmenjene kopije budu prebačene u masovnu
memoriju, tada je produženje rasute datoteke uspešno obavljeno. Ako ni jedna od
kopija ne dospe u masovnu memoriju, tada produženje rasute datoteke nije
obavljeno, jer nije registrovano u masovnoj memoriji. Ali, ako samo jedna od
promenjenih kopija dospe u masovnu memoriju, tada se javljaju problemi
konzistentnosti sistema datoteka. U jednom slučaju, kada samo promenjena kopija
bloka evidencije slobodnih blokova dospe u masovnu memoriju, blok isključen iz
ove evidencije postaje izgubljen, jer njegov redni broj nije prisutan niti u ovoj
evidenciji, a niti u tabeli pristupa neke od rasutih datoteka. U drugom slučaju, kada
samo promenjena kopija bloka tabele pristupa dospe u masovnu memoriju, blok,
pridružen ovoj tabeli, ostaje i dalje uključen u evidenciju slobodnih blokova. Prvi
slučaj je bezazlen, jer se izgubljeni blokovi mogu pronaći. Pronalaženje izgubljenih
blokova se zasniva na traženju blokova koji nisu prisutni ni u evidenciji slobodnih
blokova, ni u tabelama pristupa rasutih datoteka. Za razliku od prvog slučaja, drugi
slučaj je neprihvatljiv, jer može da izazove istovremeno uključivanje istog bloka u
više rasutih datoteka, čime se narušava njihova konzistentnost. Zato je neophodno
uvek prebacivati u masovnu memoriju prvo promenjenu kopiju bloka evidencije
slobodnih blokova, pa tek iza toga i promenjenu kopiju bloka tabele pristupa. Znači,
potrebno je paziti na redosled u kome se izmenjene kopije blokova prebacuju u
masovnu memoriju.
Nakon izmene kopije bloka u radnoj memoriji, važno je što pre izmenjenu
kopiju prebaciti u masovnu memoriju, radi smanjenja mogućnosti da se ona izgubi
(na primer, kao posledica nestanka napajanja). To je naročito važno, ako izmena
nije rezultat automatske obrade, nego ljudskog rada (na primer, editiranja), jer se
tada ne može automatski rekonstruisati.
BAFERSKI PROSTOR
Pristupi sadržaju datoteke mogu zahtevati prebacivanje više blokova u radnu
memoriju, na primer, jednog bloka sa deskriptorom datoteke, pa, eventualno,
jednog ili više dodatnih blokova tabele pristupa i, na kraju, bloka sa traženim
bajtima sadržaja. Pošto je, sa stanovišta procesora, prenos blokova na relaciji radna
i masovna memorija, spor (dugotrajan), dobra ideja je zauzeti u radnoj memoriji
prostor za više bafera, namenjenih za čuvanje kopija korišćenih blokova (block
cache, buffer cache). Pošto je radna memorija mnogo manja od masovne, njeni
baferi mogu istovremeno da sadrže mali broj kopija blokova masovne memorije.
200
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Zato je važno da baferi sadrže kopije blokova, koje će biti korišćene u neposrednoj
budućnosti, jer se samo tako značajno ubrzava obrada podataka. Problem se javlja
kada su svi baferi napunjeni, a potrebno je pristupiti bloku masovne memorije, čija
kopija nije prisutna u nekom od bafera. U tom slučaju neizbežno je oslobađanje
nekog od bafera, da bi se u njega smestila kopija potrebnog bloka. Iskustvo
pokazuje da je najbolji pristup osloboditi bafer za koga je trenutak poslednjeg
pristupa njegovom sadržaju stariji od trenutaka poslednjeg pristupa sadržajima svij
ostalih bafera. (Least Recently Used - LRU). Za takav bafer se kaže da ima
najstariju referencu. Pri oslobađanju bafera, njegov dotadašnji sadržaj se poništava,
kada bafer sadrži neizmenjenu kopiju bloka, jer je ona identična bloku masovne
memorije. U suprotnom slučaju, neophodno je sačuvati izmene, pa se kopija
prebacuje u masovnu memoriju. U oslobođeni bafer se prebacuje kopija potrebnog
bloka masovne memorije. Da bi se znalo koja od kopija ima najstariju referencu,
baferi se uvezuju u listu. Na početak ovakve liste se uvek prebacuje bafer sa upravo
korišćenom kopijom (sa najnovijom referencom), pa na njen kraj nužno dospeva
bafer sa najstarijom referencom.
Baferovanje izmenjenih kopija blokova u radnoj memoriji zahteva da se
odredi trenutak u kome se izmenjena kopija prebacuje u masovnu memoriju. Ako se
izmenjena kopija prebacuje u masovnu memoriju odmah nakon svake izmene, tada
se usporava rad, a ako se izmenjena kopija prebacuje u masovnu memoriju tek pri
oslobađanju bafera, tada se povećava mogućnost gubljenja izmene (na primer, zbog
nestanka napajanja). Rešavanje ovoga problema se može posredno prepustiti
korisniku, ako se uvede posebna operacija (sync) za izazivanje prebacivanja
sadržaja bafera (sa izmenjenim kopijama blokova) u masovnu memoriju. U tom
slučaju izmenjene kopije dospevaju u masovnu memoriju ili kada se baferi
oslobađaju ili kada to zatraži korisnik. Potreba da se kopija bloka što brže nakon
izmene prebaci u masovnu memoriju je u suprotnosti sa nastojanjem da se blokovi
što ređe prenose između radne i masovne memorije.
Na brzinu prebacivanja podataka između radne i masovne memorije važan
uticaj ima i veličina bloka, jer, što je blok veći, u proseku se potroši manje vremena
na prebacivanje jednog bajta između radne i masovne memorije. Međutim, što je
blok veći, veća je i interna fragmentacija. Ta dva oprečna zahteva utiču na izbor
veličine bloka, koja se kreće od 512 bajta do 8192 bajta.
DESKRIPTOR DATOTEKE
Deskriptor datoteke, pored atributa koji omogućuju preslikavanje rednih
brojeva bajta u redne brojeve blokova, sadrži i:
1. numeričku oznaku vlasnika datoteke,
2. prava pristupa datoteci za njenog vlasnika, za njegove saradnike i za ostale
korisnike,
3. podatak da li je datoteka zaključana ili ne,
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
201
4. podatak da li numerička oznaka vlasnika datoteke postaje numerička
oznaka vlasnika procesa stvorenog na osnovu sadržaja datoteke (važi samo
za izvršne datoteke), kao i
5. datum poslednje izmene datoteke.
Činjenica, da deskriptor datoteke sadrži prava pristupa datoteci, podrazumeva
da je sadržaj masovne memorije fizički zaštićen, tako da nema načina za
neovlašteni pristup masovnoj memoriji. To podrazumeva da se centralni delovi
računara (procesor, radna i masovna memorija, kontroleri i sabirnica) nalaze u
zaštićenoj (sigurnoj) prostoriji, a da su samo periferni delovi računara (kao što su
terminali ili štampači) direktno na raspolaganju korisnicima.
Podatak da li je datoteka zaključana ili ne je uveden radi ostvarenja
međusobne isključivosti procesa u toku pristupa datoteci. Pri tome se podrazumeva
da su aktivnosti ovih procesa međusobno isključive u toku obavljanja operacije
zaključavanja datoteke, u kojoj se proverava da li je datoteka zaključana i
eventualno obavi njeno zaključavanje. Sinhronizacija procesa u toku obavljanja ove
operacije je neophodna, da bi se sprečilo da dva ili više procesa istovremeno
zaključe da je ista datoteka otključana i da, nezavisno jedan od drugog, istovremeno
zaključaju pomenutu datoteku. Pomenuta sinhronizacija obezbeđuje da uvek najviše
jedan proces zaključa datoteku, jer samo on pronalazi otključanu datoteku, dok svi
preostali istovremeno aktivni procesi pronalaze zaključanu datoteku. Ako je za
nastavak aktivnosti ovih preostalih procesa neophodno da pristupe datoteci, tada se
njihova aktivnost zaustavlja do otključavanja datoteke. Zbog ovakvih procesa
potrebna je operacija otključavanja datoteke. Njeno izvršavanje omogućuje
nastavak aktivnosti samo jednog od procesa, čija aktivnost je zaustavljena do
otključavanja datoteke. Ako takav proces postoji, datoteka se i ne otključava, nego
se samo prepušta novom procesu. Inače, datoteka se otključava. I operacija
otključavanja datoteka zahteva sinhronizaciju procesa.
Prethodno opisana sinhronizacija procesa predstavlja primer uslovne
sinhronizacije, jer je aktivnost jednog procesa zaustavljena dok se ne ispuni uslov
(da datoteka bude otključana), što se obezbeđuje u toku aktivnosti drugog procesa.
U slučaju zaključavanja datoteke, uslovna sinhronizacija nije uvek obavezna, jer je
moguće da proces nastavi svoju aktivnost i nakon neuspešnog pokušaja
zaključavanja datoteke. Jasno, tada se podrazumeva da on neće pristupati
pomenutoj datoteci. Prema tome, operacija zaključavanja datoteke je blokirajuća,
kada, radi uslovne sinhronizacije, u toku njenog obavljanja dolazi do zaustavljanja
aktivnosti procesa, dok se ne stvore uslovi za međusobno isključive pristupe
zaključanoj datoteci. Ova operacija je neblokirajuća, kada njena povratna vrednost
ukazuje na neuspešan pokušaj zaključavanja datoteke i na nemogućnost pristupa
datoteci, koju je zaključao neki drugi proces.
Sinhronizaciju procesa moraju da obezbede ne samo operacije zaključavanja i
otključavanja datoteke, nego i sve druge operacije za rukovanje deskriptorima
202
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
datoteka. Jedino tako se može obezbediti očuvanje konzistentnosti deskriptora (i
njima odgovarajućih datoteka).
IMENICI
Ime datoteke je prirodno vezano za njen deskriptor. Pošto se ime datoteke
nalazi u imeniku, uz njega bi, u imenik, mogao da bude smešten i deskriptor
datoteke. Međutim, tipičan način korišćenja imenika je njihovo pretraživanje, radi
pronalaženja imena imenika ili imena datoteke, navedene u datoj putanji. Ovakvo
pretraživanje prirodno prethodi pristupu sadržaju datoteke, odnosno sadržaju
imenika. Brzina tog pretraživanja je veća što su imenici kraći, jer se tada manje
podataka prebacuje između radne i masovne memorije. Zato je uputno da imenici
sadrže samo imena datoteka i imenika, a ne i njihove deskriptore, pogotovo ako su
oni veliki. Da bi se uspostavila veza između imena datoteka, odnosno imena
imenika sa jedne strane i njihovih deskriptora sa druge strane, u imenicima se, uz
imena datoteka, odnosno uz imena imenika, navode i redni brojevi njihovih
deskriptora, koji jednoznačno određuju ove deskriptore. Prema tome, imenik
odgovara tabeli, čiji elementi sadrže imena datoteka (odnosno, imena imenika) i
redne brojeve njihovih deskriptora. Da bi se iz rednog broja deskriptora mogao
odrediti redni broj bloka masovne memorije, u kome se deskriptor nalazi, izvestan
broj susednih blokova se rezerviše samo za smeštanje deskriptora. Pod
pretpostavkom da blok sadrži celi broj deskriptora, količnik rednog broja
deskriptora i ukupnog broja deskriptora u bloku određuje redni broj bloka sa
deskriptorom. Pri tome se podrazumeva da je deskriptor sa rednim brojem 0
rezervisan za korenski imenik. Zahvaljujući ovoj pretpostavci, moguće je uvek
pronaći deskriptor korenskog imenika i od njega započeti pretraživanje imenika, što
obavezno prethodi pristupu sadržaju datoteke. Tako, na primer, za pristup sadržaju
datoteke sa putanjom:
/fakultet/odsek1/godina1.txt
potrebno je prebaciti u radnu memoriju blok sa deskriptorom korenskog imenika,
koji je poznat, zahvaljujući činjenici da je redni broj (0) deskriptora korenskog
imenika unapred zadan. U ovom deskriptoru se nalaze redni brojevi blokova sa
sadržajem korenskog imenika. Nakon prebacivanja ovih blokova u radnu memoriju,
moguće je pretražiti sadržaj korenskog imenika, da bi se ustanovilo da li on sadrži
ime fakultet. Ako sadrži, uz ovo ime je i redni broj deskriptora odgovarajućeg
imenika, iz koga se može odrediti redni broj bloka u kome se nalazi ovaj deskriptor.
Po prebacivanju ovog bloka u radnu memoriju, u pomenutom deskriptoru se
pronalaze redni brojevi blokova sa sadržajem imenika fakultet. Nakon
prebacivanja ovih blokova u radnu memoriju moguće je pretražiti sadržaj i ovog
imenika, da bi se ustanovilo da li on sadrži ime odsek1. Ako sadrži, uz ovo ime je i
redni broj deskriptora odgovarajućeg imenika, iz koga se može odrediti redni broj
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
203
bloka u kome se nalazi ovaj deskriptor. Po prebacivanju ovog bloka u radnu
memoriju, u pomenutom deskriptoru se pronalaze redni brojevi blokova sa
sadržajem imenika odesk1. Nakon prebacivanja ovih blokova u radnu memoriju
moguće je pretražiti sadržaj i ovog imenika, da bi se ustanovilo da li on sadrži ime
datoteke godina1.txt. Ako sadrži, uz nju je i redni broj deskriptora odgovarajuće
datoteke, iz koga se može odrediti redni broj bloka u kome se nalazi ovaj
deskriptor. Po prebacivanju ovog bloka u radnu memoriju, u pomenutom
deskriptoru se pronalaze redni brojevi blokova sa sadržajem datoteke godina1.txt.
Tek tada je moguć pristup ovom sadržaju.
U toku pristupa imenicima, neophodno je njihovo zaključavanje, radi
obezbeđenja međusobne isključivosti pristupa raznih procesa istom imeniku, čime
se obezbeđuje očuvanje konzistentnosti imenika.
SISTEMSKE OPERACIJE SLOJA ZA RUKOVANJE DATOTEKAMA
Prethodno opisano pretraživanje imenika se dešava u okviru izvršavanja
sistemske operacije otvaranja datoteke (open). Zato je putanja datoteke obavezni
argument poziva ove operacije. Rezultat njenog izvršavanja je prebacivanje kopije
deskriptora datoteke u radnu memoriju. Ova kopija se ne uključuje u deskriptor
procesa, u toku čije aktivnosti je inicirano njeno prebacivanje, jer ista datoteka
može istovremeno biti otvorena u toku aktivnosti više procesa. Da bi svaki od njih
koristio istu kopiju deskriptora datoteke, u deskriptoru svakog procesa postoji
tabela otvorenih datoteka. Njeni elementi sadrže adresu kopije deskriptora
odgovarajuće datoteke. Indeks elementa tabele otvorenih datoteka (u kome je adresa
kopije deskriptora otvorene datoteke) predstavlja povratnu vrednost poziva
sistemske operacije otvaranja datoteke. Ovaj indeks otvorene datoteke se koristi kao
argument u pozivima drugih sistemskih operacija sloja za rukovanje datotekama.
On određuje datoteku na koju se pomenuti poziv odnosi. Od veličine tabele
otvorenih datoteka zavisi najveći mogući broj istovremeno otvorenih datoteka
nekog procesa.
Kao dodatni argumenti poziva sistemske operacije otvaranja datoteke mogu
se javiti oznaka nameravane vrste pristupa otvaranoj datoteci, koja pokazuje da li se
datoteka otvara samo za čitanje, ili za pisanje, što podrazumeva i njeno čitanje, kao
i naznaka zaključavanja datoteke. U toku izvršavanja sistemske operacije otvaranja
datoteke se proverava da li proces, koji poziva ovu operaciju, poseduje pravo
nameravanog pristupa otvaranoj datoteci. Za ovo se koristi numerička oznaka
vlasnika procesa iz njegovog deskriptora, numerička oznaka vlasnika otvarane
datoteke, kao i prava pristupa datoteci za njenog vlasnika, za njegove saradnike i za
ostale korisnike, što je navedeno u deskriptoru otvarane datoteke. Takođe, u toku
obavljanja sistemske operacije otvaranja datoteke, pokušava se zaključati otvarana
datoteka, za šta se, takođe, pristupa njenom deskriptoru. Otvaranje datoteke je
uspešno samo ako proces poseduje pravo nameravanog pristupa datoteci i ako je
moguće njeno zaključavanje. Ovo poslednje je bitno samo ako je zatraženo
204
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
zaključavanje datoteke. Tada poziv sistemske operacije otvaranja datoteke vraća
indeks otvorene datoteke. Inače, on vraća kod greške. Kod greške sadrži
obaveštenje o neuspehu zaključavanja datoteke, kada je operacija otvaranja
datoteke neblokirajuća, odnosno kada se oslanja na neblokirajuću operaciju
zaključavanja datoteka. Ako je operacija otvaranja datoteke blokirajuća, znači ako
se oslanja na blokirajuću operaciju zaključavanja datoteka, tada se, do
zaključavanja datoteke, zaustavlja aktivnost procesa, kome je potebno otvaranje
datoteke. Po obavljanju blokirajuće operacije otvaranja datoteke, datoteka je
zaključana, pa je uvek obezbeđena međusobna isključivost pristupa datoteci.
Oznaka vrste nameravanog pristupa datoteci se čuva u posebnom polju
odgovarajućeg elementa tabele otvorenih datoteka, radi naknadne provere
ispravnosti pristupa datoteci.
Pokušaj otvaranja nepostojeće datoteke dovodi do njenog stvaranja, ako se
tako navede u argumentima poziva sistemske operacije otvaranja datoteke.
Međutim, moguće je da postoji i posebna sistemska operacija za stvaranje datoteke.
Korišćenje datoteke se završava pozivom sistemske operacije zatvaranja
datoteke (close). Ona, eventualno, otključava datoteku, čisti odgovarajući element
tabele otvorenih datoteka procesa, koji je pozvao ovu sistemsku operaciju, i
prebacuje, eventualno, kopiju deskriptora i baferovane kopije blokova sadržaja
zatvarane datoteke u masovnu memoriju (što je neophodno samo ako je ona
izmenjena). Kopija deskriptora zatvarane datoteke ostaje u radnoj memoriji, ako,
pored procesa koji zatvara datoteku, postoje i drugi procesi koji joj pristupaju. Zato
kopija deskriptora datoteke sadrži broj procesa koji pristupaju datoteci. Obavezni
argument poziva sistemske operacije zatvaranja datoteke je indeks otvorene
datoteke.
Važno je uočiti da u periodu dok je datoteka otvorena, znači, između
uzastopnih poziva sistemskih operacija otvaranja i zatvaranja datoteke, proces nije
pod uticajem izmena prava pristupa otvorenoj datoteci, jer izmena prava pristupa
postaje delotvorna tek pri narednom otvaranju datoteke, pošto se tek tada ova prava
ponovo proveravaju.
Korisna praksa je da se, na kraju aktivnosti procesa, automatski zatvore sve
otvorene datoteke.
Nakon otvaranja, sadržaj datoteke se može čitati pozivom sistemske operacije
čitanja datoteke (read) i pisati pozivom sistemske operacije pisanja datoteke (write),
ako je to u skladu sa namerama, izraženim u otvaranju datoteke. Obavezni
argumenti ovih poziva su indeks otvorene datoteke i broj bajta (koji se čitaju ili
pišu). Pored toga, poziv sistemske operacije čitanja sadrži, kao argument, adresu
zone radne memorije u koju se smeštaju pročitani bajti, a poziv sistemske operacije
pisanja sadrži, kao argument, adresu zone radne memorije iz koje se preuzimaju
bajti za pisanje. Oba poziva vraćaju vrednost, koja ukazuje na uspešan poziv ili na
grešku. Podrazumeva se da sistemske operacije pisanja i čitanja nude sekvencijalne
pristupe datotekama. To znači, da, ako jedan poziv sistemske operacije čitanja
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
205
(pisanja) pročita (upiše) prvi bajt datoteke, naredni takav poziv datoteke će da
pročita (upiše) drugi bajt datoteke. Radi podrške sekvencijalnom pristupu, svaki
element tabele otvorenih datoteka sadrži i posebno polje pozicije datoteke sa rednim
brojem bajta od koga se primenjuje naredno čitanje ili pisanje. Svako čitanje ili
pisanje pomera poziciju na prvi naredni nepročitani (neupisani) bajt. Da bi bili
mogući direktni pristupi bajtima datoteke (u proizvoljnom redosledu), postoji
sistemska operacija izmene pozicije datoteke (seek). Obavezni argumenti njenog
poziva su indeks otvorene datoteke i podatak o novoj poziciji, dok povratna
vrednost ovog poziva ukazuje da li je poziv bio uspešan ili ne. Tako, na primer, ako
se želi pisati na kraj datoteke, neophodno je prvo pozvati sistemsku operaciju
izmene pozicije datoteke, radi pozicioniranja iza poslednjeg bajta datoteke, i zatim
pozvati sistemsku operaciju pisanja. Prvi poziv izmeni poziciju datoteke u
odgovarajućem elementu tabele otvorenih datoteka. Drugi poziv, na osnovu ove
pozicije i kopije deskriptora datoteke, odredi redni broj bloka, ako on postoji, i
prebaci njegovu kopiju u radnu memoriju, ako ona već nije bila prisutna u radnoj
memoriji. U ovu kopiju se smeste dopisivani bajti i ona se prebaci (odmah ili
kasnije, zavisno od strategije baferovanja) u masovnu memoriju. Ako blok ne
postoji, ili ako se upisivanje proteže na više blokova, upisivanju bajta prethodi
zauzimanje blokova. Radi toga se menja (proširuje) tabela pristupa datoteke, što
dovodi i do izmene njenog deskriptora. Nakon toga se u radnoj memoriji oblikuje
novi sadržaj blokova i oni se prebacuju u masovnu memoriju.
Sloj za rukovanje datotekama nudi posebne sistemske operacije za izmenu
atributa datoteke, sadržanih u njenom deskriptoru (kao što su numerička oznaka
vlasnika datoteke, prava pristupa datoteci za njenog vlasnika, za njegove saradnike i
za ostale korisnike, ili podatak da li numerička oznaka vlasnika datoteke postaje
numerička oznaka vlasnika procesa, stvorenog na osnovu sadržaja datoteke).
Obavezni argumenti poziva ovih sistemskih operacija su putanja datoteke i nova
vrednost menjanog atributa datoteke. Njihova povratna vrednost ukazuje na
uspešnost poziva. U okviru izvršavanja ovih sistemskih operacija pretražuju se
imenici, radi prebacivanja u radnu memoriju kopije deskriptora datoteke. Zatim se
proverava da li je ove sistemske operacije pozvao vlasnik datoteke, jer jedino on
ima pravo da menja atribute datoteke. Ako jeste, tada se menja zadani atribut i blok
sa izmenjenom kopijom deskriptora vraća u masovnu memoriju. U sistemske
operacije za izmenu atributa datoteke spada i sistemska operacija za izmenu imena
datoteke. Iako se u okviru ove operacije ne menja deskriptor datoteke, nego njen
imenik, ovde je, pored pristupa deskriptoru njenog imenika, neophodan i pristup
deskriptoru datoteke, radi provere da li je ovu operaciju pozvao vlasnik datoteke.
Za uništenje datoteke (delete) neophodna je posebna sistemska operacija.
Obavezni argument njenog poziva je putanja uništavane datoteke. U okviru
sistemske operacije uništenja datoteke, pretražuju se imenici, radi prebacivanja u
radnu memoriju kopije deskriptora uništavane datoteke. Zatim se proverava da li je
ovu sistemsku operaciju pozvao vlasnik datoteke i, ako jeste, oslobađaju se blokovi
206
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
datoteke i njen deskriptor. Na kraju se uništava ime datoteke u odgovarajućem
imeniku, što predstavlja izmenu njegovog sadržaja. Povratna vrednost ove operacije
ukazuje da li je ona uspešno obavljena. Na primer, uništenje nepostojeće datoteke
ne može biti uspešno obavljeno.
Prethodno opisane sistemske operacije omogućuju pristup svim datotekama,
znači i imenicima. Ali za pristupe imenicima je potrebno poznavati detalje njihove
organizacije, kao što su broj bajta koji je rezervisan za imena datoteka i imenika ili
za redne brojeve njihovih deskriptora, odnosno kao što su oznake sa posebnim
značenjem, poput oznaka, koje se odnose na prethodni i naredni imenik u hijerarhiji
i slično. Potreba za poznavanjem organizacije imenika se izbegava, ako se ponude
posebne sistemske operacije za rukovanje imenicima.
SPECIJALNE DATOTEKE
Važno svojstvo pojma datoteke je da je on primenljiv i za opisivanje ulaznih i
izlaznih uređaja. Tako, ulazni uređaj, kao što je tastatura, odgovara datoteci, čiji
sadržaj se može samo čitati, a sastoji se od bajta, koji stižu sa tastature. Slično,
izlazni uređaj, kao što je ekran, odgovara datoteci, čiji sadržaj se može samo pisati,
a sastoji se od bajta, koji se upućuju na ekran. Takođe, ulazno izlazni uređaj, kao što
je disk, odgovara datoteci, čiji sadržaj se može i pisati i čitati, a sastoji se od bajta iz
pojedinih blokova diska. Datoteke, koje predstavljaju pojedine ulazne ili izlazne
uređaje, se nazivaju specijalne datoteke (special file). Specijalne datoteke se dele
na znakovne, koje odgovaraju uređajima kao što su tastatura, ekran, štampač ili
mrežni kontroler, i na blokovske, koje odgovaraju, na primer, diskovima. Znakovne
specijalne datoteke podržavaju sekvencijalno čitanje ili pisanje znakova (koji dolaze
sa odgovarajućeg urađaja, ili odlaze na odgovarajući uređaj). Za ove datoteke
izmena pozicije nema smisla. Blokovske specijalne datoteke podržavaju čitanje ili
pisanje blokova. Za njih izmena pozicije omogućuje određivanje rednog broja bloka
na koga se primenjuje naredna operacija.
Blokovske specijalne datoteke omogućuju direktno pristupe blokovima diska,
što je važno, na primer, kod pronalaženja izgubljenih blokova, kod sabijanja
datoteka, ili kod pripremanja disk jedinica za korišćenje. U poslednjem slučaju se
određuje namena pojedinih blokova diska. To prikazuje slika 5.2.3.
blok 0
blok 1
blok 2
...
blok n
prvi (boot) blok
drugi (super) blok
blokovi namenjeni za deskriptore
blokovi namenjeni za sadržaj datoteka i za tabele
pristupa
...
Slika 5.2.3 Namena pojedinih blokova diska
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
207
Prvi (boot) blok (sa slike 5.2.3) je rezervisan za podatke, koji su potrebni za
pokretanje računara (operativnog sistema), a drugi (super) blok sadrži:
1. podatke o nizu susednih blokova, koji su namenjeni za smeštanje
deskriptora datoteka (ovi podaci obuhvataju redni broj prvog bloka iz ovog
niza, kao i ukupan broj blokova u ovom nizu),
2. podatke o slobodnim mestima za deskriptore datoteka i
3. podatke potrebne za evidenciju (preostalih) slobodnih blokova (u ovu
evidenciju se uključuju svi blokovi, koji nisu upotrebljeni za smeštanje
sadržaja datoteka ili za tabele pristupa).
Pre korišćenja specijalnih datoteka, neophodno je njihovo otvaranje, radi
provere prava pristupa (čime se proverava pravo pristupa uređaju, koga datoteka
predstavlja), ili radi zaključavanja (čime se zaključava uređaj, koga datoteka
predstavlja). Nakon korišćenja, sledi zatvaranje specijalne datoteke. Za podršku
otvaranja i zatvaranja specijalnih datoteka, neophodno je da one poseduju svoje
deskriptore. Deskriptori specijalnih datoteka obuhvataju atribute, kao što su, na
primer numerička oznaka vlasnika datoteke, prava pristupa datoteci za njenog
vlasnika, za njegove saradnike i za ostale korisnike, ili podatak da li je datoteka
zaključana ili ne. Međutim, za operacije čitanja ili pisanja specijalnih datoteka nije
bitno preslikavanje rednih brojeva bajta u redne brojeve blokova masovne
memorije. Umesto toga za njih je bitno pozivanje odgovarajućih operacija drajvera
uređaja, koje ove datoteke predstavljaju. Zato se u deskriptorima specijalnih
datoteka ne nalaze podaci o tabeli pristupa, nego podaci o odgovarajućem drajveru
i primerku uređaja, koga on opslužuje. Za jednoznačno identifikovanje drajvera
uvodi se redni broj drajvera (major number), koji služi kao indeks posebne
tabele drajvera. Polja indeksiranog elementa ove tabele sadrže adrese operacija
dotičnog drajvera. Prema tome, ako se redni broj drajvera čuva u deskriptoru
specijalne datoteke, na osnovu njega je moguće pronaći adrese operacija ovog
drajvera, uz konsultovanje odgovarajućeg elementa tabele drajvera. U pomenutom
deskriptoru se čuva i redni broj (minor number) uređaja koga predstavlja
specijalna datoteka, da bi se na njega mogla usmeriti odabrana operacija drajvera.
Za izmenu atributa specijalnih datoteka primenljive su sistemske operacije,
koje su uvedene s tom namerom za obične datoteke. Isto važi i za sistemske
operacije za izmenu imena datoteke i za njeno uništenje. Pri tome, kod uništenja
specijalne datoteke, nema oslobađanja blokova, nego se oslobađa samo njen
deskriptor. Sistemske operacije prave razliku između običnih i specijalnih datoteka
na osnovu posebne oznake, navedene u deskriptoru datoteke.
STANDARDNI ULAZ I STANDARDNI IZLAZ
Pojmovi datoteke i procesa su čvrsto povezani, jer je aktivnost procesa
posvećena obradi podataka, sadržanih u datotekama. Pri tome je tipično da
obrađivani podaci stižu u proces iz jedne, ulazne datoteke, a da obrađeni podaci
208
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
napuštaju proces, završavajući u drugoj, izlaznoj datoteci. Ovakav model obrade
podataka je dovoljno čest, da opravda uvođenje naziva standardni ulaz (standard
input) za ulaznu datoteku i standardni izlaz (standard output) za izlaznu datoteku.
Pri tome se podrazumeva da se u toku stvaranja procesa otvore i njegov standardni
ulaz i njegov standardni izlaz. Zahvaljujući tome, bez otvaranja se može čitati
standardni ulaz i pisati standardni izlaz. Kao podrazumevajući standardni ulaz služi
specijalna datoteka, koja predstavlja tastaturu, a kao podrazumevajući standardni
izlaz služi specijalna datoteka, koja predstavlja ekran. U slučaju da proces stvaralac
zaustavlja svoju aktivnost, dok traje aktivnost stvorenog procesa, tada je prirodno
da stvoreni proces nasledi standardni ulaz i standardni izlaz od procesa stvaraoca i
da tako, preuzimajući opsluživanje terminala, nastavi interakciju sa korisnikom.
Kao indeks otvorene datoteke, koja odgovara standardnom ulazu, služi vrednost 0, a
kao indeks otvorene datoteke, koja odgovara standardnom izlazu, služi vrednost 1.
SPAŠAVANJE DATOTEKA
U nadležnosti sloja za rukovanje datotekama nalazi se i podrška spašavanju
(saving) datoteka, čiji cilj je da se redovno prave kopije postojećih (svih, ili samo u
međuvremenu izmenjenih, odnosno stvorenih) datoteka. Na osnovu ovakvih kopija
moguće je rekonstruisati sadržaj oštećenih datoteka. Do oštećenja datoteka dolazi
na razne načine, kao što je, na primer, pojava loših (neispravnih) blokova. Kada se
otkriju, loši blokovi se izbacuju iz upotrebe. Jedan način da se to postigne je da se,
na primer, formira datoteka loših blokva.
OSNOVA SLOJA ZA RUKOVANJE DATOTEKAMA
Sloj za rukovanje datotekama se oslanja na operacije drajvera iz sloja za
rukovanje kontrolerima. On može da koristi i operacije sloja za rukovanje radnom
memorijom, radi zauzimanja bafera, namenjenih za smeštanje kopija blokova, ili
kopija deskriptora, na primer.
5.4 SLOJ ZA RUKOVANJE RADNOM MEMORIJOM
Zadatak sloja za rukovanje radnom memorijom je da omogući zauzimanje
zona susednih lokacija (sa uzastopnim adresama) sistemske slobodne radne
memorije, kao i da omogući oslobađanje prethodno zauzetih zona sistemske radne
memorije. Radi toga ovaj sloj nudi operacije zauzimanja i oslobađanja (sistemske
radne memorije). Iako ove operacije može da koristi i sloj za rukovanje datotekama,
one su, pre svega, podrška sloju za rukovanje procesima, a pozivaju se prilikom
stvaranja i uništenja slike procesa. Argument poziva operacije zauzimanja je dužina
zauzimane zone (broj njenih lokacija), a povratna vrednost ovog poziva je adresa
zauzete zone (adresa prve od njenih lokacija), ili indikacija da je operacija
zauzimanja završena neuspešno. Argumenti poziva operacije oslobađanja su adresa
oslobađane zone (adresa prve od njenih lokacija) i njena dužina (broj njenih
lokacija).
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
209
KONTINUALNA RADNA MEMORIJA
Sistemsku slobodnu radnu memoriju obrazuju lokacije radne memorije, koje
ne koristi operativni sistem. On obično zauzima lokacije sa početka i, eventualno, sa
kraja adresnog prostora, a između njih se nalaze lokacije sistemske slobodne radne
memorije. Na početku adresnog prostora su, najčešće, lokacije, namenjene za tabelu
prekida. Iza njih slede lokacije sa naredbama i promenljivim operativnog sistema
(koje obuhvataju i prostor za smeštanje deskriptora i sistemskih stekova procesa).
Na kraju adresnog prostora su, najčešće, lokacije, koje odgovaraju registrima
kontrolera. Sloj za rukovanje radnom memorijom obavezno vodi evidenciju o
sistemskoj slobodnoj radnoj memoriji. Ova evidencija može da bude u obliku niza
bita (bit map), u kome svaki bit odgovara grupi susednih lokacija. Podrazumeva se
da je broj lokacija u ovakvoj grupi unapred zadan. On ujedno predstavlja jedinicu u
kojoj se izražava dužina zauzimane i oslobađane zone. Ako je grupa lokacija
slobodna, njoj odgovarajući bit sadrži 1. Inače, on sadrži 0. Mana ovakve evidencije
je da su i operacija zauzimanja i operacija oslobađanja dugotrajne, jer prva
pretražuje evidenciju, radi pronalaženja dovoljno dugačkog niza jedinica, a druga
postavlja takav niz jedinica u evidenciju. Zato se češće evidencija sistemske
slobodne radne memorije pravi u obliku liste slobodnih odsečaka sistemske radne
memorije. Na početku rada operativnog sistema ovakva lista sadrži jedan odsečak,
koji obuhvata celu sistemsku slobodnu radnu memoriju. Ovakav odsečak se drobi u
više kraćih odsečaka, kao rezultat višestrukog stvaranja i uništavanja procesa u
slučajnom redosledu. Novonastali kraći odsečci se nalaze na mestu slika uništenih
procesa, a između njih su slike postojećih procesa. Na početku svakog odsečka su
njegova dužina i adresa narednog odsečka. Broj lokacija, potrebnih za smeštanje
dužine dotičnog odsečka i adrese narednog odesečka, određuje najmanju dužinu
odsečka i predstavlja jedinicu u kojoj se izražavaju dužine odsečaka (odnosno,
dužine zauzimanih i oslobađanih zona). Odsečci su uređeni u rastućem redosledu
adresa njihovih početnih lokacija. To, prilikom oslobađanja zone, olakašava
operaciji oslobađanja:
1. da pronađe dva susedna odsečka, koji mogu da se spoje u jedan, kada se
između njih ubaci oslobađana zona, ili
2. da pronađe odsečak, kome može da se doda (spreda ili straga) oslobađana
zona, ili
3. da pronađe mesto u listi u koje će oslobađana zona biti uključena kao
poseban odsečak.
Listu slobodnih odsečaka pretražuje i operacija zauzimanja, radi pronalaženja
dovoljno dugačkog odsečka. Pri tome se zauzima samo deo odsečka, u koji se može
smestiti zauzimana zona, dok preostali deo odsečka ostaje u listi kao novi odsečak.
Na ovaj način se odsečci dalje usitnjavaju. To dovodi do eksterne fragmentacije.
Ona posredno uzrokuje neupotrebljivost odsečaka, jer onemogućuje zauzimanje
zone, čija dužina je veća od dužine svakog od postojećih odsečaka, bez obzira na
činjenicu da je suma dužina postojećih odsečaka veća od dužine zauzimane zone.
210
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Iskustvo pokazuje da se eksterna fragmentacija povećava, ako se, umesto traženja
prvog dovoljno dugačkog odsečka (first fit), pokušava naći najmanji dovoljno
dugačak odsečak (best fit), ili najveći dovoljno dugačak odsečak (worst fit).
Poboljšanje ne nudi ni ideja da lista odsečaka bude ciklična i da se pretražuje ne od
početka, nego od tačke u kojoj je zaustavljeno poslednje pretraživanje (next fit).
Ideje da dužina zauzimanih zona bude uvek jednaka stepenu broja 2 (quick fit,
buddy system) i da postoji posebna lista odsečaka za svaku od mogućih dužina
takođe nisu najbolje, jer, pored eksterne, uvode i internu fragmentaciju, pošto se na
ovaj način u proseku zauzimaju duže zone od stvarno potrebnih, čime nastaje
neupotrebljiva radna memorija.
Važno je uočiti da rukovanje evidencijom sistemske slobodne radne
memorije zahteva sinhronizaciju procesa, u toku čije aktivnosti se istovremeno
obavljaju operacije zauzimanja i oslobađanja. Sinhronizacija treba da obezbedi
međusobnu isključivost obavljanja ovih operacija, radi očuvanja konzistentnosti
pomenute evidencije.
Rukovanje radnom memorijom se zasniva na pretpostavci postojanja
logičkog i fizičkog adresnog prostora i automatskog pretvaranja logičkih adresa u
fizičke. Time se obezbeđuje, ne samo zaštita operativnog sistema od korisničkih
procesa i međusobna zaštita korisničkih procesa, nego i dinamička relokacija. Ona
je značajna, jer predstavlja preduslov za zamenu slika procesa (swapping), odnosno
za sabijanje slika procesa (compaction), radi eliminisanja eksterne fragmentacije.
Automatsko pretvaranje logičkih adresa u fizičke omogućuju granični i bazni
registar. Sadržaj prvog određuje najvišu dozvoljenu logičku adresu procesa, a
sadržajem drugog se uvećava ispravna logička adresa, radi formiranja
korespondentne fizičke adrese. Sadržaji ovih registara se razlikuju za razne procese,
pa se zato čuvaju u njihovim deskriptorima.
Za zauzimanje i oslobađanje zona sistemske radne memorije potrebno je da
operativni sistem neograničeno pristupa svim lokacijama radne memorije. Da
mehanizam zaštite, koji se zasniva na korišćenju graničnog registra, ne bi sputavao
operativni sistem, neophodan je privilegovani režim rada procesora, u kome je
moguće menjati sadržaj graničnog (i baznog) registra, ili u kome je mehanizam
zaštite isključen. Pozivanje sistemskih operacija, kao što su operacije zauzimanja ili
oslobađanja, automatski prevodi procesor u privilegovani režim rada. U
privilegovanom režimu rada operativni sistem koristi sistemski stek aktivnog
procesa. Podrazumeva se da je, za vreme aktivnosti korisničkih procesa van
operativnog sistema, procesor u neprivilegovanom režimu rada.
SEGMENTACIJA
Rukovanje radnom memorijom ima zadatak da zauzme dovoljno veliku zonu
radne memorije, sastavljenu od lokacija sa uzastopnim fizičkim adresama, da bi u
njih sistemska operacija stvaranja procesa smestila sliku stvaranog procesa.
Međutim, pošto se slika procesa sastoji od tri segmenta (segmenta mašinskih
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
211
naredbi, segmenta promenljivih i segmenta steka), nema stvarne potrebe da cela
slika procesa bude u lokacijama sa uzastopnim fizičkim adresama. Dovoljno je da
to važi samo za svaki od njenih segmenata. Pri tome, jasno, za svaki od segmenata
mora biti vezan poseban par graničnih i baznih registara. Ova tri para graničnih i
baznih registara obrazuju tabelu segmenata, tako da svaki od elemenata ove tabele
sadrži dva polja. Jedno, granično polje odgovara graničnom registru, a drugo, bazno
polje odgovara baznom registru. Pošto se sadržaji parova graničnih i baznih polja
razlikuju od procesa do procesa, deskriptor svakog od njih sadrži kopiju tabele
segmenata. To znači da, prilikom preključivanja procesora sa procesa na proces, u
deskriptor procesa, sa koga se procesor preključuje, dospeva kopija tabele
segmenata, a zatim se u tabelu segmenata prebacuje njena kopija iz deskriptora
procesa, na koga se procesor preključuje. Prema tome, rukovanje tabelom
segmenata produžava (usporava) preključivanje.
Razbijanje slike procesa u tri segmenta, smeštena u razdvojene zone radne
memorije, utiče na način interpretiranja logičke adrese, jer njen jedan deo mora da
ukaže na segment, a drugi na lokaciju u segmentu. Tako prvi deo (najznačajnija dva
bita) logičke adrese predstavlja adresu segmenta i služi kao indeks za tabelu
segmenata. Drugi deo (preostali biti) logičke adrese predstavlja unutrašnju adresu
lokacije u segmentu. Unutrašnja adresa lokacije u segmentu se poredi sa sadržajem
graničnog polja iz elementa tabele segmenata, koga indeksira adresa segmenta i,
ako nije veća, na nju se dodaje sadržaj baznog polja iz pomenutog elementa tabele
segmenata. Tako nastaje fizička adresa. Za ovakvo pretvaranje logičke adrese u
fizičku adresu neophodan je poseban segmentacioni hardver (koji automatski na
opisani način interpretira bite logičke adrese).
Važna posledica segmentacije je mogućnost korišćenja istog segmenta u
slikama raznih procesa, čime se postiže racionalnije korišćenje radne memorije.
Tako, slike svih procesa, nastalih na osnovu iste izvršne datoteke, mogu da dele isti
segment naredbi. To znači da kopija tabele segmenata svakog od ovih procesa ima
element sa istim sadržajima graničnog i baznog polja segmenta mašinskih naredbi.
Sem uštede radne memorije, segmentacija ubrzava zamenu slika procesa
(swapping), jer se segment naredbi ne mora izbacivati, ako je ranije već izbačen u
masovnu memoriju, niti ubacivati, ako već postoji u radnoj memoriji. Jasno,
rukovanje procesima mora voditi posebnu evidenciju segmenata, prisutnih u radnoj
memoriji, da bi moglo otkriti kada je moguće iskoristiti isti segment u slikama
raznih procesa. Ovakva evidencija obuhvata jednoznačnu oznaku segmenta,
podatak o broju slika procesa u koje je segment uključen, dužinu segmenta,
odnosno, sadržaj njegovog graničnog polja, kao i fizičku adresu njegove početne
lokacije, odnosno, sadržaj njegovog baznog polja. Radi očuvanja konzistentnosti
evidencije segmenata, operacije za rukovanje ovom evidencijom moraju obezbediti
sinhronizaciju procesa.
Prethodno opisana (osnovna) segmentacija se razvija u punu segmentaciju,
ako se dozvoli da svakom potprogramu ili promenljivoj odgovara poseban segment.
212
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
U tom slučaju, stvaraju se uslovi za dinamičko linkovanje (povezivanje)
potprograma za program. Ono se ne dešava u toku pravljenja izvršne datoteke, nego
u trenutku prvog poziva potprograma, kada se pronalazi njegov segment i,
eventualno, ubacuje u radnu memoriju. Puna segmentacija dozvoljava i deljenje
promenljivih između raznih procesa, ako se segment iste promenljive uključi u slike
više procesa. Jasno, ovakav način ostvarenja saradnje procesa zahteva njihovu
sinhronizaciju, radi očuvanja konzistentnosti deljenih promenljivih. Za segmente sa
deljenim promenljivim su važna i prava pristupa segmentu, jer nekim od procesa
treba dozvoliti samo da čitaju deljenu promenljivu, a drugima treba dozvoliti i da
pišu u deljenu promenljivu. Zato se elementi tabele segmenata proširuju trećim
poljem, koje sadrži oznaku prava pristupa segmentu. U pogledu prava pristupa,
segmenti se ne razlikuju od datoteka. Prema tome, prava pristupa segmentu
obuhvataju pravo čitanja, pravo pisanja i pravo izvršavanja segmenta. Prva dva
prava se primenjuju na segmente, koji sadrže promenljive, a treće pravo se
primenjuje na segment sa mašinskim naredbama programa ili potprograma.
Na pokušaj narušavanja prava pristupa segmentu reaguje segmentacioni
hardver, generisanjem prekida (izuzetka), što dovodi do uništenja aktivnog procesa.
Rukovanje segmentima se obavlja posredstvom sistemskih programa, kao što
su kompajler ili linker.
Uspeh segmentacije zavisi od efikasnosti pretvaranja logičke adrese u
fizičku. Za punu segmentaciju probleme izaziva veličina tabele segmenata. Inače,
segmentacija se oslanja na već opisane operacije zauzimanja i oslobađanja, koje se
posebno pozivaju prilikom stvaranja i uništavanja svakog od segmenata slike
procesa.
VIRTUELNA MEMORIJA
Prethodno opisano rukovanje radnom memorijom nije u stanju da reši
problem, koji se javlja, ako su slika procesa, ili njen segment veći od ukupno
raspoložive sistemske slobodne radne memorije (ili njenih odsečaka). Za rešavanje
ovog problema važno je uočiti da je za izvršavanje bilo koje mašinske naredbe
neophodno da u radnoj memoriji budu samo bajti njenog mašinskog formata, kao i
bajti njenih operanada. To znači da u radnoj memoriji mora da bude samo kopija
trenutno potrebnog dela slike procesa, dok se cela slika može da nalazi u masovnoj
memoriji. Prema tome, veličinu slike procesa ne ograničava veličina radne
memorije, nego veličina masovne memorije. Zahvaljujući tome, moguće je uvesti
virtuelni adresni prostor koji je mnogo veći od fizičkog adresnog prostora, uz
pretpostavku da se kopije neophodnih delova slike procesa, kada zatreba,
automatski prebacuju iz masovne memorije u radnu memoriju i obrnuto. Do
prebacivanja u obrnutom smeru dolazi, kada je potrebno osloboditi lokacije radne
memorije sa kopijama delova slika procesa, koje su u međuvremenu izmenjene.
Ovo prebacivanje se obavlja, da bi se oslobodila radna memorija i, istovremeno,
obezbedilo da masovna memorija uvek sadrži ažurnu sliku procesa. Da bi se znalo
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
213
koja kopija je izmenjena, neophodno je automatski registrovati svaku izmenu svake
od kopija delova slike procesa.
Virtuelni adresni prostor pripada virtuelnoj memoriji (virtual memory).
Ostvarenje ideje virtuelne memorije je u nadležnosti slojaa za rukovanje virtuelnom
memorijom. On predstavlja specijalizovani oblik slojaa za rukovanje radnom
memorijom.
Za virtuelnu memoriju je neophodno uspostaviti korespondenciju virtuelnih i
fizičkih adresa, radi stvaranja uslova za automatsko pretvaranje virtuelnih adresa u
fizičke. Zbog toga, virtuelna memorija deli virtuelni i fizički adresni prostor u
stranice (page). Svaka stranica obuhvata isti broj lokacija, jednak nekom stepenu
broja 2. Virtuelni adresni prostor se sastoji od virtuelnih stranica, smeštenih u
masovnoj memoriji, a fizički adresni prostor sačinjavaju fizičke stranice, smeštene
u radnoj memoriji. Kada zatreba, kopija virtuelne stranice se prebacuje u fizičku
stranicu, a samo izmenjena kopija se vraća iz fizičke stranice u virtuelnu stranicu.
Pošto stranica predstavlja jedinicu prenosa na relaciji masovna i radna memorija,
prirodno je da ona obuhvata celi broj blokova masovne memorije.
Podela virtuelnog i fizičkog adresnog prostora u stranice znači da se adresa
svake lokacije sastoji od adrese stranice i od unutrašnje adrese lokacije u stranici.
Činjenica da svaka i virtuelna i fizička stranica obuhvata isti broj lokacija (jednak
nekom stepenu broja 2) znači da se unutrašnja adresa lokacije u stranici ne menja
pri pretvaranju virtuelne adrese u fizičku. Pri tome, unutrašnju adresu lokacije u
stranici određuje n manje značajnih bita i u virtuelnoj i u fizičkoj adresi, ako
stranica obuhvata 2n bajta. Preostali, značajniji biti virtuelne, odnosno fizičke adrese
određuju adresu virtuelne, odnosno fizičke stranice. Pošto je virtuelni adresni
prostor veći od fizičkog, pa sadrži više stranica nego fizički, adresa virtuelne
stranice sadrži više bita od adrese fizičke stranice. Iz prethodnog sledi da se
problem pretvaranja virtuelne adrese u fizičku svodi na problem pretvaranja adrese
virtuelne stranice u adresu fizičke stranice. Ovaj problem se rešava pomoću tabele
stranica. Pri tome se podrazumeva da svaka virtuelna stranica odgovara jednom
elementu tabele stranica, i to onom, koga indeksira adresa ove stranice. Takođe se
podrazumeva da se adresa fizičke stranice, sa kopijom neke virtuelne stranice,
smešta u element tabele stranica, koga indeksira adresa ove virtuelne stranice.
Zahvaljujući tome, za pretvaranje adrese virtuelne stranice u adresu fizičke stranice,
u kojoj je kopija ove virtuelne stranice, dovoljno je pročitati element tabele stranica,
koga indeksira adresa pomenute virtuelne stranice. Svaki element tabele stranica,
pored polja za adresu fizičke stranice, sadrži i polje prisustva adrese fizičke
stranice. Ovo polje pokazuje da li je dotični element tabele stranica u upotrebi.
Element tabele stranica je u upotrebi, ako je u njemu prisutna adresa fizičke
stranice, sa kopijom virtuelne stranice, čija adresa indeksira dotični element. Uz
pomenuta dva polja, svaki element tabele stranica sadrži i polje izmene, u kome se
registruje izmena kopije virtuelne stranice, čija adresa indeksira dotični element. I
za polje prisustva i za polje izmene dovoljan je jedan bit.
214
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Tabela stranica se razlikuje od procesa do procesa, pa deskriptor svakog od
njih sadrži kopiju tabele stranica. To produžava (usporava) preključivanje, jer se u
toku preključivanja kopija tabele stranica prebacuje u deskriptor jednog procesa, a
zatim se tabela stranica puni svojom kopijom iz deskriptora drugog procesa.
Za virtuelnu memoriju je važno pitanje dužine stranice. Za dugačke stranice
postaje izražen problem interne fragmentacije, jer sve stranice nisu uvek potpuno
iskorišćene, pa se u njima javljaju neupotrebljive lokacije. Za kratke stranice postaje
izražen problem veličine tabele stranica, jer tada virtuelni adresni prostor ima više
stranica, pa zato i tabela stranica ima više elemenata. Praksa je veličinu stranice
smestila između 512 i 8192 bajta.
Pretvaranje virtuelne adrese virtuelne memorije u fizičku adresu obavlja
poseban stranični hardver. On izdvaja adresu virtuelne stranice, radi indeksiranja
tabele stranica. Nakon što u indeksiranom elementu tabele stranica pronađe
odgovarajuću adresu fizičke stranice, stranični hardver zameni tom adresom adresu
virtuelne stranice i tako formira fizičku adresu. Ako polje prisustva indeksiranog
elementa pokazuje da on nije u upotrebi, tada to znači da se u radnoj memoriji ne
nalazi kopija odgovarajuće virtuelne stranice, pa nije moguće pretvaranje virtuelne
adrese u fizičku, jer ne postoji fizička stranica sa kopijom dotične virtuelne stranice.
U toj situaciji stranični hardver mora da aktivira sloj za rukovanje virtuelnom
memorijom, radi prebacivanja potrebne kopije virtuelne stranice u neku fizičku
stranicu. To se ostvaruje pomoću mehanizma prekida (izuzetka). Stranični hardver
izaziva stranični prekid (page fault), na koga reaguje obrađivač straničnog prekida
sloja za rukovanje virtuelnom memorijom. Njegov zadatak je da prebaci kopiju
potrebne virtuelne stranice u neku fizičku stranicu. Obrađivač straničnog prekida,
pri tome, ažurira odgovarajući element tabele stranica. Tom prilikom on postavlja
polje sa adresom fizičke stranice i polje prisustva odgovarajućeg elementa tabele
stranica, a čisti polje izmene. Podrazumeva se da polje izmene automatski postavlja
stranični hardver, nakon svake izmene kopije virtuelne stranice.
Važno je uočiti da se kopije virtuelnih stranica prebacuju na zahtev (demand
paging), a ne unapred (prepaging), jer u opštem slučaju ne postoji način da se
predvidi redosled korišćenja virtuelnih stranica.
Koncept virtuelne memorije omogućuje da se, umesto cele slike procesa, u
radnoj memoriji nalaze kopije samo neophodnog podskupa njenih virtuelnih
stranica. Prema tome, svakom procesu je pridružen skup fizičkih stranica, u kojima
se nalaze kopije virtuelnih stranica, neophodne za aktivnost procesa. Kada se, u
toku aktivnosti procesa, desi stranični prekid, koji zahteva prebacivanje kopije nove
virtuelne stranice u radnu memoriju, pre zahtevanog prebacivanja neophodno je
razrešiti dilemu da li uvećati skup fizičkih stranica procesa novom fizičkom
stranicom i u nju smestiti kopiju nove virtuelne stranice, ili u postojećem skupu
fizičih stranica procesa zameniti sadržaj neke od njih kopijom nove virtuelne
stranice. Uvećanje skupa fizičkih stranica procesa ima smisla samo ako to dovodi
do smanjivanja učestanosti straničnih prekida (njihovog prosečnog broja u jedinici
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
215
vremena). Znači, kada je, u toku aktivnosti procesa, učestanost straničnih prekida
iznad neke (iskustveno određene) gornje granice, tada ima smisla uvećanje skupa
fizičih stranica procesa, da bi se učestanost straničnih prekida svela na prihvatljiv
nivo. To je važno, jer obrada svakog prekida troši procesorsko vreme, pa veliki broj
obrada straničnih prekida može u potpunosti da angažuje procesor i da tako vrlo
uspori, ili potpuno spreči njegovu bilo kakvu korisnu aktivnost (trashing).
Međutim, ako je učestanost straničnih prekida ispod neke (iskustveno određene)
donje granice, tada ima smisla smanjenje skupa fizičkih stranica procesa, jer i sa
manjim skupom fizičkih stranica učestanost straničnih prekida ostaje u
prihvatljivom rasponu. U opštem slučaju učestanost straničnih prekida ne pada na
nulu, ako sve kopije virtuelnih stranica procesa ne mogu stati u radnu memoriju.
Smanjenje skupa fizičkih stranica procesa je važno, jer se tako omogućuje
neophodni rast skupova stranica drugih procesa, odnosno jer se time podiže stepen
multiprogramiranja. U slučaju da je učestanost straničnih prekida između pomenute
dve granice, tada nema potrebe za izmenom broja fizičkih stranica u skupu fizičkih
stranica aktivnog procesa (slika 5.3.1). U ovom slučaju skup fizičkih stranica
(odnosno, njima odgovarajući skup virtuelnih stranica) obrazuje radni skup
(working set).
učestanost straničnih prekida
gornja granica
donja granica
broj stranica u skupu fizičkih stranica procesa
radni skup
Slika 5.3.1 Odnos učestanosti straničnih prekida i broja stranica u skupu fizičkih
stranica procesa
Radni skup procesa nije statičan. U proseku, on se sporo menja u toku
aktivnosti procesa (iako su, povremeno, moguće značajne kratkotrajne varijacije
radnog skupa). Važno je uočiti da se u toku aktivnosti procesa obavezno javlja
trashing, kada njegov radni skup ne može da stane u radnu memoriju. U ovom
slučaju pomaže izbacivanje (swapping) procesa, dok se ne oslobodi dovoljan broj
fizičkih stranica. Ovaj pristup ima smisla samo ako je radni skup procesa manji od
ukupne sistemske slobodne radne memorije.
216
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Stepen multiprogramiranja kod virtuelne memorije zavisi od broja radnih
skupova, koji se istovremeno mogu smestiti u raspoloživu radnu memoriju. Za
uspeh koncepta virtuelne memorije važno je da se stalno prate radni skupovi
istovremeno postojećih procesa i da se povremeno izbacuju procesi, čim radna
memorija postane pretesna za sve radne skupove (load control). Na ovaj način se
oslobađaju fizički blokovi za preostale procese, neophodni za smeštanje njihovih
radnih skupova. Prilikom kasnijeg ubacivanja procesa, uputno je ubacivati kopije
svih virtuelnih stranica, koje obrazuju njegov radni skup.
Pored radnog skupa, procese karakteriše i minimalni skup. Broj fizičkih
stranica u minimalnom skupu zavisi od procesora. Na primer, za procesor, čije
naredbe imaju najviše dva operanda, minimalni skup sadrži šest fizičkih stranica, jer
se, u ekstremnom slučaju, i bajti mašinske naredbe, kao i bajti oba njena operanda,
mogu nalaziti u različitim susednim fizičkim stranicama. Pošto se naredba može
izvršiti samo kada su u radnoj memoriji prisutni svi bajti njenog mašinskog formata
i svi bajti njenih operanada, prethodno pomenuti ekstremni slučaj uslovljava da je
pridruživanje minimalnog skupa procesu preduslov bilo kakve njegove aktivnosti.
Pad učestanosti straničnih prekida ispod donje granice ukazuje na mogućnost
smanjenja radnog skupa. U ovoj situaciji je potrebno odlučiti koju virtuelnu stranicu
izbaciti iz radnog skupa. Za izbacivanje iz radnog skupa kao kandidat se nameće
virtuelna stranica, koja neće biti referencirana do kraja aktivnosti procesa, ili će biti
referencirana iza svih ostalih virtuelnih stranica iz radnog skupa. Pri tome se pod
referenciranjem podrazumeva pristup bilo kojoj lokaciji stranice, radi preuzimanja
ili izmene njenog sadržaja. Međutim, u opštem slučaju nema načina da se precizno
ustanovi da li će i kada neka virtuelna stranica biti referencirana. Ipak, zahvaljujući
lokalnosti referenciranja, odnosno zapažanju da se pristupi lokacijama sa bliskim
adresama dešavaju u bliskim trenucima, moguće je sa priličnom pouzdanošću
zaključivati o referenciranju stranica u neposrednoj budućnosti na osnovu njihovog
referenciranja u neposrednoj prošlosti. Prema tome, najmanju verovatnoću da bude
referencirana u neposrednoj budućnosti ima stranica, čije poslednje referenciranje je
najstarije, odnosno prethodi referenciranju svih ostalih stranica iz radnog skupa. Za
pronalaženje najmanje korišćene stranice (Least Recently Used - LRU), odnosno
stranice sa najstarijom referencom, neophodno je registrovanje starosti
referenciranja. To je moguće, ako postoji brojač, koji se automatski uvećava za
jedan nakon izvršavanja svake naredbe. Ako se njegova zatečena vrednost
automatski pridružuje virtuelnoj stranici prilikom njenog svakog referenciranja,
tada je stranici sa najstarijom referencom pridružena najmanja vrednost ovog
brojača. Opisani pristup ima samo teoretsko značenje, jer se oslanja na hardver, koji
u opštem slučaju nije raspoloživ. Međutim, moguće je i softverski simulirati
pronalaženje najmanje korišćene stranice (Not Frequently Used - NFU). Ovakva
simulacija se zasniva na postojanju podatka o referenciranju svake virtuelne
stranice. Zato se u elemente tabele stranica dodaje i polje referenciranja, veliko 1
bit. Za ovo polje se podrazumeva da ga stranični hardver automatski postavlja
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
217
prilikom svakog referenciranja stranice. Takođe, elementima tabele stranica se
dodaje i polje starosti referenciranja. Ono sadrži n bita, po jedan bit za svaku od
vrednosti polja referenciranja u poslednjih n trenutaka. Polje starosti referenciranja
se periodično ažurira, tako što se iz njega periodično izbacuje najstariji bit
referenciranja. Radi toga se periodično, u trenucima sa pravilnim vremenskim
razmacima, aktivira obrađivač vremenskog prekida kao posledica dešavanja
posebnog vremenskog prekida. On (1) pomera u desno za jedan bit polje starosti
referenciranja svake od virtuelnih stranica iz radnog skupa aktivnog procesa, (2)
dodaje s leva, u upražnjenu bitnu poziciju ovog polja, zatečeni sadržaj polja
referenciranja dotične virtuelne stranice i (3) zatim očisti polje referenciranja. Na
ovaj način polje starosti referenciranja sadrži najmanju vrednost za virtuelnu
stranicu, koja je najranije referencirana u prethodnih n trenutaka.
Polje starosti referenciranja se može iskoristiti i za određivanje radnog skupa.
Kriterij može biti postavljenost nekog od k najznačajnijih bita iz polja starosti
referenciranja (k < n). Po tom kriteriju radnom skupu pripadaju sve virtuelne
stranice, koje imaju bar jedan bit postavljen u najznačajnijih k bita svog polja
starosti referenciranja.
Povećanje učestanosti straničnih prekida preko gornje granice ukazuje na
potrebu proširenja radnog skupa. U ovom slučaju, potrebno je procesu pridružiti
novu fizičku stranicu, da bi se u nju smestila kopija potrebne virtuelne stranice. Ako
nema slobodnih fizičkih stranica, tada se oslobađa, kada je to moguće, fizička
stranica, koja je pridružena nekom drugom procesu. Time se radni skup ovog
drugog procesa smanjuje. U oslobođenoj fizičkoj stranici zatečenu kopiju
zamenjuje (replacement) kopija potrebne virtuelne stranice. Do ovakve zamene
dolazi i kada se javi potreba za ubacivanjem kopije nove stranice, a učestanost
straničnih prekida je između gornje i donje granice, pa se radni skup aktivnog
procesa ne proširuje, nego se oslobađa jedna od fizičkih stranica iz njegovog radnog
skupa. U oba prethodna slučaja kandidat za zamenu je fizička stranica, koja sadrži
kopiju virtuelne stranice sa najstarijom referencom. Znači i ovde se može primeniti
pristup softverske simulacije pronalaženja najmanje korišćene stranice. Još je
jednostavniji pristup u kome se pronalazi stranica koja nije korišćena u nekom
prethodnom periodu (Not Recently Used - NRU). Za ovaj pristup je dovoljno
posmatrati dvobitni broj, čiji značajniji bit odgovara bitu polja reference, a manje
značajan bit odgovara bitu polja izmene. Pri tome se podrazumeva da stranični
hardver automatski postavlja ove bite nakon svakog referenciranja kopije virtuelne
stranice, odnosno nakon svake njene izmene, a da obrađivač vremenskog prekida
čisti bit reference. Takođe se podrazumeva da se oba bita čiste i prilikom
zauzimanja fizičke stranice, odnosno prilikom zamene njenog sadržaja. Prema
tome, pomenuti dvobitni broj sadrži 00, kada kopija virtuelne stranice nije korišćena
nakon nekog od poslednjih vremenskih prekida (znači, niti referencirana niti
izmenjena). Pomenuti dvobitni broj sadrži 01, kada kopija virtuelne stranice nije
referencirana nakon nekog od poslednjih vremenskih prekida (ali je prethodno
218
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
izmenjena). Ovaj broj sadrži 10, kada kopija virtuelne stranice nije izmenjena, ali je
referencirana nakon poslednjeg vremenskog prekida. Pomenuti dvobitni broj sadrži
11, kada je kopija virtuelne stranice izmenjena i, uz to, referencirana nakon
poslednjeg vremenskog prekida. Kandidati za zamenu su stranice, čiji dvobitni broj
je najmanji.
Prethodno opisani algoritmi zamene, odnosno, načini određivanja fizičke
stranice, čiji sadržaj se zamenjuje, obezbeđuju smanjenje učestanosti straničnih
prekida nakon proširenja radnog skupa. Znači, obavljanje liste istih zahteva za
pristupe virtuelnim stranicama dovodi do pojave manje straničnih prekida nakon
proširenja radnog skupa, nego pre toga. Ovo je važno istaći, jer svi algoritmi
zamene ne dovode obavezno do smanjenja učestanosti straničnih prekida nakon
proširenja radnog skupa. To je karakteristično, na primer, za algoritam zamene, kod
koga se za zamenu odabira fizička stranica sa najstarijim sadržajem (First In First
Out - FIFO), bez obzira da li je on skoro referenciran. Ovaj algoritam zamene ima
tendenciju da u radnom skupu čuva stranice, čiji sadržaj je svežiji, čak i ako one
neće biti uskoro referencirane. Međutim, mana poslednje pomenutog algoritma
zamene se otklanja, ako se on modifikuje, tako da se za zamenu prvo traži fizička
stranica sa najstarijim nereferenciranim sadržajem, a ako su sadržaji svih fizičkih
stranica referencirani, tek tada se odabira fizička stranica sa najstarijim sadržajem
(second chance i clock algoritmi zamene). U sva tri poslednje pomenuta algoritma
zamene (FIFO, second chance i clock) fizičke stranice iz radnog skupa procesa se
uvezuju u listu. Pri tome položaj u listi ukazuje na starost sadržaja fizičke stranice.
U clock algoritmu zamene ovakva lista je kružna, a poseban pokazivač, kao
kazaljka na satu, pokazuje na stranicu sa najstarijim sadržajem.
Virtuelna memorija pokazuje najbolje rezultate, kada uvek ima slobodnih
fizičkih stranica. To se može postići, ako se uvede poseban stranični sistemski
proces, koji se periodično aktivira, da bi oslobodio izvestan broj fizičkih stranica.
On, pri tome, odabira fizičke stranice za oslobađanje po nekom od prethodno
opisanih algoritama zamene. Zadatak straničnog sistemskog procesa je i da u
masovnu memoriju prebacuje izmenjene kopije virtuelnih stranica i da tako čuva
ažurnost masovne memorije.
Sloj za rukovanje virtuelnom memorijom podržava operacije zauzimanja i
oslobađanja, a oslanja se na stranični sistemski proces i obrađivače vremenskog i
straničnog prekida. Obrađivač straničnog prekida se aktivira, kada je referencirana
virtuelna stranica, čija kopija nije prisutna u radnoj memoriji, odnosno, u nekoj od
njenih fizičkih stranica. Ako je, greškom, referencirana virtuelna stranica, koja
uopšte ne postoji u slici procesa, obrađivač straničnog prekida završava aktivnost
prekinutog procesa (uz odgovarajuću poruku). Inače, ovaj obrađivač odabira
slobodnu fizičku stranicu i prema njoj usmerava prenos kopije potrebne virtuelne
stranice sa masovne memorije. Kada se taj prenos završi, obrađivač straničnog
prekida ažurira polja odgovarajućeg elementa tabele stranica i omogućuje nastavak
aktivnosti prekinutog procesa. Kada nema slobodne fizičke stranice, ovaj obrađivač
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
219
oslobađa neku od fizičkih stranica. Ako ona sadrži izmenjenu kopiju virtuelne
stranice, on pokreće prenos ove kopije u masovnu memoriju, radi ažuriranja
odgovarajuće virtuelne stranice. Po završetku ovoga prenosa, obrađivač straničnog
prekida usmerava ka oslobođenoj fizičkoj stranici prenos kopije potrebne virtuelne
stranice, a, nakon završetka ovog prenosa i ažuriranja polja odgovarajućeg elementa
tabele stranica, on omogućava nastavak aktivnosti prekinutog procesa. Za uspešno
obavljanje posla, obrađivaču straničnog prekida je potrebna evidencija o položaju
virtuelnih stranica u masovnoj memoriji. On, takođe, koristi i evidenciju slobodnih
fizičkih stranica. Nju koriste i operacija zauzimanja i operacija oslobađanja, pa
rukovanje ovom evidencijom mora obezbediti sinhronizaciju procesa, i to
onemogućenjem prekida. Ova evidencija može da ima oblik niza bita (bit map). Pri
tome, svaki od ovih bita pokazuje stanje odgovarajuće fizičke stranice. Evidencija
slobodnih fizičkih stranica može da ima i oblik liste, u koju su uvezane sve
slobodne fizičke stranice. Ovaj pristup je efikasniji. Operacija zauzimanja
omogućuje zauzimanje bar minimalnog skupa, radi stvaranja procesa, a operacija
oslobađanja oslobađa sve fizičke stranice iz radnog skupa unišavanog procesa.
STRANIČNA SEGMENTACIJA
Koncept virtuelne memorije može da se primeni na celu sliku procesa, ali i na
pojedine njene segmente. U ovom drugom slučaju reč je o straničnoj segmentaciji
(paging segmentation). Za straničnu segmentaciju je karakteristično da tabela
stranica nije vezana za proces, nego za njegove segmente. Znači, za svaki segment
postoji posebna tabela stranica. U tom slučaju logička adresa se sastoji od adrese
segmenta, od adrese virtuelne stranice segmenta i od unutrašnje adrese lokacije u
ovoj stranici. Pri tome se menja namena graničnog i baznog polja iz elemenata
tabele segmenata. Tako se u graničnom polju nalazi podatak o broju stranica
dotičnog segmenta, odnosno, podatak o najvišoj dozvoljenoj adresi virtuelne
stranice tog segmenta, a u baznom polju se nalazi adresa tabele stranica dotičnog
segmenta. Uz ova dva polja i dalje je prisutno i polje sa pravima pristupa segmentu.
Kod stranične segmentacije, prilikom pretvaranja logičke adrese u fizičku, iz
logičke adrese se izdvajaju adresa segmenta i adresa virtuelne stranice segmenta.
Izdvojena adresa segmenta indeksira element tabele segmenata. Sadržaj graničnog
polja ovog elementa se poredi sa izdvojenom adresom virtuelne stranice segmenta.
Ako ova adresa nije veća od sadržaja pomenutog graničnog polja, tada se sadržaj
baznog polja istog elementa tabele segmenata koristi kao adresa tabele stranica
dotičnog segmenta. Pri tome, izdvojena adresa virtuelne stranice segmenta indeksira
element pomenute tabele stranica. Ako se u ovom elementu nalazi adresa fizičke
stranice, ona se spaja sa unutrašnjom adresom lokacije u stranici, da bi se formirala
fizička adresa. Inače se izaziva stranični prekid i dalji postupak je kao kod virtuelne
memorije. Prethodno opisano pretvaranje logičke adrese u fizičku se nalazi u
nadležnosti stranično-segmentacionog hardvera.
220
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Prednost stranične segmentacije je da ona omogućava dinamičko proširenje
segmenata (dodavanjem novih stranica), što je važno za segmente promenljivih i
steka. Na ovoj osnovi je moguće otvorenim datotekama dodeljivati posebne
segmente i na taj način ponuditi koncept memorijski preslikane datoteke (memory
mapped file). Pristup ovakvoj datoteci ne zahteva sistemske operacije za čitanje,
pisanje ili pozicioniranje, jer se direktno pristupa lokacijama sa odgovarajućim
sadržajem datoteke. Mana koncepta memorijski preslikane datoteke je da se
veličina datoteke izražava celim brojem stranica, jer nema načina da operativni
sistem odredi koliko je popunjeno bajta iz poslednje stranice. Takođe, problem je i
što virtuelni adresni prostor segmenta može biti suviše mali za pojedine datoteke.
Stranična segmentacija pati od interne fragmentacije i izaziva produžavanje
(usporavanje) preključivanja procesa.
OSNOVA SLOJA ZA RUKOVANJE VIRTUELNOM MEMORIJOM
Sloj za rukovanje virtuelnom memorijom se oslanja na operacije sloja za
rukovanje kontrolerima, da bi obezbedio prenos kopija virtuelnih blokova na relaciji
masovna i radna memorija i da bi smestio adrese svojih obrađivača prekida u
odgovarajuće elemente tabele prekida.
5.5 SLOJ ZA RUKOVANJE KONTROLERIMA
Ulazni i izlazni uređaji računara se dele na blokovske i znakovne uređaje.
Ovakva podela je uslovljena razlikama između ove dve vrste ulaznih i izlaznih
uređaja u pogledu jedinice pristupa, u pogledu načina pristupa i u pogledu
upravljanja. Tako je za blokovske uređaje jednica pristupa blok, a za znakovne
uređaje jedinica pristupa je znak. Dalje, dok značajan broj blokovskih uređaja
dozvoljava direktan pristup, znakovni uređaji podržavaju samo sekvencijalni
pristup. Na kraju, za razliku od blokovskih uređaja, znakovni uređaji dozvoljavaju
dinamičko podešavanje njihovih pojedinih funkcionalnih karakteristika, kao što je,
na primer, brzina prenosa znakova od računara i ka računaru. Prethodne razlike
utiču na oblikovanje drajvera, zaduženih za rukovanje kontrolerima.
Važno je uočiti da klasifikacija uređaja na znakovne i blokovske ne obuhvata
sve uređaje. Na primer, sat ili miš ne spadaju ni u znakovne ni u blokovske uređaje.
Zato se drajveri ovakvih uređaja razlikuju od drajvera znakovnih i blokovskih
uređaja.
DRAJVERI
Zajedničko svojstvo drajvera je da je svaki od njih namenjen za rukovanje
određenom klasom uređaja. Pri tome, obično, jedan drajver može da opsluži više
primeraka uređaja iste klase. Drajveri se nalaze u tesnoj saradnji sa kontrolerima
ulaznih i izlaznih uređaja i kriju sve detalje i posebnosti funkcionisanja ovih
kontrolera. Van drajvera su vidljive samo operacije, kao što su, na primer, operacije
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
221
ulaza ili izlaza, koje omogućuju jednoobrazno korišćenje ulaznih i izlaznih uređaja.
Tipične operacije drajvera blokovskih uređaja su:
1. operacija inicijalizacije (koja se poziva samo u toku pokretanja operativnog
sistema) i
2. operacije ulaza i izlaza blokova (koje koristi sloj za rukovanje datotekama).
Tipične operacije drajvera znakovnih uređaja su:
1. operacija inicijalizacije (koja se poziva samo u toku pokretanja operativnog
sistema),
2. operacije ulaza i izlaza znakova (koje koristi sloj za rukovanje datotekama),
kao i
3. upravljačka operacija (koja omogućuje dinamičko podešavanje
funkcionalnih karakteristika znakovnih uređaja, na primer, njihove brzine
prenosa znakova).
Za adresu svake od ovih operacija predviđeno je posebno polje u elementu
tabele drajvera. Podrazumeva se da redni broj drajvera indeksira element ove tabele,
koji sadrži polja sa adresama pojedinih operacija ovog drajvera. Pri tome, polja,
namenjena za adrese operacija, koje dotični drajver ne podržava, sadrže adresu
posebne (lažne) operacije, čije obavljanje nema efekta. To važi, na primer, za
upravljačku operaciju kod drajvera diska, ili za operaciju ulaza kod drajvera
štampača.
Tabela drajvera nudi standardan način za povezivanje sloja za rukovanje
datotekama i sloja za rukovanje ulaznim i izlaznim uređajima, radi vezivanja
operacija običnih i specijalnih datoteka za operacije drajvera ulaznih i izlaznih
uređaja. Zahvaljujući ovoj tabeli, moguće je u operativni sistem dodavati (statički i
dinamički) nove drajvere. Uslov za to je, ne samo dopunjavanje tabele drajvera
adresama operacija novog drajvera, nego i dodavanje objektnog oblika novog
drajvera objektnom obliku operativnog sistema.
U sklopu opsluživanja kontrolera, drajveri moraju da reaguju i na prekide,
koji stižu od kontrolera. Prekidi, na primer, objavljuju da je završen prenos
podataka ka kontroleru, ili od kontrolera. U ovakvom slučaju, obrada prekida
obuhvata ili preuzimanje podatka, pristiglih od kontrolera, ili pripremu prenosa
novih podataka ka kontroleru. Za ovakve obrade su zaduženi obrađivači prekida
drajvera. Za razliku od operacija drajvera, koje se pozivaju iz slojeva iznad sloja za
rukovanje ulaznim i izlaznim uređajima, obrađivače prekida poziva mehanizam
prekida, znači hardver ispod operativnog sistema. Zato operacije drajvera obrazuju
gornji deo drajvera, a obrađivači prekida obrazuju donji deo drajvera. Uslov, da
hardverski mehanizam prekida pozove nekog od obrađivača prekida, je da adresa
ovog obrađivača dospe u odgovarajući element tabele prekida. To se ostvaruje u
okviru drajverske operacije inicijalizacije.
222
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
DRAJVERI BLOKOVSKIH UREĐAJA
Aktivnost svakog drajvera započinje inicijalizacijom njegovog kontrolera. To
se obavi pozivanjem drajverske operacije inicijalizacije u toku pokretanja
operativnog sistema. Nakon toga, aktivnost drajvera blokovskih uređaja se svodi na
prenos blokova ka ovim uređajima i od njih. Zato su za drajverske operacije ulaza i
izlaza blokova obavezni argumenti redni broj prenošenog bloka i adresa bafera u
koji, ili iz kog se prenosi blok. Pomenuti bafer pripada slojau za rukovanje
datotekama. Drajversku operaciju ulaza bloka poziva sistemska operacija čitanja
sloja za rukovanje datotekama. Ako se sistemska operacija čitanja odnosi na običnu
datoteku, ona, kao prvi argument u pozivu drajverske operacije ulaza bloka, navodi
izračunati redni broj bloka. U slučaju da se pomenuta sistemska operacija čitanja
odnosi na specijalnu datoteku, ona, kao prvi argument u pozivu drajverske operacije
ulaza bloka, navodi sadržaj polja pozicije ove datoteke, koje se nalazi u elementu
tabele otvorenih datoteka procesa pozivaoca pomenute sistemske operacije. U
svakom slučaju, kao drugi argument pozivane drajverske opercije koristi se adresa
nekog od slobodnih bafera sloja za rukovanje datotekama.
Drajversku operaciju izlaza bloka poziva sistemska operacija pisanja sloja za
rukovanje datotekama. Ako se sistemska operacija pisanja odnosi na običnu
datoteku, ona, kao prvi argument u pozivu drajverske operacije izlaza bloka, navodi
izračunati redni broj bloka u koji se smeštaju pisani bajti. U slučaju da se pomenuta
sistemska operacija pisanja odnosi na specijalnu datoteku, ona, kao prvi argument u
pozivu drajverske operacije izlaza bloka, navodi sadržaj polja pozicije ove datoteke,
koje se nalazi u elementu tabele otvorenih datoteka procesa pozivaoca pomenute
sistemske operacije. U svakom slučaju, kao drugi argument pozivane drajverske
opercije koristi se adresa nekog od slobodnih bafera sloja za rukovanje datotekama,
u kome je pripremljen novi sadržaj upisivanog bloka.
Drajverska operacija izlaza bloka se poziva i kada je potrebno izmenjene
sadržaje bafera sloja za rukovanje datotekama sačuvati na disku. Ovo se dešava
prilikom oslobađanja bafera, koji sadrži izmenjenu kopiju bloka, ali i periodično,
radi smanjenja mogućnosti gubljenja izmena ovakvih kopija. Za periodično pisanje
ovakvih sadržaja bafera može biti zadužen poseban sistemski proces.
Za drajverske operacije ulaza ili izlaza bloka je prirodno da se oslone na
mehanizam direktnog memorijskog pristupa, ako to omogućuje kontroler. U ovom
slučaju, postavlja se pitanje šta učiniti sa aktivnošću procesa pozivaoca neke od
ovih operacija, nakon pokretanja mehanizma direktnog memorijskog pristupa, radi
prenosa bloka. Ako je za nastavak aktivnosti pomenutog procesa neophodno da
prenos bloka bude završen, tada je neizbežno zustavljanje aktivnosti pomenutog
procesa, nakon pokretanja mehanizma direktnog memorijskog pristupa, dok se
zatraženi prenos bloka ne obavi. Drajverske operacije ulaza ili izlaza bloka, koje
zaustavljaju aktivnost svog procesa pozivaoca, dok se ne obavi zatraženi prenos
bloka, spadaju u klasu blokirajućih operacija. Zaustavljanje aktivnosti jednog
procesa u okviru drajverske operacije ulaza ili izlaza bloka prirodno dovodi do
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
223
preključivanja procesora na drugi proces. U toku aktivnosti ovog drugog procesa se
može, takođe, javiti potreba za prenosom bloka. Ako se, u okviru drajverske
operacije ulaza ili izlaza bloka, ustanovi da se zatraženi prenos ne može pokrenuti,
jer je ulazni ili izlazni uređaj zauzet već pokrenutim prenosom bloka za potrebe
prvog procesa, neizbežno je zaustavljanje aktivnosti i drugog procesa. Pri tome,
mora ostati trag o zahtevu za prenosom novog bloka, da bi se taj prenos pokrenuo
po završetku već pokrenutog prenosa. Sticaj okolnosti može dovesti do toga da
postoji više ovakvih zahteva, jer je rad ulaznog ili izlaznog uređaja sporiji od rada
procesora. Znači, moguće je da se, u toku prenosa jednog bloka, procesor više puta
preključi na razne procese, čije aktivnosti se, jedna za drugom, zaustavljaju zbog
zahteva za prenosom novih blokova. Uvezivanje svih istvoremeno postojećih
zahteva za prenosom blokova u listu zahteva omogućuje ne samo registrovanje
svih zahteva, nego i registrovanje redosleda njihovog obavljanja. Pri tome, svaki
zahtev u ovakvoj listi zahteva mora da sadrži:
1. smer zahtevanog prenosa bloka,
2. redni broj ovog bloka,
3. adresu bafera koji učestvuje u prenosu, kao i
4. adresu deskritpora procesa, čija aktivnost se zaustavlja do obavljanja
zahtevanog prenosa bloka.
Drajverske operacije ulaza ili izlaza bloka započinju pripremanjem zahteva
za prenos bloka i njegovim ubacivanjem u listu zahteva. Time se, ujedno, zaustavlja
aktivnost procesa pozivaoca ovakve operacije, ako nije moguće pokrenuti zahtevani
prenos bloka, jer je drugi prenos u toku. U suprotnom, pokreće se mehanizam
direktnog memorijskog pristupa, radi obavljanja zahtevanog prenosa bloka, i opet
se zaustavlja aktivnost procesa pozivaoca. Nastavak ove aktivnosti omogućuje
odgovarajući obrađivač prekida. Njega pozove kontroler, izazivajući prekid nakon
obavljanja zahtevanog prenosa bloka. Pomenuti obrađivač prekida prvo izbaci iz
liste zahteva upravo opsluženi zahtev, pamteći, pri tome, adresu deskriptora
procesa, čija aktivnost se može nastaviti. Pre omogućavanja nastavljanja ove
aktivnosti, obrađivač prekida pokreće prenos novog bloka, ako lista zahteva nije
prazna. Važno je uočiti da rukovanje listom zahteva u toku drajverskih operacija
ulaza ili izlaza bloka mora biti pod onemogućenim prekidima, da bi obrađivač
prekida uvek zaticao tu listu u ispravnom (konzistentnom) stanju.
Drajver blokovskog uređaja mora da poznaje karakteristike uređaja koga
opslužuje. Na primer, kada opslužuje magnetni disk, drajver mora da zna koliko
sektora ima u stazi, koliko staza ima u cilindru i koliko cilindara ima na disku. Na
osnovu tih podatka, drajver preračunava redni broj bloka u redne brojeve cilindara,
staza i sektora, da bi izazvao pozicioniranje glava diska na potrebni cilindar i
usmerio prenos podataka na odgovarajuće staze i sektore. Jedan blok može da
sadrži više sektora. Preračunavanje rednog broja bloka u redne brojeve cilindara,
staza i sektora može biti i u nadležnosti kontrolera.
224
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
U nadležnosti drajvera blokovskog uređaja je i određivanje načina
preslikavanja blokova u sektore, mada i to može obavljati kontroler. Ovo
preslikavanje je bitno, jer od njega zavisi propusnost, odnosno broj blokova koji se
mogu preneti u jedinici vremena do ili od uređaja, kao što je magnetni disk. Tako,
na primer, zbog dužne prenosa jednog sektora između kontrolera i radne memorije,
moguće je da glava diska pređe preko početka drugog sektora, koji prostorno sledi
odmah iza prenošenog sektora, pre nego se pomenuti prenos završi. Tada se pristup
drugom sektoru mora odložiti za jedan obrtaj, dok njegov početak ponovo ne dođe
ispod glave diska. To znači da je moguć pristup samo jednom bloku u jednom
obrtaju, ako se pretpostavi da sektor odgovara bloku i ako se pristupa uzastopnim
blokovima, koji su preslikani u prostorno uzastopne sektore. Zato je bolje da
uzastopni blokovi ne budu preslikani u prostorno uzastopne sektore (interleaving).
Ako se uzastopni blokovi preslikavaju u sektore, međusobno razdvojene jednim
sektorom (interleaving factor 1), i ako se prenos jednog sektora završi pre nego kraj
njegovog prostornog sledbenika prođe ispod glave diska, tada se u toku jednog
obrtaja može pročitati n/2 uzastopnih blokova, uz pretpostavku da staza sadrži n
sektora i da sektor odgovara bloku. Broj sektora (interleaving factor), koji
razdvajaju sektore, dodeljene uzastopnim blokovima, zavisi od odnosa vremena
prenosa bloka (između kontrolera i radne memorije) i vremena za koje sektor prođe
ispod glave diska. Ako kontroler automatski prebacuje sve sektore staze, iznad koje
se kreće glava diska, u svoju lokalnu radnu memoriju (track_at_time caching), tada
nema smetnje da se uzastopni blokovi preslikaju u prostorno uzastopne sektore.
Propusnost magnetnog diska zavisi i od redosleda usluživanja zahteva za
prenosom blokova, jer, sem vremena prenosa bloka (transfer time), vremena za koje
staza prođe ispod glave diska (rotational delay), na propusnost diska značajno utiče
i vreme premeštanja glave diska sa staze na stazu (seek time). Da bi se ovo vreme
minimiziralo, potrebno je što manje pokretati glavu diska. To znači, da je bolje da
se zahtevi za prenosom blokova ne uslužuju hronološki, nego u redosledu, koji
obezbeđuje minimalno pokretanje glave diska. Zato se lista ovakih zahteva sortira u
rastućem redosledu staza, na kojima se nalaze blokovi, čije prenošenje se zahteva, a
glava diska se pomera iz početne pozicije samo u jednom smeru, dok svi zahtevi u
smeru njenog kretanja ne budu usluženi. Posle toga, ona se vraća u početnu
poziciju, radi usluživanja zahteva, koji su pristigli nakon što je glava diska prešla
preko staza na kojima se nalaze blokovi, čije prenošenje se zahteva. Na ovaj način
se, pored optimizacije kretanja glave diska, obezbeđuje i pravedno usluživanje svih
zahteva, jer nema mogućnosti za nepredvidivo dugo odlaganje usluživanja
pojedinih zahteva. Prethodno opisani pristup se naziva elevator algoritam, jer se
po njemu upravlja kretanjem liftovima u visokim zgradama. O optimizaciji kretanja
glave diska može da se brine i kontroler. U ovom slučaju, drajver samo ubacuje
zahteve u listu zahteva, a kontroler se brine o redosledu njihovog usluživanja. To je
naročito važno, kada se kontroler brine o zameni loših sektora ispravnim rezervnim
sektorima, koji se nalaze na posebnim rezervnim stazama. Pošto, u ovom slučaju,
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
225
jedino kontroler zna kojoj stazi stvarno pripada koji sektor, jedino on može da
optimizira kretanje glave diska.
Zadatak drajvera je da iskoristi sve mogućnosti kontrolera. Tako, ako
kontroler podržava više magnetnih diskova i omogućuje istovremena nezavisna
pozicioniranja glava raznih diskova, tada to drajver može da iskoristi za smanjenje
srednjeg vremena premeštanja glave diska sa staze na stazu.
Drajver može da poveća pouzdanost diska, ako reaguje na prolazne greške u
radu diska. Na primer, u slučaju pojave zrnca prašine između glave magnetnog
diska i magnetne površine, ulaz ili izlaz neće biti uspešan. Međutim, višestrukim
ponavljanjem operacije, drajver može da otkloni prethodnu grešku, jer je verovatno
da će se zrnce prašine pomeriti u narednim pokušajima ulaza ili izlaza. Na sličan
način drajver može da reaguje i na pozicioniranje glave diska na pogrešnu stazu,
kao i na neke druge greške kontrolera. Drajver može i da smanji trošenje magnetnih
disketa, kod kojih glava disketne jedinice klizi po površini sa magnetnim
premazom, zaustavljanjem obrtanja diskete, čim nestanu zathevi za prenos blokova
na nju i sa nje.
BLOKOVSKI I ZNAKOVNI UREĐAJI KAO SPECIJALNE
DATOTEKE
Za blokovske uređaje, poput magnetnog diska, je tipično da ih koriste
istovremeno postojeći procesi u toku pristupa (raznim) datotekama. Zato je i
moguće da se u listi zahteva istovremeno zateknu zahtevi raznih procesa.
Blokovskim uređajima se retko pristupa kao specijalnim datotekama, a kada se to i
desi, koristi ih samo jedan proces, čiji je zadatak najčešće provera ispravnosti
blokova, radi pronalaženja izgubljenih ili loših blokova. Za razliku od blokovskih
uređaja, za znakovne uređaje je tipično da im procesi pristupaju kao specijalnim
datotekama i da ih zaključavaju, da bi obezbedili međusobnu isključivost u toku
njihovog korišćenja. Takav način upotrebe znakovnih uređaja je uobičajen ne samo
za terminale, za koje je prirodno da ih opslužuje samo jedan proces, nego i za
štampače ili mrežne kontrolere, za koje je prirodno da ih koristi više procesa. Zato
se, u slučaju znakovnih uređaja, kao što su štampači ili mrežni kontroleri, uvode
posebni sistemski procesi posrednici (spooler), koji posreduju u korišćenju
pomenutih uređaja. Svaki od ovih procesa pristupa svom znakovnom uređaju kao
specijalnoj datoteci, koju zaključava, da bi obezbedio međusobnu isključivost u
toku njenog korišćenja. Istovremeno, uz svaki od sistemskih procesa posrednika
postoji i poseban imenik. Kada neki korisnički proces želi da odštampa tekst, ili da
pošalje poruku kroz mrežu, on prvo pripremi datoteku sa odgovarajućim sadržajem,
a zatim tu datoteku ubaci u pomenuti imenik. Odgovarajući sistemski proces
posrednik vadi datoteke iz svog imenika (jednu po jednu), da bi njihove sadržaje
(jedan po jedan) uputio, posredstvom svoje specijalne datoteke, na željeni uređaj.
Pri tome se korisnički procesi (s jedne strane) i sistemski proces posrednik (s druge
strane) nalaze u odnosu proizvođač i potrošač, jer prvopomenuti procesi
226
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
“proizvode” datoteke, koje “troši” drugopomenuti proces. Da bi ovakva saradnja
procesa bila uspešna, neophodno je da sloj za rukovanje datotekama obezbedi
sinhronizaciju procesa tokom njihovog pristupa imenicima, koji posreduju u
razmeni datoteka.
DRAJVERI ZNAKOVNIH UREĐAJA
Blokovski i znakovni uređaji se razlikuju ne samo po načinu korišćenja, nego
i po načinu aktiviranja. Tako, dok blokovske uređaje uvek aktiviraju procesi,
aktivnost znakovnih uređaja zavisi i od aktivnosti korisnika. Na primer, prispeće
znakova sa tastature ne zavisi od aktivnosti procesa, nego od aktivnosti korisnika.
Slično, upućivanje znakova na ekran zavisi i od aktivnosti korisnika (eho), ali i od
aktivnosti procesa. Zato u sastav drajvera znakovnih uređaja obavezno ulaze i
baferi. Oni su namenjeni za smeštanje znakova, koji su, nezavisno od aktivnosti
procesa, prispeli sa ovih uređaja, odnosno, koji su upućeni ka ovim uređajima. U
ovakvim baferima znakovi se čuvaju dok ih procesi ili uređaji ne preuzmu. Tako, na
primer, za drajver terminala je potreban par takvih bafera za svaki od terminala koje
drajver opslužuje. Pri tome, jedan, ulazni bafer služi za smeštanje znakova,
prispelih sa tastature, a drugi, eho bafer služi za smeštanje znakova, upućenih ka
ekranu. Svaki pritisak dirke sa tastature izaziva prekid, koji aktivira obrađivača
prekida tastature. Ako je ulazni bafer pun, obrađivač prekida ignoriše prispeli znak.
Inače, on ga preuzima i smešta u ulazni bafer. U ulaznom baferu znak čeka da neki
proces zatraži njegovo preuzimanje. Preuzimanje znaka iz ulaznog bafera
omogućuje drajverska operacija ulaza znaka. U okviru ove operacije se zaustavlja
aktivnost procesa njenog pozivaoca, ako je ulazni bafer prazan. Tada nastavljanje
ove aktivnosti omogućuje obrađivač prekida tastature, po smeštanju znaka u ulazni
bafer. U svakom slučaju, drajverska operacija ulaza znaka vraća preuzeti znak iz
ulaznog bafera kao svoju vrednost.
Obrađivač prekida tastature ima, takođe zadatak da proveri da li je eho bafer
prazan. Ako ovaj bafer nije prazan, tada obrađivač prekida tastature smešta prispeli
znak u eho bafer. Inače, obrađivač prekida tastature upućuje prispeli znak ka
ekranu. Nakon prikazivanja znaka, ekran izaziva prekid, koji aktivira obrađivača
prekida ekrana. Ako je eho bafer prazan, aktivost obrađivača prekida se odmah
završava. Inače, on preuzima naredni znak iz eho bafera i upućuje ga ka ekranu.
Eho bafer je podesan i za čuvanje znakova, koje procesi žele da prikažu na
ekranu. Prikazivanje znaka omogućuje drajverska operacija izlaza znaka. Jedini
argument njenog poziva je prikazivani znak. U okviru ove operacije se zaustavlja
aktivnost procesa njenog pozivaoca, ako je eho bafer pun. Tada nastavljanje ove
aktivnosti omogućuje obrađivač prekida ekrana i to nakon pražnjenja jednog znaka
(ili više znakova) iz ovog bafera. Ako drajverska operacija izlaza znaka zatekne eho
bafer prazan, ona upućuje prikazivani znak ka ekranu. U slučaju da eho bafer nije ni
pun ni prazan, drajverska operacija izlaza znaka samo smešta prikazivani znak u
eho bafer.
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
227
Prethodno opisane drajverske operacije ulaza i izlaza znaka spadaju u
blokirajuće operacije. Ove operacije se pozivaju iz sistemskih operacija čitanja i
pisanja sloja za rukovanje datotekama, kada se čita, odnosno piše specijalna
datoteka. Poziv drajverske operacije ulaza nema argumenata, a njegova povratna
vrednost je pročitani znak (njegov kod). Za poziv drajverske operacije izlaza kao
jedini argument služi pisani znak (njegov kod). Ovaj poziv nema povratnu vrednost.
Rukovanje ulaznim i eho baferom, u okviru drajverskih operacija ulaza i
izlaza znaka, mora biti pod onemogućenim prekidima, da bi obrađivači prekida
tastature i ekrana uvek zaticali bafere u ispravnom (konzistentnom) stanju. Inače,
obrađivač prekida tastature i proces pozivalac drajverske operacije ulaza znaka se
nalaze u odnosu proizvođač i potrošač. U istom odnosu se nalaze proces pozivalac
operacije izlaza znaka i obrađivač prekida ekrana.
Za grafičke (memorijski preslikane) terminale nije potreban eho bafer, niti
obrađivač prekida ekrana, jer ovakvi terminali poseduju video memoriju čiji sadržaj
se periodično prikazuje prilikom osvežavanja ekrana. Zato je, kod grafičkog
terminala, za prikazivanje znaka na ekranu dovoljno smestiti znak u odgovarajuću
lokaciju video memorije. U slučaju da se želi podržati više prozora (window) na
ekranu grafičkog terminala, za svaki od prozora je potreban poseban ulazni bafer.
Znak prispeo sa tastature se smešta u ulazni bafer aktivnog prozora, a prikazivani
znak se upućuje u deo video memorije prozora na kome znak treba da se pojavi. O
aktivnom prozoru se brine rukovalac prozorima.
Drajver terminala, pored operacije inicijalizacije, namenjene za inicijalizaciju
kontrolera terminala, i operacija ulaza i izlaza znakova, nudi i upravljačku
operaciju. Argumenti njenog poziva utiču ne samo na funkcionisanje, na primer,
terminala, nego i na funkcionisanje drajvera terminala. Upravljačka operacija
omogućuje da se drajveru terminala saopšti da interpretira znakove koji dolaze sa
tastature (cooked mode), ili da ih ne interpretira (raw mode). U prvom slučaju, u
nadležnosti drajvera terminala se nalazi editiranje znakova prispelih sa tastature. U
sklopu toga, drajver terminala mora, na primer, da omogući brisanje poslednje
prispelog znaka. Znači, kada primi odgovarajući upravljački znak (delete), drajver
terminala, odnosno, njegov obrađivač prekida tastature, mora da prethodno prispeli
znak izbaci iz ulaznog i iz eho bafera, kao i da obezbedi brisanje tog znaka sa
ekrana, ako je on već prikazan. Takođe, drajver terminala se brine o interpretaciji
upravljačkih znakova “prelazak na novu liniju” (line feed), “prelazak na početak
linije” (carriage return), kao i drugih upravljačkih znakova (escape, return ili enter
i slično). U slučaju kada ne interpretira znakove, drajver terminala samo prosleđuje
znakove koji su pristigli u njegov ulazni bafer.
Eho znaka, pristiglog sa tastature, nije uvek poželjan. To je slučaj, na primer,
kod zadavanja znakova lozinke. Zato upravljačka operacija omogućuje da se
drajveru terminala saopšti kada da vrši, a kada da ne vrši eho pristiglih znakova.
Drajver terminala mora da razdvoji eho znakova od prikazivanja znakova koje na
ekran šalju procesi.
228
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Drajver terminala može da interpretira znakove, koji su mu prosleđeni, radi
pomeranja kursora, pomeranja linija, kao i drugih rukovanja ekranom, poput
rukovanja prozorima.
DRAJVER SATA
U nadležnosti sloja za rukovanje kontrolerima se nalazi i praćenje proticanja
vremena. Praćenje proticanja vremena se zasniva na brojanju periodičnih prekida,
koje u pravilnim vremenskim intervalima generiše sat. Obrađivač prekida sata broji
prekide sata, a njihov zbir predstavlja sistemsko vreme (lokalno vreme u računaru).
Ovaj obrađivač prekida predstavlja donji deo drajvera sata. Gornji deo ovog
drajvera predstavljaju sistemske operacije za preuzimanje ili izmenu sistemskog
vremena i za uspavljivanje procesa, odnosno, za odlaganje njegove aktivnosti, dok
ne istekne zadani vremenski interval.
Sistemsko vreme se može predstaviti kao broj prekida sata ili kao broj
sekundi i broj prekida sata u tekućoj sekundi. Druga predstava zahteva manje
prostora. Sistemsko vreme može da se računa u odnosu na neki nepromenljivi
trenutak u prošlosti, ili u odnosu na trenutak poslednjeg pokretanja operativnog
sistema.
Kvantum se predstavlja kao celi broj prekida sata.
Važno je uočiti da, dok je sistemsko vreme precizno, jer je sat precizan, dotle
pripisivanje procesorskog vremena procesima, odnosno, merenje trajanja aktivnosti
procesa, ne mora biti precizno. Kada postoji ovakva nepreciznost, nju izaziva
nemogućnost merenja dužine vremenskih intervala, koji su kraći od perioda prekida
sata, a kojih ima u toku aktivnosti procesa. Na primer, trajanje obrade prekida je
obično kraće od perioda prekida sata i pripisuje se prekinutom procesu, iako
pomenuta obrada prekida ne mora biti sa njim povezana. Obrada prekida obično
predstavlja deo aktivnosti prekinutog procesa, jer se u obradi prekida, zbog brzine,
izbegava preključivanje sa prekinutog procesa, odnosno preključivanje na prekinuti
proces. Slično, ako se u toku jednog perioda prekida sata desi više preključivanja,
celi period se pripisuje kvantumu poslednjeg aktivnog procesa, koga je prekinuo
prekid sata. Pomenute nepreciznosti se mogu izbeći, ako procesor broji svoje
cikluse i njihovu sumu čuva u posebnom registru. Ako se sadržaj ovog registra
preuzme na početku i na kraju perioda aktivnosti procesora koji je kraći od perioda
sata, iz razlike ovih sadržaja se može ustanoviti koliko ciklusa je potrošeno u ovom
periodu i iz toga odrediti trajanje dotičnog perioda.
Procesorsko vreme troši i obrađivač prekida sata, jer se u njegovoj
nadležnosti nalazi više poslova, kao što su:
1. održavanje sistemskog vremena,
2. praćenje isticanja kvantuma aktivnog procesa,
3. praćenje ukupnog korišćenja procesorskog vremena aktivnog procesa,
4. provera da li je nastupilo vreme buđenja uspavanog procesa (čija aktivnost
se nastavlja tek kada istekne zadani vremenski interval), ili
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
229
5. skupljanje statistika o aktivnosti procesa (koje se svodi na registrovanje
sadržaja programskog brojača, radi otkrivanja učestanosti izvršavanja
pojedinih delova programa).
RUKOVANJE TABELOM PREKIDA
Sloj za rukovanje kontrolerima omogućuje i smeštanje adresa obrađivača
prekida u tabelu prekida, čime dozvoljava da viši slojevi operativnog sistema mogu
da reaguju na prekide. Za operaciju smeštanja adresa obrađivača prekida u tabelu
prekida nije uputno da bude sistemska operacija, jer ona pruža mogućnost da se
ugrozi funkcionisanje operativnog sistema i naruši njegov mehanizam zaštite.
OSNOVA SLOJA ZA RUKOVANJE KONTROLERIMA
Sloj za rukovanje kontrolerima se oslanja na sloj za rukovanje procesorom,
jer su preključivanja sastavni deo aktivnosti drajvera. Za operacije sloja za
rukovanje kontrolerima, odnosno za drajverske operacije, je zajedničko da se
obavljaju pod onemogućenim prekidima, što je prihvatljivo, jer je reč o
kratkotrajnim operacijama.
5.6 SLOJ ZA RUKOVANJE PROCESOROM
Osnovni zadatak rukovanja procesorom je preključivanje procesora sa
aktivnog procesa na neki od spremnih procesa. O izboru spremnog procesa, na koga
se preključuje procesor, brine raspoređivanje. Ovaj izbor zavisi od cilja
raspoređivanja. Tipični ciljevi raspoređivanja su, na primer, poboljšanje
iskorišćenja procesorskog vremena, ravnomerna raspodela procesorskog vremena
ili što kraći odziv na korisničku akciju. Ovakvi ciljevi nisu uvek saglasni, jer se ne
mogu istovremeno ostvariti.
Za interaktivno korišćenje računara cilj raspoređivanja je da ravnomerno
rasporedi procesorsko vreme između istovremeno postojećih procesa, odnosno,
između njihovih vlasnika (korisnika, koji istovremeno koriste računar). Ovakav cilj
se ostvaruje kružnim raspoređivanjem (round robin scheduling), koje svakom od
istovremeno postojećih procesa dodeljuje isti vremenski interval, nazvan kvantum.
Po isticanju kvantuma, aktivni proces prepušta procesor spremnom procesu, koji
najduže čeka na svoj kvantum. Neophodan preduslov za primenu kružnog
raspoređivanja je da se preključivanje vezuje za trenutak u kome se završava tekući
kvantum. Zato je neophodno da se preključivanje poziva iz obrađivača prekida sata.
Kružno raspoređivanje se uspešno primenjuje i u situaciji kada hitnost svih
procesa nije ista, pa se, zbog toga, procesima dodeljuju razni prioriteti. Pri tome se
podrazumeva da kružno raspoređivanje važi u okviru grupe procesa sa istim
prioritetom. Procesor se preključuje na procese sa nižim prioritetom samo kada se
završi (zaustavi) aktivnost i poslednjeg od procesa sa višim prioritetom. Procesor se
preključuje na proces sa višim prioritetom odmah po pojavi ovakvog procesa
230
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
(preemptive scheduling), odnosno odmah po omogućavanju nastavka aktivnosti
prioritetnijeg procesa.
Dinamička izmena prioriteta procesa doprinosi ravnomernosti raspodele
procesorskog vremena između procesa, ako se uspostavi obrnuta proporcionalnost
između prioriteta procesa i obima u kome je on iskoristio poslednji kvantum. Pri
tome se periodično proverava iskorišćenje poslednjeg kvantuma svakog od procesa
i, u skladu s tim, procesima se dodeljuju novi prioriteti. Takođe, dinamička izmena
prioriteta procesa doprinosi ravnomernosti raspodele procesorskog vremena između
korisnika, ako se uspostavi obrnuta proporcionalnost između prioriteta procesa, koji
pripadaju nekom korisniku, i ukupnog udela u procesorskom vremenu tog korisnika
u toku njegove interakcije sa računarom. Znači, što je ukupan udeo korisnika više
ispod željenog proseka, to prioritet njegovih procesa više raste.
Ravnomerna raspodela procesorskog vremena se može postići i bez izmena
prioriteta, ako se uvede lutrijsko raspoređivanje (lottery scheduling). Ono se
zasniva na dodeli procesima lutrijskih lozova. Nakon svakog kvantuma na slučajan
način se izvlači broj loza, a procesor se preključuje na proces koji poseduje
izvučeni loz. Tako, ako ukupno ima m lozova, proces, koji poseduje n od m lozova,
u proseku koristi n/m kvantuma procesorskog vremena.
Od dužine kvantuma zavisi iskorišćenje procesora, ali i odziv računara,
odnosno brzina kojom on reaguje na korisničku akciju posredstvom terminala. Pri
tome, skraćenje (do određene granice) kvantuma doprinosi poboljšanju odziva, ali i
smanjenju iskorišćenja procesora, jer se na preključivanje troši procesorsko vreme.
Suviše kratak kvantum može da ugrozi i odziv, ako se prevelik procenat
procesorskog vremena troši na preključivanje. Sa stanovišta iskorišćenja procesora
prihvatljiva su samo neophodna preključivanja, kada nije moguć nastavak
aktivnosti procesa, ili, bar značajno smanjivanje učestanosti preključivanja. S tom
idejom na umu moguće je opet iskoristiti dinamičku izmenu prioriteta procesa za
(1) održavanje dobrog odziva za procese, koji su u interakciji sa korisnicima, kao i
za (2) održavanje dobrog iskorišćenja procesora za pozadinske (background)
procese, koji nisu u (čestoj) interakciji sa korisnicima. Pri tome se interaktivnim
procesima dodeljuje najviši prioritet i najkraći kvantum. Pozadinskim procesima,
koji su vrlo dugo aktivni bez ikakve interakcije sa korisnikom, se dodeljuje najniži
prioritet i najduži kvantum. Procesu automatski opada prioritet i produžava se
kvantum što je on duže aktivan i ima manju interakciju sa korisnikom. Povećanje
interakcije sa korisnikom dovodi do porasta prioriteta procesa i smanjenja njegovog
kvantuma. I u ovom slučaju, dinamička izmena prioriteta se obavlja periodično.
Prethodno izloženo ukazuje na veliku važnost postojanja mogućnosti za
uticanje na prioritet procesa, jer je to način da se upravlja ponašanjem računara.
Za operacije sloja za rukovanje procesorom je zajedničko da se obavljaju pod
onemogućenim prekidima, što je prihvatljivo, jer je reč o kratkotrajnim
operacijama. To je naročito značajno za operacije koje rukuju deskriptorima
procesa, jer jedino onemogućenje prekida osigurava ispravnost rukovanja listama u
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
231
koje se uključuju i iz kojih se isključuju deskriptori procesa u toku ovih operacija
(odnosno, osigurava konzistentnost ovih listi). U ovakve operacije spadaju operacija
preključivanja (i operacije raspoređivanja), sistemska operacija za izmenu prioriteta
procesa, ili sistemske operacije za sinhronizaciju procesa.
Operacije raspoređivanja obuhvataju bar dve operacije. Jedna ubacuje proces
među spremne procese, tako što njegov deskriptor uvezuje na kraj liste deskriptora
spremnih procesa, koja odgovara prioritetu dotičnog procesa. U ovom slučaju se
podrazumeva da za svaki prioritet postoji posebna lista deskriptora spremnih
procesa, na koju se primenjuje kružno raspoređivanje. Druga od ove dve operacije
izvezuje iz listi deskriptora spremnih procesa deskriptor najprioritetnijeg spremnog
procesa.
5.7 PITANJA
1. Šta se dešava u toku stvaranja procesa?
2. Šta se dešava u toku uništavanja procesa?
3. Od čega se sastoji slika procesa?
4. Gde se smeštaju dinamičke promenljive procesa?
5. Koje atribute poseduje proces?
6. Kada proces stvaralac čeka na kraj aktivnosti stvaranog procesa?
7. Kada se poziva operacija uništavanja procesa?
8. Šta je stepen multiprogramiranja?
9. Da li je broj deskriptora procesa veći od stepena multiprogramiranja?
10. Zašto se izbacuju slike procesa iz radne u masovnu memoriju?
11. Kada nastaje kopija slike procesa u masovnoj memoriji?
12. Šta je dugoročno raspoređivanje?
13. Zašto je dobro da rukovanje nitima bude u nadležnosti operativnog
sistema?
14. Da li rukovanje nitima izvan operativnog sistema ima prednosti?
15. Koji sistemski procesi postoje?
16. Ko i kako podržava predstavljanje korisnika?
17. Šta sadrži datoteka lozinki?
18. Zašto je potrebno da vlasnik izvršne datoteke bude i vlasnik procesa,
nastalih na osnovu ove izvršne datoteke?
19. Šta je trojanski konj?
20. Zašto je potrebna sinhronizacija procesa prilikom pristupanja datoteki
lozinki?
21. Šta je potrebno za kriptovanje i dekriptovanje?
22. U čemu je razlika simetrične i asimetrične kriptografije?
23. Gde se koristi simetrična, a gde asimetrična kriptografija?
24. Na čemu se zasniva tajnost kriptovanja?
25. Kako se iz rednog broja bajta određuje redni broj bloka koji sadrži ovaj
bajt kod kontinualnih datoteka?
232
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
26. Kako se iz rednog broja bajta određuje redni broj bloka koji sadrži ovaj
bajt kod rasutih datoteka?
27. Koje mane imaju kontinualne datoteke?
28. Koje vrline imaju kontinualne datoteke?
29. Šta je interna fragmentacija?
30. Kakva evidencija slobodnih blokova pogoduje kontinulanim datotekama?
31. Šta je eksterna fragmentacija?
32. Kako se može rešiti problem eksterne fragmentacije?
33. Koje mane imaju rasute datoteke?
34. Koje vrline imaju rasute datoteke?
35. Čemu služi tabela pristupa?
36. Kakvu organizaciju ima tabela pristupa?
37. Gde se čuva tabela pristupa?
38. Kakva evidencija slobodnih blokova pogoduje rasutim datotekama?
39. Da li je potrebno sinhronizovati procese kada pristupaju evidenciji
slobodnih blokova (objasniti na primeru)?
40. Šta ugrožava konzistentnost sistema datoteka (objasniti na primeru)?
41. Čemu služi baferski prostor?
42. Koji od bafera iz baferskog prostora se oslobađa, ako su svi baferi
zauzeti?
43. Kada se izmenjeni sadržaji bafera iz baferskog prostora prebacuju u
masovnu memoriju?
44. Od čega zavisi veličina bloka?
45. Šta sadrži deskriptor datoteke?
46. Da li je potrebna sinhronizacija procesa kada pristupaju istom deskriptoru
datoteke?
47. Šta sadrže imenici?
48. Koji blokovi se prebacuju sa masovne u radnu memoriju u toku
pretraživanja imenika, radi pristupanja sadržaju datoteke sa zadanom
putanjom (objasniti na primeru)?
49. Gde se nalazi tabela otvorenih datoteka?
50. Šta sadrži tabela otvorenih datoteka?
51. Šta se dešava u toku otvaranja datoteke?
52. Šta se dešava u toku zatvaranja datoteke?
53. Zašto izmena prava pristupa datoteci ne utiče na procese koji pristupaju
datoteci u toku izmene njenih prava pristupa?
54. Šta se dešava u toku čitanja datoteke?
55. Šta se dešava u toku pisanja datoteke?
56. Kako se omogućuje sekvencijalni pristup datotekama?
57. Koja prava mora da ima korisnik da bi mogao da izmeni ime datoteci?
58. Koja prava mora da ima korisnik da bi mogao da izbriše datoteku?
59. Zašto su potrebne sistemske operacije za rukovanje imenicima?
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
233
60. Čemu služe specijalne datoteke?
61. Kakvu namenu imaju pojedini blokovi diska?
62. U čemu se razlikuju deskriptori specijalnih i ostalih datoteka?
63. Kakvu ulogu imaju standardni ulaz i standardni izlaz?
64. Zašto je važno spašavanje datoteka?
65. U kom obliku može biti evidencija o sistemskoj slobodnoj radnoj
memoriji kod kontinualne radne memorije?
66. Kada se javlja eksterna fragmentacija kod kontinulane radne memorije?
67. Kako se rešava problem eksterne fragmentacije kod kontinulane radne
memorije?
68. Zašto je važna dinamička relokacija za kontinulanu radnu memoriju?
69. Koje prednosti nudi segmentacija radne memorije?
70. Kako se interpretira logička adresa lokacija iz segmentirane radne
memorije?
71. Šta sadrži tabela segmenata?
72. Koje prednosti nudi puna segmentacija?
73. Koje prednosti nudi virtuelna memorija?
74. Zašto se virtuelna i fizička memorija dele u stranice?
75. Kako se uspostavlja korespondencija virtuelnih i fizičkih adresa?
76. Čemu služi tabela stranica?
77. Kada se prebacuju kopije stranica iz masovne u fizičku memoriju?
78. Šta utiče na dužinu stranice?
79. Kada se javlja stranični prekid?
80. Koje strancie obrazuju radni skup?
81. Kada se menja broj stranica iz radnog skupa?
82. Šta je minimalni skup?
83. Kako se bira stranica koja se izbacuje iz radnog skupa?
84. Čemu služi polje starosti referenciranja?
85. Čemu služe biti reference i izmene?
86. Da li povećanje broja stranica u radnom skupu uvek dovodi do smanjenja
učestanosti straničnih prekida?
87. Šta sadrži tabela stranica?
88. Koju ulogu ima stranični sistemski proces?
89. Šta se dešava u toku obrade straničnog prekida?
90. Koje su vrline stranične segmentacije?
91. Kako stranična segmentacije interpretira logičku adresu lokacije?
92. Šta sadrži tabela segmenata kod stranične segmentacije?
93. Šta su memorijski preslikane datoteke?
94. Šta su drajveri?
95. Koje operacije nude drajveri?
96. Šta sadrži tabela drajvera?
97. Čemu služi tabela drajvera?
234
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
98. Šta se nalazi u gornjem, a šta u donjem delu drajvera?
99. Ko poziva drajverske operacije ulaza i izlaza?
100. Čemu služi lista zahteva drajvera blokovskih uređaja?
101. Šta sadrže zahtevi iz liste zahteva drajvera blokovskih uređaja?
102. Ko uključuje, a ko isključuje zahteve iz liste zahteva drajvera
blokovskih uređaja?
103. Kako se preslikavaju blokovi u sektore?
104. Zašto je važno minimiziranje kretanja glave za čitanje i pisanje diska?
105. Šta je elevator algoritam?
106. Kako drajver može da poveća pouzdanost diska?
107. Kada se blokovski uređaji koriste kao specijalne datoteke?
108. Šta je namena sistemskih procesa posrednika?
109. Koji baferi su potrebni za rad drajvera terminala?
110. Kako se sinhronizuje rukovanje baferima drajvera terminala?
111. Koji bafer nije potreban grafičkim terminalima?
112. Kakvu ulogu imaju upravljačke operacije drajvera terminala?
113. Koje nadležnosti ima drajver sata?
114. Kada pripisivanje procesorskog vremena procesima nije precizno?
115. Kako se može precizno meriti trajanje aktivnosti procesa?
116. Šta su ciljevi raspoređivanja?
117. Kako se ostvaruje ravnomerna raspodela procesorskog vremena?
118. Kako skraćenje kvantuma utiče na iskorišćenje procesora i na odziv
računara?
119. Na koji način se za interaktivne procese poboljšava odziv računara, a za
pozadinske procese se povećava iskorišćenje procesorskog vremena?
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
235
6. MRTVA PETLJA
6.1 USLOVI ZA POJAVU MRTVE PETLJE
Mrtva petlja opisuje situaciju u kojoj je trajno zaustavljena aktivnost
međusobno zavisnih procesa. Na primer, to se desi kada dva procesa žele da u
režimu međusobne isključivosti pristupaju dvema datotekama. Ako prvi od njih
otvori prvu datoteku, a zatim drugi od njih otvori drugu datoteku, tada nema
mogućnosti za nastavak aktivnosti tih procesa, bez obzira da li je sistemska
operacija otvaranja blokirajuća ili ne. U slučaju blokirajućih sistemskih operacija
otvaranja, pokušaj prvog procesa da otvori drugu datoteku dovodi do trajnog
zaustavljanja njegove aktivnosti. Isto se dešava sa drugim procesom prilikom
njegovog pokušaja da otvori prvu datoteku. Zaustavljanje aktivnosti ova dva
procesa je trajno, jer je svaki od njih zauzeo datoteku koja treba onom drugom
procesu za nastavak njegove aktivnosti i nema nameru da tu datoteku oslobodi. U
slučaju neblokirajuće sistemske operacije otvaranja, procesi upadaju u beskonačnu
petlju (starvation), pokušavajući da otvore datoteku, koju je isključivo za sebe već
otvorio drugi proces. Ovakav oblik međusobne zavisnosti procesa se naziva i živa
petlja (livelock). Ona se, po svom ishodu, suštinski ne razlikuje od mrtve petlje.
Pojava mrtve petlje je vezana za zauzimanje resursa, kao što su, na primer,
prethodno pomenute datoteke. Pri tome, za pojavu mrtve petlje je potrebno da budu
ispunjena četiri uslova:
1. zauzimani resursi se koriste u režimu međusobne isključivosti,
2. resursi se zauzimaju jedan za drugim, tako da proces, nakon zauzimanja
izvesnog broja resursa, mora da čeka da zauzme preostale resurse,
3. resurse oslobađaju samo procesi koji su ih zauzeli i
4. postoji cirkularna međuzavisnost procesa (prvi proces čeka oslobađanje
resursa koga drži drugi proces, a on čeka oslobađanje resursa koga drži treći
proces, i tako redom do poslednjeg procesa iz lanca procesa, koji čeka
oslobađanje resursa koga drži prvi proces).
6.2 TRETIRANJE MRTVE PETLJE
Postoje četiri pristupa rešavanja problema mrtve petlje:
1. sprečavanje (prevention) pojave mrtve petlje (onemogućavanjem važenja
nekog od četiri neophodna uslova za njenu pojavu),
2. izbegavanje (avoidance) pojave mrtve petlje
3. otkrivanje (detection) pojave mrtve petlje i oporavak (recovery) od nje i
4. ignorisanje problema mrtve petlje.
Kod sprečavanja pojave mrtve petlje, važenje prvog uslova nije moguće
sprečiti, ako je neophodno korišćenje resursa u režimu međusobne isključivosti.
Važenje drugog uslova se može sprečiti, ako se unapred zna koliko treba resursa i
236
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
ako se oni svi zauzmu pre korišćenja. Pri tome, neuspeh u zauzimanju bilo kog
resursa dovodi do oslobađanja prethodno zauzetih resursa, što je moguće učiniti bez
posledica, jer nije započelo njihovo korišćenje. Važenje trećeg uslova se može
sprečiti, ako postoji način da se zauzeti resurs privremeno oduzme procesu. I
konačno, važenje četvrtog uslova se može sprečiti, ako se resursi uvek zauzimaju u
unapred određenom redosledu, koji isključuje mogućnost cirkularne međuzavisnosti
procesa.
Izbegavanje pojave mrtve petlje zahteva poznavanje podataka (1) o
maksimalno mogućim zahtevima za resursima, (2) o ukupno postavljeni zahtevima
za resursima i (3) o stanju resursa. Podrazumeva se da se udovoljava samo onim
zahtevima za koje se proverom ustanovi da, nakon njihovog usvajanja, postoji
redosled zauzimanja i oslobađanja resursa u kome se mogu zadovoljiti maksimalno
mogući zahtevi svih procesa. Praktična vrednost ovoga pristupa nije velika, jer se
obično unapred ne znaju maksimalno mogući zathevi procesa za resursima, a to je
neophodno za proveru da li se može udovoljiti pojedinim zahtevima. Sem toga,
ovakva provera je komplikovana, a to znači i neefikasna.
Otkrivanje pojave mrtve petlje se zasniva na sličnom pristupu kao i
izbegavanje pojave mrtve petlje. U ovom slučaju se proverava da li postoji proces,
čijim zahtevima se ne može udovoljiti ni za jedan redosled zauzimanja i
oslobađanja resursa. Pored komplikovanosti ovakve provere, problem je i šta
učiniti, kada se i otkrije pojava mrtve petlje. Ako se resursi ne mogu privremeno
oduzeti od procesa, preostaje jedino uništavanje procesa, radi oslobađanja resursa
koje oni drže. Međutim, to nije uvek prihvatljiv zahvat. Zbog toga ni ovaj pristup
nema veliki praktični značaj.
Ignorisanje problema mrtve petlje je pristup koji je najčešće primenjen u
praksi, jer se ovaj problem ne javlja tako često da bi se isplatilo da ga rešava
operativni sistem. Prema tome, kada se mrtva petlja javi, na korisniku je da se suoči
sa ovim problemom i da ga reši na način, koji je primeren datim okolnostima.
6.3 PITANJA
1.
2.
3.
4.
5.
Šta je mrtva petlja?
Kako nastaje mrtva petlja (objasniti na primeru)?
Po čemu se razlikuju mrtva i živa petlja?
Koji su neophodni preduslovi za pojavu mrtve petlje?
Koji pristupi su mogući za rešavanje problema mrtve petlje?
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
237
7. KOMUNIKACIJA SA OPERATIVNIM SISTEMOM
7.1 PROGRAMSKI NIVO KOMUNIKACIJE SA
OPERATIVNIM SISTEMOM
Komunikacija sa operativnim sistemom na programskom nivou se ostvaruje
pozivanjem sistemskih operacija. Jedan način dobijanja usluga od operativnog
sistema podrazumeva da se za svaku uslugu poziva odgovarajuća sistemska
operacija. Na primer, da bi se preuzeo znak sa tastature, u korisničkom programu je
neophodno navesti poziv odgovarajuće sistemske operacije. Međutim, postoji i
drugi način dobijanja usluga od operativnog sistema, kod koga operativni sistem
pokreće izvršavanje odabranih korisničkih funkcija nakon dešavanja zadanih
događaja. Na primer, kada se neka korisnička funkcija odabere da reaguje na
pritisak dirke na tastaturi, tada, po dešavanju takvog događaja, operativni sistem
pokreće njeno izvršavanje, da bi posredstvom nje isporučio znak sa tastature.
7.2 INTERAKTIVNI NIVO KOMUNIKACIJE SA
OPERATIVNIM SISTEMOM
Interaktivni nivo korišćenja operativnog sistema se ostvaruje pomoću
komandi komandnog jezika. One omogućuju rukovanje datotekama i procesima.
Najjednostavniju komandu komandnog jezika predstavlja putanja izvršne datoteke.
Kao operand ovakve komande se može, opet, javiti putanja datoteke, ako komanda
omogućuje rukovanje datotekama. Prema tome, na prethodni način oblikovana
komanda započinje operatorom u obliku putanje izvršne datoteke, koja opisuje
rukovanje, a završava operandom (ili operandima) u obliku putanja datoteka,
kojima se rukuje. Tako:
kopiraj
godina1.txt
godina2.txt
predstavlja primer prethodno opisane komande znakovnog komandnog jezika. U
ovom primeru se pretpostavlja da radni imenik obuhvata izvršnu datoteku sa
imenom kopiraj.bin i tekst datoteku godina1.txt. Takođe se pretpostavlja da
izvršavanje programa iz izvršne datoteke kopiraj.bin dovodi do stvaranja datoteke
godina2.txt, koja je po sadržaju identična datoteci godina1.txt i koja pripada
radnom imeniku. Prethodna komanda opisuje korisnu operaciju, ako svi studenti
prve godine studija upisuju drugu godinu studija.
Komandni jezik može olakšati zadavanje komandi, ako omogući korisniku da
operator komande bira u spisku operatora (menu), umesto da ga pamti i u celosti
navodi. Spisak operatora se prikazuje na ekranu, a izbor operatora se vrši pomoću
namenskih dirki tastature ili miša. Nakon izbora operatora sledi, po potrebi, dijalog
u kome korisnik navodi (ili opet bira) operand (operande) komande. Ovakvi
238
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
komandni jezici se nazivaju grafički komandni jezici (menu driven user interface,
graphical user interface). Oni još više pojednostavljuju zadavanje komandi, ako
korisniku omogućuju da ne bira operator, nego samo operande komandi. U ovom
slučaju, izbor operanda se svodi na izbor nekog od imena datoteka, prikazanih na
ekranu, a operator se podrazumeva ili na osnovu tipa odabrane datoteke, ili,
eventualno, na osnovu upotrebljene namenske dirke.
U svakom slučaju, zadatak komandnog jezika je da omogući korisniku da
zada komandu, koja precizno određuje i vrstu rukovanja i objekat rukovanja, a
zadatak interpretiranja komande je da pokrene proces, u okviru čije aktivnosti
usledi rukovanje, zatraženo komandom.
ZNAKOVNI KOMANDNI JEZICI
Izgled, način rada i mogućnosti interpretera znakovnog komandnog jezika
(command language interpreter, shell) zavise od ciljeva, koje komandni jezik treba
da ostvari. Ciljevi znakovnih komandnih jezika obuhvataju:
1. omogućavanje izvršavanja pojedinih (korisničkih) programa,
2. omogućavanje kombinovanja izvršavanja više (korisničkih) programa i
3. omogućavanje pravljenja komandnih datoteka (command file, shell
script).
Interpreter znakovnog komandnog jezika ostvaruje prethodne ciljeve tako što
sa standardnog ulaza prima niz znakova, koji obrazuju komandu, prepoznaje u tom
nizu znakova operator komande (i, eventualno, njene operande) i preduzima
zahtevanu akciju. Rezultat svoje akcije ovaj interpreter prikazuje na standardnom
izlazu.
Prilikom preduzimanja zahtevane akcije, interpreter znakovnog komandnog
jezika se oslanja na sistemske operacije. Pri tome, on koristi delove komandi,
odnosno njen operator i njene operande, kao argumente sistemskih operacija. Na
primer, do izvršavanja pojedinih korisničkih programa dolazi tako što interpreter
znakovnog komandnog jezika poziva sistemsku operaciju stvaranja procesa, a kao
njene argumente upotrebi operator i operande komande, odnosno putanju izvršne
datoteke i putanje datoteka sa obrađivanim podacima. Ovi argumenti su namenjeni
stvaranom proces.
Interpreter znakovnog komandnog jezika obavlja obradu znakova komande,
pre nego što ih iskoristi kao argumente neke sistemske operacije. Zahvaljujući
tome, u okviru komandi se mogu javiti čarobni znakovi (magic character, wild
cards), kao što je, na primer, znak *. Njegova upotreba je vezana, pre svega, za
imena datoteka i namenjena je za skraćeno označavanje grupa datoteka. Tako, na
primer:
*.obj
označava sve objektne datoteke u radnom imeniku, a
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
239
d*1.txt
označava sve tekst datoteke u radnom imeniku, čiji prvi deo imena započinju
znakom d, a završava cifrom 1. Zahvaljujući magičnim znakovima, moguće je, na
primer, jednom komandom uništiti sve objektne datoteke iz radnog imenika:
unisti *.obj
ili odštampati sve tekst datoteke iz radnog imenika, čiji prvi deo imena započinje
znakom d, a završava znakom 1:
stampaj d*1.txt
Za obavljanje ovakvih akcija, neophodno je pretraživanje imenika i provera imena
datoteka.
Zahvaljujući obradi znakova komande, moguće je interpreteru znakovnog
komandnog jezika saopštiti i da preusmeri (redirect) standardni ulaz i standardni
izlaz sa tastature i ekrana na proizvoljno odabrane datoteke. Ovo je važno za
pozadinske procese, koji nisu u interakciji sa korisnikom. Zahvaljujući
preusmeravanju, pozadinski proces ne ometa interaktivni rad korisnika, jer, umesto
tastature i ekrana, koristi odabrane datoteke. Međutim, da bi se korisnik upozorio na
greške u toku aktivnosti pozadinskog procesa, uz standardni izlaz se uvodi i
standardni izlaz greške. Pošto je standardni izlaz greške namenjen, pre svega, za
prikazivanje poruka o greškama, kao podrazumevajući standardni izlaz greške služi
specijalna datoteka, koja odgovara ekranu. Ova datoteka se otvara za vreme
stvaranja procesa, a kao njen indeks otvorene datoteke služi vrednost 2. I standardni
izlaz greške se može preusmeriti na proizvoljnu datoteku, čiji sadržaj tada ukazuje
na eventualne greške u toku aktivnosti pozadinskog procesa.
Uobičajeno je da preusmeravanje standardnog ulaza najavljuje znak <, a da
preusmeravanje standardnog izlaza (kao i standardnog izlaza greške) najavljuje
znak >. Tako, na primer, komanda:
kompiliraj
< program.c
> program.obj
saopštava interpreteru znakovnog komandnog jezika da stvori proces na osnovu
izvršne datoteke kompiliraj.bin, pri čemu kao standardni ulaz procesa služi
datoteka program.c, a kao njegov standardni izlaz datoteka program.obj. U ovom
primeru ekran i dalje služi kao standardni izlaz greške. Izvršavanje prethodne
komande omogućuje kompilaciju C programa, sadržanog u datoteci program.c.
Rezultat kompilacije se smešta u datoteku program.obj, a eventualne poruke o
greškama kompilacije se prikazuju na ekranu.
240
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Preusmeravanje standardnog ulaza i izlaza predstavlja osnovu za
kombinovanje izvršavanja više korisničkih programa. Važnost pomenutog
kombinovanja se može pokazati na primeru uređivanja (sortiranja) reči iz nekog
rečnika po kriteriju rimovanja. Nakon sortiranja reči po ovom kriteriju, sve reči,
koje se rimuju, nalaze se jedna uz drugu. Umesto pravljenja posebnog programa za
sortiranje reči po kriteriju rimovanja, jednostavnije je napraviti program za obrtanje
redosleda znakova u rečima (tako da prvi i poslednji znak zamene svoja mesta u
reči, da drugi i pretposlednji znak zamene svoja mesta u reči i tako redom) i
kombinovati izvršavanje ovog programa sa izvršavanjem postojećeg programa za
sortiranje:
obrni
< recnik.txt
> obrnuti_recnik.txt
sortiraj
< obrnuti_recnik.txt
> sortirani_obrnuti_recnik.txt
obrni
< sortirani_obrnuti_recnik.txt
> rime.txt
Umesto preusmeravanja standardnog ulaza i standardnog izlaza, moguće je
nadovezati standardni izlaz jednog procesa za standardni ulaz drugog procesa i tako
obrazovati tok procesa (pipe). Nadovezivanje u tok se označava pomoću znaka |.
Za prethodni primer ovakav tok bi izgledao:
obrni
< recnik.txt
|
sortiraj
|
obrni
> rime.txt
U ovom primeru su u tok nadovezana tri procesa. Prvi je nastao na osnovu
komande:
obrni
< recnik.txt
drugi je nastao na osnovu komande
sortiraj
a treći je nastao na osnovu komande:
obrni
> rime.txt
Ako bi se reči zadavale sa tastature, a po rimama sortirani rečnik prikazivao
na ekranu, prethodni tok bi izgledao:
obrni | sortiraj | obrni
Razmena podataka između dva procesa, koji su povezani u tok, se ostvaruje
posredstvom privremene datoteke. Ona služi prvom od ovih procesa kao standardni
izlaz, a drugom od njih kao standardni ulaz. Znači, prvi proces samo piše u ovu
datoteku, a drugi samo čita iz nje. Prilikom obrazovanja toka procesa, interpreter
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
241
znakovnog komandnog jezika stvara procese, koji se povezuju u tok. Pri tome on
koristi istu privremenu datoteku kao standardni izlaz i ulaz za svaki od parova ovih
procesa. Zatim interpreter znakovnog komandnog jezika čeka na kraj aktivnosti
poslednjeg od ovih procesa.
Pozadinski procesi se razlikuju od običnih (interaktivnih) procesa po tome što
interpreter znakovnog komandnog jezika, nakon stvaranja pozadinskog procesa, ne
čeka kraj njegove aktivnosti, nego nastavlja interakciju sa korisnikom. Zato su
pozadinski procesi u principu neinteraktivni. Na primer, komanda:
kompiliraj
< program.c
> program.obj
&
omogućuje stvaranje pozadinskog procesa, koji obavlja kompilaciju programa,
sadržanog u datoteci program.c, i rezultat kompilacije smešta u datoteku
program.obj, a eventualne greške u kompilaciji prikazuje na ekranu. U prethodnom
primeru znak & sa kraja komande je naveo interpreter znakovnog komandnog jezika
na stvaranje pozadinskog procesa.
Svaka komanda, upućena interpreteru znakovnog komandnog jezika, ne
dovodi do stvaranja procesa. Komande, koje se često koriste, pa je važno da budu
brzo obavljene, interpreter znakovnog komandnog jezika obavlja sam. Međutim,
kada stvori proces i sačeka kraj njegove aktivnosti, interpreter znakovnog
komandnog jezika dobije od stvorenog procesa, kao povratnu informaciju, završno
stanje stvorenog procesa. Ovo stanje se obično kodira celim brojem. Ako ga
interpreter znakovnog komandnog jezika protumači kao logičku vrednost (0 - tačno,
različito od 0 - netačno) tada on može da podrži uslovno izvršavanje programa.
Tako, na primer, komanda:
IF
kompiliraj
< program.c
THEN
linkuj
< program.obj
> program.obj
> program.bin
označava da do linkovanja (povezivanja) dolazi samo nakon uspešne kompilacije.
Pri tome su IF i THEN rezervisane reči za interpreter znakovnog komandnog jezika.
Prva najavljuje komandu, na osnovu koje interpreter znakovnog komandnog jezika
stvori proces. Za vreme aktivnosti ovog procesa usledi kompilacija programa,
sadržanog u datoteci program.c. Ako kompilacija prođe bez grešaka, stvoreni proces
vraća interpreteru znakovnog komandnog jezika vrednost 0 kao svoje završno
stanje. U ovom slučaju, interpreter znakovnog komandnog jezika interpretira
komandu iza rezervisane reči THEN i stvara proces, čija aktivnost dovodi do
linkovanja datoteke program.obj sa potprogramima iz sistemske biblioteke. Ime
sistemske biblioteke se ne navodi, jer se podrazumeva. U suprotnom slučaju, ako je
bilo grešaka u kompilaciji, pa je završno stanje procesa, zaduženog za kompilaciju,
bilo veće od vrednosti 0, interpreter znakovnog komandnog jezika ne interpretira
komandu iza rezervisane reči THEN i ne stvara proces zadužen za linkovanje.
242
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Pored prethodno opisanog načina za uslovno izvršavanje programa,
interpreteri znakovnih komandnih jezika podržavaju ponavljanje izvršavanja
programa, ali i druge mogućnosti, tipične za procedurne programske jezike, kao što
su rukovanje promenljivim, konstantama, ulazom, izlazom i slično. To dozvoljava
pravljenje komandnih datoteka. One opisuju okolnosti pod kojima se izvršavaju
korisnički programi, a njihov sadržaj preuzima i interpretira interpreter znakovnog
komandnog jezika. Zato komandne datoteke imaju poseban tip, da bi ih interpreter
znakovnog komandnog jezika mogao prepoznati. Zahvaljujući tome, ime svake
komandne datoteke, uostalom, kao i ime svake izvršne datoteke, predstavlja
ispravnu komandu znakovnog komandnog jezika. Iako broj i vrste ovakvih komandi
nisu ograničene, jer zavise samo od kreativnosti i potreba korisnika, ipak je moguće
napraviti njihovu klasifikaciju i navesti neke neizbežne grupe komandi. Najgrublja
podela komandi je na:
1. korisničke komande i
2. administratorske komande.
Korisničke komande, između ostalog, omogućuju:
1. rukovanje datotekama,
2. rukovanje imenicima,
3. rukovanje procesima i
4. razmenu poruka između korisnika.
Standardne komande za rukovanje datotekama omogućuju:
1. izmenu imena (kao i atributa) datoteke,
2. poređenje sadržaja datoteka,
3. kopiranje datoteka i
4. uništenje datoteka.
U komande za rukovanje imenicima spadaju:
1. komande za stvaranje i uništenje imenika,
2. komanda za promenu radnog imenika,
3. komanda za pregledanje sadržaja imenika (imena datoteka i imena imenika,
sadržanih u njemu) i
4. komande za izmenu imena i ostalih atributa imenika.
Administratorske komande omogućuju:
1. pokretanje i zaustavljanje rada računara,
2. spašavanje (arhiviranje) i vraćanje (dearhiviranje) datoteka,
3. rukovanje vremenom,
4. sabijanje (compaction) datoteka,
5. ažuriranje podataka o korisnicima računara i njihovim pravima,
6. generisanje izveštaja o korišćenju računara (o korišćenju procesorskog
vremena ili o korišćenju prostora na disku),
7. opisivanje konfiguracije računara (pravljenje spiska uređaja koji ulaze u
njegov sastav, kao što su razni ulazni i izlazni uređaji, i navođenje njihovih
karakteristika),
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
243
8. proveru ispravnosti rada računara,
9. kao i pripremu diskova za korišćenje (ovo obuhvata pronalaženje oštećenih
blokova i njihovo isključivanje iz upotrebe, pronalaženje izgubljenih
blokova i njihovo uključivanje u evidenciju slobodnih blokova, formiranje
skupa datoteka na disku i njegovo uključivanje u skup datoteka računara).
GRAFIČKI KOMANDNI JEZICI
Interpreteri grafičkih komandnih jezika omogućuju pozivanje bilo koje od
prethodnih komandi, a da pri tome ne zahtevaju od korisnika da znaju napamet
imena komandi, niti da zadaju komande posredstvom tastature, uz obavezu strogog
poštovanja sintakse znakovnog komandnog jezika. Umesto toga, grafički komandni
jezici uvode grafičku predstavu komandi (icon), ili spiskove sa imenima komandi
(menu), dozvoljavajući korisnicima da pozovu komandu izborom njene grafičke
predstave, ili izborom njenog imena sa spiska imena komandi. Grafički komandni
jezici dozvoljavaju i da se komanda automatski pokrene izborom nekog od
prikazanih imena datoteka. Pretpostavka za ovo je da izabrana datoteka predstavlja
podrazumevajući operand date komande.
Za komunikaciju sa grafičkim komandnim jezicima potrebni su pokazivački
uređaji kao što je miš. Oni omogućuju pokazivanje tačke ekrana. Zadatak
interpretera grafičkog komandnog jezika je da, na osnovu pozicije (koordinata)
pokazane tačke i pritisaka na odgovarajuću dirku pokazivačkog uređaja, odredi šta
korisnik želi. Na primer, ako pokazana tačka pripada skupu tačaka zone ekrana koja
sadrži grafičku predstavu komande ili njeno ime, tada dva uzastopna pritiska na
odgovarajuću dirku pokazivačkog uređaja izazivaju obavljanje odabrane komande.
7.3 PITANJA
1.
2.
3.
4.
5.
Na koji način se mogu dobiti usluge operativnog sistema?
Kako izgleda tipična komanda znakovnog komandnog jezika?
Kako se zadaju komande kod grafičkih komandnih jezika?
Koje ciljeve ostvaruju znakovni komandni jezici?
Da li interpreter znakovnog komandnog jezika sve komande izvršava
sam?
6. Šta su čarobni znakovi?
7. Čemu služi preusmeravanje standardnog ulaza i standardnog izlaza?
8. Zašto je potreban standardni izlaz greške?
9. Kako se obrazuje tok procesa?
10. Kakvu ulogu imaju komandne datoteke?
11. Kako se klasifikuju komande operativnog sistema?
12. Kako se prepoznaju komande kod grafičkih komandnih jezika?
244
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
8. KLASIFIKACIJA OPERATIVNIH SISTEMA
8.1 KRITERIJUM KLASIFIKACIJE OPERATIVNIH SISTEMA
Jedan od mogućih kriterijuma za klasifikaciju operativnih sistema je vrsta
računara kojim operativni sistem upravlja. Po tom kriteriju mogu se izdvojiti:
1. operativni sistemi realnog vremena
2. multiprocesorski operativni sistemi i
3. distribuirani operativni sistemi.
8.2 OPERATIVNI SISTEMI REALNOG VREMENA
Operativni sistemi realnog vremena (real time operating system) su
namenjeni za primene računara u kojima je neophodno obezbediti reakciju na
vanjski događaj u unapred zadanom vremenu. Ovakvi operativni sistemi su, zbog
toga, podređeni ostvarenju što veće brzine izvršavanja korisničkih programa.
Za operativne sisteme realnog vremena je tipično da su, zajedno sa
računarom, ugrađeni (embedded) u sistem, čije ponašanje se ili samo prati, ili čijim
ponašanjem se upravlja. Zadatak operativnih sistema realnog vremena je da samo
stvore okruženje za korisničke programe, jer komunikaciju sa krajnjim korisnikom
obavljaju korisnički programi. Zato se operativni sistemi realnog vremena obično
koriste samo na programskom nivou.
Modul za rukovanje procesima je podređen potrebi osiguranja brzog
stvaranja i uništenja procesa, njihove brze i lake saradnje, kao i brzog
preključivanja procesora sa procesa na proces. Zato obično svi procesi dele isti
fizički adresni prostor. To je moguće, jer ne postoji potreba za međusobnom
zaštitom procesa, kada oni imaju istog autora, ili kada njihovi autori pripadaju istom
timu, pa nema mesta sumnji u njihove namere.
Modul za rukovanje datotekama nije obavezni deo operativnog sistema
realnog vremena, jer sve primene realnog vremena ne zahtevaju masovnu
memoriju. Kada rukovanje datotekama postoji, ono obično podržava kontinuirane
datoteke, zbog brzine pristupa podacima. Kontinuirane datoteke ovde ne predstavlja
manu, jer su unapred poznati svi zahtevi primene.
Modul za rukovanje radnom memorijom obično podržava efikasno
zauzimanje memorijskih zona sa unapred određenom veličinom, da bi se izbegla ili
umanjila eksterna fragmentacija.
Modul za rukovanje kontrolerima podržava tipične ulazne i izlazne uređaje i,
uz to, omogućava jednostavno uključivanje novih drajvera za specifične uređaje. Pri
tome se nude blokirajuće i neblokirajuće sistemske operacije, ali i vremenski
ograničene blokirajuće sistemske operacije. Zahvaljujući neblokirajućim
sistemskim operacijama, moguće je vremenski preklopiti aktivnosti procesora i
kontrolera. Vremenski ograničene blokirajuće sistemske operacije omogućuju
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
245
reakciju u zadanom vremenskom intervalu na izostanak željenog događaja,
odnosno, na izostanak obavljanja pozvane sistemske operacije.
Modul za rukovanje procesorom mora da obezbedi efikasno rukovanje
vremenom. Za to se često koriste posebni satovi - tajmeri. U sklopu toga, mora se
obezbediti da aktivnost procesa bude završena do graničnog trenutka (deadline
scheduling). To se postiže sortiranjem deskriptora spremnih procesa po dužini
preostalog vremena do graničnog trenutka (earliest dedline first). Podrazumeva se
da ovo sortiranje dovodi na prvo mesto deskriptor procesa sa najkraćim preostalim
vremenom do graničnog trenutka, tako da je njegov proces prvi na redu za
aktiviranje. Ako je aktivnost procesa periodična sa unapred određenim i
nepromenljivim trajanjem aktivnosti u svakom periodu, tada se procesima mogu
dodeliti prioriteti jednaki broju njihovi perioda u jedinici vremena (rate monotonic
scheduling). Tako se može obezbediti da aktivnost procesa bude obavljena pre kraja
svakog od njegovih perioda. U svakom slučaju, postavljeni cilj raspoređivanja može
da se ostvari samo ako ima dovoljno procesorskog vremena za sve procese.
Modul za rukovanje procesorom operativnog sistema realnog vremena
obično podržava mehanizam semafora, jer je on jednostavan za korišćenje, brz i jer
ne zahteva izmene kompajlera.
8.3 MULTIPROCESORSKI OPERATIVNI SISTEMI
Multiprocesorski operativni sistemi upravljaju računarskim sistemom sa više
procesora opšte namene, koji pristupaju zajedničkoj radnoj memoriji. Podrazumeva
se da ove procesore i radnu memoriju povezuje sabirnica.
Specifičnosti multiprocesorskog operativnog sistema su vezane za modul za
rukovanje procesorom i posledica su istovremene aktivnosti više procesa na raznim
procesorima. Zato se sinhronizacija procesa u ovakvim okolnostima više ne može
zasnivati na onemogućenju prekida, nego na zauzimanju sabirnice, jer je to jedini
način da se spreči da više od jednog procesa pristupa istoj lokaciji radne memorije.
Mogućnost istovremene aktivnosti više procesa na raznim procesorima usložnjava
raspoređivanje, jer ono mora da odabere ne samo proces, koji će biti aktivan, nego i
da odabere procesor, koji će da se preključi na odabrani proces.
8.4 DISTRIBUIRANI OPERATIVNI SISTEMI
Distribuirani operativni sistemi upravljaju međusobno povezanim
računarima, koji su prostorno udaljeni. Potrebu za povezivanjem prostorno
udaljenih (distribuiranih) računara nameće praksa. S jedne strane, prirodno je da
računari budu na mestima svojih primena, na primer, uz korisnike ili uz delove
industrijskih postrojenja, koje opslužuju. Na taj način računari mogu biti potpuno
posvećeni lokalnim poslovima, koji su vezani za mesta njihove primene, pa mogu
efikasno obavljati ovakve poslove. S druge strane, neophodno je omogućiti saradnju
između prostorno udaljenih korisnika, odnosno obezbediti usaglašeni rad prostorno
udaljenih delova istog industrijskog postrojenja. Za to je potrebno obezbediti
246
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
razmenu podataka između računara, posvećenih pomenutim korisnicima, odnosno
posvećenih pomenutim delovima industrijskog postrojenja. Radi toga, ovakvi,
prostorno udaljeni računari se povezuju komunikacionim linijama, koje omogućuju
prenos (razmenu) podataka, organizovanih u poruke. Na ovaj način nastaje
distribuirani računarski sitem (distributed computer system). Za svaki od
računara, povezanih u distribuirani računarski sistem, je neophodno da sadrže
procesor, radnu memoriju i mrežni kontroler. Prisustvo masovne memorije i raznih
ulaznih i izlaznih uređaja u sastavu ovakvih računara zavisi od mesta njihove
primene i, u opštem slučaju, nije obavezno. Zato nema ni potrebe da ih podržava
operativni sistem, prisutan na pojedinim računarima iz distribuiranog računarskog
sistema. Ovakav operativni sistem ima smanjenu funkcionalnost u odnosu na
“običan” operativni sistem, pa se naziva mikrokernel (microkernel), jer.
Hijerarhijska struktura mikrokernela je prikazana na slici 8.3.1.
modul za rukovanje procesima
modul za razmenu poruka
modul za rukovanje radnom memorijom
modul za rukovanje kontrolerima
modul za rukovanje procesorom
Slika 8.3.1 Hijerarhijska struktura mikrokernela
Mikrokernel ne sadrži modul za rukovanje datotekama, jer on nije potreban
za svaki od računara iz distribuiranog računarskog sistema. Zato se ovaj modul
prebacuje u korisnički sloj (iznad mikrokernela), koji je predviđen za korisničke
procese.
Modul za rukovanje procesima se oslanja na modul za razmenu poruka, da bi
pristupio izvršnoj datoteci koja je locirana na nekom drugom računaru. Modul za
razmenu poruka se oslanja na modul za rukovanje radnom memorijom, radi
dinamičkog zauzimanja i oslobađanja bafera, namenjenih za privremeno smeštanje
poruka. Modul za razmenu poruka se oslanja i na modul za rukovanje kontrolerima,
u kome se nalazi drajver mrežnog kontrolera, posredstvom koga se fizički
razmenjuju poruke. Na kraju, modul za razmenu poruka se oslanja i na modul za
rukovanje procesorom. Ovo je potrebno, da bi se, na primer, privremeno zaustavila
aktivnost procesa do prijema poruke, bez koje nastavak aktivnosti nije moguć, ali i
da bi se moglo reagovati na dugotrajni izostanak očekivanog prijema poruke.
Modul za razmenu poruka nije samo na raspolaganju modulu za rukovanje
procesima. On sadrži sistemske operacije, koje omogućuju razmenu poruka,
odnosno saradnju između procesa, aktivnih na raznim računarima, ako i saradnju
između procesa, aktivnih na istom računaru. Tipičan oblik saradnje procesa je da
jedan proces traži uslugu od drugog procesa. To je potrebno, na primer, kada jedan
proces želi da na svom računaru, koji je bez masovne memorije, stvori novi proces.
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
247
On se, tada, posredstvom modula za rukovanje procesima, obraća drugom procesu,
aktivnom na računaru sa masovnom memorijom, zahtevajući od njega, kao uslugu,
da mu pošalje sadržaj odgovarajuće izvršne datoteke. Uobičajeni način traženja i
dobijanja usluge se sastoji od pozivanja operacije, čije obavljanje dovodi do
pružanja tražene usluge. Ako pozivana operacija ne odgovara potprogramu koji se
lokalno izvršava u okviru aktivnosti procesa pozivaoca, nego odgovara
potprogramu koji se izvršava u okviru aktivnosti drugog, udaljenog procesa,
aktivnog na udaljenom računaru, reč je o pozivu udaljene operacije (Remote
Procedure Call - RPC). Proces, koji poziva udaljenu operaciju, nalazi u ulozi
klijenta (primaoca usluge), a proces, koji obavlja udaljenu operaciju, se nalazi u
ulozi servera (davaoca usluge).
POZIV UDALJENE OPERACIJE
Poziv udaljene operacije liči na poziv (lokalne) operacije. Znači, on ima oblik
poziva potprograma, u kome se navode oznaka (ime) operacije i njeni argumenti.
Ovakav potprogram se naziva klijentski potprogram (client stub), jer je klijent
njegov jedini pozivalac. U klijentskom potprogramu je sakriven niz koraka, koji se
obavljaju, radi dobijanja zahtevane usluge. U ove korake spadaju:
1. pronalaženje procesa servera, koji pruža zahtevanu uslugu,
2. pakovanje (marshalling) argumenata (navedenih u pozivu klijentskog
potprograma) u poruku zahteva,
3. slanje serveru ove poruke zahteva,
4. prijem od servera poruke odgovora sa rezultatom pružanja zahtevane
usluge,
5. raspakivanje prispele poruke odgovora i
6. isporuka rezultata pružanja zahtevane usluge pozivaocu klijentskog
potprograma.
Simetrično klijentskom potprogramu postoje dva serverska potprograma
(server stub). Njih poziva jedino server, a oni kriju više koraka, koji se obavljaju,
radi pružanja zahtevane usluge. Prvi od serverskih potprograma obuhvata:
1. prijem poruke zahteva i
2. raspakivanje argumenata iz ove poruke.
Drugi od serverskih potprograma obuhvata:
1. pakovanje rezultata usluge (koju je pružio server) u poruku odgovora i
2. slanje klijentu ove poruke odgovora.
Između poziva ova dva serverska potprograma se nalazi lokalni poziv
operacije, koja odgovara zahtevanoj usluzi, odnosno, programski tekst, koji opisuje
aktivnost servera na pružanju zahtevane usluge.
Oslanjanje na poziv udaljene operacije olakšava posao programeru, jer od
njega krije, na prethodno opisani način, detalje saradnje klijenta i servera. Pri tome,
klijentski potprogram pripada biblioteci udaljenih operacija. Ova biblioteka sadrži
po jedan klijentski potprogram za svaku od postojećih udaljenih operacija.
248
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Klijentski potprogram se generiše, zajedno sa serverskim potprogramima, prilikom
prevođenja programa, koji odgovara serveru.
PROBLEMI POZIVA UDALJENE OPERACIJE
Uprkos nastojanju da što više liči na poziv lokalne operacije, poziv udaljene
operacije se značajno razlikuje od svog uzora. Te razlike su posledica koraka,
sakrivenih u pozivu udaljene operacije, koji uzrokuju da se u toku poziva udaljene
operacije mogu da pojave problemi, čija pojava nije moguća kod poziva lokalne
operacije. Tako je moguće:
1. da se ne pronađe server, koji pruža zahtevanu uslugu,
2. da se, u toku prenosa, izgube ili poruka zahteva ili poruka odgovora, kao i
3. da dođe do otkaza ili servera, ili klijenta u toku njihovog rada.
Ako nema servera, tada nije moguće pružanje tražene usluge. To je
nemoguća situacija kod poziva lokalne operacije. Do istog rezultata dovode smetnje
na komunikacionim linijama, koje onemogućuju prenos bilo poruke zahteva, bilo
poruke odgovora. Kada u očekivanom vremenu izostane prijem poruke odgovora,
bilo zbog gubljenja poruke zahteva, bilo zbog gubljenja poruke odgovora, jedino što
se na strani klijenta može uraditi je da se ponovo pošalje (retransmituje) poruka
zahteva. Pri tome je broj retransmisija ograničen. Ako je izgubljena poruka zahteva,
njenom retransmisijom se stvara mogućnost da ona stigne do servera i da on pruži
traženu uslugu. Međutim, ako je izgubljena poruka odgovora, tada treba sprečiti da,
po prijemu retransmitovane poruke zahteva, server ponovi pružanje već pružene
usluge. Da bi server razlikovao retransmitovanu poruku od originalne, dovoljno je
da svaka originalna poruka ima jedinstven redni broj i da server za svakog klijenta
pamti redni broj poslednje primljene poruke zahteva od tog klijenta. Prijem poruke
sa zapamćenim rednim brojem ukazuje na retransmitovanu poruku, koja je već
primljena.
Otkaz servera, izazvan kvarom računara, je problematičan, jer, u opštem
slučaju, nema načina da se ustanovi da li je do otkaza došlo pre, u toku, ili posle
pružanja usluge. Zato poziv udaljene operacije ne može garantovati da će zahtevana
usluga biti pružena samo jednom, kao kod poziva lokalne operacije. Na strani
klijenta otkaz servera se odražava kao izostajanje poruke odgovora. Tada
retransmisija poruke zahteva može navesti ponovo pokrenutog servera da još
jednom pruži već pruženu uslugu, jer je, u ovom slučaju, server izgubio, zbog
otkaza, evidenciju o rednim brojevima poslednje primljenih poruka zahteva od
klijenata. Kod poziva lokalne operacije ovo se ne može desiti, jer otkaz računara
znači i kraj izvršavanja celog programa, bez pokušaja njegovog automatskog
oporavka.
Otkaz klijenta znači da server uzaludno pruža zahtevanu uslugu. Ovo se
izbegava tako što server obustavlja pružanje usluga klijentima, za koje ustanovi da
su doživeli otkaz. To klijenti sami mogu da jave serveru, nakon svog ponovnog
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
249
pokretanja, ili to server može sam da otkrije, periodičnom proverom stanja
klijenata, koje opslužuje.
Poziv udaljene operacije praktično dozvoljava da argumenti budu samo
vrednosti, a ne i adrese, odnosno pokazivači, zbog problema kopiranja pokazanih
vrednosti sa klijentovog računara na računar servera i u obrnutom smeru. Pored
toga, ako su ovi računari različiti, javlja se i problem konverzije vrednosti, jer se, na
primer, predstava realnih brojeva razlikuje od računara do računara.
Činjenica da se u okviru klijentskog potprograma javlja potreba za
pronalaženjem servera, ukazuje da u vreme pravljenja izvršnog oblika klijentskog
programa nije poznato koji server će usluživati klijenta. U opštem slučaju, može biti
više servera iste vrste i svaki od njih može istom klijentu da pruži zatraženu uslugu.
Radi toga se uvodi poseban server imena (name server, binder). Njemu se, na
početku svoje aktivnosti, obraćaju svi serveri i ostavljaju podatke o sebi, kao što je,
na primer, podatak o vrsti usluge koje pružaju. Serveru imena se obraćaju i klijenti,
radi pronalaženja servera, koji pruža zahtevanu uslugu. Na ovaj način se ostvaruje
dinamičko linkovanje (dynamic binding) klijenta, koji zahteva uslugu, i servera,
koji pruža zahtevanu uslugu.
RAZMENA PORUKA
Klijentski i serverski potprogrami, koji omogućuju poziv udaljene operacije,
se oslanjaju na sistemske operacije modula za razmenu poruka. Prva od ovih
operacija je sistemska operacija zahtevanja usluge, a druge dve su sistemske
operacije prijema zahteva i slanja odgovora. Sistemska operacija zahtevanja
usluge je namenjena klijentu i poziva se iz njegovog potpograma. Ona omogućuje
slanje poruke zahteva i prijem poruke odgovora. Sistemske operacije prijema
zahteva i slanja odgovora su namenjene serveru i omogućuju prijem poruke zahteva
i slanje poruke odgovora. Sistemska operacija prijema zahteva se poziva iz prvog
serverskog potprograma, a sistemska operacija slanja odgovora se poziva iz drugog
serverskog potprograma. Ove tri sistemske operacije ostvaruju poseban protokol
razmene poruka (request reply protocol), koji je prilagođen potrebama poziva
udaljene operacije.
Sistemske operacije zahtevanja usluge, prijema zahteva i slanja odgovora su
blokirajuće. Prva zaustavlja aktivnost klijenta do stizanja odgovora, ili do isticanja
zadanog vremenskog perioda. Druga zaustavlja aktivnost servera do stizanja
zahteva, a treća zaustavlja aktivnost servera do isporuke odgovora ili do isticanja
zadanog vremenskog intervala. Ove tri sistemske operacije su zadužene za prenos
poruka. Pored slanja i prijema poruka, one potvrđuju prijem poruka, retransmituju
poruke, čiji prijem nije potvrđen, šalju upravljačke poruke, kojima se proverava i
potvrđuje aktivnost servera, čime se omogućuje otkrivanje njegovog otkaza i slično.
U nadležnosti ovih operacija je i rastavljanje poruka u pakete, koji se prenose preko
komunikacionih linija, sastavljanje poruka od paketa, pristiglih preko
komunikacionih linija, potvrda prijema paketa i retransmisija paketa, čiji prijem nije
250
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
potvrđen, kao i prilagođavanje brzine slanja paketa brzini kojom oni mogu biti
primani (flow control). Pomenute tri sistemske operacije koriste usluge drajvera
sata, radi reagovanja na isticanje zadanih vremenskih intervala, nakon kojih je, na
primer, potrebno ili retransmitovati poruku, ili poslati poruku potvrde. One pozivaju
i (neblokirajuće) operacije gornjeg dela drajvera mrežnog kontrolera, radi fizičkog
prenosa i prijema paketa. U donjem delu ovog drajvera se nalaze obrađivači
prekida, zaduženi za registrovanje uspešnog slanja i uspešnog prijema paketa.
Sistemske operacije modula za razmenu poruka se brinu o baferima,
namenjenim za (privremeno) smeštanje poruka. Na primer, ako server nije pozvao
sistemsku operaciju prijema zahteva, jer je aktivan na usluživanju prethodno
primljenog zahteva od jednog klijenta, a pristigla je poruka zahteva od drugog
klijenta, ova poruka se smešta u slobodan bafer, da bi bila sačuvana i kasnije
isporučena serveru. Ako ne postoji slobodan bafer, poruka zahteva se odbacuje, uz,
eventualno, slanje odgovarajuće upravljačke poruke drugom klijentu.
Svaka od poruka, koje se razmenjuju između procesa, se sastoji:
1. od upravljačkog dela poruke i
2. od sadržaja poruke.
Upravljački deo poruke obuhvata:
1. adresu odredišnog procesa (kome se poruka upućuje),
2. adresu izvorišnog procesa (od koga poruka kreće, a kome se, eventualno,
kasnije upućuje odgovor) i
3. opis poruke (njenu vrstu, njen redni broj i slično).
Adresa (odredišnog ili izvorišnog) procesa sadrži jedinstven redni broj
računara, kome proces pripada (a po kome se razlikuju svi računari), kao i
jedinstven redni broj procesa (po kome se razlikuju procesi, koji pripadaju istom
računaru). Na osnovu rednog broja računara, mrežni kontroler utvrđuje da li
prihvata ili propušta poruku, a na osnovu rednog broja procesa se određuje proces,
kome se poruka isporučuje. U toku programiranja, zgodnije je, umesto ovih rednih
brojeva, koristiti imena za označavanje i računara i procesa. Korespondenciju
između imena i rednih brojeva uspostavlja već pomenuti server imena. Ove podatke
o sebi ostavljaju svi serveri, kada se, na početku svoje aktivnosti, obrate serveru
imena.
PROBLEMI RAZMENE PORUKA
Slaba tačka razmene poruka je sigurnost, jer su komunikacione linije
pristupačne svim korisnicima, pa je svaki od njih u poziciji da preuzima tuđe
poruke i da šalje poruke u tuđe ime. Sprečavanje preuzimanja tuđih poruka se
zasniva na kriptovanju (encryption) poruka, a sprečavanje slanja poruka u tuđe ime
se zasniva na nedvosmislenoj međusobnoj identifikaciji procesa (authentication). U
slučaju simetrične kriptografije, da bi klijent i server mogli da razmenjuju poruke sa
šifrovanim sadržajima, oba moraju da znaju i algoritam kriptovanja i svoj interni
ključ kriptovanja. Pod pretpostavkom da je algoritam poznat svim procesima, a da
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
251
interni ključ kriptovanja treba da znaju samo klijent i server, koji razmenjuju
poruke, javlja se problem kako dostaviti interni ključ kriptovanja samo pomenutom
klijentu i serveru. U tome može da pomogne poseban server, u koga svi procesi
imaju poverenje i koji se, zato, naziva poverenik. Pri tome se podrazumeva da
poverenik poseduje unapred dogovoren poseban ključ kriptovanja za komunikaciju
sa svakim procesom. Zahvaljujući tome, klijent može da pošalje povereniku poruku,
koja sadrži ime klijenta i ime servera sa kojim klijent želi da ostvari sigurnu
komunikaciju. Ssadržaj ove poruke je kriptovan ključem, koji je poznat samo
klijentu i povereniku, tako da je razumljiv samo za poverenika, a on, na osnovu
adrese izvorišnog procesa iz upravljačkog dela ove poruke, može da pronađe ključ
za dekriptovanje njenog sadržaja. Poverenik tada odredi interni ključ kriptovanja i
pošalje poruku serveru, koja sadrži interni ključ kriptovanja i ime klijenta. Sadržaj
ove poruke je kriptovan ključem, koji je poznat samo povereniku i serveru, tako da
je razumljiv samo za servera. Takođe, poverenik šalje poruku i klijentu, koja sadrži
interni ključ kriptovanja i ime servera. Sadržaj ove poruke je kriptovan ključem,
koji znaju samo poverenik i klijent, tako da je razumljiv samo za klijenta. Na ovaj
način, samo klijent i samo server dobiju interni ključ kriptovanja za sigurnu
međusobnu komunikaciju i ujedno se obavi njihova međusobna identifikacija, tako
da se drugi procesi ne mogu neprimećeno umešati u njihovu komunikaciju.
Ako se sigurna razmena poruka zasniva na asimetričnoj kriptografiji, tada je
uloga poverenika da čuva javne ključeve i tako osigura međusobnu identifikaciju
procesa. Znači, kada je potrebno ostvariti sigurnu komunikaciju između dva
procesa, oni se obraćaju povereniku, da bi dobili javni ključ svog komunikacionog
partnera. Za komunikaciju sa poverenikom ovi procesi koriste javni ključ
poverenika, a za komunikaciju sa njima poverenik koristi njihove javne ključeve.
Asimetrična kriptografija, sa komutativnim algoritmima kriptovanja i
dekriptovanja, omogućuje i digitalno potpisivanje poruka, radi neopozivog
pripisivanja poruke njenom pošiljaocu. Digitalni potpis (digital signature) se šalje
uz poruku. On sadrži unapred dogovoreni, referentni deo poruke, koji je
dekriptovan (transformisan) primenom algoritma dekriptovanja i privatnog ključa.
Primalac poruke kriptuje (retransformiše) digitalni potpis primenom algoritma
kriptovanja i javnog ključa. Ako se rezultat kriptovanja digitalnog potpisa poklapa
sa referentnim delom poruke, tada je poruka nedvosmisleno stigla od pošiljaoca.
Sigurnu komunikaciju klijenta i servera mogu ometati drugi procesi
zlonamernim retransmisijama starih poruka, ili izmenom sadržaja poruka.
Ugrađivanjem u sadržaj poruke njenog rednog broja, mogu se otkriti retransmisije
starih poruka, a ugrađivanjem u sadržaj poruke kodova za otkrivanje i oporavak od
izmena sadržaja, mogu se otkriti, pa i ispraviti izmene sadržaja poruka.
RAZLIKA KLIJENATA I SERVERA
Različita uloga, koju klijent i server imaju u toku međusobne komunikacije
(saradnje), je prirodna posledica njihove namene. Iz toga proizlaze i razlike u
252
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
njihovoj internoj organizaciji. Dok je za klijenta prihvatljivo da njegova aktivnost
bude strogo sekvencijalna, za servera stroga sekvencijalnost njegove aktivnosti
znači manju propusnost i sporije pružanje usluga. To je najlakše ilustrovati na
primeru servera datoteka, zaduženog za pružanje usluga, kao što je čitanje ili
pisanje datoteke. Strogo sekvencijalna aktivnost ovoga servera bi izazvala
zaustavljanje njegove aktivnosti, radi usluživanja jednog klijenta, dok kontroler ne
prenese blok sa sadržajem datoteke između masovne i radne memorije. U
međuvremenu ne bi bilo usluživanja drugih klijenata, čak i ako bi se njihovi zahtevi
odnosili na blokove datoteka, prisutne u baferima radne memorije. Ovakva
sekvencijalnost nije prisutna kod tradicionalnih operativnih sistema, jer nakon
zaustavljanja aktivnosti jednog procesa u modulu za rukovanje datotekama, drugi
proces može nastaviti aktivnost u istom modulu. Zato je za servere potrebno
obezbediti više niti. Pri tome, svaka od niti, u okviru istog servera, opslužuje
različitog klijenta, a broj ovih niti zavisi od broja postavljanih zahteva i menja se u
vremenu. Postojanje više niti zahteva njihovu sinhronizaciju, dok pristupaju
globalnim (statičkim) promenljivim servera.
FUNKCIJE DISTRIBUIRANOG OPERATIVNOG SISTEMA
Mikrokerneli stvaraju osnovu za obrazovanje distribuiranog operativnog
sistema (distributed operating system, middleware). Njegov zadatak je da objedini
sve računare distribuiranog računarskog sistema, tako da korisnik distribuiranog
operativnog sistema ne vidi pojedine računare, nego jedinstven sistem, koji pruža
mnoštvo usluga. Sve ove usluge se dobijaju na uniforman način, pri čemu korisnik
nije svestan mesta na kome se usluge pružaju.
Distribuirani operativni sistem obrazuje (nudi) jedinstven skup datoteka, sa
uniformnim načinom označavanja datoteka i sa jedinstvenom hijerarhijskom
organizacijom datoteka, koju na isti način vidi svaki korisnik. Ovakav distribuirani
skup datoteka se oslanja na više servera imenika i na više servera datoteka.
Serveri imenika podržavaju hijerarhijsku organizaciju datoteka, a serveri datoteka
podržavaju pristup sadržaju (običnih) datoteka. U imenicima, kojima rukuju serveri
imenika, uz imena datoteka, odnosno uz imena imenika, ne stoje samo redni brojevi
deskriptora datoteka, nego i redni brojevi servera datoteka, odnosno servera
imenika, kojima pripadaju pomenuti deskriptori.
U distribuiranom skupu datoteka pristup datoteci podrazumeva konsultovanje
servera imena, radi pronalaženja servera imenika, od koga kreće pretraživanje
imenika. Pretraživanje imenika može zahtevati kontaktiranje različitih servera
imenika, dok se ne stigne do servera datoteka sa traženom datotekom.
Serveri imenika i datoteka mogu da ubrzaju pružanje usluga, ako kopiju često
korišćenih podataka čuvaju u radnoj memoriji. Ubrzanju pružanja usluga doprinosi i
repliciranje datoteka, da bi one bile fizički bliže korisnicima. Međutim, to stvara
probleme, kada razni korisnici istovremno menjaju razne kopije iste datoteke, jer se
tada postavlja pitanje koja od izmena je važeća.
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
253
Za distribuirani skup datoteka zaštita datoteka se češće zasniva na
dozvolama (capability), nego na pravima pristupa. To znači da u okviru deskriptora
datoteka, odnosno imenika,ne postoje navedena prava pristupa za pojedine grupe
korisnika, nego se za svaku datoteku, odnosno imenik, generišu različite dozvole.
One omogućuju razne vrste pristupa datoteci, odnosno imeniku. Da bi klijent dobio
neku uslugu, on mora da poseduje odgovarajuću dozvolu, koju prosleđuje serveru u
okviru zahteva za uslugom. Dozvola sadrži:
1. redni broj servera,
2. redni broj deskriptora datoteke (odnosno, deskriptora imenika),
3. oznaku vrste usluge i
4. oznaku ispravnosti dozvole.
Sadržaj dozvole je zaštićen kriptovanjem, tako da nije moguće, izmenom
oznake vrste usluge prepraviti dozvolu. Pre pružanja usluge, server proverava
dozvolu, da ustanovi da li je ispravna i da li se njena oznaka vrste usluge podudara
sa zatraženom uslugom. Dozvole deli server na zahtev klijenata, a čuvaju ih, i po
potrebi prosleđuju jedan drugom, klijenti. Pri tome se podrazumeva da klijent,
stvaralac datoteke, po njenom stvaranju automatski dobije dozvolu za sve vrste
usluga, koja uključuje i uslugu stvaranja drugih, restriktivnijih dozvola. Kada želi
da poništi određenu dozvolu, server samo proglasi njenu oznaku ispravnosti
nevažećom.
Prednost zasnivanja zaštite datoteka na dozvolama umesto na pravima
pristupa je u tome da prvi pristup ne zahteva razlikovanje korisnika, niti njihovo
označavanje. To je važno, jer rukovanje jedinstvenim i neponovljivim oznakama
korisnika u distribuiranom računarskom sistemu nije jednostavno. Sem toga,
dozvole omogućuju veću selektivnost, jer grupišu korisnike po kriteriju
posedovanja dozvole određene vrste, a ne na osnovu njihovih unapred uvedenih
(numeričih) oznaka.
Pored obrazovanja distribuiranog skupa datoteka, za distribuirani operativni
sistem je bitno da obezbedi automatsko upravljanje svim računarima (procesorima)
distribuiranog računarskog sistema. Za to su potrebni posebni serveri procesa.
Ovakvo upravljanje se svodi na raspoređivanje procesa po procesorima, radi
ostvarenja njihovog najboljeg iskorišćenja, ili radi ostvarenja najkraćeg vremena
odziva, odnosno, najbržeg usluživanja korisnika. Zadatak upravljanja komplikuju
razlike između računara, jer tada svaki računar ne može da prihvati svaki izvršni
oblik programa, pošto su izvršni oblici programa vezani za procesor, za raspoloživu
radnu memoriju i slično. Upravljanje komplikuje i zahtev za omogućavanje
migracije procesa sa računara na računar, da bi se prezaposlen računar rasteretio, a
nezaposlen zaposlio. Upravljanje je olakšano, ako su unapred poznate karakteristike
opterećenja računara, odnosno vrsta i broj njihovih procesa. Kod raspoređivanja
procesa po procesorima, važno je voditi računa o saradnji procesa i procese, koji
tesno međusobno sarađuju, raspoređivati na isti procesor.
254
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
Saradnja procesa, aktivnih na raznim procesorima, zatheva njihovu
sinhronizaciju, što se ostvaruje razmenom poruka. Pri tome, najjednostavniji način
za ostvarenje sinhronizacije se zasniva na uvođenju procesa koordinatora. Njemu
se obraćaju svi procesi, zainteresovani za sinhronizaciju, a koordinator donosi
odluke o njihovoj sinhronizaciji. Tako, ako je potrebno, na primer, ostvariti
međusobnu isključivost procesa u pristupu istoj datoteci, svi procesi traže od
koordinatora dozvolu za pristup, a on dozvoljava uvek samo jednom procesu da
pristupi datoteci. Na sličan način se može ostvariti i uslovna sinhronizacija. Za
razliku od ovakvog centralizovanog algoritma sinhronizacije, koji se zasniva na
uvođenju koordinatora, postoje i distribuirani algoritmi sinhronizacije, koji se
zasnivaju na međusobnom dogovaranju procesa, zainteresovanih za sinhronizaciju.
Distribuirani algoritmi sinhronizacije zahtevaju sredstva za grupnu komunikaciju
procesa, odnosno, sredstva za efikasan način da jedan proces pošalje poruke svim
ostalim procesima iz grupe procesa, zainteresovanih za sinhronizaciju, i da od njih
primi odgovore. Pored veće razmene poruka, distribuirani algoritmi sinhronizacije
su komplikovaniji od centralizovanih algoritama, a pri tome ne nude prednosti, tako
da je njihov razvoj više od principijelnog, nego od praktičnog značaja.
Za distribuirane operativne sisteme nije samo bitno da omoguće efikasnu
sinhronizaciju procesa, nego i da podrže poseban oblik sinhronizacije procesa, koji
obezbeđuje da se obave ili sve operacije iz nekog niza pojedinačnih operacija, ili ni
jedna od njih. Ovakav niz operacija se naziva transakcija, a transakcije, koje imaju
svojstvo da se obave u celosti ili nikako, se nazivaju atomske transakcije (atomic
transaction). Primer niza operacija, za koje je neophodno da obrazuju atomsku
transakciju, je prebacivanje nekog iznosa sa računa jedne banke na račun druge
banke. Pri tome, proces klijent, koji u ime korisnika obavlja ovo prebacivanje,
kontaktira dva servera, koji reprezentuju dve banke, da bi obavio transfer iznosa sa
jednog na drugi račun. Transfer se mora obaviti tako, da drugi klijenti mogu videti
oba računa samo u stanju ili pre, ili posle transakcije. Znači, za atomske transakcije
je neophodno da budu međusobno isključive, ako pristupaju istim podacima, ali i da
njihovi rezultati budu trajni, da se, jednom napravljena izmena ne može izgubiti.
Aktivnost (saradnja) procesa, aktivnih na raznim procesorima, otvara
mogućnost pojave mrtve petlje. Pri tome, u uslovima distribuiranog računarskog
sistema, algoritmi za izbegavanje pojave mrtve petlje, odnosno za otkrivanje i za
oporavak od pojave mtrve petlje su još neefikasniji i sa još manjim praktičnim
značajem, nego u slučaju centralizovnog (jednoprocesorskog) računara. Zato, u
uslovima distribuiranog računarskog sistema, kada nije prihvatljiv pristup
ignorisanja problema mrtve petlje, preostaje da se spreči njena pojava, na primer,
sprečavanjem ispunjenja uslova, neophodnih za pojavu mrtve petlje.
Distribuirani operativni sistem je zamišljen tako da integriše mnoštvo
računara u moćan multiračunarski sistem. Na taj način je moguće od više jeftinih i
malih računara napraviti multiračunarski sistem, koji je jeftiniji, a moćniji od
jednog velikog i skupog računara. Ovakav multiračunarski sistem, uz to, nudi i veću
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
255
pouzdanost, jer kvar pojedinačnog računara nije fatalan za ceo sistem, kao i
mogućnost proširenja, jer je moguće naknadno dodavanje računara u sistem. Pored
integrisanja pojedinačnih računara u multiračunarski sistem, distribuirani operativni
sistem omogućuje i deljenje skupih resursa ovakvog multiračunarskog sistema
između više korisnika, a nudi i prilagodljivost zahtevima korisnika, željenu
raspoloživost i predvidivost odziva, pa, čak, i veću sigurnost, jer korisnici mogu da
čuvaju poverljive podatke na svom računaru, koga fizički štite i čije korišćenje
mogu da kontrolišu. Slabe tačke distribuiranog operativnog sistema su nesigurnost
komunikacionih linija i teško ostvarenje njegove ukupne funkcionalnosti. Zato se u
praksi sreću mrežni operativni sistemi, koji se u pojedinim osobinama samo
približavaju distribuiranim operativnim sistemima.
MREŽNI OPERATIVNI SISTEMI
Mrežni operativni sistemi se ne zasnivaju na mikrokernelima, nego nastaju
dogradnjom postojećih (različitih) operativnih sistema, s ciljem objedinjavanja
njihovih skupova datoteka u jedinstven skup datoteka. Pri tome su korisnici svesni
prisustva i uloge pojedinih računara, koje objedinjuje mrežni operativni sistem, pa o
tome vode računa u toku svog rada. Mrežni operativni sistemi objedinjuju skupove
datoteka raznih računara, tako što dozvoljavaju klijentu da, posredstvom svog
lokalnog skupa datoteka, pristupa skupovima datoteka posebnih mrežnih servera
datoteka. Ovakvo pristupanje se ostvaruje, na primer, pomoću posebnih imenika,
koji reprezentuju skupove datoteka mrežnih servera datoteka. Pokušaj pristupa
datoteci iz nekog od ovih imenika automatski dovodi do komunikacije klijenta i
odgovarajućeg mrežnog servera datoteka. Ovakva komunikacija zahteva usaglašeno
tumačenje formata i značenja poruka. To je neophodna, a često i jedina dodirna
tačka računara, obuhvaćenih mrežnim operativnim sistemom.
8.5 PITANJA
1. Šta je cilj operativnih sistema realnog vremena?
2. Kako operativni sistemi realnog vremena ostvaruju svoj cilj?
3. Kako se ostvaruje sinhronizacija procesa kod multiprocesorskog
operativnog sistema sa zajedničkom sabirnicom?
4. Šta je distribuirani računarski sistem?
5. Po čemu je specifičan mikrokernel?
6. Kakvu ulogu ima poziv udaljene operacije?
7. Kako se implementira poziv udaljene operacije?
8. Po čemu se razlikuju poziv lokalne operacije i poziv udaljene operacije?
9. Kakvu ulogu ima server imena?
10. Na koje operacije se oslanja poziv udaljene operacije?
11. Šta ulazi u sastav poruka koje se razmenjuju između procesa?
12. Kako se obezbeđue sigurnost razmene poruka?
13. Čemu služi digitalni potpis?
256
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
14.
15.
16.
17.
18.
19.
20.
21.
22.
Po čemu se razlikuje interna organizacija klijenata i servera?
Šta je zadatak distribuiranog operativnog sistema?
Kako distribuirani operativni sistem obrazuje jedinstven skup datoteka?
Zašto je potrebno repliciranje datoteka?
Na čemu se bazira zaštita datoteka u distribuiranom operativnom
sistemu?
Kakvu ulogu imaju serveri procesa?
Kako se ostvaruje sinhronizacija u distribuiranom operativnom sistemu?
Šta su atomske transakcije?
Šta su mrežni operativni sistemi?
Miroslav Hajduković - Operativni sistemi (problemi i struktura)
257
LITERATURA
Za potpunije upoznavanje materije vezane za operativne sisteme autor
preporučuje knjigu:
TANENBAUM, A.S.: Modern operating systems, Upper Saddle River, NJ:
Prentice Hall, 2001.
Za sveobuhvatnije
preporučuje knjigu:
upoznavanje
konkurentnog
programiranja
autor
ANDREWS, G.R.: Concurrent Programming – Principles and Practice,
Redwood City, CA: Benjamin/Cummings, 1991.
Za upoznavanje rigoroznog pristupa konkurentnom programiranju autor
preporučuje knjigu:
SCHNEIDER, F.B.: On Concurrent Programming, New York: SpringerVerlag, 1997.
Za detaljno upoznavanje programskog jezika C++ autor preporučuje knjige:
STROUSTRUP, B.: The C++ Programming Language, Reading, MA: AddisonWesley,1991.
i
STROUSTRUP, B.: The Design and Evolution of C++, Reading, MA: AddisonWesley,1995.
Preporučene knjige sadrže iscrpan pregled literature, važne za oblast
operativnih sistema i konkurentnog programiranja.
U pisanju ove knjige autor je, kao izvor, koristio sve prethodno navedene
knjige.
Download

OPERATIVNI SISTEMI (problemi i struktura) Miroslav Hajduković