490 likes | 634 Views
Planen for i dag. Repetition af kerner med afbrydelser Kerner med tvungent processkift Præsentation af K1. Kerner med afbrydelser. Vi erstattede aktiv venten med: ventende processer placeres i ventekøer afbrydelser aktiverer ventede processer ved at flytte dem til klarkøen
E N D
Planen for i dag • Repetition af kerner med afbrydelser • Kerner med tvungent processkift • Præsentation af K1 Datalogi 1F: Multiprogrammering[4]
Kerner med afbrydelser • Vi erstattede aktiv venten med: • ventende processer placeres i ventekøer • afbrydelser aktiverer ventede processer ved at flytte dem til klarkøen • Bedre udnyttelse af CPU • Introduktion af parallelisme i kernen: • afbrydelsesroutiner kan udføres når som helst (undtagen når vi lukker for afbrydelser) • fokus på kritiske regioner Datalogi 1F: Multiprogrammering[4]
Kerne med afbrydelser readerProc writerProc KReadLine() KWriteLine() KInitSem() KReadChar() KWait() KWriteChar() KSignal() KReadQ KWriteQ KInitProc(…) KReadyQ KSleep() KCurProc KPause() KSelectNewProcess() KInterruptHandler() Datalogi 1F: Multiprogrammering[4]
Tætkoblede drivprogrammer • Hver slags hændelse (tegn skrevet, tegn læst, …) er tilknyttet en ventekø: char KReadChar() { while(!(rdio(com1lsr) & 0x01)) KPause(KReadQ); return rdio(com1Rbr); } Datalogi 1F: Multiprogrammering[4]
Synkronisering med ydre enheder void KInterruptHandler() { if( rdio(com1Iir) & 2) while(!KReadQ.isEmpty()) KReadyQ.Put(KReadQ.Get()); else if( rdio(com1Iir) & 4) while(!KWriteQ.isEmpty()) KReadyQ.Put(KWriteQ.Get()); } Datalogi 1F: Multiprogrammering[4]
Uheldig rækkefølge <KReadChar> while(!(rdio(com1lsr) & 0x01)) <UART> sætter ready-bit <KInterruptHandler> while(!KWaitQ.isEmpty()) <KReadChar> KPause(KReadQ); <KSelectNewProcess> <sætter proces i ventekø> AAAAARGH: vi opdager ikke at tegnet er læst Datalogi 1F: Multiprogrammering[4]
Problem • Vi har to parallelle processer: • brugerproces • afbrydelsesroutine der deler data: • kontrolregistre • proceskøer • Vi må sikre udelelig udførsel af kritiske regioner Datalogi 1F: Multiprogrammering[4]
Implementering af udelelighed • Luk for afbrydelser: char KReadChar() { forbid(); while(!(rdio(com1lsr) & 0x01)) KPause(KReadQ); char ch = rdio(com1Rbr); permit(); return ch; } • Nu bliver vi ikke afbrudt mellem check af statusregister og KWaitQ.Put() • Vi åbner for afbrydelser i KPause() Datalogi 1F: Multiprogrammering[4]
Ny KPause • KPause skal skifte afbrydelsesniveau (ipl er en del af processens tilstand): void KPause() { <gem afbrydelsesniveau i PKB>; <gem registre på stakken>; <gem stakpeger i PKB>; <find ny proces>; <gendan stakpeger fra PKB>; <gendan registre>; <gendan afbrydelsesniveau fra PKB>; } Datalogi 1F: Multiprogrammering[4]
Andre routiner derskal beskyttes? • Afbrydelsesroutiner og alm. kernefunktioner deler køstrukturer • Beskyt køoperationerne: int isEmpty() { int oldipl = forbid(); int b = (size == 0); permit(oldipl); return b; }; Datalogi 1F: Multiprogrammering[4]
Kerner med tvungent processkift • Indtil nu har vi kun set på kerner med frivilligt processkift via KPause() • En afbrydelsesroutine sætter processen i klarkøen, men der kan gå lang tid inden den aktive proces opgiver CPU’en • Vi vil tvinge et processkift som en del af afbrydelsen: • dette sikrer hurtigere behandling af afbrydelser Datalogi 1F: Multiprogrammering[4]
Ny afbrydelsesprocedure void KInterruptHandler() { if(rdio(com1Iir) & 2) KSleep (KReadyQ, KWriteQ); else if(rdio(com1Iir) & 4) KSleep (KReadyQ, KReadQ); …… } Datalogi 1F: Multiprogrammering[4]
KSleep og venner Queue<Process>* KPutQ, KGetQ; void KSleep(Queue<Process>& put, Queue<Process>& get) { KPutQ = &put, KGetQ = &get; KPause(); } Registers* KSelectNewProcess (Registers* sp) { KCurProc->sp = sp; KPutQ->Put(KCurProc); KCurProc = KGetQ->Get(); return KCurProc->sp; }
a2 a1 a0 gp pc ps Tvungent processkift - eksempel BP2 sp BP1 sp afbrydelse AP:BP2_start() AP:BP1_start() AP: Wait(sem) AP: printl() PAL stakramme P2 AP: KWriteLine AP: ent_int AP: KWriteChar Registre P2 Processkift sker EFTER behandling af og kvittering for afbrydelse AP: KPause AP: KInterruptH… AP: KSelectNewP… AP: KSleep • Skift: • stakpeger • afbrydelsesniveau AP: KPause AP: KSelectNewP…
Semaforroutiner Nu kan vores semaforroutiner også blive afbrudt: void KWait (KSem *sem) { if (!sem->value) KSleep(sem->WaitQ, KReadyQ); sem->value--; } • Førhen var dette sikkert fordi afbrydelser ikke rørte ved semaforerne • Men nu kan en vilkårlig proces blive afbrudt Datalogi 1F: Multiprogrammering[4]
En anden uheldig rækkefølge <BP1> if(!sem->value) <antag sem->value == 1> <Afbrydelse> KPause(KReadyQ, KReadQ); . . . . . . <BP2> if(!sem->value) <sem->value stadig == 1> <BP2> sem->value--; <Afbrydelse> KPause(KReadyQ, KWriteQ); . . . . . . <BP1> sem->value--; UUUPS: der er 2 processer i kritisk region!!
Nye semaforroutiner Nu kan vores semaforroutiner ikke mere blive afbrudt: void KWait (KSem *sem) { forbid(); if (!sem->value) KSleep(sem->WaitQ, KReadyQ); sem->value--; permit(); } Datalogi 1F: Multiprogrammering[4]
Andre routiner derhar problemer? • Måske KSleep generelt? • Kan vi klare at skifte proces mens vi skifter proces? Registers* KSelectNewProcess (Registers* sp) { KCurProc->sp = sp; KPutQ->Put(KCurProc); KCurProc = KGetQ->Get(); return KCurProc->sp; } • De to globale variable KPutQ og KGetQ ser suspekte ud Datalogi 1F: Multiprogrammering[4]
Endnu en uheldig rækkefølge <BP1> < KSleep(KReadyQ, myWaitQ) > < KPutQ = KReadyQ, KGetQ = myWaitQ > <Afbrydelse> < KSleep(KReadyQ, KReadQ) > < KPutQ = KReadyQ, KGetQ = KReadQ > <BP4> . . . . . . < KSleep(KReadQ, KReadyQ) > < KPutQ = KReadQ, KGetQ = KReadyQ > <BP1> < KSelectNewProcess > < Sætter sig selv på KReadQ men aktiverer proces fra KReadyQ > Datalogi 1F: Multiprogrammering[4]
Morale • Man skal være forsigtig!!!! • Identificer alle variable der deles mellem afbrydelsesroutiner og alm. kerneroutiner • Foretag sikring af kritiske regioner • Ved aflusning af kerner (f.eks. K1) kan det være en god ide at starte med helt at lukke for afbrydelser i kernen og så langsom bløde det op Datalogi 1F: Multiprogrammering[4]
Kerner med periodisk processkift • Hidtil har vi udskiftet den kørende proces ved afbrydelse fra I/O enhed: • Har vi kun en CPU tung proces, er det fint nok, da den vil blive afbrudt • Men hvis vi har flere, kan der stadig gå lang tid inden en ventende proces ”kommer til fadet” • KPause() kaldes typisk som led i et systemkald: • ved hvert systemkald kunne man undersøge om en proces har kørt for længe, og derefter kalde KPause • Hvad med beregningstunge processer, der sjældent bruger systemkald? • Kræv at de skal indsætte frivillige processkift • Tving dem væk fra CPU’en Datalogi 1F: Multiprogrammering[4]
Implementering af periodisk processkift void KInterruptHandler (ulong a0) { if ( a0 & 0x01) KSleep(KReadyQ, KReadyQ); else . . . . . . } • Hver gang uret afbryder puttes den aktive proces bagerst i klarkøen (round robin) • Men hvad hvis der ingen aktiv proces er? Datalogi 1F: Multiprogrammering[4]
Tomgangsprocessen • For at sikre, at der altid er en proces i klarkøen, har vi en tomgangsproces: void KIdleProcess { for(;;;) KSleep(KReadyQ,KReadyQ); } • men man kunne nu stadig bruge en venteløkke i selve KSelectNewProcess Datalogi 1F: Multiprogrammering[4]
Brug af to sikkerhedsniveauer • Hardware indeholder en eller flere bits, der viser sikkerhedsniveau, f.eks. (0) brugertilstand og (1) kernetilstand • Ved afbrydelser skiftes til kernetilstand, f.eks. ved en trap operation • Specielle privilegerede instruktioner kan kun udføres i kernetilstand, typisk instruktioner der har med ressourcedeling at gøre kernetilstand skift til brugerniveau trap/ exception brugertilstand Datalogi 1F: Multiprogrammering[4]
2 slags stakke • Når brugerprogrammer udføres i brugertilstand benytter de en brugerstakpeger • Systemkald samt afbrydelser udføres i kernetilstanden og benytter en kernestakpeger • Kernestakpegeren peger på et andet lagerområde end brugerstakpegeren • Kerne sp vil typisk være beskyttet mod skrivning og læsning fra brugertilstand • Skiftet mellem stakpegerne foretages på Alphaerne af PAL koden ved skift mellem bruger og kernetilstand Datalogi 1F: Multiprogrammering[4]
Brugerstak og kernestakeksempel BP1 sp AP:BP1_start() BP1_start(void) { findmin(t3_root); } AP: findmin() afbrydelse AP: findmin() PAL registre: bruger sp: BP1 sp kerne sp: 0x200000 PAL registre: bruger sp: ikke def. kerne sp: 0x200000 kerne sp PAL stakramme $sp = kerne sp $sp = bruger sp kerne sp = $sp AP: ent_int AP: KInterruptH… Datalogi 1F: Multiprogrammering[4]
Processkift i afbrydelsesprocedure • Hvis vi ønsker at skifte proces i en afbrydelsesprocedure skal vi dels: • gemme kørende proces’ tilstand • ændre brugerstakpeger i PAL register • gendanne nye proces’ tilstand • Ændring af bruger sp er nemt: • PAL_rdusp læser bruger stakpeger fra PAL register • PAL_wrusp skriver en ny bruger stakpeger til PAL register • Men processernes tilstande omfatter: • PAL stakramme • Registre gemt af ent_int (afbrydelsesniveau) Datalogi 1F: Multiprogrammering[4]
Skift mellem stakke vedprocesskift under afbrydelse Vi ser på fire eksempler • Skift mellem processer i brugertilstand: • én kernestak • en kernestak pr proces • Processkift med en proces i systemtilstand og en i brugertilstand • Processkift ved indlejrede afbrydelser, f.eks.: • først afbrydelse fra UART (ipl 3) • KInterruptHandler afbrydes af uret (ipl 5) Datalogi 1F: Multiprogrammering[4]
2 processer i brugertilstand:én kernestak BP2 sp BP1 sp AP:BP2_start() AP:BP1_start() AP: calc() AP: calc() afbrydelse AP: calc() AP: calc() kerne sp PAL stakramme P1 PAL stakramme P2 Registre P2 Registre P1 PAL registre: bruger sp: BP1 sp kerne sp: 0x200000 PAL registre: bruger sp: XXXXX kerne sp: 0x200000 PAL registre: bruger sp: BP2 sp kerne sp: 0x200000 Gem kopi af BP2s kernestak (f.eks. på brugerstakken) Gendan kopi af BP1s kernestak AP: ent_int AP: KInterruptH… AP: KSelectNewP…
2 processer i brugertilstand:en kernestak per proces BP2 sp BP1 sp PAL registre: bruger sp: BP1 sp kerne sp: 0x200000 PAL registre: bruger sp: BP1 sp kerne sp: 0x220000 PAL registre: bruger sp: BP2 sp kerne sp: 0x200000 PAL registre: bruger sp: XXXXX kerne sp: 0x200000 AP:BP2_start() AP:BP1_start() AP: calc() AP: calc() afbrydelse AP: calc() AP: calc() BP1 kerne sp BP2 kerne sp PAL stakramme P1 PAL stakramme P2 Registre P1 Registre P2 AP: ent_int AP: ent_int AP: KInterruptH… AP: KInterruptH… AP: KSelectNewP… AP: KSelectNewP…
2 processer: en i brugertilstand ogen i kernetilstand BP2 sp BP1 sp afbrydelse AP:BP2_start() AP:BP1_start() AP: calc() AP: printl() BP1 kerne sp • Skift: • brugerstakpeger • aktiv stakpeger • afbrydelsesniveau BP2 kerne sp PAL stakramme P1 PAL stakramme P2 AP: ent_sys AP: ent_int Registre P1 Registre P2 AP: KWriteChar AP: KInterruptH… AP: KPause AP: KPause AP: KSelectNewP… AP: KSelectNewP…
Indlejrede afbrydelser PAL stakramme P2 Registre P2 kerne sp Er flag for processkift sat og er vi den sidste afbrydelse? AP: ent_int AP: KInterruptH… BP2 sp AP: KPause PAL stakramme P2 AP:BP2_start() AP: KSelectNewP… Registre P2 Så kan vi skifte til en anden proces AP: calc() AP: ent_int afbrydelse AP: calc() afbrydelse AP: KInterruptH… Vi sætter et flag, der fortæller at vi skal foretage et processkift Duer ikke – vi var allerede i gang med at behandle et interrupt AP: KPause AP: KSelectNewP…
Indlejrede afbrydelser • Hvordan ved man at man er det ”eneste” interrupt? • På stakken ligger et statusregister, der fortæller hvad afbrydelsesniveau var inden den aktuelle afbrydelse: • ipl == 0 => ingen forudgående afbrydelser Datalogi 1F: Multiprogrammering[4]
Opsummering • Kerner med afbrydelser: • introduktion af parallelisme i kernen • implementation af udelelighed ved styring af afbrydelsesniveau • Tvungent processkift: • øget parallelisme i kernen (alle processer kan afbrydes) • periodisk processkift • indlejrede afbrydelser Datalogi 1F: Multiprogrammering[4]
K1: Multiprogrammeringskerne med prioriteret skedulering • Udvikling af en multiprogrammeringskerne: • procesafvikling • procesadministration • synkronisering • I/O • der skal kunne afvikle et sæt brugerprogrammer Datalogi 1F: Multiprogrammering[4]
Procesafvikling • Brugerprocesser skal køre i brugertilstand og kernefunktioner afvikles i kernetilstand: • i modsætning til kernerne i kursusbog bind 4 hvor alt foregår i kernetilstand • En konsekvens (og et krav) er at alle kernefunktioner skal aktiveres vha. alpha’ernes systemkaldsmekanisme: • PAL_callsys, PAL_rti • Kernen skal implementere tvungen tidsdeling: • uret afbryder 1024 gange i sekundet • processkift under udførsel af kerneoperationer Datalogi 1F: Multiprogrammering[4]
Procesadministration • start_proc: starter en proces • exit: terminerer en proces • sleep: sætter proces til at vente i X 1/1024 sekunder • yield: frivilligt processkift • Ved kernens start overgives kontrollen til processen INIT(), der starter de resterende brugerprocesser. Datalogi 1F: Multiprogrammering[4]
Skedulering • Prioritetsbaseret skedulering: • 32 prioritetsniveauer: 0 – højest, 31 – lavest • round-robin indenfor hvert prioritetsniveau • ældning af lavprioritetsprocesser for at undgå udsultning • Systemkald: set_priority(unsigned int priority) sætter processens prioritet unsigned int get_priority() returnerer processens aktuelle prioritet Datalogi 1F: Multiprogrammering[4]
Synkronisering • Klassisk tællesemafor: struct KSem { unsigned int value; Queue<Process> queue; } • med operationerne: • KSem *new_sem(int i) • wait_sem(KSem *) • signal_sem(KSem *) • free_sem(KSem *) Datalogi 1F: Multiprogrammering[4]
Prioritetsinvertering • I systemer med prioritetsbaseret skedulering kan der optræde prioritetsinvertering: • en højprioritetsproces venter på en kritisk region, der er låst af en lavprioritetsproces Datalogi 1F: Multiprogrammering[4]
S1 S2 S3 S3 Prioritetsinvertering - eksempel P1 aktiv høj P2 aktiv medium aktiv P3 lav aktiv Datalogi 1F: Multiprogrammering[4]
Prioritetsnedarvning • For at undgå prioritetsinvertering: • arver en proces i en kritisk region prioriteten fra den højest prioriterede proces associeret med den kritiske region • Prioritetsnedarvning er transitiv: • P1 (pri = 0) venter på P2 (pri = 15) • P2 venter på P3 (pri = 31) • P3 skal arve prioritet = 0 • Indlejrede kritiske regioner Datalogi 1F: Multiprogrammering[4]
Kritiske regioner • Kritiske regioner synliggøres overfor kernen ved hjælp af en speciel mutex semafor med operationerne: • KSem *new_mutex() • wait_mutex(KSem *) • signal_mutex(KSem *) • free_mutex(KSem *) • Prioritetsnedarvning kan knyttes til wait_mutex og signal_mutex Datalogi 1F: Multiprogrammering[4]
I/O • readdata: indlæser et antal tegn fra COM og returnerer hurtigst muligt • writedata: udskriver et antal tegn på COM • readpacket: indlæser en pakke fra ETH0 • readline: indlæser en linie fra COM • writeline: udskriver en linie på COM • I/O operationer skal foregå udeleligt Datalogi 1F: Multiprogrammering[4]
Afbrydelser • Kernen skal håndtere afbrydelser fra COM, ETH0 samt uret • Kernen skal kunne håndtere indlejrede afbrydelser • Drivprogramskoden til Ethernet kortet og uret udleveres Datalogi 1F: Multiprogrammering[4]
Sporingsmekanisme • Sporingsmekanisme udviklet af Søren Debois: • en cyklisk hændelsesbuffer: • systemkald • afbrydelser • returnering fra systemkald • brug den fra starten af!! Datalogi 1F: Multiprogrammering[4]
Milepæle • Start med at få brugerprocesser til at køre i brugertilstand: • Start uden afbrydelser • INIT i brugertilstand • Systemkaldsgrænseflade med simpelt kald • Slå afbrydelser til • Udvid systemkald • Dynamisk lagerallokering • Procesoprettelse • Prioriteret skedulering • Tvungent processkift • Tillad afbrydelser fra uret uden processkift • Tillad processkift ved urafbrydelse i brugertilstand • Tillad processkift ved urafbrydelse i begge tilstande Datalogi 1F: Multiprogrammering[4]
Instruktorvagter • Fra på mandag vil der være instruktorvagter alle hverdage • Lørdag den 27. marts er der også instruktorvagt • Kernemaskinen archimedes er reserveret til instruktorvagten under instruktorvagten • Der er ingen øvelser i de næste 3 uger Datalogi 1F: Multiprogrammering[4]
Kilder • Disse slides er baseret på indholdet i Datalogi 1F kursusbog bind 4, kapitlerne 7 & 8. Datalogi 1F: Multiprogrammering[4]