1(11) Institutionen för datavetenskap LUNDS TEKNISKA HÖGSKOLA Tentamen EDA698 – Realtidssystem (Helsingborg) 2015–01–09, 14.00–19.00 Det är tillåtet att använda Java snabbreferens och miniräknare, samt ordbok. Det går bra att använda både engelska och svenska uttryck för svaren. Den här tentamen består av två delar: teori, frågor 1-5 (18 poäng), och programmering & design, frågor 6-7 (12 poäng). För godkänd (betyg 3) krävs sammanlagt ca hälften av alla 30 möjliga poäng samt att det krävs ca en tredjedel av poängen i varje del för sig. För högsta betyg (5) krävs dessutom att en del av poängen uppnås med (del)lösningar till båda uppgifterna i programmering & design (preliminärt). 1. Jämlöpande exekvering För jämlöpande aktiviteter som exekverar på en processor måste trådbyten schemaläggas enligt någon princip. a) Förklara (använd två meningar eller ett kort exempel) följande koncept: • Frivillig tidsdelning (cooperative multitasking, non-preemptive scheduling). • Påtvingad tidsdelning med pre-emption som använder pre-emptionpunkter (pre-emption point based scheduling). • Påtvingad tidsdelning med pre-emption utan pre-emptionpunkter. (2p) b) Java stödjer påtvingad tidsdelning med pre-emption. Stödjer Java också frivillig tidsdelning? Vad innebär metoden Thread.yield()? Motivera ditt svar. (1p) 2. Exekveringstider, RMS, EDF Ett realtidssystem består av fyra oberoende trådar A, B, C och D. Trådarnas egenskaper (värstafallsexekveringstid och periodtid) ges i tabellen nedan: Thread A B C D C [ms] 3 3 2 2 T [ms] 10 14 13 8 Antag att varje tråds deadline är lika med periodtiden och att alla trådar börjar exekvera vid tiden t = 0 ms. Antag även att varje tråd utnyttjar sin värstafallsexekveringstid fullt ut. a) Rita ett schemaläggningsdiagram som visar hur trådarna exekverar under tidsintervallet 0-30 ms. Antag att schemaläggningen följer RMS-principen (Rate Monotonic Scheduling). (2p) b) Rita ett diagram som ovan, fast antag nu att EDF-principen följs (Earliest Deadline First). (2p) c) Vilka är värstafallsresponstiderna för trådarna i det första problemet a? Studera även responstiderna för trådarna i problemet b (tills varje tråd exekverat två gånger). Visa responstiderna för varje tråd för varje exekvering. Varför går det inte att läsa ut värstafallsresponstiden för EDF? (1p) 2(11) 3. Dödlägesanalys Ett realtidssystem innehåller fem mutex-semaforer A-E. Dessa används för att åstadkomma ömsesidig uteslutning till fem delade resurser. En analys av systemet visar att resurserna används av två trådtyper T1 och T2. Nedan visas implementationen av dessa trådars run-metod. Trådarna exekverar periodiskt. T2: T1: B.take(); C.take(); useBC(); D.take(); useBCD(); C.give(); B.give(); E.take(); useDE(); D.give(); E.give(); A.take(); B.take(); useAB(); B.give(); A.give(); C.take(); E.take(); useCE(); E.give(); C.give(); D.take(); A.take(); useAD(); A.give(); D.give(); a) I vilka situationer finns det en risk för att dödläge inträffar i detta system? Hur många trådar krävs (av varje typ) för att dödläge ska uppstå? Svaret måste tydligt motiveras. (2p) b) Föreslå kodändringar som tar bort risk för dödläge. I ovanstående kod finns anrop till metoder så som useXY();. Detta betyder att när metoden anropas måste semaforerna X och Y hållas. Detta måste även stämma efter dina ändringar. Motivera ditt svar. (1p) 4. Schemaläggningsanalys Ett realtidssystem består av fyra trådar A, B, C och D. Trådarna är schemalagda enligt RMS med prioritetsarv (basic priority inheritance). All trådkommunikation sker via tre monitorer M1, M2 och M3 enligt figur nedan. Varje gång respektive tråd exekverar anropas monitoroperationen en gång. För tråd C anropas monitoroperationerna i sekvens (inga nästlade monitoranrop). Värstafallsexekveringstider för respektive monitoranrop visas i figuren. Periodtider och värstafallsexekveringstider för trådarna anges i tabellen. Thread A B C D C [ms] 0.2 0.7 0.4 1.0 T [ms] 0.8 1.1 1.2 3.0 a) Vilka är de maximala blockeringstiderna (dvs tiden som varje tråd kan bli blockerad av trådar med lägre prioritet) för de fyra trådarna? Motivera ditt svar. (2p) (2p) b) Är systemet schemaläggningsbart? Motivera ditt svar. Tips: Ri = Ci + Bi + ∑ j∈hp(i ) & Ri Tj ' Cj 3(11) 5. Synkronisering I kursen har vi påpekat att de tre vanliga synkroniseringsprimitiverna semafor, monitor, och brevlåda (mailbox) är lika kraftfulla ur synkroniseringssynpunkt. Har man tillgång till en av dessa kan man med dess hjälp implementera de andra. Illustrera detta genom att skriva en klass BinarySemaphore i Java som implementerar en binär semafor med de operationer som är normala för en sådan (take och give). Använd Javas inbyggda monitorbegrepp för att åstadkomma synkronisering. (3p) 6. Programmering: Hiss-simulering Kursen Realtidssystem innehåller en laborationsuppgift som handlar om en simulation av en hiss, som är avsevärt förenklad jämfört med en verklig hiss. Nu ska den blir mera realistiskt, dvs hissen ska förses med en dörrsimulator och det är dörrarnas tillstånd som avgör när personerna kan kliva på eller av och när hissen får köras vidare. Personerna måste alltså bara anmäla sig (trycka på knappen utanför eller i hissen) när ingen har gjort det tidigare. Dörrarna är i princip tidsstyrda men reagerar också när någon går genom öppningen, då det sitter en sensor (ljusstråle). Efter ett sådant passage anpassas tiden tills dörrarna stängs igen. Du ska nu implementera dörrsimulationen, dvs fyra metoder i en monitorklass (se kodskelettet nedan). Följande krav gäller: • En dörr som öppnas får inte avbrytas för att stängas igen. • En dörr som stängs får avbrytas och öppnas igen (en person som passerar genom bryter ljusstrålen så att sensorn reagerar och dörren stannar upp / öppnas). • En öppnad dörr hålls öppen minst en viss tidsperiod. • När sensorn registrerar ett passage (ljusstrålen bryts) påbörjas en ny tidsperiod med öppen dörr (eventuellt måste dörren öppnas först). • En person som försöker komma genom en dörr som stängs kan komma för sent, dvs det måste finnas en viss andel öppning kvar för att det ska fungera. • Personerna väntar tills dörrarna är öppnade innan de börjar kliva genom, hissmotorn måste vänta tills dörrarna har lyckats stängas helt innan den får köra hissen vidare (du behöver inte implementera personernas eller hissens beteende, men dörrsimulationen måste kunna användas på detta sätt). Liksom i den ursprungliga simuleringen finns det två typer av trådar, hisstråden och persontrådarna. Vid användning kommer persontrådarna att anropa metoderna awaitDoorsOpen och tryPassage. Hisstråden kommer att anropa closeDoors och openDoors. Utöver detta så får inte metoden tryPassage vara blockerande, vilket övriga kan vara. Ingen metod får hålla klassens monitorlås en längre tid. Uppgift: a) Implementera awaitDoorsOpen-metoden. (1p) b) Implementera tryPassage-metoden. (2p) c) Implementera openDoors-metoden. (2p) d) Implementera closeDoors-metoden. (3p) Du kan lägga till attribut efter behov. Du måste redovisa alla extra tillstånd och deras initialisering i din kod. Kodskelett till dörrsimulationen (klassen DoorSimSkeleton) och en kort Java-referens finns på sidorna 5 och 6! 7. Design: Hiss-kontroller En hiss innehåller ett antal aktuatorer och sensorer som behöver styras från ett inbyggt system. Hissrörelsen aktueras av en motor monterad i hiss-schaktet. Sensorer på varje våningsplan signalerar när hissen befinner sig i våningsplanet. Knappar för att kalla på hissen finns monterade på varje våningsplan (upp och ner). Indikatorlampor visar när knapparna tryckts in. En skylt ovanför hissen visar vilket våningsplan hissen befinner sig på. Inuti hissen finns knappar för att välja önskad våning samt högtalare som annonserar vilket våningsplan hissen befinner sig på. Dörrarna 4(11) aktueras av en dörrmotor, en dörrsensor upptäcker om det finns hinder ivägen när dörren stängs (vilket gör att dörren öppnas igen). Specificera meddelande-baserade kontrollers (jämför med tredje laborationsuppgiften i kursen, tvättmaskinen) för hiss-systemet. Visa vilka trådar och meddelanden som du anser behövs för att uppfylla hissfunktionaliteten. Beskriv varje tråds ansvarsområde/uppgift. Beskriv nödvändiga meddelanden. Beskriv meddelandeutbyten mellan trådar. Rita en figur som visar ditt system-design, med respektive trådar, gränssnitt till hårdvara (aktuatorer och sensorer) och meddelanden. (4p) 5(11) A Kodskelett till uppgift 6 package lift; public class DoorSimSkeleton { private static long closingTime = 2000; // Closing time of doors (duration) private static long openingTime = 2000; // Opening time of doors (duration) private static long keepOpenTime = 5000; // Amount of time to keep doors open // before closing private long lastDoorPassageTime; // Last time someone passed the door or // doors reached OPEN state // Door private private private private private private state static int CLOSED = 0; static int OPENING = 1; static int OPEN = 2; static int CLOSING = 3; static int ABORT_CLOSING = 4; int doorState; /** Constructor */ public DoorSimSkeleton() { doorState = OPEN; // ... } /** Wait until doors are OPEN */ public synchronized void awaitDoorsOpen() throws InterruptedException { // ... } /** Try to pass through the doors. Passing is possible if the doors are * OPEN or if the doors are less than one quarter closed upon CLOSING. * If the doors are CLOSING but the passage was possible, * the closing process should be aborted (ABORT_CLOSING). * * Returns true upon successful passage, false otherwise. */ public synchronized boolean tryPassage() throws InterruptedException { // ... } /** Open doors, changing door state to OPEN through intermediary state OPENING. * The duration for OPENING is openingTime. */ public synchronized void openDoors () throws InterruptedException { // ... } /** Keep doors open until they are eligible to close (lastPassageTime + keepOpenTime), * then try to close the doors through intermediary state CLOSING. * The doors need closingTime to close, if not aborted. * Only close doors if not OPENING. If closing was aborted, initiate opening, before * keeping doors open again. */ public synchronized void closeDoors () throws InterruptedException { // ... } } 6(11) B Kort, specifik Java-referens med eventuellt lämpliga metoder Tidshantering, trådhantering, väntelägen, etc • klass Math och klass System: – public static double Math.random(): ger ett slump-värde mellan 0 och 1. – public static long System.currentTimeMillis(): aktuell systemtid i millisekunder • klass Thread: – – – – – public static void sleep( long t): försätta en tråd i viloläge under t ms public static void yield(): dra tråden tillbaka och sätt i kön för ny schemaläggning public void join(): vänta att tråden dör public void start(): starta upp tråden public void run(): trådens arbete • klass Object: – public final void wait(): försätta en tråd i vänteläge – public final void wait( long t): försätta en tråd i vänteläge under max t ms. Kan dock väckas i förtid! – public final void notify(): meddela nästa tråden i vänte-kön att ett tillstånd har ändrats – public final void notifyAll(): meddela alla trådar i vänteläge att ett tillstånd har ändrats 7(11) Kortfattad lösning 1. a) a) Frivillig tidsdelning innebär att trådbyte endast sker när den exekverande tråden själv tar initiativet till det. Detta underlättar kraftigt uppgiften att synkronisera trådarna med varandra, men ger dåliga realtidsprestanda. b) Påtvingad tidsdelning med s.k. preemption points innebär att realtidskärnan tar beslut om när trådbyte ska ske, men det kan bara ske vid vissa väldefinierade tidpunkter i exekveringen av den nu körande tråden. Dessa tillfällen infaller när man vet att tråden är i ett säkert tillstånd. c) Även i detta fall tar realtidskärnan (via klockavbrott) initiativ till trådbyte, men till skillnad från det förra fallet kan trådbyte ske mellan vilka maskininstruktioner som helst. Realtidsprestanda är mycket bra för denna lösning, men man måste vara mycket noggrann med att se till att synkroniseringen mellan trådarna är korrekt. b) Java stödjer inte frivillig tidsdelning utan trådbyten kan ske vid godtyckliga tidpunkter. Metoden Thread.yield() anropas för att tala om för schemaläggaren att detta kan vara ett lämpligt tillfälle att låta en annan tråd köra, men det är ingen garanti att trådbyte sker. 2. a) b) c) Problem A A: 5ms, B: 19ms, C: 7ms, and D: 2ms Problem B A: 5ms/5ms, B: 10ms/8ms, C: 7ms/6ms, and D: 2ms/4ms Den aktuella responstiden för tråd D är längre andra gången tråden exekverar. Detta talar mot antagandet att värstafallsresponstiden inträffar då alla trådar börjar exekvera samtidigt (kritiska tidpunkten). 3. a) 8(11) b) Ett sätt att undvika dödläge är att ändra sekvensen A.take(); B.take(); i T1 till B.take(); A.take();. Den här ändringen bryter det cirkulära beroendet i grafen genom att byta riktning på pilarna mot T1 mellan A och B i figuren. a) Tråd A kan blockeras av tråd C om C håller M1. Pga prioritetsarvet kan det inte bli någon prioritetsinvertering och den maximala blockeringstiden för A blir då värstafallsexekveringstiden för metoden b(), dvs 0.3 ms. Tråd B kan bli blockerad direkt av C, om denna håller M2 för tiden det tar att köra e(); alltså 0.1ms. Om dock C istället håller M1 och A ska ta över (högre prioritet), kommer C att ärva As prioritet och blockerar B - push-through-blocking uppstår, under maximalt 0.3ms. Därmed blir värstafallsblockeringstiden för B max(0.1,0.3) = 0.3 ms (C kan enbart håller antingen M1 eller M2). Tråd C kan blockeras av D, om D håller M3 med ett anrop till f() under maximalt 0.6 ms. Pushthrough blocking förekommer inte, eftersom det inte finns två trådar med högre respektive lägre prioritet än C som kommunicerar direkt med varandra via en delad resurs. Tråd D kan inte blockeras på resurs, då det inte finns någon tråd med lägre prioritet, därav har den blockerinstiden 0ms. 4. b) R A = 0.5ms ok R B = 1.4 ≥ 1.1 not ok 5. public class Semafor { private boolean flag = true; public synchronized void take() throws InterruptedException { while (!flag) wait(); flag = false; notifyAll(); } public synchronized void give() throws InterruptedException { while (flag) wait(); flag = true; notifyAll(); } } 6. package lift; public class DoorSim { private private private private long closingTime, openingTime, keepOpenTime; long closingStartTime, timeLeftToClose, lastDoorPassageTime; static int CLOSED = 0; static int OPENING = 1; 9(11) private private private private static int OPEN = 2; static int CLOSING = 3; static int ABORT_CLOSING = 4; int doorState; public DoorSim( long closingT, long openingT, long keepOpenT) { closingTime = closingT; openingTime = openingT; keepOpenTime = keepOpenT; doorState = OPEN; } public synchronized void awaitDoorsOpen() throws InterruptedException { while( doorState != OPEN) wait(); } public synchronized boolean tryPassage() throws InterruptedException { boolean passed = false; if( doorState == OPEN || (doorState == CLOSING && (System.currentTimeMillis() - closingStartTime) < 0.25*closingTime)) { lastDoorPassageTime = System.currentTimeMillis(); if( doorState == CLOSING) doorState = ABORT_CLOSING; passed = true; notifyAll(); } return passed; } public synchronized void openDoors () throws InterruptedException { long t, t0; if( doorState != OPENING) { doorState = OPENING; notifyAll(); } t0 = System.currentTimeMillis(); t = t0 + openingTime - timeLeftToClose; while( (t0 = System.currentTimeMillis()) < t) wait( t-t0); doorState = OPEN; lastDoorPassageTime = System.currentTimeMillis(); notifyAll(); } public synchronized void closeDoors () throws InterruptedException { long t, t0; while( doorState != CLOSED) { t0 = System.currentTimeMillis(); t = lastDoorPassageTime + keepOpenTime; while( (t0 = System.currentTimeMillis()) < (t = (lastDoorPassageTime + keepOpenTime))) wait( t-t0); if( doorState != OPENING) { doorState = CLOSING; notifyAll(); 10(11) closingStartTime = System.currentTimeMillis(); t0 = closingStartTime; t = closingStartTime + closingTime; while( (doorState != ABORT_CLOSING) && (t0 = System.currentTimeMillis()) < t) { wait( timeLeftToClose = t-t0); } if( doorState != ABORT_CLOSING) { doorState = CLOSED; timeLeftToClose = 0; notifyAll(); } else { timeLeftToClose = t - System.currentTimeMillis(); openDoors(); } } } } } 7. OBS: Det här är ETT förslag till en lösning, det kan fungera på annat sätt också! Följande Trådklasser (Controllers, samtliga utrustad med egen mailbox) behövs: • ElevatorController: En periodiskt löpande tråd med inbyggd mailbox som hanterar hissmotorn efter inkommande önskemål om hur långt den ska åka åt vilket håll med hårdvarukommandon (Up, Down, Stop). Skickar ett meddelande till DoorController när den har stannat, samt inväntar ett meddelande om att dörrarna har stängts innan den fortsätter. • LevelController: En tråd som löpande läser av våningssensorerna. Tar emot meddelanden om önskade våningar från knapphanteringstrådarna (se nedan), räknar ut vart hissen ska åka och meddelar till ElevatorController. Skickar meddelanden tillbaka till respektive knapphantering när en önskad våning är nått. Hanterar hårdvaran (display) för visning av våningen där hissen befinner sig både i hissen samt utanför på varje våning. • DoorController: Hanterar dörrarnas motor. Inväntar meddelanden om att hissen har stannat, öppnar dörren, hanterar dörrsensorn, stänger dörren när det går och meddelar till ElevatorController när dörrarna är stängda. • LevelButtonController: Hanterar knapptryck på "hit”-knappen på våningarna - lämpligen en controller-instans per våning. Vid knapptryck skickar den ett meddelande om önskad våning till LevelController samt att den slår på LEDen i knappen. När den får meddelande från LevelController om att våningen är nått, släcker den LEDen. • ElevatorButtonController: Hanterar knappsatsen inuti hissen. Vid tryck på en knapp skickar den meddelande till LevelController, slår på LEDen i knappen och inväntar meddelande från LevelController om att en önskad våning är nått. Släckar då LEDen och slår på respektive utlåtande i högtalar-hårdvaran (skickar dit text). Följande meddelanden (RTEvents) antas skickas mellan trådarna enligt ritningen: • LevelReqMsg - meddelande om att hissen ska komma till / stanna på respektive våning. • IntLevelReqMsg - meddelande om att hissen ska åka till / stanna på en viss våning, inkommande från knappsatsen inuti hissen (ifall prioriteringar ska göras mellan våningsknappar och hissknappar). • ReqLevelReachedMsg - önskad våning har nåtts. • MoveToNextLevelMsg - kör hissen åt beräknat håll, beroende hur man tänker att hårdvaran är uppbyggt, kan det vara olika innehåll i meddelanden ("hela vägen", "en våning i taget"... etc). • StartOpenCloseMsg - hissen har stannat, dörrarna ska öppnas och enligt specifikationen stängas efter det. 11(11) • DoorsClosedMsg - dörröppnings- och stängningssekvensen har avslutats, det går bra att fortsätta körning, om det föreligger ett uppdrag till detta. LevelSensors (HW) LevelSensors (HW) LevelSensors LevelSensors(HW) (HW) LevelSensors (HW) LevelIndicators LevelIndicators LevelIndicators (HW) LevelIndicators (HW) LevelIndicators (HW) (HW) (HW) LevelController reached ReqLReachedMsg Buffer LevelReqMsg LevelButtonLevelButtonLevelButtonController LevelButtonController LevelButtonController Controller Controller Buffer Buffer Buffer Buffer Buffer pressed Button (HW) ButtonLED (HW) set/unset show IntLevelReqMsg ElevatorUIController pressed Buffer MoveToNextLevelMsg set/unset ReqLReachedMsg Button (HW) ButtonLED (HW) ButtonLED (HW) ButtonLED ButtonLED(HW) (HW) ButtonLED (HW) Button (HW) Button (HW) Button Button(HW) (HW) announce Speaker (HW) ElevatorController DoorController StartOpenCloseMsg Buffer Buffer open/close DoorsClosedMsg up/down/stop ElevatorEngine (HW) passed (triggered) DoorSensor (HW) DoorEngine (HW)
© Copyright 2024