Tentamen EDA698 – Realtidssystem (Helsingborg)

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)