De presentatie wordt gedownload. Even geduld aub

De presentatie wordt gedownload. Even geduld aub

Inhoud RTSYST 0 Week 1Week 2Week 3Week 4Week 5Week 6Week 7.

Verwante presentaties


Presentatie over: "Inhoud RTSYST 0 Week 1Week 2Week 3Week 4Week 5Week 6Week 7."— Transcript van de presentatie:

1 Inhoud RTSYST 0 Week 1Week 2Week 3Week 4Week 5Week 6Week 7

2 Real-Time Systems (RTSYST) Week 1

3 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).

4 3 Leermiddelen Boeken 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

5 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.

6 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.

7 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

8 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 RTOS of RTL moet dit ondersteunen Interactie met hardware

9 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)

10 9 Concurrent programming (2) Distributed system (wordt verder niet behandeld in RTSYST) Parallel Verschillende systemen (elk met een eigen geheugen) verbonden met een netwerk

11 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

12 11 Sequential Maze Search

13 12 Concurrent Maze Search

14 13 Processor beter benutten op single processor systeem Concurrent programming time XWXWXXWXWXCC c=calculate x=transmit w=wait for ACK XWXWX C XWXWXW CCC sneller

15 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 S N = 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)

16 15 Process versus Thread Process Eigen stack = veilig Eigen (data) memory map Eigen virtueel geheugen = veilig, communicatie = traag Process switch is traag (zwaar) Cache flush, MMU TLB flush Thread Eigen stack = veilig Gedeelde (data) memory map binnen hetzelfde process Gedeeld geheugen = onveilig Communicatie = snel Thread switch is snel (licht) binnen hetzelfde process Geen flushes

17 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

18 QNX (POSIX compatible RTOS) 17

19 Huiswerk Bestudeer: Boek H1 t/m 1.3. Bestudeer: Artikel uit Embedded Computer Design: RTOS versus GPOS RTOS versus GPOS Achtergrondinformatie: Artikel The Free Lunch Is OverThe Free Lunch Is Over Artikel TLAs: QNX, RIM, ARM, CES, RPM, and MPH. An RTOS for the Automotively InclinedTLAs: QNX, RIM, ARM, CES, RPM, and MPH. An RTOS for the Automotively Inclined Web seminar: Why do I need an RTOS anyway?Why do I need an RTOS anyway? 18

20 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

21 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.

22 21 Concurrent OOP Actieve 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).

23 22 Specificatie van concurrent taken System call UNIX, win32 Concurrent blok Concurrent Pascal, High Performance Fortran Expliciete declaratie ADA, Java task body NAME is begin … end NAME; cobegin s1; s2; s3 coend; pthread_t t; pthread_create(&t, NULL, &func, NULL)

24 23 Concurrent execution IEEE POSIX 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:

25 Waarom haakjes? pnf is een pointer naar een functie met een int als parameter en een int returnwaarde 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. Pointers naar functies 24 #include 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 pnf wijst naar de functie dubbel ( pnf wordt gelijk aan het adres van de functie dubbel ) 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. Pointers naar functies 25 #include 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);

27 De functie waar pnf naar wijst wordt aangeroepen met de waarde van a als argument 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. Pointers naar functies 26 #include 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?

28 Verkorte schrijfwijze. Naam van een functie  beginadres (van de code) van de functie. Pointers naar functies 27 #include 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);

29 Wat is het nut? Functie als parameter. Pointers naar functies 28 #include /* … */ 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);

30 Uitvoer 29 De kwadraten van 1 t/m De dubbelen van de drievouden van 0 t/m

31 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. void* 30 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; }

32 31 pthread (1 van 3) #include void check(int error) { if (error != 0) { fprintf(stderr, "Error: %s\n", strerror(error)); exit(EXIT_FAILURE); }

33 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, }; int i; for (i = 0; i < 10; i++) { nanosleep(&ts, NULL); printf("print2\n"); } return NULL; }

34 33 pthread (1 van 3) 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; } print1 print2 print1 print2 print1 print2 print1 print2 print1 print2 print1 print2

35 34 pthread Alternatieve implementatie 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* typedef struct { char* msg; long ns; } par_t;

36 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. Command line argumenten 35 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

37 Real-Time Systems (RTSYST) Week 2

38 Process/Thread states 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. runningready blocked / sleeping Wait for I/O or … I/O or … completion

39 Problem with shared memory 38 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); Wat is de uitvoer? #./a.out aantal = #./a.out aantal = #./a.out aantal = # time./a.out aantal = s real 0.08s user 0.01s system

40 Problem with shared memory De operatie aantal++ is niet ondeelbaar (in machinecode). Bijvoorbeeld: loadreg, aantal increg storereg, 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?

