Download de presentatie
De presentatie wordt gedownload. Even geduld aub
0
Inhoud RTSYST Week 1 Week 2 Week 3 Week 4 Week 5 Week 6 Week 7
1
Real-Time Systems (RTSYST)
Week 1
2
Real-Time Systems (RTSYST)
Onderwerpen: Concurrent programming (threads). Real-Time OS (VxWorks, QNX, FreeRTOS). Concurrent programming in C en C++. Synchronisation and Communication. Real-Time faciliteiten (clocks, timeouts). Scheduling. Werkvormen: 14 lessen theorie + 7 lessen begeleid practicum. 5 uur/week zelfstudie (inclusief onbegeleid practicum).
3
Leermiddelen Boeken Blackboard en http://bd.eduweb.hhs.nl/rtsyst/
Real-Time Systems and Programming Languages (Fourth Edition), Alan Burns and Andy Wellings, ISBN: Hoofdstuk 1, 4 t/m 6, 9 en 11. QNX Neutino 2, Robert Krten (Staat in D1.052) Blackboard en Studiewijzer met uitgebreide planning Practicumopdrachten + uitgebreide practicumhandleiding Sourcecode van alle voorbeelden Sheets Links
4
Real-Time Systeem Definitie(s):
Systeem waarvan de reactietijd op een onvoorspelbare inputverandering voorspelbaar is. Systeem waarbij de uitvoer niet alleen correct moet zijn maar ook op het juiste moment.
5
Indeling Real-Time Systemen
Hard real-time Missen van een deadline is fataal. Soft real-time Missen van een deadline is ongewenst. Interactief (niet real-time) Er zijn geen expliciete deadlines maar wachten is wel irritant.
6
Voorbeelden Real-Time Systeem
Procesbesturing (meet en regeltechniek) Productie besturingssysteem (industriële automatisering) Embedded systemen ABS (Anti-Blokeer-Systeem) Pacemaker Besturing kruisraket Kopieer apparaat DVD recorder
7
Karakteristieken Real-Time Systeem
Groot en complex (niet altijd) Onderhoudbaar: uitbreidbaar, aanpasbaar en herbruikbaar Betrouwbaar en veilig Intensive care apparatuur Kerncentrale Automatische piloot Concurrent gedrag Multitasking, multiprocessor, distributed RTOS of RTL moet dit ondersteunen Timing faciliteiten Taak op bepaalde tijd starten, taak binnen bepaalde tijd afronden Interactie met hardware
8
Concurrent programming (1)
Single processor system Multitasking m.b.v. time sharing Multi processor system met gedeeld geheugen (SMP) of multi-core processor systeem Parallel (true multitasking)
9
Concurrent programming (2)
Distributed system (wordt verder niet behandeld in RTSYST) Parallel Verschillende systemen (elk met een eigen geheugen) verbonden met een netwerk
10
Why Concurrent programming
Programma model komt overeen met de werkelijkheid Benutten van parallellisme in applicatie Zoek je weg in een doolhof Vergelijken van vingerafdrukken Processor beter benutten op single processor systeem
11
Sequential Maze Search
12
Concurrent Maze Search
13
Concurrent programming
Processor beter benutten op single processor systeem X W C sneller X W C time c=calculate x=transmit w=wait for ACK
14
Beperking van parallellisme
Amdahl's Law (boek p. 96) De versnelling (speedup) van een program door het gebruik van meerdere parallellle processoren (of cores) is begrensd door het deel van het programma dat sequentieel uitgevoerd moet worden. N = aantal processoren SN = speedup met N processors (cores) P = deel van het programma dat parallel uitgevoerd kan worden Voorbeeld: Bereken de maximale speedup voor een programma waarvan 25% sequentieel uitgevoerd moet worden? Wat is de maximale speedup bij 2, 4, 8 en 16 cores? (4) (1.60, 2.29, 2.91 en 3.37)
15
Process versus Thread Process Thread Eigen stack = veilig
Eigen (data) memory map Eigen virtueel geheugen = veilig, communicatie = traag Process switch is traag (zwaar) Cache flush, MMU TLB flush Thread Gedeelde (data) memory map binnen hetzelfde process Gedeeld geheugen = onveilig Communicatie = snel Thread switch is snel (licht) binnen hetzelfde process Geen flushes
16
Process versus Thread Veel GPOSs (General Purpose Operating Systems) gebruiken: Processes om verschillende applicaties van elkaar te scheiden Threads om concurrency binnen applicatie mogelijk te maken Voorbeelden: Windows en Linux Veel RTOSs (Real-Time Operating Systems) gebruiken: Threads / Tasks om concurrency mogelijk te maken Voorbeeld: FreeRTOS QNX gebruikt processes en threads Wij behandelen alleen threads
17
QNX (POSIX compatible RTOS)
18
Huiswerk Bestudeer: Boek H1 t/m 1.3.
Bestudeer: Artikel uit Embedded Computer Design: RTOS versus GPOS Achtergrondinformatie: Artikel The Free Lunch Is Over Artikel TLAs: QNX, RIM, ARM, CES, RPM, and MPH. An RTOS for the Automotively Inclined Web seminar: Why do I need an RTOS anyway?
19
Concurrent programming
Sequentiële programmeertaal (C of C++) + OS (Linux, Windows, QNX, FreeRTOS) Portable als taal en OS portable zijn(IEEE POSIX 1003 compliant) Meerdere talen combineren in 1 applicatie is mogelijk Concurrent programmeertaal (ADA, Java, C#, C++11, C11) Beter leesbaar Beter onderhoudbaar Portable als taal portable is Concurrent framework (OpenMP, OpenCL) Portable (ondersteund meerdere talen en OS-en) Middleware (RPC, RMI, CORBA) Vereenvoudigt bouwen van distributed applicaties
20
Fundamentele vragen Hoe kun je processen / threads beheren?
Support in OS via API (= Application Programming Interface) of library Support in programmeertaal Hoe kunnen processen / threads communiceren? IPC = Inter Process Communication (term wordt ook voor communicatie tussen threads gebruikt) Hoe kun je processen / threads synchroniseren? IPC zonder dataoverdracht.
21
Concurrent OOP Actieve objecten Passieve objecten
Object heeft eigen thread of process. Versturen zelf actief (spontaan) messages. Passieve objecten Object heeft geen eigen thread of process. Reageren op binnenkomende messages en kunnen als reactie: Zelf message versturen. Toestand van aanroepende thread of process veranderen (b.v. van running naar waiting).
22
Specificatie van concurrent taken
System call UNIX, win32 Concurrent blok Concurrent Pascal, High Performance Fortran Expliciete declaratie ADA, Java pthread_t t; pthread_create(&t, NULL, &func, NULL) cobegin s1; s2; s3 coend; task body NAME is begin … end NAME;
23
Concurrent execution IEEE POSIX 1003.1-2013 fork() en wait()
posix_spawn() Combinatie van fork(), exec() en wait() pthread_create() en pthread_join() Documentatie: IEEE Std = The Open Group Base Specifications Issue 7 QNX documentation:
24
Pointers naar functies
In C kun je een pointer naar een functie definiëren. De waarde van de pointer is het beginadres (van de code) van de functie. pnf is een pointer naar een functie met een int als parameter en een int returnwaarde #include <stdio.h> int kwadraat(int c) { return c * c; } int dubbel(int c) { return c + c; int main(void) { int a = 7, b; int (*pnf)(int); pnf = &dubbel; b = (*pnf)(a); Waarom haakjes?
25
Pointers naar functies
In C kun je een pointer naar een functie definiëren. De waarde van de pointer is het beginadres (van de code) van de functie. pnf wijst naar de functie dubbel (pnf wordt gelijk aan het adres van de functie dubbel) #include <stdio.h> int kwadraat(int c) { return c * c; } int dubbel(int c) { return c + c; int main(void) { int a = 7, b; int (*pnf)(int); pnf = &dubbel; b = (*pnf)(a);
26
Pointers naar functies
In C kun je een pointer naar een functie definiëren. De waarde van de pointer is het beginadres (van de code) van de functie. De functie waar pnf naar wijst wordt aangeroepen met de waarde van a als argument #include <stdio.h> int kwadraat(int c) { return c * c; } int dubbel(int c) { return c + c; int main(void) { int a = 7, b; int (*pnf)(int); pnf = &dubbel; b = (*pnf)(a); Waarom haakjes?
27
Pointers naar functies
Verkorte schrijfwijze. Naam van een functie beginadres (van de code) van de functie. #include <stdio.h> int kwadraat(int c) { return c * c; } int dubbel(int c) { return c + c; int main(void) { int a = 7, b; int (*pnf)(int); pnf = dubbel; b = pnf(a);
28
Pointers naar functies
Wat is het nut? Functie als parameter. #include <stdio.h> /* … */ void printTabel(int (*p)(int), int van, int tot, int stap) { int x; for (x = van; x < tot; x += stap) { printf("%10d %10d\n", x, (*p)(x)); } int main(void) { printf("De kwadraten van 1 t/m 10\n"); printTabel(&kwadraat, 1, 11, 1); printf("De dubbelen van de drievouden van 0 t/m 30\n"); printTabel(&dubbel, 0, 31, 3);
29
Uitvoer De kwadraten van 1 t/m 10 1 1 2 4 3 9 4 16 5 25 6 36 7 49 8 64
De dubbelen van de drievouden van 0 t/m 30
30
void* Een void* kan wijzen naar elk type.
Als we de waarde willen ophalen waar een void* naar wijst dan moeten we de pointer casten naar het juiste type. int main(void) { int i = 3; double d = 4.3; void* vp = &i; printf("%d\n", *(int*)vp); vp = &d; printf("%lf\n", *(double*)vp); return EXIT_SUCCESS; }
31
pthread (1 van 3) #include <stdio.h> #include <stdlib.h>
#include <string.h> #include <time.h> #include <pthread.h> void check(int error) { if (error != 0) { fprintf(stderr, "Error: %s\n", strerror(error)); exit(EXIT_FAILURE); }
32
pthread (2 van 3) void* print1(void* par) {
struct timespec ts = {0, }; int i; for (i = 0; i < 10; i++) { nanosleep(&ts, NULL); printf("print1\n"); } return NULL; void* print2(void* par) { struct timespec ts = {0, }; printf("print2\n");
33
pthread (1 van 3) int main(void) { pthread_t t1, t2;
print1 print2 int main(void) { pthread_t t1, t2; check( pthread_create(&t1, NULL, &print1, NULL) ); check( pthread_create(&t2, NULL, &print2, NULL) ); check( pthread_join(t1, NULL) ); check( pthread_join(t2, NULL) ); return EXIT_SUCCESS; }
34
pthread Alternatieve implementatie
typedef struct { char* msg; long ns; } par_t; void* print(void* p) { par_t* pp = p; struct timespec ts = {0, pp->ns}; int i; for (i = 0; i < 10; i++) { nanosleep(&ts, NULL); printf(pp->msg); } return NULL; int main(void) { pthread_t t1, t2; par_t p1 = {"print1\n", }; par_t p2 = {"print2\n", }; check( pthread_create(&t1, NULL, &print, &p1) ); check( pthread_create(&t2, NULL, &print, &p2) ); // ... Pas op: void*
35
Command line argumenten
Aan een programma kunnen command line argumenten worden doorgegeven. Deze zijn in main beschikbaar via de parameters argc en argv int main(int argc, char* argv[]) { … } argv[0] is de naam van het programma. argc geeft het aantal meegegeven parameters + 1 argv[1] is de eerste meegegeven parameter. Enz. int main(int argc, char* argv[]) { int i; for (i = 0; i < argc; i++) { printf("%s\n", argv[i]); } return EXIT_SUCCESS; $ a.exe dat is leuk a.exe dat is leuk
36
Real-Time Systems (RTSYST)
Week 2
37
Process/Thread states
ready running Wait for I/O or … blocked / sleeping I/O or … completion Scheduler = deel van OS dat de toestanden van processen/threads bepaald. OS gebruikt timerinterrupt om Scheduler regelmatig aan te roepen. In een single processor N-core machine kunnen maximaal N processen/threads running zijn. RTOS gebruikt preemptive priority based scheduling.
38
Problem with shared memory
volatile int aantal = 0; void* teller(void* par) { int i; for (i = 0; i < ; i++) { aantal++; } return NULL; int main(void) { pthread_t t1, t2, t3; check( pthread_create(&t1, NULL, &teller, NULL) ); check( pthread_create(&t2, NULL, &teller, NULL) ); check( pthread_create(&t3, NULL, &teller, NULL) ); check( pthread_join(t1, NULL) ); check( pthread_join(t2, NULL) ); check( pthread_join(t3, NULL) ); printf("aantal = %d\n", aantal); # ./a.out aantal = aantal = aantal = # time ./a.out aantal = 0.10s real 0.08s user 0.01s system Wat is de uitvoer?
39
Problem with shared memory
De operatie aantal++ is niet ondeelbaar (in machinecode). Bijvoorbeeld: load reg, aantal inc reg store reg, aantal Wat is de minimale en de maximale waarde die geprint kan worden? en Wat gebeurt er als op dit moment van taak gewisseld wordt?
40
Oplossing? Er zijn oplossingen die gebruik maken van variabelen (2 vlaggen en 1 beurt variabele) en busy waiting. Dekker’s algoritme: Peterson’s algorithm (zie boek paragraaf 5.2) Busy waiting kost klokcycles! OSen bieden oplossingen zonder busy waiting.
41
IPC inter process communication
Shared variabele based (H8) Busy waiting Inefficiënt Mutual exclusion is moeilijk (Dekker of Petterson algoritme) Spinlock (niet in H8 wel IEEE Std ) Mutex Semaphore Monitor Mutex en Conditionele variabelen Barrier (niet in H8 wel IEEE Std ) ReadWriteLock (niet in H8 wel IEEE Std ) Message based (H9)
42
Mutex Simpele manier om mutual exclusive kritisch gebied te creëren.
Er kan zich maar 1 process/thread in het kritische gebied bevinden. Mutex heeft een lock en een unlock functie. OS zorgt dat deze functies ondeelbaar zijn! Aan het begin van het kritische gebied lock je de mutex en aan het einde van het kritische gebied unlock je de mutex.
43
Process/Thread states
Lock mutex m which is not locked ready running Lock mutex m which is already locked Unlock mutex m Blocked on m
44
Bij RTOS afhankelijk van prioriteit!
Mutex Als een thread t een mutex m probeert te locken die al locked is dan wordt de thread t blocked on m. We zeggen ook wel: Thread t wacht op mutex m. Thread t slaapt tot mutex m unlocked wordt. Volgorde van wakker maken: concurrent: random general purpose: FIFO real-time: hoogste prioriteit Bij RTOS afhankelijk van prioriteit!
45
Mutex with shared memory
Geen volatile meer nodig! int aantal = 0; pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; void* teller(void* par) { int i; for (i = 0; i < ; i++) { check( pthread_mutex_lock(&m) ); aantal++; check( pthread_mutex_unlock(&m) ); } return NULL; int main(void) { // idem. # ./a.out aantal = # time ./a.out 2.03s real 1.96s user 0.02s system 20x trager!
46
Deadlock (voorbeeld) Er zijn 5 filosofen.
Het leven van een filosoof bestaat uit: Denken Eten Elke filosoof heeft één bord en één vork. Om te kunnen eten heeft de filosoof 2 vorken nodig.
47
Dining Philosophers pthread_mutex_t vork[5] = { PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER }; void* philosopher(void* par) { int i = *(int*)par; while (1) { printf("philosopher %d is sleeping\n", i); sleep(1); check( pthread_mutex_lock(&vork[i]) ); check( pthread_mutex_lock(&vork[(i + 1) % 5]) ); printf("philosopher %d is eating\n", i); check( pthread_mutex_unlock(&vork[i]) ); check( pthread_mutex_unlock(&vork[(i + 1) % 5]) ); } return NULL;
48
Dining Philosophers Dit programma kan vastlopen (deadlock)!
int main(void) { int i; pthread_t t[5]; for (i = 0; i < 5; i++) { check( pthread_create(&t[i], NULL, &philosopher, &i) ); } check( pthread_join(t[i], NULL) ); return EXIT_SUCCESS; Dit programma kan vastlopen (deadlock)! Oplossing? Zie huiswerk verderop (uitwerking in theorieplanning).
49
Bij RTOS afhankelijk van prioriteit!
Semaphore Bewerkingen: Psem (prolaag (probeer te verlagen), wait): ga wachten (slapen) als count == 0 anders verlaag count met 1. Vsem (verhoog, signal, post): maak een wachtend proces (of thread) wakker als count == 0 anders verhoog count met 1. Volgorde van vrijgeven (wakker maken): concurrent: random general purpose: FIFO real-time: hoogste prioriteit Voorbeeld: Zie practicum en BlackBoard. Gebruik is erg foutgevoelig. Abstractere (higher-level) oplossing nodig. Bij RTOS afhankelijk van prioriteit! Edsger Dijkstra
50
Uitwerking staat op BlackBoard.
Huiswerk Los het probleem van de dinerende filosofen op door er m.b.v. een semaphore voor te zorgen dat er niet meer dan 4 filosofen tegelijkertijd aan tafel kunnen. Uitwerking staat op BlackBoard.
51
Semaphore versus Mutex
Semaphore kan ook bij processes worden gebruikt. Mutex alleen bij theads. Mutex alleen voor mutual exclusion (thread die lock uitvoert moet ook unlock uitvoeren). Semaphore kan ook voor andere synchronisatie doeleinden worden gebruikt. Huiswerk: Thread a bestaat uit twee sequentiële delen a1 en a2. Thread b bestaat uit twee sequentiële delen b1 en b2. Thread c bestaat uit twee sequentiële delen c1 en c2. Zorg er voor (met behulp van een semaphoor) dat de delen b2 en c2 altijd na deel a1 worden uitgevoerd.
52
Bij RTOS afhankelijk van prioriteit!
Monitor Een monitor is een poging om de problemen van semaphoren (zie boek 5.4.8) op te lossen. Een monitor is een taalconstructie en moet dus door de programmeertaal worden ondersteund. Een monitor is een module (verzameling functies + data). De data in de module is alleen toegankelijk via de functies van de module. De monitor zorgt automatisch voor mutual exclusion. Er kan maar 1 proces tegelijk de monitor binnengaan. Synchroniseren kan met behulp van conditionele variabelen. Bij RTOS afhankelijk van prioriteit! Tony Hoare
53
Conditionele variabelen
Een cv bevindt zich in een monitor. Bewerkingen: wait = verlaat de monitor en wacht (slaap) tot conditie gesignaleerd wordt. signal (notify) = maak een proces wakker dat op deze conditie wacht. Mutual exclusion blijft gegarandeerd: Een proces dat wakker wordt moet wachten tot de monitor vrij is. Bij RTOS afhankelijk van prioriteit! Bij RTOS afhankelijk van prioriteit!
54
Monitor IEEE Std POSIX POSIX definieert geen monitors (monitor is een taalconstructie). Maar wel: mutex conditionele variabele Hiermee kun je zelf een (soort van) monitor maken.
55
Mutex voor mutual exclusion
Bewerkingen: pthread_mutex_init pthread_mutex_destroy pthread_mutex_lock Bezet de mutex. Wacht (ga slapen) als de mutex al bezet is. pthread_mutex_trylock Return met EBUSY als mutex al bezet is. pthread_mutex_unlock Geef de mutex vrij (maak een proces dat op de mutex staat te wachten wakker). Bij RTOS afhankelijk van prioriteit!
56
Conditionele variabele
Een POSIX conditionele variabele is altijd gekoppeld met een POSIX mutex. Bewerkingen: pthread_cond_init pthread_cond_destroy pthread_cond_wait Gekoppelde mutex moet bezet zijn. Wacht (ga slapen) tot conditie gesignaleerd wordt en geef gekoppelde mutex vrij. pthread_cond_signal Maakt (minstens) 1 van de threads die op deze conditie wachten wakker. Een proces dat wakker wordt wacht op de mutex voordat pthread_cond_wait verlaten wordt. pthread_cond_broadcast Maak alle threads wakker die op deze conditie wachten. Bij RTOS afhankelijk van prioriteit!
57
Monitor voorbeeld #include <pthread.h> int ei_teller = 0;
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t c = PTHREAD_COND_INITIALIZER; // consumer thread(s) // ... pthread_mutex_lock(&m); while (ei_teller < 12) pthread_cond_wait(&c, &m); ei_teller -= 12; pthread_mutex_unlock(&m);
58
Monitor voorbeeld // producer thread(s) // ... pthread_mutex_lock(&m); ei_teller += n; pthread_cond_broadcast(&c); pthread_mutex_unlock(&m); Waarom moeten we pthread_cond_broadcast gebruiken in plaats van pthread_cond_signal? Omdat er meerdere consumers kunnen zijn en die moeten allemaal wakker gemaakt worden!
59
Uitwerking staat op BlackBoard.
Huiswerk Los het probleem van de dinerende filosofen op door er m.b.v. een monitor (mutex i.c.m. conditionele variabele) voor te zorgen dat er niet meer dan 4 filosofen tegelijkertijd aan tafel kunnen. Uitwerking staat op BlackBoard.
60
Real-Time Systems (RTSYST)
Week 3
61
C++ concurrent programmeren
C++ heeft sinds C++11 een standaard library voor concurrent programmeren. Alternatieve libraries: Boost Thread library Intel Threading Building Blocks (TBB) Microsoft Parallel Pattern Library (PPL) Open Multi-Processing (OpenMP) Er zijn ook uitbreidingen van C++ die concurrency aan de taal toevoegen (met taalconstructies). μC++ voegt o.a. taalconstructies task en monitor toe. Beschikbaar in MS VC++ ≥2012 en in GCC ≥4.7 Real-Time?
62
C++11 concurrency Threads Synchronisatie Asynchrone taken en Futures
Mutexen Locks Conditionele variabelen Call once Asynchrone taken en Futures Atomics
63
C++11 Thread voorbeeld 1 (1)
#include <thread> #include <iostream> using namespace std; void print1() { for (int i = 0; i < 10; ++i) { this_thread::sleep_for(chrono::milliseconds(10)); cout << "print1" << endl; } void print2() { this_thread::sleep_for(chrono::milliseconds(20)); cout << "print2" << endl;
64
C++11 Thread voorbeeld 1 (2)
int main() { thread t1(&print1); thread t2(&print2); t1.join(); t2.join(); return 0; }
65
Wel type-safe (in tegenstelling tot pthreads).
C++11 Thread voorbeeld 2 print1 print2 #include <thread> // ... void print(int delay, const string& msg) { for (int i = 0; i < 10; ++i) { this_thread::sleep_for( chrono::milliseconds(delay)); cout << msg << endl; } int main() { thread t1(&print, 10, "print1"); thread t2(&print, 20, "print2"); t1.join(); t2.join(); return 0; Wel type-safe (in tegenstelling tot pthreads).
66
C++11 Thread thread::hardware_concurrency() t.native_handle()
Geeft het aantal hardware threads wat op het huidige systeem beschikbaar is (= aantal CPU’s of cores of hyperthreading units), of 0 als deze informatie niet beschikbaar is. t.native_handle() Geeft een instantie van native_handle_type wat gebruikt kan worden met de platform-specific API om de onderliggende implementatie te programmeren. Kan in een real-time pthread omgeving gebruikt worden om de prioriteit in te stellen. C++11 std: The presence of native_handle() and its semantics is implementation-defined. Actual use of this member is inherently non-portable.
67
C++11 Synchronization Mutexen Locks Conditionele variabelen mutex
void lock(); bool try_lock(); void unlock(); recursive_mutex Idem maar telt aantal locks (en aantal unlocks). Locks Conditionele variabelen
68
C++11 Mutex voorbeeld y x==y x
class Point { public: Point(): x(0), y(0) { } void stepNorthEast() { ++x; ++y; bool isNorthEast() const { return x == y; private: int x, y; }; y x==y x Wat gebeurt er als deze class in een multi-threaded omgeving gebruikt wordt?
69
C++11 Mutex voorbeeld class TestPoint { private: Point p;
void thread1() { for (int i = 0; i < ; ++i) p.stepNorthEast(); } void thread2() { if (!p.isNorthEast()) cout << "Probleem!" << endl; public: void test() { thread t1(&TestPoint::thread1, this); thread t2(&TestPoint::thread2, this); t1.join(); t2.join(); cout << "Einde." << endl; }; Probleem! … Einde.
70
Kan aangepast worden in const memberfunctie
C++11 Mutex voorbeeld class Point { public: Point(): x(0), y(0) { } void stepNorthEast() { m.lock(); ++x; ++y; m.unlock(); } bool isNorthEast() const { bool res = x == y; return res; private: mutable mutex m; int x, y; }; Einde. Run-time Zonder mutex 0.265 s Met mutex s Kan aangepast worden in const memberfunctie
71
Je wilt niet onnodig wachten!
C++11 Mutex voorbeeld class Point { public: Point(): x(0), y(0) { } void stepNorthEast() { m.lock(); ++x; ++y; m.unlock(); } bool isNorthEast() const { m.lock(); bool res = x == y; m.unlock(); return res; bool isWithinLimits() const { m.lock(); bool res = x >= 0 && x <= && y >= 0 && y <= ; private: mutable mutex m; int x, y; }; Je wilt niet onnodig wachten!
72
isNorthEast() en isWithinLimits() kunnen nu parallel draaien!
boost::shared_mutex class Point { public: Point(): x(0), y(0) { } void stepNorthEast() { m.lock(); ++x; ++y; m.unlock(); } bool isNorthEast() const { m.lock_shared(); bool res = x == y; m.unlock_shared(); return res; bool isWithinLimits() const { m.lock_shared(); bool res = x >= 0 && x <= && && y >= 0 && y <= ; private: mutable boost::shared_mutex m; int x, y; }; isNorthEast() en isWithinLimits() kunnen nu parallel draaien!
73
C++11 Mutex C++11 kent geen shared_mutex
Je kunt zelf met behulp van conditionele variabelen en mutexen een multiple readers / single-writer mutex maken. Huiswerk: Implementeer zelf een shared_mutex class met de in C++11 beschikbare mutex en condition_variable.
74
Zie C++ exceptions (OGOPRG dictaat par.6.8).
C++11 Synchronization Een lock maakt het gebruik van een mutex eenvoudiger en exception safe. Mutexen Locks lock_guard Gebruikt RAII (Resource Acquisition Is Initialization): lock() mutex in constructor, unlock() mutex in destructor unique_lock Idem als lock_guard + lock(), try_lock() en unlock() memberfuncties Conditionele variabelen Zie C++ exceptions (OGOPRG dictaat par.6.8).
75
C++11 Lock voorbeeld class Point { public: Point(): x(0), y(0) { }
void stepNorthEast() { lock_guard<mutex> lock(m); ++x; ++y; bool isNorthEast() const { return x == y; private: mutable mutex m; int x, y; };
76
Wat gaat er mis als at een exception gooit?
C++11 Lock voorbeeld vector<int> v; mutex m; // ... m.lock(); v.at(10) = 27; m.unlock(); m.lock(); try { v.at(10) = 27; m.unlock(); catch(...) { throw; } Niet handig! Wat gaat er mis als at een exception gooit? Oplossing ? Oplossing ! { lock_guard<mutex> l(m); v.at(10) = 27; }
77
C++11 Synchronization Mutexen Locks Conditionele variabelen
conditional_variable void notify_one(); void notify_all(); void wait(unique_lock<mutex>& lock); void wait(unique_lock<mutex>& lock, pred_type pred); conditional_variable_any Idem maar werkt met elk type lock
78
C++11 Monitor voorbeeld #include <thread> #include <mutex>
#include <condition_variable> int ei_teller = 0; mutex m; condition_variable c; // consumer thread(s) // ... unique_lock<mutex> lock(m); while (ei_teller < 12) c.wait(lock); ei_teller -= 12;
79
Waarom moeten we notify_all gebruiken in plaats van notify_one?
C++ Monitor voorbeeld // producer thread(s) // ... lock_guard<mutex> lock(m); ei_teller += n; c.notify_all(); Waarom moeten we notify_all gebruiken in plaats van notify_one? Omdat er meerdere consumers kunnen zijn en die moeten allemaal wakker gemaakt worden!
80
Monitor voorbeeld while (ei_teller < 12) c.wait(lock);
// alternatief met predicate functie: bool predicate() { return ei_teller >= 12; } // ... c.wait(lock, &predicate);
81
Lambda functie C++11 Een lambda functie is een anonieme functie die eenmalig gebruikt wordt als een functie object (functor). De lambda functie wordt gedefinieerd op de plaats waar het functie object nodig is. Een lambda functie kan dus als predicate gebruikt worden.
82
geen hergebruik mogelijk
Monitor voorbeeld while (ei_teller < 12) c.wait(lock); // alternatief met predicate functie: bool predicate() { return ei_teller >= 12; } // ... c.wait(lock, &predicate); // alternatief met lambda functie c.wait(lock, []() { }); Voordeel? “eenvoudige” syntax Nadeel? geen hergebruik mogelijk
83
Boost Synchronization
Mutexen Locks Conditionele variabelen Barrier boost::barrier barrier(unsigned count); bool wait(); Een barrier is een ontmoetingspunt (rendezvous). Pas nadat count threads een wait hebben gedaan mogen ze samen verder.
84
C++11 Synchronization Mutexen Locks Conditionele variabelen
C++11 kent geen barrier Huiswerk: Implementeer zelf een barrier class met de in C++11 beschikbare mutex en condition_variable. barrier(unsigned count); bool wait(); Een barrier is een ontmoetingspunt (rendezvous). Pas nadat count threads een wait hebben gedaan mogen ze samen verder.
85
C++11 concurrency Memory model Threads Synchronisatie
Mutexen Locks Conditionele variabelen Call once Asynchrone taken en Futures Atomics
86
async en future Bedoeld om eenvoudig gebruik van concurrency mogelijk te maken. Met behulp van async kunnen we een functie f als een asynchrone taak starten. We krijgen dan een future object terug (van hetzelfde type als het returntype van de functie f). We kunnen de memberfunctie get() van dit future object gebruiken om het resultaat van de (asynchrone) functie f op te vragen (indien nodig wordt er gewacht). De asynchrone taak kan in een aparte thread uitgevoerd worden (dat bepaalt de implementatie / de programmeur).
87
async voorbeeld #include <vector> #include <iostream> using namespace std; int som(const vector<int>& v) { int s = 0; for (auto e: v) s += e; return s; } int main() { vector<int> v1( , 1); vector<int> v2( , 2); vector<int> v3( , 4); cout << "som=" << som(v1) + som(v2) + som(v3) << endl; cin.get(); return 0; som= Tijdsduur: 7.97 sec som(v1), som(v2) en som(v3) kunnen parallel worden uitgevoerd. Hoe vertellen we dat aan de compiler?
88
De implementatie bepaalt of er aparte theads worden gestart!
async voorbeeld #include <future> #include <vector> #include <iostream> using namespace std; int som(const vector<int>& v) { /* idem */ } int main() { vector<int> v1( , 1); vector<int> v2( , 2); vector<int> v3( , 4); future<int> s1 = async(&som, ref(v1)); future<int> s2 = async(&som, ref(v2)); future<int> s3 = async(&som, ref(v3)); cout << "som=" << s1.get() + s2.get() + s3.get() << endl; cin.get(); return 0; De implementatie bepaalt of er aparte theads worden gestart! som= Tijdsduur: 3.99 sec Waarom niet 3x zo snel?
89
async voorbeeld (met auto)
#include <future> #include <vector> #include <iostream> using namespace std; int som(const vector<int>& v) { /* idem */ } int main() { vector<int> v1( , 1); vector<int> v2( , 2); vector<int> v3( , 4); auto s1 = async(&som, ref(v1)); auto s2 = async(&som, ref(v2)); auto s3 = async(&som, ref(v3)); cout << "som=" << s1.get() + s2.get() + s3.get() << endl; cin.get(); return 0; De implementatie bepaalt of er aparte theads worden gestart!
90
async voorbeeld #include <future> #include <vector> #include <iostream> using namespace std; int som(const vector<int>& v) { /* idem */ } int main() { vector<int> v1( , 1); vector<int> v2( , 2); vector<int> v3( , 4); auto s1 = async(launch::async, &som, ref(v1)); auto s2 = async(launch::deferred, &som, ref(v2)); auto s3 = async(launch::async, &som, ref(v3)); cout << "som=" << s1.get() + s2.get() + s3.get() << endl; cin.get(); return 0; De programmeur bepaald of er aparte theads worden gestart! Start aparte thread Start geen aparte thread
91
async voorbeeld (deferred)
#include <future> #include <vector> #include <iostream> using namespace std; int som(const vector<int>& v) { /* idem */ } int main() { vector<int> v1( , 1); vector<int> v2( , 2); vector<int> v3( , 4); auto s1 = async(launch::deferred, &som, ref(v1)); auto s2 = async(launch::deferred, &som, ref(v2)); auto s3 = async(launch::deferred, &som, ref(v3)); cout << "som=" << s1.get() + s2.get() + s3.get() << endl; cin.get(); return 0; De implementatie start geen aparte theads! som= Tijdsduur: 7.92 sec
92
C++11 concurrency Memory model Threads Synchronisatie
Mutexen Locks Conditionele variabelen Call once Asynchrone taken en Futures Atomics
93
Atomics Een variabele van het type atomic<T> kan (zonder problemen) gedeeld worden door meerdere threads. T kan alleen een POD (Plain Old Datatype) zijn. De implementatie bepaalt of busy-waiting (lock-free) of locking wordt gebruikt. Lock-free is vaak sneller.
94
Mutex probleem #include <thread> #include <iostream>
using namespace std; volatile int aantal = 0; void teller() { for (int i = 0; i < ; ++i) { ++aantal; } int main(void) { thread t1(&teller), t2(&teller), t3(&teller); t1.join(); t2.join(); t3.join(); cout << "aantal = " << aantal << endl; return 0; $ ./a.exe aantal = aantal = aantal = $ time ./a.exe aantal = 0.065 s real
95
Mutex oplossing mutex #include <thread> #include <mutex>
#include <iostream> using namespace std; int aantal = 0; mutex m; void teller() { for (int i = 0; i < ; ++i) { m.lock(); ++aantal; m.unlock(); } int main(void) { /* idem */ $ ./a.exe aantal = $ time ./a.exe 2.397 s real
96
Mutex oplossing lock_guard
#include <thread> #include <mutex> #include <iostream> using namespace std; int aantal = 0; mutex m; void teller() { for (int i = 0; i < ; ++i) { lock_guard<mutex> l(m); ++aantal; } int main(void) { /* idem */ $ ./a.exe aantal = $ time ./a.exe 2.496 s real
97
Mutex oplossing atomic
#include <thread> #include <atomic> #include <iostream> using namespace std; atomic<int> aantal(0); void teller() { for (int i = 0; i < ; ++i) { ++aantal; } int main(void) { /* idem */ $ ./a.exe aantal = $ time ./a.exe 0.610 s real
98
Vergelijk mutex oplossingen
Programma Correct? Executietijd mutex_problem Nee 0.065 s mutex_solution_mutex Ja 2.397 s mutex_soution_guard_lock 2.496 s mutex_solution_atomic 0.610 s
99
Meer over C++11 concurrency...
Memory model Threads Synchronisatie Mutexen Locks Conditionele variabelen Call once Asynchrone taken en Futures Atomics C++ Concurrency in Action Practical Multithreading Anthony Williams February, 2012 | 528 pages ISBN:
100
Real-Time Systems (RTSYST)
Week 4
101
IPC inter process communication
Shared variabele based (H5) Message based (H6) Kan ook gebruikt worden in systemen zonder gedeeld geheugen (gedistribueerde systemen). POSIX: message queue QNX: Neutrino kernel is volledig message based. Zie H2 QNX boek.
102
Messages Synchronisatie
Receive: Wacht als er nog niet gezonden is. Send: Asynchroon: wacht niet. Buffer nodig, wat als buffer vol is? Bijvoorbeeld: POSIX message queue. Synchroon (rendezvous): wacht op ontvangst. Geen buffer nodig. Remote invocation (extended rendezvous): wacht op antwoord. Bijvoorbeeld: QNX messages.
103
Messages asyn_send receive syn_send receive ri_send receive reply
104
Messages Synchroon met behulp van 2 asynchrone messages
Proces 1 asyn_send(mes) receive(ack) Proces 2 receive(mes) async_send(ack) Remote invocation met behulp van 4 asynchrone messages Proces 1 asyn_send(mes) receive(ack) receive(reply) async_send(ack) Proces 2 receive(mes) async_send(ack) //... construct reply async_send(reply) receive(ack)
105
Asynchroon Voordelen: Nadelen: Flexibeler. Buffers nodig.
Complexer: Aparte message voor acknowledge en/of reply nodig. Moeilijk om correctheid van een programma te bewijzen. Asynchrone communicatie kan in een OS dat op synchrone messages is gebaseerd (QNX) worden gerealiseerd door expliciete buffer threads.
106
Messages Adressering Direct: sender geeft receiver proces (of thread) op. Indirect: sender geeft port, channel of mailbox op. Symmetrisch: receiver geeft sender, port, channel of mailbox op. Asymmetrisch: receiver geeft niets op.
107
Messages inhoud Tussen threads: Tussen processen: Tussen machines:
Geen beperkingen. Tussen processen: Geen pointers (elk proces heeft zijn eigen memory map). Tussen machines: Geen pointers + mogelijk problemen met representatie: Character codering. Big-endian, Little-endian.
108
POSIX message queue Kenmerken:
Synchronisatie: asynchroon Adressering: indirect en symmetrisch Meerdere senders en receivers kunnen dezelfde mq gebruiken. Aan een message kan een prioriteit worden meegegeven. Bij creatie wordt o.a. opgegeven: Naam Max aantal messages Max size message API: mq_open (create en open) mq_send, mq_receive mq_close, mq_unlink (destroy) mq_getattr, mq_setattr mq_notify
109
Voorbeeld embedded system
thermocouple pressure transducer ADC ADC T P S DAC Switch Screen pump/valve heater
110
Beschikbare functies double readTemp(void); void writeSwitch(int i);
double readPres(void); void writeDAC(double d); int tempControl(double temp); double presControl(double pres);
111
Sequentieel int main(void) { double temp, pres, dac; int switch_;
while (1) { temp = readTemp(); switch_ = tempControl(temp); writeSwitch(switch_); pres = readPres(); dac = presControl(pres); writeDAC(dac); printf("%4.1lf, %4.1lf, %d, %5.1lf\n", temp, pres, switch_, dac); } return EXIT_SUCCESS;
112
Sequentieel problemen
Sample rate van temperatuur en druk is gelijk. Kan wel wat aan worden gedaan met tellers maar: Wat doe je als pressureControl langer duurt dan gewenste sample rate van temperatuur? Als readTemperature niet werkt (blijft pollen) dan loopt ook de drukregeling vast. De temperatuurregeling en de drukregeling zijn twee afzonderlijke “processen”. Maar in het sequentiële programma zitten ze verweven!
113
POSIX Threads Zie volgende sheet… void* tempThread(void* p) {
double temp; int switch_; while (1) { temp = readTemp(); printf("temperature = %4.1lf, ", temp); switch_ = tempControl(temp); printf("switch = %d\n", switch_); writeSwitch(switch_); sleep(3); } return NULL; Zie volgende sheet…
114
POSIX Threads Zie volgende sheet… void* presThread(void* p) {
double pres, dac; while (1) { pres = readPres(); printf("pressure = %4.1lf", pres); dac = presControl(pres); printf(", DAC = %5.1lf\n", dac); writeDAC(dac); sleep(1); } return NULL; Zie volgende sheet…
115
POSIX Threads #include <pthread.h> void check(int error) {
if (error != 0) { fprintf(stderr, "Error: %s\n", strerror(error)); exit(EXIT_FAILURE); } int main(void) { pthread_t t1, t2; check( pthread_create(&t1, NULL, tempThread, NULL) ); check( pthread_create(&t2, NULL, presThread, NULL) ); check( pthread_join(t1, NULL) ); check( pthread_join(t2, NULL) ); return EXIT_SUCCESS; pressure = 14.4, DAC = -4.4 pressure = 14.0temperature = 3.0, , DAC = -4.0 switch = 0 pressure = 13.6, DAC = -3.6
116
Synchronized Threads pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void* tempThread(void* p) { double temp; int switch_; while (1) { temp = readTemp(); switch_ = tempConvert(temp); check( pthread_mutex_lock(&mutex) ); printf("temperature = %4.1lf, switch = %d\n", temp, switch_); check( pthread_mutex_unlock(&mutex) ); writeSwitch(switch_); sleep(3); } return NULL;
117
C++11 Synchronized Threads
class Temp { private: double temp; bool switch_; mutex& display; void read(); void write(); void control(); public: Temp(mutex& m); void run(); }; class Pres { private: double pres, dac; mutex& display; void read(); void write(); void control(); public: Pres(mutex& m); void run(); };
118
C++11 Synchronized Threads
Temp::Temp(mutex& m): temp(0.0), switch_(false), display(m) { } void Temp::run() { while (1) { read(); control(); { lock_guard<mutex> lock(display); cout << "temperature = " << fixed << setw(4) << setprecision(1) << temp << ", switch = " << switch_ << endl; write(); this_thread::sleep(chrono::seconds(3));
119
C++11 Synchronized Threads
int main() { mutex m; Temp t(m); Pres p(m); thread t1(&Temp::run, &t); thread t2(&Pres::run, &p); t1.join(); t2.join(); return 0; }
120
Voorbeeld embedded system
thermocouple pressure transducer ADC ADC T P S DAC Switch Screen pump/valve heater
121
Message Queue Zie volgende sheet… #include <mqueue.h> // ...
void* tempThread(void* p) { double temp; int switch_; mqd_t m; char buffer[128]; int i; check_errno( m = mq_open("/mq_par74", O_WRONLY) ); for (i = 0; i < 10; i++) { int n = 0; temp = readTemp(); n = snprintf(buffer, sizeof buffer, "temperature = %4.1lf, ", temp); switch_ = tempControl(temp); snprintf(&buffer[n], sizeof buffer-n, "switch = %d\n", switch_); check_errno( mq_send(m, buffer, sizeof buffer, 2) ); writeSwitch(switch_); sleep(3); } check_errno( mq_close(m) ); return NULL; Zie volgende sheet…
122
Message Queue Zie volgende sheet… void* presThread(void* p) {
double pres, dac; mqd_t m; char buffer[128]; int i; check_errno( m = mq_open("/mq_par74", O_WRONLY) ); for (i = 0; i < 10; i++) { int n = 0; pres = readPres(); n = snprintf(buffer, sizeof buffer, "pressure = %4.1lf, ", pres); dac = presControl(pres); snprintf(&buffer[n], sizeof buffer - n, "DAC = %5.1lf\n", dac); check_errno( mq_send(m, buffer, sizeof buffer, 3) ); writeDAC(dac); sleep(1); } check_errno( mq_close(m) ); return NULL; Zie volgende sheet…
123
Message Queue Zie volgende sheet… void* dispThread(void* p) {
mqd_t m; struct mq_attr ma; char buffer[128]; int doorgaan = 1; check_errno( m = mq_open("/mq_par74", O_RDONLY) ); while (doorgaan) { do { check_errno( mq_receive(m, buffer, sizeof buffer, NULL) ); if (strcmp(buffer, "CLOSE") == 0) doorgaan = 0; else printf(buffer); check_errno( mq_getattr(m, &ma) ); } while (ma.mq_curmsgs > 0); sleep(5); putchar('\007'); check_errno( mq_close(m) ); Zie volgende sheet…
124
Message Queue int main(void) {
pthread_t t1, t2, t3; mqd_t m; struct mq_attr ma; ma.mq_maxmsg = 40; ma.mq_msgsize = 128; check_errno( m = mq_open("/mq_par74", O_CREAT|O_RDWR, 0666, &ma) ); check( pthread_create(&t1, NULL, tempThread, NULL) ); check( pthread_create(&t2, NULL, presThread, NULL) ); check( pthread_create(&t3, NULL, dispThread, NULL) ); check( pthread_join(t1, NULL) ); check( pthread_join(t2, NULL) ); check_errno( mq_send(m, "CLOSE", 6, 1) ); check( pthread_join(t3, NULL) ); check_errno( mq_close(m) ); check_errno( mq_unlink("/mq_par74") ); return EXIT_SUCCESS; } BEEP! pressure = 15.6, DAC = -5.6 pressure = 15.2, DAC = -5.2 pressure = 14.8, DAC = -4.8 pressure = 14.4, DAC = -4.4 temperature = 2.5, switch = 0
125
/dev/mqueue
126
shared_mutex implementation
shared_mutex heeft de volgende functies: lock() unieke toegang claimen (voor schrijven) unlock() unieke toegang vrijgeven (voor schrijven) shared_lock() gedeelde toegang claimen (voor lezen) shared_unlock() gedeelde toegang vrijgeven (voor lezen)
127
shared_mutex implementation
Monitor mutex m condition_variable c lock() writers numberOfWriters unlock() shared_lock() readers numberOfReaders shared_unlock() Writers moeten wachten als: numberOfWriters == 1 || numberOfReaders > 0 Readers moeten wachten als: numberOfWriters == 1
128
shared_mutex implementation
cclass shared_mutex { public: shared_mutex(); void lock(); void unlock(); void lock_shared(); void unlock_shared(); private: int numberOfWriters, numberOfReaders; mutex m; condition_variable c; }; shared_mutex::shared_mutex(): numberOfWriters(0), numberOfReaders(0) { }
129
shared_mutex implementation
void shared_mutex::lock() { unique_lock<mutex> lock(m); while (numberOfWriters == 1 || numberOfReaders > 0) { c.wait(lock); } ++numberOfWriters; void shared_mutex::unlock() { lock_guard<mutex> lock(m); --numberOfWriters; c.notify_all();
130
shared_mutex implementation
void shared_mutex::lock_shared() { unique_lock<mutex> lock(m); while (numberOfWriters == 1) { c.wait(lock); } ++numberOfReaders; void shared_mutex::unlock_shared() { lock_guard<mutex> lock(m); --numberOfReaders; c.notify_all();
131
Huiswerk shared_mutex
Werkt de bovenstaande implementatie correct als de notify_all in shared_mutex::unlock() wordt vervangen door een notify_one? Werkt de bovenstaande implementatie correct als de notify_all in shared_mutex::shared_unlock() wordt vervangen door een notify_one?
132
Je kunt de prioriteit van een thread niet opvragen in standaard C++11)
shared_mutex Als een schrijver zich meldt en moet wachten omdat er lezers zijn dan kunnen zich steeds nieuwe lezers melden zodat de schrijver nooit aan de beurt komt (starvation). Is dit een probleem in een RTOS? Ja! Schrijver met hoge prioriteit moet wachten als er steeds nieuwe lezers met lage prioriteit zijn. Oplossing? Nieuwe lezer moeten wachten als er een wachtende schrijver is met een hogere prioriteit dan de lezer. Je kunt de prioriteit van een thread niet opvragen in standaard C++11)
133
Priority correct Monitor writers readers
mutex m condition_variable c numberOfWriters lock() writers unlock() waitingWriters shared_lock() readers numberOfReaders shared_unlock() Writers moeten wachten als: numberOfWriters == 1 || numberOfReaders > 0 Readers moeten wachten als: numberOfWriters == 1 || waitingWriters > 0
134
Priority correct class shared_mutex { public: shared_mutex();
void lock(); void unlock(); void lock_shared(); void unlock_shared(); private: int numberOfWriters, numberOfReaders, waitingWriters; mutex m; condition_variable c; }; shared_mutex::shared_mutex(): numberOfWriters(0), numberOfReaders(0), waitingWriters(0) { }
135
Priority correct void shared_mutex::lock() {
unique_lock<mutex> lock(m); ++waitingWriters; while (numberOfWriters == 1 || numberOfReaders > 0) { c.wait(lock); } --waitingWriters; ++numberOfWriters; void shared_mutex::unlock() { lock_guard<mutex> lock(m); --numberOfWriters; c.notify_all();
136
Priority correct void shared_mutex::lock_shared() {
unique_lock<mutex> lock(m); while (numberOfWriters == 1 || waitingWriters > 0) { c.wait(lock); } ++numberOfReaders; void shared_mutex::unlock_shared() { lock_guard<mutex> lock(m); --numberOfReaders; c.notify_one();
137
Real-Time Systems (RTSYST)
Week 5
138
Real-time faciliteiten
Wat willen we met tijd in een RT systeem? Gebruik van de tijd. Tijd(sduur) meten. (Tot een) bepaalde tijd slapen. Beperkte tijd wachten = time-outs. Tijdafhankelijke requirements opstellen. Specificeren van periodetijden. Specificeren van deadlines. Voldoen aan de tijdafhankelijke requirements = scheduling (zie hoofdstuk 11).
139
Hoe snel is tijd? Elektronica is veel sneller dan de mechanische werkelijkheid. Voorbeeld: Auto botst tegen een muur. Hoeveel instructies kan de boordcomputer nog uitvoeren? snelheid auto = 108 km/uur kreukzone = 30 cm processor freq = 200 MHz gemiddeld 2 clockcycles / instructie
140
RTC real-time clock POSIX (niet nauwkeurig, granularity = 1 s)
time_t, stuct tm, time() and localtime() Zie MICPRG: POSIX optional REALTIME (nauwkeuriger) int clock_gettime(clockid_t clock_id, struct timespec *tp) clock_id CLOCK_REALTIME is verplicht int clock_getres(clockid_t clock_id, struct timespec *res) granularity POSIX max 20 ms, QNX instelbaar (default 1 ms, minimum 10 μs) C++11 (granularity = ?) chrono::system_clock, chrono::high_resolution_clock
141
Slapen POSIX (niet nauwkeurig, granularity = 1 s)
unsigned int sleep(unsigned int) POSIX optional REALTIME (nauwkeuriger) int nanosleep(struct timespec* rqtp, struct timespec* rmtp); Granularity van CLOCK_REALTIME QNX instelbaar (default = 1 ms, minimum = 10 μs) C++11 (granularity = ?) std::this_thread::sleep_for(… rel_time) std::this_thread::sleep_until(… abs_time)
142
Al deze ...timed... functies geven ETIMEDOUT terug bij een timeout
POSIX timeout Semaphore int sem_timedwait(sem_t* sem, const struct timespec* abs_time); Mutex int pthread_mutex_timedlock(..., const struct timespec* abs_time); Conditionele variabele int pthread_cond_timedwait(..., Message queue ssize_t mq_timedreceive(..., const struct timespec* abs_time); int mq_timedsend(..., const struct timespec* abs_time); Al deze ...timed... functies geven ETIMEDOUT terug bij een timeout
143
Handig om een absolute time te gebruiken!
POSIX timeout int ei_teller = 0; pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t c = PTHREAD_COND_INITIALIZER; // ... struct timespec ts; int r = 0; pthread_mutex_lock(&m); clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += 5; while (ei_teller < 12 && r == 0) { r = pthread_cond_timedwait(&c, &m, &ts); } if (r != 0) { if (r == ETIMEDOUT) else /* error */ Handig om een absolute time te gebruiken! Snap je waarom? else { ei_teller -= 12; } pthread_mutex_unlock(&m); // ...
144
C++ timeout timed_mutex, recursive_timed_mutex condition_variable
bool try_lock_until(const chrono::time_point<C, D>& abs_time); bool try_lock_for(const chrono::duration<R, P>& rel_time); condition_variable cv_status wait_until(unique_lock<mutex>& lock, const chrono::time_point<C, D>& abs_time); bool wait_until(unique_lock<mutex>& lock, const chrono::time_point<C, D>& abs_time, … p); cv_status wait_for(unique_lock<mutex>& lock, const chrono::duration<R, P>& rel_time); bool wait_for(unique_lock<mutex>& lock, const chrono::duration<R, P>& rel_time, … p); Memberfuncties die een bool teruggeven, geven false terug bij een timeout. Memberfuncties die cv_status teruggeven, geven cv_status::timeout terug bij een timeout.
145
C++11 timeout // producer thread(s) // ... // ... int ei_teller(0); {
// ... { lock_guard<mutex> g(m); ei_teller += n; c.notify_all(); } // ... int ei_teller(0); mutex m; condition_variable c; // consumer thread(s) { unique_lock<mutex> u(m); if (c.wait_for(u, chrono::seconds(5), []() { return ei_teller >= 12; })) { ei_teller -= 12; else cout << "Schiet eens op!" << endl; }
146
Time requirements Deadline taak moet klaar voor deadline (hard, soft, firm) Minimum delay taak mag pas starten na min delay Maximum delay taak moet starten na max delay Maximum execution time taak mag niet langer runnen Maximum elapse time taak mag niet langer duren Soorten taken Periodic (vaste tijd tussen events) Aperiodic (random events) Geen worst-case analyse mogelijk. Sporadic (min tijd tussen events is gespecificeerd)
147
Drift cumulatieve drift lokale drift
De extra vertraging bij sleep wordt lokale drift genoemd. Deze drift kan niet voorkomen worden. Je kunt wel voorkomen dat een cumulatieve drift ontstaat doordat lokale drifts bij elkaar worden opgeteld. while (1) { actie(); sleep(5); // slaap minstens 5 seconden } sigwait(&set, &signum); // slaap tot signal cumulatieve drift lokale drift Met een timer (zie ) kan een periodiek signal (zie 7.5.1) worden opgewekt.
148
Timing error detectie Deadline overrun detectie
Zet een POSIX timer (10.4.2) die een signal (zie 7.5.1) geeft als de deadline verstrijkt = watchdog timer Zie 13.2 en Maximum execution time overrun detectie POSIX definieert clocks: CLOCK_PROCESS_CPUTIME_ID en CLOCK_THREAD_CPUTIME_ID Zie 13.3 en
149
Real-Time Systems (RTSYST)
Week 6
150
Scheduling Dienstregeling van taken
N taken kun je in N! verschillende schedules uitvoeren. Bijvoorbeeld 10 taken: mogelijke schedules. Met preemption nog veel meer mogelijkheden. De gekozen scheduling moet aan alle time requirements voldoen. Een scheduling scheme (=plan) bestaat uit: Een algoritme om een schedule te bepalen. Een methode om het “worst-case” gedrag van een, met het algoritme bepaalde, schedule te voorspellen.
151
Scheduling wanneer? Statisch: schedule ligt vast voor uitvoering.
Taken, worst-case executietijden en deadlines moeten van tevoren bekend zijn. Je kunt door middel van een analyse aantonen dat alle deadlines worden gehaald. Responsetijden zijn voorspelbaar! Kan niet reageren op “onvoorziene” situaties. Dynamisch: schedule bepaald tijdens uitvoeren. Gedrag minder goed vooraf voorspelbaar. Kan dynamisch inspelen op onvoorziene omstandigheden (b.v. berekening die langer duurt dan verwacht).
152
Scheduling RT systemen
Bijna altijd wordt een statische methode gebruikt. Meest gebruikt: Preemptive Priority Based scheduling op elk moment runt de ready taak met de hoogste prio In dit geval bestaat een scheduling scheme uit: Een algoritme om de prioriteit van elke taak te bepalen. Een methode om de schedulability te voorspellen = Een methode om het “worst-case” gedrag bij de gekozen prioriteiten te voorspellen en te bekijken of aan alle time requirements wordt voldaan.
153
Scheduling Simpel beginnen
Het aantal taken is bekend. N Alle taken zijn periodiek en de perioden zijn bekend. Ti De taken zijn onafhankelijk van elkaar (geen synchronisatie en communicatie). Systeem overhead wordt verwaarloosd. De deadline van elke taak is gelijk aan de periode. Di = Ti De worst-case executietijd van elke taak is vast en bekend. Ci Dit process model is te simpel (maar al moeilijk genoeg). Later zullen we realistische modellen bekijken.
154
Cyclic executive Als schedule van tevoren bepaald is kun je het uitvoeren van de schedule expliciet programmeren. Voorbeeld: // zet timer: elke 25 ms een signal while (1) { sigwait(&set, &signum); a(); b(); c(); sigwait(&set, &signum); a(); b(); d(); e(); sigwait(&set, &signum); a(); b(); d(); } Taak T C a 25 10 b 8 c 50 5 d 4 e 100 2 Hoe bepaal je de schedulability? Hoe vind je een schedule?
155
Cyclic executive Hoe bepaal je de schedulability?
Utilization (benutting) U = 10/25+8/25+5/50+ 4/50+2/100 = 0,92 Als Ce = 4 dan geldt U = 0,94 en is er geen schedule mogelijk! Hoe bepaal je de schedulability? Hoe vind je een schedule?
156
Cyclic executive Eigenschappen: Problemen: Alternatieven:
Er zijn geen processen (of threads) de taken zijn gewone functies. Er is shared memory voor communicatie. Protectie is niet nodig. Alle T ’s moeten een veelvoud zijn van de minor cycle time. Systeem is deterministisch. Problemen: Taken met grote verschillen in T ’s geeft lange major cycle. Sporadic taken zijn niet in te passen! Slecht onderhoudbaar, aanpasbaar en uitbreidbaar. Schedule bepalen is moeilijk! Alternatieven: Fixed-Priority Scheduling (FPS) Earliest Deadline First (EDF)
157
FPS Fixed-Priority Scheduling
Elke taak draait als thread (of process) met een statisch bepaalde vaste prioriteit. De prioriteit wordt bepaald door de time requirements. Een thread met een hogere prioriteit krijgt ‘voorrang’ van het RTOS (zoals bijvoorbeeld bij practicumopdracht 2)
158
FPS Fixed-Priority Scheduling
Preemptive ↔ NonPreemptive Preemptive: Als thread met hogere prioriteit ready wordt dan wordt running thread meteen onderbroken. (practicumopdracht 2) NonPreemptive: Als thread eenmaal gestart is wordt hij ook afgemaakt. Deferred Preemption: Als thread met hogere prioriteit ready wordt dan wordt running thread na een bepaalde tijd zeker onderbroken.
159
Rate Monotonic Priority Assignment
De periode van een proces bepaalt de prioriteit van dat proces. Hoe korter de periode hoe hoger de prioriteit. Deze methode is optimaal! Dat wil zeggen dat als er een mogelijke preemptive FPS schedule is dan is rate monotonic FPS ook mogelijk.
160
FPS-RMPA Utilization based schedulability test:
1 U ≤ 1.000 2 U ≤ 0.828 3 U ≤ 0.780 4 U ≤ 0.757 5 U ≤ 0.743 10 U ≤ 0.718 oneindig U ≤ 0.693 Als deze test true geeft worden geen deadlines gemist! Als deze test false worden er misschien deadlines gemist!
161
FPS-RMPA Schedulability voorbeelden
Boek: Voorbeeld A: Tabel 11.5, figuur 11.2 (time line) en figuur 11.3 (Gantt chart). Voldoet niet aan de test en haalt deadlines niet. Voorbeeld B: Tabel Voldoet wel aan de test en haalt deadlines wel. Voorbeeld C: Tabel 11.7 en figuur Voldoet niet aan de test maar haalt deadlines wel. Als aan de test voldaan wordt is dat voldoende bewijs dat de deadlines gehaald worden. Maar het is niet noodzakelijk dat aan de test voldaan wordt om de deadlines te kunnen halen.
162
FPS-RMPA Responsetijd analyse
In tegenstelling tot de vorige test geeft deze analyse de exacte responsetijden. We kunnen dus exact zeggen of alle deadlines gehaald worden (en met welke marge). Ri komt zowel links als rechts voor. De vergelijking is niet eenvoudig op te lossen (door de niet inverteerbare ceiling functie) Ri is de responsetijd van taak i hp(i) is de verzameling taken met een hogere prioriteit dan taak i
163
FPS-RMPA Responsetijd analyse
Voor de taak met de hoogste prioriteit geldt: Alle andere taken kunnen onderbroken worden en voor deze taken geldt: Waarbij Ii de maximale “interference” is. Deze treed op als alle taken met een hogere prioriteit gelijk met taak i starten. Het aantal keer dat een taak met een hogere prioriteit j gestart wordt voordat i afgelopen is: Ii,j is dus:
164
FPS-RMPA Responsetijd analyse
De totale maximale interference is de som van de maximale interference van alle taken met een hogere prioriteit: Oplossing met behulp van recurrente betrekking: Start met = 0 en ga door tot: of
165
FPS-RMPA Responsetijd analyse
Je kunt een programma schrijven om alle Ri ’s te berekenen: zie boek p. 376. Voorbeelden: zie boek p. 377 en 378. Verdere uitbreiding is nog nodig: Sporadic taken. Taken met D < T. Interactie tussen taken. Release jitter. Taken met D > T. Deferred preemption. Fault tolerance. Release offset.
166
FPS-RMPA D < T en Sporadic taken
Gebruik DMPA in plaats van RMPA (geeft DMPO): Gebruik bij responsetijd analyse als stop voorwaarde: Sporadic taken: Vul voor de periodetijd de minimale tijd tussen twee “starts” van deze taak in. T = minimum inter-arrival interval. Vaak geldt voor sporadic taken D < T.
167
FPS-DMPO Blocking Als een taak met een hoge prioriteit moet wachten op een taak met een lagere prioriteit dan wordt deze taak blocked (priority inversion). Een taak die unblocked sluit achter de taken met dezelfde prioriteit in de readyqueue aan. Als een taak met een lagere prioriteit moet wachten op een taak met een hogere prioriteit dan wordt deze taak preempted. Een taak die preempted wordt sluit voor de taken met dezelfde prioriteit in de readyqueue aan = vooraan de readyqueue! Om het real-time gedrag te kunnen voorspellen moet de maximale tijd dat een taak blocked kan zijn voorspelbaar zijn (bound blocking).
168
Priority inversion voorbeeld
4 taken (a, b, c en d) delen 2 resources (Q en V). Deze resources kunnen slechts door 1 taak tegelijk gebruikt worden (en zijn dus beschermd met bijvoorbeeld een mutex). taak prio execution release time d 4 EEQVE c 3 EVVE 2 b EE a 1 EQQQQE E = taak heeft alleen processor nodig Q = taak heeft resource Q nodig V = taak heeft resource V nodig
169
Priority inversion voorbeeld
taak prio execution release time d 4 EEQVE c 3 EVVE 2 b EE a 1 EQQQQE
170
FPS-DMPO Priority inversion
Taak d wordt blocked door taak a, b en c (alle processen met een lagere prioriteit)! Blocking (priority inversion) is niet te voorkomen als we mutual exclusive recources delen. Blocking is wel te beperken door het toepassen van priority inheritance: Als een taak blocked voor een resource dan krijgt de taak die de resource bezit de prioriteit van de taak die blocked wordt.
171
Huiswerk Boek: H11 Inleiding, 11.1 tot 11.6.2 en 11.7 bestuderen.
Opgaven 1, 2, 4 en 7 maken.
172
Real-Time Systems (RTSYST)
Week 7
173
Priority inheritance voorbeeld
taak prio execution release time d 4 EEQVE c 3 EVVE 2 b EE a 1 EQQQQE
174
Blocking Priority inheritance
De tijd dat een taak blocked kan zijn is nu beperkt. B = maximum blocking time K = aantal gebruikte resources usage(k, i) = boolean functie 1 als er een taak is met prioriteit lager dan Pi en een taak met prioriteit hoger of gelijk aan Pi (dit kan taak i zelf zijn) die de resource k delen. C(k) = maximale tijd dat resource k gebruikt wordt.
175
Blocking Response time analyse
176
Blocking Priority inheritance
Priority inheritance kan niet eenvoudig geïmplementeerd worden! Bij semaphoren en condition variabelen is vaak niet te achterhalen op wie je wacht (wie de sem_post of pthread_cond_signal zal geven)! Bij message passing is het vaak niet te achterhalen op wie je wacht (wie de mq_send zal doen waarop de mq_receive moet wachten)! Alternatief: priority ceiling Wie doet sem_post? Taak 1 Taak 2 Taak 3 Taak 4 sem_wait sem_post OS kan niet in de toekomst kijken. Wie wel?
177
FPS-DMPO Priority ceiling
Original Ceiling Priority Protocol OCPP: Elke taak heeft een static priority. Elke resource heeft een ceiling priority = maximale prioriteit van de processen die deze resource delen. Een taak die een taak met een hogere prioriteit blocked erft de hogere prioriteit. Een taak kan alleen een resource gebruiken als zijn prioriteit hoger is dan de ceiling van alle door andere taken gebruikte (en gelockte) resources. Voordelen: Proces kan maar 1x blocked worden. Deadlock is niet mogelijk. Mutual exclusive gaat altijd “van zelf” goed. Transitive blocking is niet mogelijk.
178
OCPP voorbeeld ceiling Q = 4 ceiling V = 4 taak prio execution
release time d 4 EEQVE c 3 EVVE 2 b EE a 1 EQQQQE
179
OCPP Blocking Eerste resource kan altijd worden gebruikt.
Tweede resource kan alleen worden gebruikt als er geen taak is met een hogere prioriteit die beide resources gebruikt. De tijd dat een taak blocked kan zijn wordt nu: Impementatie is nog steeds net zo moeilijk! Alternatief: Immediate CPP.
180
FPS-DMPO Priority ceiling
Immediate Ceiling Priority Protocol ICPP: Elke taak heeft een static priority. Elke resource heeft een ceiling priority = maximale prioriteit van de processen die deze resource delen. Als een taak een resource gebruikt krijgt die taak de ceiling prioriteit van de resource. Voordelen: Proces kan maar 1x blocked worden. Deadlock is niet mogelijk. Mutual exclusive gaat altijd “van zelf” goed. Transitive blocking is niet mogelijk. Ten opzichte van OCPP: Eenvoudiger te implementeren. Minder taak wisselingen.
181
ICPP voorbeeld ceiling Q = 4 ceiling V = 4 taak prio execution
release time d 4 EEQVE c 3 EVVE 2 b EE a 1 EQQQQE
182
POSIX Priority Based Scheduling
FIFO Een thread blijft running totdat de thread blocked of preempted wordt. Een thread die preempted wordt komt vooraan de readyqueue te staan. Round-Robbin Een thread blijft running totdat de thread blocked of preempted wordt of totdat de time-slice is verstreken. Een thread waarvan de time-slice is verstreken komt achter de threads met gelijke prioriteit op de readyqueue te staan. Sporadic Server Other
183
Priority Protect Protocol =ICPP
int pthread_mutexattr_getprotocol(const thread_mutexattr_t* attr, int* protocol); int pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, int protocol); mogelijke waarden voor protocol: PTHREAD_PRIO_NONE PTHREAD_PRIO_INHERIT PTHREAD_PRIO_PROTECT int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t* attr, int* prioceiling); int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *attr, int prioceiling); Priority inheritance POSIX naam voor ICPP
184
Huiswerk Boek: H11 11.8 en 11.9 bestuderen. Opgaven 5 en 6 maken.
185
Tentamenopgave Een programma bestaat uit 5 threads T1 t/m T5. Deze threads gebruiken 4 gedeelde resources R1 t/m R4. Elke resource is mutual exclusive beveiligd met een mutex M1 t/m M4. Deze mutexen gebruiken het priority inheritance protocol. Als een thread een resource toegewezen heeft gekregen dan kan deze thread de resource voor een bepaalde maximale tijd bezetten. In de onderstaande tabel staat k voor het nummer van de resource en C(k) voor de maximale tijd dat resource k bezet kan worden.
186
Alle gegeven tijden zijn in ms.
Tentamenopgave In de volgende tabel staat aangegeven welke resources elke thread gebruikt. In de onderstaande tabel staat i voor het nummer van de thread, T(i) voor de periodetijd van thread i en C(i) voor de maximale executietijd van thread i. Gegeven is dat de deadline van elke taak gelijk is aan zijn periodetijd. Alle gegeven tijden zijn in ms.
187
Tentamenopgave A. (5) Bepaal de prioriteit van de 5 processen als RMPA (Rate Monotonic Priority Assignment) wordt gebruikt. De hoogste prioriteit is 5 en de laagste prioriteit is 1. B. (15) Bereken voor elke thread i de blocking time B(i). C. (15) Bereken voor thread 1, 3 en 5 of de deadline wordt gehaald en geef, indien de deadline wordt gehaald, de responsetijd Ri.
188
Tentamenopgave i P(i) T(i) C(i) Gebruikt 1 5 100 50 R1, R3 2 4 200 45
R2, R3, R4 3 300 15 - 350 20 R2 400 25 R1
Verwante presentaties
© 2024 SlidePlayer.nl Inc.
All rights reserved.