Upload
roger
View
21
Download
0
Embed Size (px)
DESCRIPTION
Planen for idag. Kerner uden afbrydelser (KB4 kap. 6): akernen: kerne med decentralt processkift bkernen: kerne med centralt processkift Kerne med afbrydelser (KB4 kap. 7): ckernen: løst koblede drivprogrammer dkernen: kerne med tætkoblede drivprogrammer. Kernestruktur: akernen. readerProc - PowerPoint PPT Presentation
Citation preview
Datalogi 1F: Multiprogrammering[3]
1
Planen for idag
• Kerner uden afbrydelser (KB4 kap. 6):– akernen: kerne med decentralt processkift– bkernen: kerne med centralt processkift
• Kerne med afbrydelser (KB4 kap. 7):– ckernen: løst koblede drivprogrammer– dkernen: kerne med tætkoblede
drivprogrammer
Datalogi 1F: Multiprogrammering[3]
2
Kernestruktur: akernen
KInitProc(…) KWaitQKPause() KCurProcKSelectNewProcess()
KInitSem() KReadChar()KWait() KWriteChar()KSignal()
KReadLine()KWriteLine()
readerProcwriterProc
Datalogi 1F: Multiprogrammering[3]
3
Brugerprogrammerne
void reader() {
rsem.Init(1);
wsem.Init(0);
for(;;) {
rsem.Wait();
buf.Read();
wsem.Signal();
}
}
void Writer() {
for(;;) {
wsem.Wait();
if(buf == ”exit\r”)
asm(”call_pal 0xAD”);
buf.Write();
rsem.Signal();
}
}
Datalogi 1F: Multiprogrammering[3]
4
Processer i akerne
• Proces kontrolblokken i akerne:– CPU registre (gemt på stakken)– Vi gemmer kun stakpeger– Køpegere beregnet for ventekø
struct Process : public Queueable {
Registers *sp;
}
Datalogi 1F: Multiprogrammering[3]
5
Processkift• akernen:
– har frivilligt processkift– benytter aktiv venten
• En proces kalder KPause hvis den venter på en hændelse (fungerer som yield() i Java)
• Aktiv venten:
void KGenericProcedure() { while(<hændelse ikke indtruffet>) KPause(); <udfør aktion efter hændelse er
indtruffet>; }
Datalogi 1F: Multiprogrammering[3]
6
KPause• KPause foretager skiftet fra en proces til en
anden:
void KPause() {
<gem registre på stakken>;
<gem stakpeger i PKB>;
<find ny proces>;
<retabler stakpeger fra PKB>;
<retabler registre>;
}
Datalogi 1F: Multiprogrammering[3]
7
Ventende processer
AP: KPause
registre P1
AP: ProcA
AP: KPause
registre P2
AP: ProcX
AP: KPause
registre P3
AP: ProcY
PCB P1: sp
PCB P2: sp
PCB P3: sp
struct process
AP: ProcB AP: ProcB
Datalogi 1F: Multiprogrammering[3]
8
Opfrisker: tællesemaforer
void TælleSem::vent() {
if(tæller > 0)
tæller--;
else {
ventende++;
<bloker proces>;
}
}
void TælleSem::signaler() {
if(ventende > 0) {
<aktiver proces>;
ventende--;
} else
tæller++;
}
Datalogi 1F: Multiprogrammering[3]
9
Akerneversionen af tællesemaforoperationer
void KSignal(KSem& sem) {sem++; // Aktivering af ventende
} // sker ved aktiv venten
void KWait(KSem& sem) {while (!sem) // Aktiv venten
KPause();sem--;
}
Datalogi 1F: Multiprogrammering[3]
10
I/O operationer
char KReadChar() {while(!(rdio(com1Lsr) & 0x01))
KPause();return rdio(com1Rbr);
}
void KWriteChar(char ch) {while(!(rdio(com1Lsr) & 0x20))
KPause();wrio(com1Thr, ch);
}
Datalogi 1F: Multiprogrammering[3]
11
Kontrolregistre på UART
Datalogi 1F: Multiprogrammering[3]
12
I/O operationer (2)void KReadLine(char* p, int max){for (int i = 0; i < max-1; i++)
if((*p++ = KReadChar()) == ’\r’)break;
*p = ’\0’;}
void KWriteLine(char *p, int max) {for (int i = 0; (i < max) && *p; i++,p++)
KWriteChar(*p);}
13
Ventende processer: igen
PCB P1: sp
PCB P2: sp
PCB P3: sp
struct process
AP:KReadLine
AP:KWriteLine
AP: KPause
registre P1
AP: KWait
AP: KPause
registre P2
AP:KReadChar
AP: KPause
registre P3
AP: KWriteChar
Datalogi 1F: Multiprogrammering[3]
14
KPause: implementation (kblib.s)KPause: ldgp gp, (pv) lda sp,-STAKRAMME(sp)
stq $0, 0(sp)stq $1, 8(sp)…stq $29, 0xE8(sp)bis sp, 0, a0 // stakpeger er
// parameterlda pv, KSelectNewProcessjsr ra, (pv)bis v0, 0, sp // ny stakpeger er
// returværdildq $0, 0(sp)ldq $1, 8(sp)…ldq $29, 0xE8(sp)lda sp, STAKRAMME(sp)ret (ra)
SAVE_REGS
REST_REGS
Datalogi 1F: Multiprogrammering[3]
15
KPause(): et par kommentarer
• Tæl stakpeger ned inden registre lægges på stakken:– en afbrydelse vil bruge samme stakpeger
og kan overskrive værdier hvis sp er for høj
• Tæl stakpeger op EFTER registre er fjernet fra stakken
• Selve skiftet af KCurProc sker ikke i KPause men i KSelectNewProcess
Datalogi 1F: Multiprogrammering[3]
16
KSelectNewProcessRegisters* KSelectNewProcess(Registers* sp) {
KCurProc->sp = sp;
KWaitQ.Put(KCurProc);
KCurProc = KWaitQ.Get();
return KCurProc->sp;
}
• Skedulering foregår round-robin
• Hvad er det for noget med Registers* ? Var det ikke stakpegeren???
17
struct Registersstruct Registers {
unsigned long r [30];
unsigned long ps, pc, gp, a0, a1, a2;
};
r[0]
r[1]
r[29]
ps
a2
struct registers placering i lageret
r[0]
r[1]
r[29]
AP:KPause
en processtak ved kald af KSelectNewProcess
PAL kaldstakramme
Registers*
18
KCurProc KCurProc
PCB P3: sp
PCB P2: sp
PCB P1: sp
void KWriteChar(char ch) { while(!(rdio(com1Lsr) & 0x20)) KPause(); wrio(com1Thr, ch);}
void KWait(KSem& sem) { while (!sem) KPause(); sem--; }
char KReadChar() { while(!(rdio(com1Lsr) & 0x01)) KPause(); return rdio(com1Rbr);}
Ventende processer: igen igen
AP: KPause
registre P1
AP: KWait
AP: KPause
registre P2
AP:KReadChar
AP: KPause
registre P3
AP: KWriteChar
struct process
AP:KReadLine
AP:KWriteLine
KCurProc
AP:KSelectNewP…
AP:KSelectNewP…
AP:KSelectNewP…
19
KCurProc KCurProc
PCB P3: sp
PCB P2: sp
PCB P1: sp
void KWriteChar(char ch) { while(!(rdio(com1Lsr) & 0x20)) KPause(); wrio(com1Thr, ch);}
void KWait(KSem& sem) { while (!sem) KPause(); sem--; }
char KReadChar() { while(!(rdio(com1Lsr) & 0x01)) KPause(); return rdio(com1Rbr);}
Ventende processer: igen igen
AP: KPause
registre P1
AP: KWait
AP: KPause
registre P2
AP:KReadChar
AP: KPause
registre P3
AP: KWriteChar
AP:KReadLine
AP:KWriteLine
KCurProc
AP:KSelectNewP…
AP:KSelectNewP…
AP:KSelectNewP…
Datalogi 1F: Multiprogrammering[3]
20
OK – nok show:hvordan starter vi akernen?
// Reader processens kernedatastrukturer
extern void Reader ();
Process readerProc;
unsigned long readerStack [stackSize];
void main() {
KWait.Init();
KInitProc(Reader, readerStack, &readerProc);
KInitProc(Writer, writerStack, &writerProc);
KCurProc = KWaitQ.Get();
KFirst(KCurProc->sp);
}
Datalogi 1F: Multiprogrammering[3]
21
Hvad sker der i KInitProc?• KInitProc initialiserer en proces’ stak, så det
ser ud som om den har kaldt KPause():
void KInitProc(void (*startAddr) (), void *Stack, Process *proc) { Stack += (stackSize * sizeof(unsigned long) – sizeof(Registers) ); proc->sp = (Registers *) Stack; proc->sp->r[26] = proc->sp->r[27] = (unsigned long) startAddr; // ra & pv KWaitQ.Put(proc);}
Datalogi 1F: Multiprogrammering[3]
22
Processtak efter KInitProc
r[0]
r[1]
startAddrr[28]
r[29]
startAddr return address
procedure value
stackSize
sp
PKB
Datalogi 1F: Multiprogrammering[3]
23
KSelectNewProcess
Registers* KSelectNewProcess(Registers* sp) {
KCurProc->sp = sp;
KWaitQ.Put(KCurProc);
KCurProc = KWaitQ.Get();
return KCurProc->sp;
}
Datalogi 1F: Multiprogrammering[3]
24
KPause: implementation (kblib.s)KPause: ldgp gp, (pv) lda sp,-STAKRAMME(sp)
stq $0, 0(sp)stq $1, 8(sp)…stq$29, 0xE8(sp)bis sp, 0, a0 // stakpeger er
parameterlda pv, KSelectNewProcessjsr ra, (pv)bis v0, 0, sp // ny stakpeger er
returværdildq $0, 0(sp)ldq $1, 8(sp)…ldq $29, 0xE8(sp)lda sp, STAKRAMME(sp)ret (ra)
SAVE_REGS
REST_REGS
Datalogi 1F: Multiprogrammering[3]
25
Og så skal det hele sættes igangKCurProc = KWaitQ.Get();KFirst(KCurProc->sp);
hvor KFirst er defineret ved:
KFirst: ldgp gp, (pv)ldq pv, 0xD8(a0) // Pop pvaddq a0, 0xF0,a0 // Skip
registrebis a0, 0, sp // Sæt spjmp (pv) // Hop til processtart
Datalogi 1F: Multiprogrammering[3]
26
OK, hvad var det for noget med decentralt skift?
• Venteløkkerne for de enkelte I/O og semaforoperationer var spredt ud over kernen:– ineffektivt: registre skal poppes og pushes
hele tiden– svært at vedligeholde:
• hvad nu hvis vi gerne vil ændre vores aktiv venten strategi
– ugennemsigtigt:• vi ved ikke hvilke hændelser de enkelte
processer venter på
Datalogi 1F: Multiprogrammering[3]
27
Kerne med centralt processkift
• I modsætning til akerne.cc ønsker vi at have en central venteløkke og processkift
• Hvordan specificerer en proces hvad den venter på?
• Vi indfører operationen KSleep(), der venter på at en specifik hændelse indtræder
Datalogi 1F: Multiprogrammering[3]
28
Hændelser
• En proces kan vente på:– CPU (den er klar til at blive udført)– Ydre enheder (en operation bliver
færdigbehandlet)– Semafor (ankomst af et signal)
• Specifikationen af en hændelse skal kunne omfatte alle ovenstående hændelser
Datalogi 1F: Multiprogrammering[3]
29
struct Event
struct Event {enum { IO, SEM, CPU } id;union {
struct { int addr; char mask; } io;struct { KSem* addr; } sem;
}Event(int, char); // vent på I/OEvent(KSem&); // vent på semaforEvent(); // vent på CPU
}
Datalogi 1F: Multiprogrammering[3]
30
Proceskontrolblokken• Nu bliver processens tilstand udvidet
med hvilken hændelse en proces venter på:
struct Process {
Registers* sp;
Event waitsFor;
} *KCurProc;
Datalogi 1F: Multiprogrammering[3]
31
KSleep()• En ”overbygning” til KPause(), der
registerer hvilken hændelse en proces venter på:
void KSleep(Event e) {
KCurProc->waitsFor = e;
KPause();
}
Datalogi 1F: Multiprogrammering[3]
32
Ny udgave af semaforoperationNy udgave:void KWait (KSem& sem) {if(!sem)
KSleep( Event(sem) );sem--;
}
Gammel udgave:void KWait(KSem& sem) {while (!sem) // Aktiv venten
KPause();sem--;
}
Datalogi 1F: Multiprogrammering[3]
33
I/O operationerne
char KReadChar() {if (!rdio(com1Lsr) & 0x01)
KSleep( Event(com1Lsr, 0x01) );return rdio(com1Rbr);
}
void KWriteChar(char ch) {if (!rdio(com1Lsr) & 0x20)
KSleep( Event(com1Lsr, 0x20) );wrio(com1Thr, ch);
}
Datalogi 1F: Multiprogrammering[3]
34
Den centrale venteløkkeRegisters* KSelectNewProcess(Registers* sp) { KCurProc->sp = sp; for(int found = 0; !found; ) { KWaitQ.Put(KCurProc); KCurProc = KWaitQ.Get(); switch(KCurProc->waitsFor.id) { case Event::CPU: found = 1;
break; case Event::IO: if(rdio(KCurProc->waitsFor.io.addr)&
KCurProc->waitsFor.io.mask) found = 1; break;
case Event::SEM: if(*KCurProc->waitsFor.sem.addr) found = 1; break;
} } return KCurProc->sp;}
35
Ventende processer: igen igen igen
AP: KPause
registre P1
AP: KWait
AP: KPause
registre P2
AP:KReadChar
AP: KPause
registre P3
AP: KWriteChar
AP: KSleep AP: KSleep AP: KSleep
PCB P1: sp waitsfor = SEM
struct process
PCB P2: sp waitsfor = IO
PCB P3: sp waitsfor = IO
KCurProc KCurProcKCurProc
AP:KSelectNewP…
AP:KSelectNewP…
AP:KSelectNewP…
void KWriteChar(char ch) { if (!rdio(com1Lsr) & 0x20) KSleep( Event(com1Lsr, 0x20); wrio(com1Thr, ch); }
Datalogi 1F: Multiprogrammering[3]
36
Kerne med centralt processkift
KInitProc(…) KWaitQKSleep() KCurProcKPause()KSelectNewProcess()
KInitSem() KReadChar()KWait() KWriteChar()KSignal()
KReadLine()KWriteLine()
readerProcwriterProc
Datalogi 1F: Multiprogrammering[3]
37
Kerner med aktiv venten• Vi har flere gange diskuteret at aktiv
venten ikke er den mest effektive måde at opdage en hændelse på:– selv med en central venteløkke spilder vi tid
på at undersøge ydre enheders statusregistre når der ikke er behov for det
• forsinker aktivering af processer, der enten venter på CPU eller har modtaget en hændelse
• Men det giver simple kerner:– det hele kan forstås som en sekventiel
proces
Datalogi 1F: Multiprogrammering[3]
38
Kerner med afbrydelser• Slut med aktiv venten
• Afbrydelsesprocedure aktiverer ventende processer
• Men alting bliver mere uforudsigeligt, idet afbrydelser kan indtræde når som helst:– data kan deles mellem
afbrydelsesprocedurer og resten af kernen– brug for udelelig adgang til delt data
Datalogi 1F: Multiprogrammering[3]
39
Processkift
• Da vi ikke har aktiv venten, skal vi holde rede på de ventende processer.
• Vi benytter to proceskøer:– KWaitQ: kø af processer der venter på en
hændelse (semaforsignal eller ydre enhed)– KReadyQ: kø af processer der er klar til at
blive udført (venter på at få adgang til CPU)
• Processer bør først forlade KWaitQ når hændelsen de venter på er indtrådt
Datalogi 1F: Multiprogrammering[3]
40
Suspendering af processer
• KPause kalder KSelectNewProcess, der sætter den aktive proces til at vente:
Registers* KSelectNewProcess(Registers* sp) {
KCurProc->sp = sp;
KWaitQ.Put(KCurProc);
KCurProc = KReadyQ.Get(); // Her er forskellen
return KCurProc->sp;
}
Datalogi 1F: Multiprogrammering[3]
41
Aktivering af processer• Vi aktiverer processerne når vi får en
afbrydelse:
void KInterruptHandler() {
while(!KWaitQ.isEmpty())
KReadyQ.Put(KWaitQ.Get());
}
• Men hov? alle processerne startes?• Ja, for vi genbruger vores kerne med
decentralt processkift
Datalogi 1F: Multiprogrammering[3]
42
I/O operationerchar KReadChar() {
while(!(rdio(com1Lsr) & 0x01))KPause();
return rdio(com1Rbr);}
void KWriteChar(char ch) {while(!(rdio(com1Lsr) & 0x20))
KPause();wrio(com1Thr, ch);
}
• Alle ventende processer aktiveres og udfører check på om hændelse er indtrådt decentralt
Datalogi 1F: Multiprogrammering[3]
43
Afbrydelseshåndtering i ckerne
• Afbrydelse fra ydre enhed aktiverer afbrydelseshåndtering via PAL kode:– først aktiveres ent_int– ent_int kalder KInterruptHandler– derefter returneres til ent_int– ent_int returnerer fra afbrydelse
• ent_int specificeres i ckernens main funktion via PAL_wrent
Datalogi 1F: Multiprogrammering[3]
44
ent_int
ent_int: SAVE_REGS
br t0, 1f
1: ldgp gp, (t0)
lda pv, KInterruptHandler
jsr ra, (pv)
REST_REGS
call_pal PAL_rti
Datalogi 1F: Multiprogrammering[3]
45
Stak under afbrydelse
AP:buf.Write()
AP:KWriteLine
procesWrite()
void KWriteLine(char *p, int max) { for (int i=0; (i<max) && *p; i++,p++) KWriteChar(*p);}
PAL stakramme
AP: ent_int
ent_int: SAVE_REGS br t0, 1f
1: ldgp gp, (t0) lda pv, KInterruptHandler jsr ra, (pv) REST_REGS call_pal PAL_rti
void KInterruptHandler() { while(!KWaitQ.isEmpty()) KReadyQ.Put(KWaitQ.Get());}
AP: KInterruptHa…
AP: KReadyQ.Put…
void KReadyQ.Put() {…}
AP:KWriteChar
Stak forProces Writer
Datalogi 1F: Multiprogrammering[3]
46
Synkronisering med ydre enhederchar KReadChar() {
while(!(rdio(com1lsr) & 0x01))
KPause();// Opdaterer KWaitQ indirekte
return rdio(com1Rbr);
}
void KInterruptHandler() {
while(!KWaitQ.isEmpty())
KReadyQ.Put(KWaitQ.Get());
}
• Test på LSR og ventekøoperation i KReadChar skal udføres udeleligt, ellers kan følgende ske …
Datalogi 1F: Multiprogrammering[3]
47
Uheldig rækkefølge<KReadChar>
while(!(rdio(com1lsr) & 0x01))<UART>
sætter ready-bit<KInterruptHandler>
while(!KWaitQ.isEmpty())<KReadChar>
KPause();<KSelectNewProcess>
<sætter proces i ventekø>AAAAARGH: vi opdager ikke at tegnet er læst
Datalogi 1F: Multiprogrammering[3]
48
Implementering af udelelighed• Luk for afbrydelser:char KReadChar() {
forbid(); while(!(rdio(com1lsr) & 0x01))
KPause();char ch = rdio(com1Rbr);permit();return ch;
}
• Nu bliver vi ikke afbrudt mellem check af statusregister og KWaitQ.Put()
Datalogi 1F: Multiprogrammering[3]
49
Køoperationerne skal også beskyttes
• Eksempel:int isEmpty() {
int oldipl = forbid();int b = (size == 0);permit(oldipl);return b;
};
• Gem ipl – så undgår vi at lukke op for afbrydelser ved et uheld
• Vigtigt hvis operationer kan benyttes af afbrydelsesprocedurer
Datalogi 1F: Multiprogrammering[3]
50
Hvad gør vi når klarkøen er tom?
• En tomgangsproces:– en proces som aldrig kommer i ventekøen,
men som hele tiden frivilligt opgiver CPU’en
• En tomgangsløkke i KSelectNewProcess:while( KReadyQ.isEmpty() ) {
permit();
/* Do nothing */;
forbid()
}
Datalogi 1F: Multiprogrammering[3]
51
Tætkoblede drivprogrammer• I stedet for at vække alle processer ved
en afbrydelse kan vi have en ventekø for hver hændelse:
char KReadChar() {
while(!(rdio(com1lsr) & 0x01))
KPause(KReadQ);
return rdio(com1Rbr);
}
Datalogi 1F: Multiprogrammering[3]
52
KSelectNewProcess tager en ventekø som argument
Registers* KSelectNewProcess(Registers* sp,
Queue<Process>& blockOn) {
KCurProc->sp = sp;
blockOn.Put(KCurProc);
while( KReadyQ.isEmpty() )
/* Do nothing */;
KCurProc = KReadyQ.Get();
return KCurProc->sp;
}
Datalogi 1F: Multiprogrammering[3]
53
Afbrydelsesprocedure ved tæt kobling
void KInterruptHandler() {if( rdio(com1Iir) & 2)
while(!KReadQ.isEmpty())
KReadyQ.Put(KReadQ.Get());else if( rdio(com1Iir) & 3)
while(!KWriteQ.isEmpty())
KReadyQ.Put(KWriteQ.Get());}
Datalogi 1F: Multiprogrammering[3]
54
Opsummering• Kerner uden afbrydelser:
– baseret på aktiv venten
• Kerne med decentralt processkift:– venteløkker i styreprogrammer og
semaforoperationer
• Kerne med centralt processkift– processer specificere en hændelse de venter på– én venteløkke der undersøger om hændelser er
sket for alle ventende processer
• Kerne med afbrydelser:– afbrydelser aktiverer ventende processer– tæt koblede drivprogrammer: en kø per hændelse
Datalogi 1F: Multiprogrammering[3]
55
Kilder
• Disse slides er baseret på indholdet i Datalogi 1F kursusbog bind 4, kapitlerne 6 & 7.