Præsentation er lastning. Vent venligst

Præsentation er lastning. Vent venligst

Datalogi 1F Forår 2003 Multiprogrammering[3] Eksempler på multiprogrammeringskerner Jørgen Sværke Hansen

Lignende præsentationer


Præsentationer af emnet: "Datalogi 1F Forår 2003 Multiprogrammering[3] Eksempler på multiprogrammeringskerner Jørgen Sværke Hansen"— Præsentationens transcript:

1 Datalogi 1F Forår 2003 Multiprogrammering[3] Eksempler på multiprogrammeringskerner Jørgen Sværke Hansen cyller@diku.dk

2 Datalogi 1F: Multiprogrammering[3] 2 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): –løst koblede drivprogrammer –kerne med tætkoblede drivprogrammer

3 Datalogi 1F: Multiprogrammering[3] 3 Kerne med decentralt processkift KInitProc(…)KWaitQ KPause()KCurProc KSelectNewProcess() KInitSem()KReadChar() KWait()KWriteChar() KSignal() KReadLine() KWriteLine() readerProc writerProc

4 Datalogi 1F: Multiprogrammering[3] 4 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(); }

5 Datalogi 1F: Multiprogrammering[3] 5 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; }

6 Datalogi 1F: Multiprogrammering[3] 6 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( ) { KPause(); <udfør aktion efter hændelse er indtruffet>; }

7 Datalogi 1F: Multiprogrammering[3] 7 KPause KPause foretager skiftet fra en proces til en anden: void KPause() { ; }

8 Datalogi 1F: Multiprogrammering[3] 8 Ventende processer AP: KPause registre P1 AP: KWait AP: KPause registre P2 AP: KReadChar AP: KPause registre P3 AP: KWriteChar PCB P1: sp PCB P2: sp PCB P3: sp struct process AP: KSelectNewP… AP: KSelectNewP… AP: KSelectNewP…

9 Datalogi 1F: Multiprogrammering[3] 9 Semaforoperationer void KSignal(KSem& sem) { sem++;// Aktivering af ventende } // sker ved aktiv venten void KWait(KSem& sem) { while (!sem) // Aktiv venten KPause(); sem--; }

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

11 Datalogi 1F: Multiprogrammering[3] 11 Kontrolregistre på UART

12 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 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 AP: KSelectNewP… AP: KSelectNewP… AP: KSelectNewP…

14 Datalogi 1F: Multiprogrammering[3] 14 KPause: implementation ( klib.s ) KPause: lda sp,-STAKRAMME(sp) stq $0, 0(sp) stq $1, 8(sp) … stq$29, 0xE8(sp) bis sp, 0, a0 // stakpeger er parameter lda pv, KSelectNewProcess jsr ra, (pv) bis v0, 0, sp // ny stakpeger er returværdi ldq $0, 0(sp) ldq $1, 8(sp) … ldq $29, 0xE8(sp) lda sp, STAKRAMME(sp) ret (ra) SAVE_REGS REST_REGS

15 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

16 Datalogi 1F: Multiprogrammering[3] 16 KSelectNewProcess Registers* 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 17 struct Registers struct 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 kald stakramme Registers*

18 18 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 19 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…

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

21 Datalogi 1F: Multiprogrammering[3] 21 Hvad sker der i KInitProc? KInitProc initialiserer en proces’ stak, så det ser ud som om den er afbrudt af 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); }

22 Datalogi 1F: Multiprogrammering[3] 22 Og så skal det hele sættes igang KCurProc = KWaitQ.Get(); KFirst(KCurProc->sp); hvor KFirst er defineret ved: KFirst:ldgp gp, (pv) ldq pv, 0xD8(a0) // Pop pv addq a0, 0xF0,a0 // Skip registre bis a0, 0, sp // Sæt sp jmp (pv) // Hop til processtart

23 Datalogi 1F: Multiprogrammering[3] 23 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å

24 Datalogi 1F: Multiprogrammering[3] 24 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 kan vente på at en hændelse indtræder

25 Datalogi 1F: Multiprogrammering[3] 25 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

26 Datalogi 1F: Multiprogrammering[3] 26 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/O Event(KSem&);// vent på semafor Event();// vent på CPU }