41 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. 40

42 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 ) Busy waiting Mutex Semaphore Monitor Mutex en Conditionele variabelen Barrier (niet in H8 wel IEEE Std ) ReadWriteLock (niet in H8 wel IEEE Std ) Message based (H9)

43 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. 42

44 Process/Thread states runningready Blocked on m Lock mutex m which is already locked Unlock mutex m Lock mutex m which is not locked

45 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 44 Bij RTOS afhankelijk van prioriteit!

46 Mutex with shared memory 45 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 = #./a.out aantal = # time./a.out aantal = s real 1.96s user 0.02s system 20x trager! Geen volatile meer nodig!

47 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. 46

48 Dining Philosophers 47 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; }

49 Dining Philosophers 48 int main(void) { int i; pthread_t t[5]; for (i = 0; i < 5; i++) { check( pthread_create(&t[i], NULL, &philosopher, &i) ); } for (i = 0; i < 5; i++) { check( pthread_join(t[i], NULL) ); } return EXIT_SUCCESS; } Dit programma kan vastlopen (deadlock)! Oplossing? Zie huiswerk verderop (uitwerking in theorieplanning).

50 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. 49 Bij RTOS afhankelijk van prioriteit! Semaphore Edsger Dijkstra

51 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. 50 Uitwerking staat op BlackBoard.

52 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. 51 Semaphore versus Mutex Huiswerk: Thread a bestaat uit twee sequentiële delen a 1 en a 2. Thread b bestaat uit twee sequentiële delen b 1 en b 2. Thread c bestaat uit twee sequentiële delen c 1 en c 2. Zorg er voor (met behulp van een semaphoor) dat de delen b 2 en c 2 altijd na deel a 1 worden uitgevoerd.

53 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. 52 Bij RTOS afhankelijk van prioriteit! Monitor Tony Hoare

54 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!

55 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.

56 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 Bezet de mutex. 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!

57 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. 56 Bij RTOS afhankelijk van prioriteit! Conditionele variabele

58 57 Monitor voorbeeld #include 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); //...

59 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!

60 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. 59 Uitwerking staat op BlackBoard.

61 Real-Time Systems (RTSYST) Week 3

62 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. Real-Time? Beschikbaar in MS VC++ ≥2012 en in GCC ≥4.7

63 C++11 concurrency Threads Synchronisatie Mutexen Locks Conditionele variabelen Call once Asynchrone taken en Futures Atomics

64 63 C++11 Thread voorbeeld 1 (1) #include using namespace std; void print1() { for (int i = 0; i < 10; ++i) { this_thread::sleep_for(chrono::milliseconds(10)); cout << "print1" << endl; } void print2() { for (int i = 0; i < 10; ++i) { this_thread::sleep_for(chrono::milliseconds(20)); cout << "print2" << endl; }

65 64 C++11 Thread voorbeeld 1 (2) int main() { thread t1(&print1); thread t2(&print2); t1.join(); t2.join(); return 0; }

66 65 C++11 Thread voorbeeld 2 #include //... 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; } print1 print2 print1 print2 print1 print2 print1 print2 print1 print2 print1 print2 Wel type-safe (in tegenstelling tot pthreads).

67 66 C++11 Thread thread::hardware_concurrency() 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.

68 67 C++11 Synchronization Mutexen mutex void lock(); bool try_lock(); void unlock(); recursive_mutex Idem maar telt aantal locks (en aantal unlocks). Locks Conditionele variabelen

69 68 C++11 Mutex voorbeeld class Point { public: Point(): x(0), y(0) { } void stepNorthEast() { ++x; ++y; } bool isNorthEast() const { return x == y; } private: int x, y; }; y x x==y Wat gebeurt er als deze class in een multi-threaded omgeving gebruikt wordt?

70 69 C++11 Mutex voorbeeld class TestPoint { private: Point p; void thread1() { for (int i = 0; i < ; ++i) p.stepNorthEast(); } void thread2() { for (int i = 0; i < ; ++i) 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! … Probleem! Einde.

71 70 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; } private: mutable mutex m; int x, y; }; Einde. Run-time Zonder mutex0.265 s Met mutex s Kan aangepast worden in const memberfunctie

72 71 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 <= ; m.unlock(); return res; } private: mutable mutex m; int x, y; }; Je wilt niet onnodig wachten!

73 72 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 <= ; m.unlock_shared(); return res; } private: mutable boost::shared_mutex m; int x, y; }; isNorthEast() en isWithinLimits() kunnen nu parallel draaien!

74 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. 73

