c2 = 1

Realtidssystem
- Semaforer, trådsynkronisering EDA698 - Realtidssystem (Helsingborg)
Elin A. Topp
2015-09-02
Stora delar baserad på: Föreläsningsmaterial EDA040 (Klas Nilsson, Mathias Haage) samt EDA698 (Mats Lilja)
1
Förtydligande: Kursens fokus och mål
Praktik-Fokus: Trådar i OS-process
Teori-Fokus: Trådarnas samspel
•
•
•
•
•
•
•
dödläge / svält / livelock
•
Man ska bygga upp en förståelse för vad
som kan hända när flera trådar måste
hanteras
•
Man ska bygga upp en uppfattning om vilka
verktyg som finns för att utföra en analys
av ett tilltänkt system och kunna utföra
enklare analyser
semaforer
monitorer
meddelanden i kö
(mailbox / event queue)
•
Man ska kunna bygga flertrådiga program
som i princip kan uppfylla realtidskrav
•
Man ska kunna läsa kod av flertrådiga
program och kunna lösa problem i koden
exekveringstider
prioriteter
schemaläggning och schemaläggningsanalys
2
Dagens agenda
•
•
Recap: Exekveringstillstånd, kontextbyte, gemensam resurshantering
Synkronisering / Ömsesidig uteslutning: Semaforer
•
•
•
MutexSem
Signalling
Counting semaphore
3
Dagens agenda
•
•
Recap: Exekveringstillstånd, kontextbyte, gemensam resurshantering
Synkronisering / Ömsesidig uteslutning: Semaforer
•
•
•
MutexSem
Signalling
Counting semaphore
4
Exekveringstillstånd
Due to scheduler / kernel / OS
Running
Priority, time-sharing
By “this”: wait,
“synchronized” call
Due to
application
Ready
By other: notify,
“synchronized” return
Blocked
Schemaläggningstillstånd (scheduling state)
•
•
•
Running
Ready (genom t.ex. Semaphore.give())
Blocked (genom t.ex. Semaphore.take())
5
Pre-emption
Det finns olika strategier för tillåtelse av kontextbyten:
•
Non-pre-emptive scheduling (“icke-avbrytbar” schemaläggning): Tråden som “kör” kan inte
avbrytas tills den släpper CPUn frivilligt
•
explicit genom att anropa yield() eller
•
implicit genom (synchronized) operationer som kan blockera.
•
Pre-emption point based scheduling (“avbrytningspunktbaserad” schemaläggning): Tråden som
“kör” kan avbrytas vid vissa punkter i programmet (definierad genom språket eller run-time
systemet)
•
Pre-emptive scheduling (“avbrytbar” schemaläggning): Tråden som “kör” kan avbrytas när som
helst av schemaläggaren (som styrs av hårdvaru-interrupts).
För att det ska bli “rätt” med schemaläggningen och väntetiderna, antar våra program att kärnan är
pre-emptive, dvs. trådar kan avbrytas vid behov och då hanterar systemet kontextbytet på ett
korrekt sätt.
6
Exekverande tråd vs trådobjekt
En exekverande tråd är en enhet i run-time-systemet (löptidssystem), som man får tillgång
till via ett trådobjekt.
Trådobjektet är - innan start() har anropats - som vilket objekt som helst; start() är
ingenting vi behöver implementera, det är en del av systemets nativa (native) kod.
När någonTrådobjekt.start() anrops, ger native-metoden start() anropet vidare till någon OSrutin som skapar den exekverande tråden.
start() anropar run() som beskriver arbetet som ska utföras. Om man enbart anropar run() utförs detta i den anropande trådens kontext, dvs det blir ingen “förgrening”!
7
Concurrent
computing
Jämlöpande
exekvering
"0
Mjukvaran måste
• utföra alla beräkningar
"
logiskt korrekt
2
• reagera på inmatningar
"
jämlöpande
"0"5
data att
• alltid ha konsistent
5%$
arbeta med
"
5+%
+1&$!
""%
+5!
0"
“Software
application” &%2!
borde vara ett reaktivt system,
%3
6$
+0"""$
som svarar
både på tidsenheter som
går (varje månad ska lönen läggas
"##$%"$#
till på kontot),
samt på externa händelser (kontoinnehavaren hämtar ut pengar),
men det ska inte förbruka resurser när “ingenting” händer.
8
Kapplöpning
class HelloWorld extends Thread {
public static void main( String[] arg) {
System.out.print( “Hello “);
new HelloWorld().start();
sleep( 1);
}
System.out.println( “World!”);
public void run() {
System.out.print( “and goodbye “);
}
}
pc:~ user$ java HelloWorld
Hello World!
and goodbye
pc:~ user$
pc:~ user$ java HelloWorld
Hello and goodbye World!
pc:~ user$
pc:~ user$ java HelloWorld
Hello and goodbye World!
pc:~ user$
pc:~ user$ java HelloWorld
Hello and goodbye World!
pc:~ user$
pc:~ user$ java HelloWorld
Hello World!
and goodbye
Vem får resursen (println, dvs konsolen...?)
Det behövs alltså fler egenskaper och mekanismer, samt regler för programmering av
multi-trådade program, än “bara” möjligheten att skapa trådar och låta dem köra samtidigt.
9
Bankkontot igen
Situation 1:
Situation 2:
A: Läs 5000
A: Läs 5000
B: Läs 5000
B: Läs 5000
B: Belopp = 5000 + 10000
B: Skriv 15000
A: Belopp = 5000 - 1000
A: Belopp = 5000 - 1000
A: Skriv 4000
B: Belopp = 5000 + 10000
A: Skriv 4000
B: Skriv 15000
Två aktiviteter (program, trådar (threads), processer) utförs samtidigt, då de
hanterar samma resurser. I båda situationer blir resultatet fel.
Här behövs det alltså någon mekanism för ömsesidig uteslutning (mutual
exclusion) för att hantera kritiska sekvenser (critical sections) och odelbara
aktioner (atomic actions).
10
Kritiska sekvenser
(Critical sections)
•
Delar av ett program (en sekvens) som behöver tillgång till en delad resurs.
•
Får inte bli avbruten av en annan programsekvens, eller av en ny upprop till sig
själv.
•
Kraven kan uppfyllas med hjälp av Semaforer, Monitorer eller
“Postlådor” (Mailboxes)
•
På låg nivå (native code) kan man också slå av interrupts (avbrott).
11
Begrepp / abstraktioner
för jämlöpande exekvering
Vad är det som ett programmeringsspråk måste stödja för att kunna erbjuda jämlöpande
exekvering?
•
Tråd - en aktivitet, programsekvens
•
Exekveringsstatus - aktiv / inaktiv, använd av en tråd eller ej; kontext + aktivitetsstatus
•
Synkronisering - hantering av gemensamma resurser
12
Dagens agenda
•
•
Recap: Exekveringstillstånd, kontextbyte, gemensam resurshantering
Synkronisering / Ömsesidig uteslutning: Semaforer
•
•
•
MutexSem
Signalling
Counting semaphore
13
Kan man få till ömsesidig uteslutning
utan systemanrop?
class T extends Thread {
public void run() {
while( true) {
nonCriticalSection();
preProtocol();
criticalSection();
postProtocol();
}
}
}
class T2 extends Thread {
public void
run()
{
class
T extends
Thread {
while( true)
{ void run() {
public
nonCriticalSection();
while( true) {
preProtocol();
nonCriticalSection();
criticalSection();
preProtocol();
postProtocol();
criticalSection();
}
postProtocol();
}
}
}
}
}
Critical Section (CS)
•
•
•
•
Jämför Bankkonto-exemplet
Vi kommer titta på konstruktionen av “pre-/postProtocol”
Antagandet: En tråd kommer inte att blockeras inom dess kritiska sekvens (critical section).
Krav: Ömsesidig uteslutning, ingen dödläge, inget “svält”, hög verkningsgrad (efficiency).
14
Ömsesidig uteslutning, tråden
class T extends Thread {
public void run() {
while( true) {
nonCriticalSection();
preProtocol();
criticalSection();
postProtocol();
}
}
}
15
Ömsesidig uteslutning, kraven
Det här måste uppfyllas:
1.
Ömsesidig uteslutning (Mutual exclusion): Exekvering av en kod i en kritisk sekvens
får inte flätas ihop med någon annan tråds kodsekvens.
2.
Inget dödläge (No deadlock): Om en eller fler tråd(ar) försöker starta exekvering av
en kritisk sekvens, måste det finnas någon av dem som faktiskt kan göra det.
3.
Ingen “svält” (No starvation): En tråd måste få möjlighet att någon gång påbörja
exekveringen av dess kritiska sekvens.
4.
Effektivitet (Efficiency): Liten overhead (förvaltningskostnad) när det finns enbart en
tråd, det hela måste fungera bra även om det bara finns en tråd (det får inte finnas en
obligatorisk “väntemoment” på en annan tråd när en sådan inte kan garanteras
finnas).
Kan de här kraven uppfyllas med “vanlig” Java-kod?
16
Ömsesidig uteslutning
- version 1 int turn = 1;
class T1 extends Thread {
public void run() {
while( true) {
nonCS1();
while( turn != 1);
cS1();
turn = 2;
}
}
}
•
•
•
•
class T2 extends Thread {
public void run() {
while( true) {
nonCS2();
while( turn != 2);
cS2();
turn = 1;
}
}
}
Ömsesidig uteslutning: OK
Inget dödläge: OK, en av dem kan alltid köra.
Inget svält: OK, omväxlande protokoll.
Effektivitet / en tråd?: NEJ (en tråd kommer hänga sig efter max en omgång), plus vi har en
busy-wait (ineffektiv). För många trådar dessutom rörig.
Ej acceptabelt!
17
Ömsesidig uteslutning
- version 2 int c1, c2; c1 = c2 = 1;
class T1 extends Thread {
public void run() {
while( true) {
nonCS1();
while( c2 != 1);
c1 = 0;
cS1();
c1 = 1;
}
}
}
•
class T2 extends Thread {
public void run() {
while( true) {
nonCS2();
while( c1 != 1);
c2 = 0;
cS1();
c2 = 1;
}
}
}
Ömsesidig uteslutning: NEJ... (testa sammanflätning)
c1 = 1;
c2 = 1;
while( c2 != 1);
while( c1 != 1);
c1 = 0;
c2 = 0;
cS1();
cS2();
Ingen lösning, även om det kan fungera länge tills det kraschar (genom interrupt i nonCS1, eller nonCS2!)
18
Ömsesidig uteslutning
- version 3 int c1, c2; c1 = c2 = 1;
class T1 extends Thread {
public void run() {
while( true) {
nonCS1();
c1 = 0;
while( c2 != 1);
cS1();
c1 = 1;
}
}
}
•
•
class T2 extends Thread {
public void run() {
while( true) {
nonCS2();
c2 = 0;
while( c1 != 1);
cS1();
c2 = 1;
}
}
}
Ömsesidig uteslutning: OK
Inget dödläge: Nej (alltså ja):
c1 = 0;
c2 = 0;
while( c2 != 1);
while( c1 != 1);
...
// I all evighet ...
// ... och lite till
Ingen lösning, även om det kan fungera länge tills det kraschar (genom interrupt i nonCS1, eller nonCS2)!
19
Ömsesidig uteslutning
- version 4 int c1, c2; c1 = c2 = 1;
class T1 extends Thread {
//...
nonCS1();
c1 = 0;
while( c2 != 1){
c1 = 1; //**
c1 = 0;
}
cS1();
c1 = 1; //..
}
•
•
•
Ömsesidig uteslutning: OK
Inget dödläge: OK (//** är yield)
Inget svält: NEJ, det kan hända att
en tråd får köra, men kommer
aldrig så långt att den får verkligen
utföra CS (kallas “livelock” om det
händer fler trådar så att hela
systemet jobbar utan att göra
något).
Ej acceptabelt!
class T2 extends Thread {
//...
nonCS2();
c2 = 0;
while( c1 != 1){
c2 = 1; //**
c2 = 0;
}
cS2();
c2 = 1; //..
}
c1 = 0;
c2 = 0;
while( c1 != 1) {
c2 = 1; ...
while( c2 != 1);
cS1();
c1 = 1;
nonCS1();
c1 = 0;
... c2 = 0; }
while( c1 != 1) {
c2 = 1;
while( c2 != 1);
cS1();
c1 = 1; ...
20
Dekkers algoritm
int c1, c2, turn; c1 = c2 = turn = 1;
class DA1 extends Thread {
//...
nonCS1();
c1 = 0;
while( c2 != 1){
if( turn == 2) {
c1 = 1;
while( turn == 2);
c1 = 0;
}
}
cS1();
c1 = 1;
turn = 2;
}
•
•
•
•
class DA2 extends Thread {
//...
nonCS2();
c2 = 0;
while( c1 != 1){
if( turn == 1) {
c2 = 1;
while( turn == 1);
c2 = 0;
}
}
cS2();
c2 = 1;
turn = 1;
}
Ömsesidig uteslutning: OK
Inget dödläge: OK
Inget svält: OK (inte bara yield, men blockerar tills CS har körts en gång minst)
Effektivitet / en tråd?: Inte bra.
Dekkers algoritm fungerar bra för många trådar, men blir då komplex, den löser problemet
“ömsesidig uteslutning”, MEN med busy-wait. Kan vara användbar i vissa system med flera
processorer.
21
Ömsesidig uteslutning
- Semafor MutexSem mutex = new MutexSem();
class T1 extends Thread {
public void run() {
while( true) {
nonCS1();
mutex.take();
cS1();
mutex.give();
}
}
}
•
•
•
•
class T2 extends Thread {
public void run() {
while( true) {
nonCS2();
mutex.take();
cS2();
mutex.give();
}
}
}
Ömsesidig uteslutning: OK
Inget dödläge: OK
Inget svält: OK (give() startar upp den blockerade tråden direkt)
Effektivitet / en tråd: OK. Blockerade trådar sövs (sleep), dvs de använder inte CPUn.
Acceptabelt!
22
Semafor - basfakta
Semaforer utgör en minimal mekanism för ömsesidig uteslutning
En semafor är egentligen “bara” en integer-variabel med två metoder, take() och give():
class SemaphorePrinciple {
int count;
public void take() {
while( count < 1)
“suspend executing thread”
--count;
}
}
// here, currentThread() is used!
public void give() {
if( “any thread suspended”)
“resume the first one in queue”;
count++;
}
OBS 1: take och give är odelbara (atomic) operationer, som kräver systemsupport när de
ska implementeras (t ex deaktivera hårdvaruinterrupts).
OBS 2: take blockerar den anropande tråden sålänge count == 0. Detta kan inte
implementeras i vanlig Java-kod.
23
Typer av semaforer
MutexSem - MutualExclusion semaphore: Den som tar flaggan får jobba med
gemensamma resurser, den som vill ha den samtidigt, får vänta (blockeras).
CountingSem - Counting semaphore, signalling: Den som gör plats / en resurs tillgänglig,
signalerar genom att lägga en flagga på högen, den som vill utnyttja en “resursplats” tar en
flagga från högen. Finns inga “fria flaggor”, måste den som vill ha en vänta.
24
Semaforer vs omvärlden
Mutual exclusion (MutexSem): Tåg ska köra mellan Malmö och Lund när det “för tillfället råder enkelspårsdrift”.
Signaling (CountingSem): Check-In på Kastrup: Man väntar i en lång rad och blir tilldelad en fri disk så fort det finns en sådan.
Rendezvous: Last från ett godståg ska lastas om / kopplas om på ett annat tåg utan att något ska lagras . Båda tågen måste alltså stå i omlastningsområdet samtidigt.
25
Signalera med en sorts signal
Patienten på rummet behöver hjälp och trycker på signalknappen - lampan utanför rummet
tänds och larmet i expeditionen går. (signal.give())
Sjuksköterskan kommer in i rummet och slår av signalen - lampan och larmet släcks och
alla utanför vet att problemet åtgärdas - ingen annan behöver eller ska komma och hjälpa. (signal.take())
Detta kan hända på flera rum samtidigt, eller med korta mellanrum. Sålänge det finns
sjuksköterskor kan de ta över rum efter rum, sen måste kanske en patient vänta lite
längre. Om ingen larmar, måste sjuksköterskorna vänta på nästa problem att lösa.
(signal.give() + signal.give() + signal.take() + signal.give() + signal.take() + signal.take())
26
Signalera med två olika signaler
Chefen och Sekreteraren har ett postfack för att överlämna dokument till varandra,
egentligen är det dock bara Sekreteraren som lägger dit dokument, och chefen som hämtar
ut dem därifrån. I facket får det enbart ligga ett dokument åt gången.
Sekreteraren ser att facket är tomt genom att en “tom”-lampa lyser på hans kontor. Han
går till facket, lägger dit ett dokument som chefen måste signera, och trycker på en knapp
som släcker “tom”-lampan och sedan slår på en “det finns ett dokument”-lampa på chefens
kontor.
(free.take() + avail.give())
Chefen ser lampan, kommer till facket, tar dokumentet, släcker “det finns ett dokument”lampan och slår på “tom”-lampan hos sekreteraren.
(avail.take() + free.give())
27
Semaforer - användning i kod
Mutual exclusion (ömsesidig uteslutning)
Signaling
(signalering)
Rendezvous
Tråd A:
Tråd B:
...
mutex.take();
***
mutex.give();
...
...
mutex.take();
***
mutex.give();
...
...
buffer.give();
...
...
buffer.take();
...
***
entry.give();
exit.take();
***
...
entry.take();
***
exit.give();
...
28
Semaforer i Java (Mutex)
Deklarera:
import se.lth.cs.realtime.semaphore.*;
Semaphore mutex1; // inte tydligt om man vill ha en mutex
MutexSem mutex2;
// bättre.
Skapa / initialisera:
/*inte så bra:*/
mutex1 = new CountingSem();
mutex1 = new CountingSem(1);
// tilldelar värdet 0 till count
// tilldelar värdet 1 till count
/*bättre:*/
mutex2 = new MutexSem();
// tilldelar 1 (eller true) till intern status
Tillämpning:
mutex2.take();
amount += change;
mutex2.give();
Förutom att det är tydligare, får man bättre felidentifiering och bättre timing om man
skapar ömsesidig uteslutning med en MutexSem istf en CountingSem
29
Mutex i trådar
import se.lth.cs.realtime.semaphore.*;
class ThreadTest {
public static void main(String[] args) {
class RogersThread extends Thread
{
String theName;
Semaphore theSem;
Thread t1,t2;
Semaphore s;
s = new MutexSem();
public RogersThread( String n, Semaphore s){
theName = n;
theSem = s;
}
t1 = new RogersThread("Thread one",s);
t2 = new RogersThread("Thread two",s);
t1.start();
t2.start();
public void run() {
theSem.take();
for(int t=1;t<=100;t++) {
System.out.println(theName + ":" + t);
sleep(1);
}
theSem.give();
}
}
}
}
30
Signal i trådar
import se.lth.cs.realtime.semaphore.*;
class ThreadTest {
public static void main(String[] args) {
class RogersThread extends Thread
{
String theName;
CountingSem mySem, hisSem;
Thread t1,t2;
CountingSem s1, s2;
s1 = new CountingSem(1);
s2 = new CountingSem(0);
public RogersThread( String n,
CountingSem s1, CountingSem s2){
theName = n;
mySem = s1;
hisSem = s2;
t1 = new RogersThread("One",s1, s2);
t1.start();
t2 = new RogersThread("Two",s2, s1);
t2.start();
}
public void run() {
for(int t=1;t<=100;t++) {
mySem.take();
System.out.println(theName + ":" + t);
hisSem.give();
sleep(1);
}
}
}
}
}
31
Objekt och blockering
Trådobjekt (som alltså kan referera till en exekverande tråd) kallas för aktiva objekt.
Andra objekt (beskriven genom ”vanliga” Java-klasser), som blir kallade eller drivna genom
aktiva objekt (trådar) kallas för passiva objekt. T ex en semafor ;-)
Semaforer erbjuder blockerande operationer; hur blir de representerade?
: Exekverande (executing)
: Blockerad (blocked)
Tråd
(aktivt objekt)
Semafor
(passivt objekt)
: möjligen blockerande (potential blocking)
32
Sekvens med mutex / blockerade trådar
T1
T2
mutex
take
take
give
give
Anrop till take - give i följd måste komma från samma tråd. Stöds av MutexSem klassen.
33
Signalering
T1
signal
give
T2
take
give
give
take
take
take
En tråd kallar take, en annan give. Stöds av CountingSem klassen.
34
Rendezvous
T1
entry
give
T2
exit
take
take
sync
sync
give
Säker dataöverföring / manipulation med T1 från T2 under sync-tiden. T1 har exklusiv tillgång till
datan genom att hålla i semaforen förutom mellan sitt give och T2s give
35
Övning 1 + Övning 2
ÖVNINGAR ÄR INGA FÖRELÄSNINGAR - TA MED MATERIALET!
Övning 1 handlar om semaforer och deras tillämpning
Teoretiska uppgifter som ska bearbetas under övningen (var gärna förberedd!)
Laboration 1 introduceras lite kort (mer under övning 2)
Labbgrupper (två studenter vanligtvis) ska bildas
(Praktiska uppgifter som ska lösas vid datorn med hjälp av Eclipse-workspace)
Övning 2 är till förberedelse för Laboration 1 (väckarklocka)
Uppgiften kommer att förklaras lite mera djupgående
(Labbgrupper ska bildas, om så inte skett innan)
Övningstillfället ska utnyttjas till att skapa ett design-förslag till laborationen, som ska granskas både i SPA-uppsättning och av läraren
36
Dagens resultat
•
•
•
•
Trådar, kapplöpningsproblem, hantering av gemensamma resurser
•
Man ska kunna lösa uppgifterna till övning 1 och påbörja arbetet med övning
2 / lab1
Semaforer
Typer av semaforer
Användning av semaforer
• Lästips:
• e-bok: delar av Kap 5 (s 103-129)
• kompendium: Kap 2-1 (Threads) samt 2-2 (Semaphores)
37