27 Datalogi 1F: Multiprogrammering[3] 27 Ny udgave af semaforoperation Ny udgave: void KWait (KSem& sem) { if(!sem) KSleep( Event(sem) ); sem--; } Gammel udgave: void KWait(KSem& sem) { while (!sem) // Aktiv venten KPause(); sem--; }

28 Datalogi 1F: Multiprogrammering[3] 28 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); }

29 Datalogi 1F: Multiprogrammering[3] 29 Proceskontrolblokken Nu bliver processens tilstand udvidet med hvilken hændelse en proces venter på (hvis nogen): struct Process { Registers* sp; Event waitsFor; } *KCurProc;

30 Datalogi 1F: Multiprogrammering[3] 30 KSleep() En ”overbygning” til KPause(), der registerer hvilken hændelse en proces venter på: void KSleep(Event e) { KCurProc->waitsFor = e; KPause(); }

31 Datalogi 1F: Multiprogrammering[3] 31 Den centrale venteløkke Registers* 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; }

32 32 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 PCB P1: sp waitsfor = SEM struct process PCB P2: sp waitsfor = IO PCB P3: sp waitsfor = IO KCurProc AP: KSelectNewP… AP: KSelectNewP… AP: KSelectNewP… void KWriteChar(char ch) { if (!rdio(com1Lsr) & 0x20) KSleep( Event(com1Lsr, 0x20); wrio(com1Thr, ch); }

33 Datalogi 1F: Multiprogrammering[3] 33 Kerne med centralt processkift KInitProc(…)KWaitQ KSleep()KCurProc KPause() KSelectNewProcess() KInitSem()KReadChar() KWait()KWriteChar() KSignal() KReadLine() KWriteLine() readerProc writerProc

34 Datalogi 1F: Multiprogrammering[3] 34 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

35 Datalogi 1F: Multiprogrammering[3] 35 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

36 Datalogi 1F: Multiprogrammering[3] 36 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

37 Datalogi 1F: Multiprogrammering[3] 37 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; }

38 Datalogi 1F: Multiprogrammering[3] 38 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

39 Datalogi 1F: Multiprogrammering[3] 39 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); } Alle ventende processer aktiveres og udfører check på om hændelse er indtrådt decentralt

40 Datalogi 1F: Multiprogrammering[3] 40 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 funktioner via PAL_wrent

41 Datalogi 1F: Multiprogrammering[3] 41 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

42 Datalogi 1F: Multiprogrammering[3] 42 Stak under afbrydelse AP: buf.Write() AP: KWriteLine proces Write() 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 for Proces Writer

43 Datalogi 1F: Multiprogrammering[3] 43 Synkronisering med ydre enheder char 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 …

44 Datalogi 1F: Multiprogrammering[3] 44 Uheldig rækkefølge while(!(rdio(com1lsr) & 0x01)) sætter ready-bit while(!KWaitQ.isEmpty()) KPause(); AAAAARGH: vi opdager ikke at tegnet er læst

45 Datalogi 1F: Multiprogrammering[3] 45 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()

46 Datalogi 1F: Multiprogrammering[3] 46 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

47 Datalogi 1F: Multiprogrammering[3] 47 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() ) /* Do nothing */;

48 Datalogi 1F: Multiprogrammering[3] 48 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); }

49 Datalogi 1F: Multiprogrammering[3] 49 KSelectNewProcess tager en ventekø som argument Registers* KSelectNewProcess(Registers* sp, Queue & blockOn) { KCurProc->sp = sp; blockOn.Put(KCurProc); while( KReadyQ.isEmpty() ) /* Do nothing */; KCurProc = KReadyQ.Get(); return KCurProc->sp; }

50 Datalogi 1F: Multiprogrammering[3] 50 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()); }

51 Datalogi 1F: Multiprogrammering[3] 51 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

52 Datalogi 1F: Multiprogrammering[3] 52 Kilder Disse slides er baseret på indholdet i Datalogi 1F kursusbog bind 4, kapitlerne 6 & 7.


Download ppt "Datalogi 1F Forår 2003 Multiprogrammering[3] Eksempler på multiprogrammeringskerner Jørgen Sværke Hansen"

Lignende præsentationer


Annoncer fra Google