75 74 C++11 Synchronization 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 Een lock maakt het gebruik van een mutex eenvoudiger en exception safe. Zie C++ exceptions (OGOPRG dictaat par.6.8).

76 75 C++11 Lock voorbeeld class Point { public: Point(): x(0), y(0) { } void stepNorthEast() { lock_guard lock(m); ++x; ++y; } bool isNorthEast() const { lock_guard lock(m); return x == y; } private: mutable mutex m; int x, y; };

77 76 { lock_guard l(m); v.at(10) = 27; } C++11 Lock voorbeeld vector v; mutex m; //... m.lock(); v.at(10) = 27; m.unlock(); Wat gaat er mis als at een exception gooit? Oplossing ? m.lock(); try { v.at(10) = 27; m.unlock(); catch(...) { m.unlock(); throw; } Niet handig! Oplossing !

78 77 C++11 Synchronization Mutexen Locks Conditionele variabelen conditional_variable void notify_one(); void notify_all(); void wait(unique_lock & lock); void wait(unique_lock & lock, pred_type pred); conditional_variable_any Idem maar werkt met elk type lock

79 78 C++11 Monitor voorbeeld #include int ei_teller = 0; mutex m; condition_variable c; // consumer thread(s) //... unique_lock lock(m); while (ei_teller < 12) c.wait(lock); ei_teller -= 12; //...

80 79 C++ Monitor voorbeeld // producer thread(s) //... lock_guard 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!

81 80 Monitor voorbeeld while (ei_teller < 12) c.wait(lock); // alternatief met predicate functie: bool predicate() { return ei_teller >= 12; } //... c.wait(lock, &predicate);

82 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.

83 82 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, []() { return ei_teller >= 12; }); Voordeel? Nadeel? “eenvoudige” syntax geen hergebruik mogelijk

84 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.

85 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.

86 C++11 concurrency Memory model Threads Synchronisatie Mutexen Locks Conditionele variabelen Call once Asynchrone taken en Futures Atomics

87 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). kan De asynchrone taak kan in een aparte thread uitgevoerd worden (dat bepaalt de implementatie / de programmeur). 86

88 87 async voorbeeld #include using namespace std; int som(const vector & v) { int s = 0; for (auto e: v) s += e; return s; } int main() { vector v1( , 1); vector v2( , 2); vector v3( , 4); cout << "som=" << som(v1) + som(v2) + som(v3) << endl; cin.get(); return 0; } som(v1), som(v2) en som(v3) kunnen parallel worden uitgevoerd. Hoe vertellen we dat aan de compiler? som= Tijdsduur: 7.97 sec

89 88 async voorbeeld #include using namespace std; int som(const vector & v) { /* idem */ } int main() { vector v1( , 1); vector v2( , 2); vector v3( , 4); future s1 = async(&som, ref(v1)); future s2 = async(&som, ref(v2)); future 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?

90 89 async voorbeeld (met auto ) #include using namespace std; int som(const vector & v) { /* idem */ } int main() { vector v1( , 1); vector v2( , 2); vector 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!

91 90 async voorbeeld #include using namespace std; int som(const vector & v) { /* idem */ } int main() { vector v1( , 1); vector v2( , 2); vector 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

92 91 async voorbeeld ( deferred ) #include using namespace std; int som(const vector & v) { /* idem */ } int main() { vector v1( , 1); vector v2( , 2); vector 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

93 C++11 concurrency Memory model Threads Synchronisatie Mutexen Locks Conditionele variabelen Call once Asynchrone taken en Futures Atomics

94 Een variabele van het type atomic 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. 93

95 94 Mutex probleem #include 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 = $./a.exe aantal = $./a.exe aantal = $ time./a.exe aantal = s real

96 95 Mutex oplossing mutex #include 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 = $./a.exe aantal = $./a.exe aantal = $ time./a.exe aantal = s real

97 96 Mutex oplossing lock_guard #include using namespace std; int aantal = 0; mutex m; void teller() { for (int i = 0; i < ; ++i) { lock_guard l(m); ++aantal; } int main(void) { /* idem */ $./a.exe aantal = $./a.exe aantal = $./a.exe aantal = $ time./a.exe aantal = s real

98 97 Mutex oplossing atomic #include using namespace std; atomic aantal(0); void teller() { for (int i = 0; i < ; ++i) { ++aantal; } int main(void) { /* idem */ $./a.exe aantal = $./a.exe aantal = $./a.exe aantal = $ time./a.exe aantal = s real

99 Vergelijk mutex oplossingen 98 ProgrammaCorrect?Executietijd mutex_problemNee0.065 s mutex_solution_mutexJa2.397 s mutex_soution_guard_lockJa2.496 s mutex_solution_atomicJa0.610 s

