planen for idag

55
Datalogi 1F: Multiprogram mering[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

Upload: roger

Post on 11-Jan-2016

22 views

Category:

Documents


0 download

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

TRANSCRIPT

Page 1: Planen for idag

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

Page 2: Planen for idag

Datalogi 1F: Multiprogrammering[3]

2

Kernestruktur: akernen

KInitProc(…) KWaitQKPause() KCurProcKSelectNewProcess()

KInitSem() KReadChar()KWait() KWriteChar()KSignal()

KReadLine()KWriteLine()

readerProcwriterProc

Page 3: Planen for idag

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();

}

}

Page 4: Planen for idag

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;

}

Page 5: Planen for idag

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>; }

Page 6: Planen for idag

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>;

}

Page 7: Planen for idag

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

Page 8: Planen for idag

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++;

}

Page 9: Planen for idag

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--;

}

Page 10: Planen for idag

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);

}

Page 11: Planen for idag

Datalogi 1F: Multiprogrammering[3]

11

Kontrolregistre på UART

Page 12: Planen for idag

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);}

Page 13: Planen for idag

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

Page 14: Planen for idag

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

Page 15: Planen for idag

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

Page 16: Planen for idag

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???

Page 17: Planen for idag

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*

Page 18: Planen for idag

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…

Page 19: Planen for idag

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…

Page 20: Planen for idag

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);

}

Page 21: Planen for idag

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);}

Page 22: Planen for idag

Datalogi 1F: Multiprogrammering[3]

22

Processtak efter KInitProc

r[0]

r[1]

startAddrr[28]

r[29]

startAddr return address

procedure value

stackSize

sp

PKB

Page 23: Planen for idag

Datalogi 1F: Multiprogrammering[3]

23

KSelectNewProcess

Registers* KSelectNewProcess(Registers* sp) {

KCurProc->sp = sp;

KWaitQ.Put(KCurProc);

KCurProc = KWaitQ.Get();

return KCurProc->sp;

}

Page 24: Planen for idag

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

Page 25: Planen for idag

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

Page 26: Planen for idag

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å

Page 27: Planen for idag

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

Page 28: Planen for idag

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

Page 29: Planen for idag

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

}

Page 30: Planen for idag

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;

Page 31: Planen for idag

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();

}

Page 32: Planen for idag

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--;

}

Page 33: Planen for idag

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);

}

Page 34: Planen for idag

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;}

Page 35: Planen for idag

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); }

Page 36: Planen for idag

Datalogi 1F: Multiprogrammering[3]

36

Kerne med centralt processkift

KInitProc(…) KWaitQKSleep() KCurProcKPause()KSelectNewProcess()

KInitSem() KReadChar()KWait() KWriteChar()KSignal()

KReadLine()KWriteLine()

readerProcwriterProc

Page 37: Planen for idag

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

Page 38: Planen for idag

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

Page 39: Planen for idag

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

Page 40: Planen for idag

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;

}

Page 41: Planen for idag

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

Page 42: Planen for idag

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

Page 43: Planen for idag

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

Page 44: Planen for idag

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

Page 45: Planen for idag

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

Page 46: Planen for idag

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 …

Page 47: Planen for idag

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

Page 48: Planen for idag

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()

Page 49: Planen for idag

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

Page 50: Planen for idag

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()

}

Page 51: Planen for idag

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);

}

Page 52: Planen for idag

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;

}

Page 53: Planen for idag

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());}

Page 54: Planen for idag

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

Page 55: Planen for idag

Datalogi 1F: Multiprogrammering[3]

55

Kilder

• Disse slides er baseret på indholdet i Datalogi 1F kursusbog bind 4, kapitlerne 6 & 7.