100 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:

101 Real-Time Systems (RTSYST) Week 4

102 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.

103 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. Geen buffer nodig. Bijvoorbeeld: QNX messages.

104 103 Messages asyn_send receive syn_send receive ri_send receive reply

105 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)

106 105 Asynchroon Voordelen: Flexibeler. Nadelen: 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.

107 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.

108 107 Messages inhoud Tussen threads: 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.

109 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

110 109 Voorbeeld embedded system T S P Switch Screen DAC ADC thermocouple pressure transducer heater pump/valve

111 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);

112 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; }

113 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!

114 113 POSIX Threads 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…

115 114 POSIX Threads 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…

116 115 POSIX Threads #include 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

117 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; }

118 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(); };

119 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 lock(display); cout << "temperature = " << fixed << setw(4) << setprecision(1) << temp << ", switch = " << switch_ << endl; } write(); this_thread::sleep(chrono::seconds(3)); }

120 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; }

121 120 Voorbeeld embedded system T S P Switch Screen DAC ADC thermocouple pressure transducer heater pump/valve

122 121 Message Queue #include //... 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…

123 122 Message Queue 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…

124 123 Message Queue 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…

125 124 Message Queue 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 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; }

126 125 /dev/mqueue

127 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)

128 shared_mutex implementation numberOfWriters numberOfReaders mutex m condition_variable c lock() unlock() shared_lock() shared_unlock() writers readers Monitor Writers moeten wachten als: numberOfWriters == 1 || numberOfReaders > 0 Readers moeten wachten als: numberOfWriters == 1

129 shared_mutex implementation 128 c class 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) { }

130 shared_mutex implementation 129 void shared_mutex::lock() { unique_lock lock(m); while (numberOfWriters == 1 || numberOfReaders > 0) { c.wait(lock); } ++numberOfWriters; } void shared_mutex::unlock() { lock_guard lock(m); --numberOfWriters; c.notify_all(); }

131 shared_mutex implementation 130 void shared_mutex::lock_shared() { unique_lock lock(m); while (numberOfWriters == 1) { c.wait(lock); } ++numberOfReaders; } void shared_mutex::unlock_shared() { lock_guard lock(m); --numberOfReaders; c.notify_all(); }

132 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 ? 131

133 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. 132 Je kunt de prioriteit van een thread niet opvragen in standaard C++11)

134 Priority correct numberOfWriters numberOfReaders mutex m condition_variable c lock() unlock() shared_lock() shared_unlock() writers readers Monitor Writers moeten wachten als: numberOfWriters == 1 || numberOfReaders > 0 Readers moeten wachten als: numberOfWriters == 1 || waitingWriters > 0 waitingWriters

135 Priority correct 134 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) { }

136 Priority correct 135 void shared_mutex::lock() { unique_lock lock(m); ++waitingWriters; while (numberOfWriters == 1 || numberOfReaders > 0) { c.wait(lock); } --waitingWriters; ++numberOfWriters; } void shared_mutex::unlock() { lock_guard lock(m); --numberOfWriters; c.notify_all(); }

137 Priority correct 136 void shared_mutex::lock_shared() { unique_lock lock(m); while (numberOfWriters == 1 || waitingWriters > 0) { c.wait(lock); } ++numberOfReaders; } void shared_mutex::unlock_shared() { lock_guard lock(m); --numberOfReaders; c.notify_one(); }

138 Real-Time Systems (RTSYST) Week 5

139 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).

140 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

141 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 140

142 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) 141

143 142 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(..., const struct timespec* abs_time); 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

144 143 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 */ } else { ei_teller -= 12; } pthread_mutex_unlock(&m); //... Handig om een absolute time te gebruiken! Snap je waarom?

145 144 C++ timeout timed_mutex, recursive_timed_mutex bool try_lock_until(const chrono::time_point & abs_time); bool try_lock_for(const chrono::duration & rel_time); condition_variable cv_status wait_until(unique_lock & lock, const chrono::time_point & abs_time); bool wait_until(unique_lock & lock, const chrono::time_point & abs_time, … p); cv_status wait_for(unique_lock & lock, const chrono::duration & rel_time); bool wait_for(unique_lock & lock, const chrono::duration & 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.

146 145 //... int ei_teller(0); mutex m; condition_variable c; // consumer thread(s) //... { unique_lock u(m); if (c.wait_for(u, chrono::seconds(5), []() { return ei_teller >= 12; })) { ei_teller -= 12; else cout << "Schiet eens op!" << endl; } //... C++11 timeout // producer thread(s) //... { lock_guard g(m); ei_teller += n; c.notify_all(); } //...

147 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)

148 147 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 } while (1) { actie(); sigwait(&set, &signum); // slaap tot signal } Met een timer (zie ) kan een periodiek signal (zie 7.5.1) worden opgewekt. cumulatieve drift lokale drift

149 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

150 Real-Time Systems (RTSYST) Week 6

151 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.

152 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).

153 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.

154 153 Scheduling Simpel beginnen Het aantal taken is bekend. N Alle taken zijn periodiek en de perioden zijn bekend. T i De taken zijn onafhankelijk van elkaar (geen synchronisatie en communicatie). Systeem overhead wordt verwaarloosd. De deadline van elke taak is gelijk aan de periode. D i = T i De worst-case executietijd van elke taak is vast en bekend. C i Dit process model is te simpel (maar al moeilijk genoeg). Later zullen we realistische modellen bekijken.

155 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(); c(); sigwait(&set, &signum); a(); b(); d(); } Taak TC a2510 b258 c505 d 4 e1002 Hoe bepaal je de schedulability? Hoe vind je een schedule?

156 155 Cyclic executive Hoe bepaal je de schedulability? Hoe vind je een schedule? Utilization (benutting) U = 10/25+8/25+5/50+ 4/50+2/100 = 0,92 Als C e = 4 dan geldt U = 0,94 en is er geen schedule mogelijk!

157 156 Cyclic executive Eigenschappen: 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)

158 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)

159 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.

160 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.

161 160 FPS-RMPA Als deze test true geeft worden geen deadlines gemist! Als deze test false worden er misschien deadlines gemist! N Test 1 U ≤ U ≤ U ≤ U ≤ U ≤ U ≤ oneindig U ≤ Utilization based schedulability test:

162 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.

163 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). R i is de responsetijd van taak i hp(i) is de verzameling taken met een hogere prioriteit dan taak i R i komt zowel links als rechts voor. De vergelijking is niet eenvoudig op te lossen (door de niet inverteerbare ceiling functie)

164 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 I i 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: I i,j is dus:

165 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

166 165 FPS-RMPA Responsetijd analyse Je kunt een programma schrijven om alle R i ’s te berekenen: zie boek p 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.

167 166 FPS-RMPA D < T en Sporadic taken D < T : 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.

168 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).

169 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). taakprioexecutionrelease time d4EEQVE4 c3EVVE2 b2EE2 a1EQQQQE0 E = taak heeft alleen processor nodig Q = taak heeft resource Q nodig V = taak heeft resource V nodig

170 169 Priority inversion voorbeeld taakprioexecutionrelease time d4EEQVE4 c3EVVE2 b2EE2 a1EQQQQE0

171 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.

172 171 Huiswerk Boek: H11 Inleiding, 11.1 tot en 11.7 bestuderen. Opgaven 1, 2, 4 en 7 maken.

173 Real-Time Systems (RTSYST) Week 7

174 173 Priority inheritance voorbeeld taakprioexecutionrelease time d4EEQVE4 c3EVVE2 b2EE2 a1EQQQQE0

175 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 P i en een taak met prioriteit hoger of gelijk aan P i (dit kan taak i zelf zijn) die de resource k delen. C(k) = maximale tijd dat resource k gebruikt wordt.

176 175 Blocking Response time analyse

177 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 Taak 1 Taak 2Taak 3Taak 4 sem_wait Wie doet sem_post? sem_post OS kan niet in de toekomst kijken. Wie wel?

178 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.

179 178 OCPP voorbeeld taakprioexecutionrelease time d4EEQVE4 c3EVVE2 b2EE2 a1EQQQQE0 ceiling Q = 4 ceiling V = 4

180 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.

181 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.

182 181 ICPP voorbeeld taakprioexecutionrelease time d4EEQVE4 c3EVVE2 b2EE2 a1EQQQQE0 ceiling Q = 4 ceiling V = 4

183 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 die preempted wordt komt vooraan de readyqueue te staan. Een thread waarvan de time-slice is verstreken komt achter de threads met gelijke prioriteit op de readyqueue te staan. Sporadic Server Other

184 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

185 184 Huiswerk Boek: H en 11.9 bestuderen. Opgaven 5 en 6 maken.

186 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. 185

187 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. 186 Alle gegeven tijden zijn in ms.

188 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. 187

189 Tentamenopgave 188 iP(i)T(i)C(i)Gebruikt R1, R R2, R3, R R R1


Download ppt "Inhoud RTSYST 0 Week 1Week 2Week 3Week 4Week 5Week 6Week 7."

Verwante presentaties


Ads door Google