Die Präsentation wird geladen. Bitte warten

Die Präsentation wird geladen. Bitte warten

Vorlesung Echtzeitbetriebssysteme VI. Posix Übung

Ähnliche Präsentationen


Präsentation zum Thema: "Vorlesung Echtzeitbetriebssysteme VI. Posix Übung"—  Präsentation transkript:

1 Vorlesung Echtzeitbetriebssysteme VI. Posix Übung
Dr.-Ing. Frank Golatowski

2 Ziele der Vorlesung Erzeugung von Threads
Diskussion von Synchronisationsproblemen Anwendung von: Semaphoren Mutex Condition Variablen

3 Prozesse vs. Threads Kein Speicherschutz innerhalb eines Prozesses
Kommunikation mit anderen Prozessen nur über Betriebssystem oder „Shared Memory“

4 Thread erzeugen pthread_create(thread_id, attr, func, args)
pthread_t pthread_self() Aufruf gibt den eigenen Thread-ID zurück pthread_yield(); informiert den Scheduler, dass der Thread sein Zeitquantum abbrechen will. pthread_exit(status) pthread_join(pthread_id) pthread_detach() Die Exit-Struktur des Threads wird nicht weiter benötigt, durch nachfolgenden pthread_join- Aufrufe. pthread_t me; me = pthread_self(); pthread_t thread; pthread_detach(thread);

5 pthread_create Benötigt vier Argumente
Threadvariable für den Thread Threadattribute Startadresse für den Thread Ein Argument für den Thread pthread_t thread_id; pthread_attr_t attr; void thread_function (void *argument); char *some_arguments pthread_create(thread_id, attr, func, args)

6 Thread Attribute Festlegen von Eigenschaften des Threads in Struktur pthread_attr_t Stacksize Priorität Schedulingverhalten Eigenschaften werden grundsätzlich über Funktionen gesetzt. Initialisierung der Attribut-Struktur vor Benutzung unbedingt erforderlich Beispiel: pthread_attr attr; pthread thread; pthread_attr_init(&attr); pthread_attr_setcpu_np(&attr, 0); pthread_attr_setschedpolicy( &attr, SCHED_RR); pthread_attr_setstacksize(&attr, 20000); pthread_create(&thread, &attr, function, 0 );

7 Hello world void print_message_function( void *ptr ); main() {
pthread_t thread1, thread2; char *message1 = "Hello"; char *message2 = "World"; pthread_create( &thread1, pthread_attr_default, (void*)&print_message_function, (void*) message1); pthread_create(&thread2, pthread_attr_default, (void*)&print_message_function, (void*) message2); exit(0); } void print_message_function( void *ptr ) char *message; message = (char *) ptr; printf("%s ", message);

8 Aufgaben Entwickeln Sie ein Programm „Hello World“, das zwei Threads erzeugt. Der erste Thread soll die Ausgabe „Hello“ und der zweite die Ausgabe „World“ machen. Experimentieren Sie mit unterschiedlichen Ausschriften, z.B. Calling thread : „Ich habe gerade 2 Threads erzeugt!“ Thread 1 : „Ich bin Thread 1 und habe die Aufgabe, die Ausgabe ‚Hello‘ zu machen“ Starten Sie das Programm mehrmals! Nutzen Sie die Online-Dokumentation im Verzeichnis /home/bj74/vorlesung/bs/help!

9 Diskussion des vorherigen Beispiels
Ist die Reihenfolge der Ausschriften gesichert? Hat die Länge der Ausschriften Einfluss auf das Verhalten? Was passiert, wenn der erzeugenden Thread sich vor den Threads mit exit(0) beendet. Welches sind die zwei Wettlaufbedingungen in dem Programm.

10 Hello World, Versuch II Ein Workaround zur Lösung der Wettlaufbedingungen. Vor dem Erzeugen des zweiten Threads und vor dem Aufruf der exit-Funktion eine kleine Verzögerung einfügen.

11 Hello World, Versuch II void print_message_function( void *ptr );
main() { pthread_t thread1, thread2; char *message1 = "Hello"; char *message2 = "World"; pthread_create( &thread1, pthread_attr_default, (void*)&print_message_function, (void*) message1); sleep(10); pthread_create(&thread2, pthread_attr_default, (void*)&print_message_function, (void*) message2); exit(0); } void print_message_function( void *ptr ) char *message; message = (char *) ptr; printf("%s ", message);

12 Diskussion War das gut?

13 Diskussion Es ist niemals sicher, sich auf Zeitverzögerungen zu verlassen, um eine Synchronisation durchzuführen Die Wettlaufbedingungen sind die gleichen wie bei parallelen Prozessen, die sich eine Ressource teilen. Ressource ist hier <stdout>. Mit sleep() wurde ein regelrechter Schnitzer eingefügt: sleep() u. exit() sind Systemrufe, die prozess-spezifisch sind D.h. mit sleep() geht der ganze Prozess schlafen. So dass folglich die gleiche Situation vorherrscht, wie in dem Beispiel ohne sleep().

14 Synchronisation zwischen Erzeuger und den kreierten Threads
pthread_t some_thread; int exit_value; int *exit_status=&exit_value; // do something // create thread // work with thread // exit now pthread_join( some_thread, &exit_status ); Aufrufender Thread wird suspendiert, bis angegebener Thread terminiert Synchronisation möglich, sinnvoll vor allem für exit() Auswertung des Rückgabewertes möglich

15 Verzögerung eines Threads
pthread_delay_np (np = not portable) struct timespec delay; delay.tv_sec = 2; delay.tv_nsec = 0; pthread_delay_np( &delay )

16 Synchronisation: Mutex
Using A Mutex: Accessing Shared Data  In einem multithreaded Programm müssen alle gemeinsam genutzten Daten, durch Anwendung von Mutexen geschützt werden. Nur derjenige Prozess, der im Besitz eines Mutexes ist (es belegt hat), hat Zugriff auf eine gemeinsam genutzte Ressource.

17 Mutex THREAD1 THREAD2 lock Mutex lock Mutex update Data update Data
Unlock Mutex THREAD2 lock Mutex update Data Unlock Mutex Mutex Daten Zustand

18 Mutex-Funktionen Mutexe sichern den Zugang zum kritischen Abschnitt
Posix-Funktionen: pthread_mutex_init(sem, init) pthread_mutex_lock (sem) pthread_mutex_trylock(sem) pthread_mutex_unlock (sem) pthread_mutex_destroy (sem) Achtung: Mutex-Funktionen immer paarweise in möglichst kurzen und übersichtlichen Abschnitten benutzen

19 Mutex Mutex ist eine einfache (speicherlose) Semaphore
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; int pthread_mutex_init ( … ); int pthread_mutex_destroy ( … ); Mutex verwenden: int pthread_mutex_lock ( … ); int pthread_mutex_trylock ( … ); int pthread_mutex_unlock ( … );

20 Beispiel: Puffer mit einem Element. Synchronisation mit Mutex
void reader_function(void); void writer_function(void); char buffer; int buffer_has_item = 0; pthread_mutex_t mutex; struct timespec delay; main() { pthread_t reader; delay.tv_sec = 2; delay.tv_nsec = 0; pthread_mutex_init(&mutex, pthread_mutexattr_default); pthread_create( &reader, pthread_attr_default, (void*)&reader_function,NULL); writer_function(); }

21 Beispiel: Puffer mit einem Element. Synchronisation mit Mutex
void writer_function(void) { while(1) pthread_mutex_lock( &mutex ); if ( buffer_has_item == 0 ) buffer = make_new_item(); buffer_has_item = 1; } pthread_mutex_unlock( &mutex ); pthread_delay_np( &delay ); void reader_function(void) { while(1) pthread_mutex_lock( &mutex ); if ( buffer_has_item == 1) consume_item( buffer ); buffer_has_item = 0; } pthread_mutex_unlock( &mutex ); pthread_delay_np( &delay );

22 Was passiert, wenn pthread_delay_np() nicht benutzt wird?
Mutex Mutex hat nur zwei Zustände: lock und unlock Entspricht einem binären Semaphor. POSIX-Mutexe können mit Condition Variable sinnvoll kombiniert werden. Z.B. derart dass ein Thread, der einem Mutex blockiert, auch auf ein Signal von einem anderen Thread warten kann. Wenn das Signal eintrifft, kann der blockierte Thread aufgeweckt werden und kann versuchen, das relevante Mutex zu belegen Was passiert, wenn pthread_delay_np() nicht benutzt wird?

23 Aufgabe Entwickeln Sie ein Programm, das Asciizeichen von <stdin> einliest. Der Asciicode dieser Zeichen soll in einem zuvor erzeugten Thread ‚reader‘ ausgegeben werden. Wie können die Threads synchronisiert werden? Welche Probleme treten auf?

24 Diskussion Puffer kann nur ein Element aufnehmen
Schnellerer Thread wird auf Geschwindigkeit des langsameren abgebremst Verschwendung von Rechenzeit durch Pollen von buffer_has_item Abbruchfunktionen fehleranfällig/kritisch: void writer_function(void) { char a; while( a != 'q' ) pthread_mutex_lock( &mutex ); if ( buffer_has_item == 0 ) a = make_new_item(); buffer=a; if ( a != 'q' ) buffer_has_item = 1; } pthread_mutex_unlock( &mutex ); void writer_function(void) { while( 1 ) pthread_mutex_lock( &mutex ); if ( buffer_has_item == 0 ) buffer = make_new_item(); if ( buffer =='q' ) break; buffer_has_item = 1; } pthread_mutex_unlock( &mutex ); void writer_function(void) { while( 1 ) pthread_mutex_lock( &mutex ); if ( buffer_has_item == 0 ) buffer = make_new_item(); buffer_has_item = 1; } pthread_mutex_unlock( &mutex ); if ( buffer =='q' ) break; Linkes Beispiel: Funktion wird im kritischen Abschnitt beendet. Unlock fehlt Mittleres Beispiel:

25 Abbruchprobleme void reader_function(void) { while( 1 )
pthread_mutex_lock( &mutex ); if ( buffer_has_item == 1) consume_item(buffer); buffer_has_item = 0; } pthread_mutex_unlock( &mutex ); if ( buffer==‘q‘ ) break; void reader_function(void) { char a; while( 1 ) pthread_mutex_lock( &mutex ); if ( buffer_has_item == 1) a=buffer; if ( a != 'q' ) consume_item(a); buffer_has_item = 0; } pthread_mutex_unlock( &mutex ); void reader_function(void) { char a=0; while( a!= 'q' ) pthread_mutex_lock( &mutex ); if ( buffer_has_item == 1) a=buffer; if ( a != 'q' ) consume_item(a); buffer_has_item = 0; } pthread_mutex_unlock( &mutex ); Linkes Beispiel: Benutzt buffer(Variable des kritischen Abschnitts nach unlock Mittleres Beispiel: Muss unsauber durch vaterthread beendet werden, da Endlossschleife Rechtes Beispiel: ist i.o.

26 Condition Variable Using A Condition Variable:
Scheduling Shared Ressources  Wird verwendet, wenn der Programmierer die Art, wie mehrere Threads auf gemeinsam genutzte Ressourcen zugreifen, beeinflussen will und der typische „Einer-Pro-Zeit“-Gegenseitige Ausschluß nicht ausreicht.

27 Condition Variable while (! predicate_on_condition (s)) pthread_cond_wait (&s, &v); Wird verwendet, um auf das Eintreffen bestimmter Ereignisse zu warten. Es können Informationen übertragen werden, die Auskunft über den Zustand gemeinsam genutzter Daten geben. Ist (im allgemeinen) mit einer einzelnen Bedingung für einer Zustandskomponente verbunden Ist verbunden mit einem Mutex, der den Zugriff auf die Zustandskomponente steuert. Mutex CV1 CV2 Daten Zustand

28 Condition Variable THREAD1 while (P1) wait CV1 Do A1
THREAD3 while (P1) wait CV1 Do B1 Mutex CV1 CV2 Daten Zustand THREAD2 while (P2) wait CV2 Do A2 THREAD4 while (P2) wait CV2 Do B2 THREAD5 update state broadcast CV1 signal CV2

29 Condition Variable Eine CV ist immer mit einem bestimmten Mutex verbunden. Und mit den Daten, die durch das Mutex geschützt sind. Monitorkonzept wird damit realisierbar Ein Monitor besteht aus einem Mutex, Daten und keiner, einer oder mehrerer Bedingungen Es gibt drei Operationen: Wait Signal Broadcast

30 Condition Variablen - Operationen
Wait (pthread_cond_wait) : Signal (pthread_cond_signal) : Broadcast (pthread_cond_broadcast) : TimedWait (pthread_cond_timedwait) : s.S.5 DEC-Paper

31 Beispiel mit Conditions
REACTOR lock state.m while pred (state.data) { wait state.cv } if pred (stat.data) { do actions; unlock state.m ACTOR lock state.m update state.data unlock state.m signal state.cv Mutex CV1 CV2 Daten Zustand

32 Beispiel mit Conditions
Dbuff - Example

33 Beispiel mit zählendem Semaphor
Keine Verschwendung von Rechenzeit zählendes Semaphor void reader_function(void); void writer_function(void); char buffer; // ohne Verzögerung (s. Mutexsample) Semaphore writers_turn; Semaphore readers_turn; main() { pthread_t reader; semaphore_init( &readers_turn ); semaphore_init( &writers_turn ); /* writer must go first */ semaphore_down( &readers_turn ); pthread_create( &reader, pthread_attr_default, (void*)&reader_function,NULL); writer_function(); }

34 Beispiel mit zählendem Semaphor
void writer_function(void) { while(1) semaphore_down( &writers_turn ); buffer = make_new_item(); semaphore_up( &readers_turn ); } void reader_function(void) { while(1) semaphore_down( &readers_turn ); consume_item( buffer ); semaphore_up( &writers_turn ); } Sieht übersichtlicher aus.

35 Aufgabe zu Semaphoren Entwickeln Sie ein Programmm, das zwei Threads „Writer“ und „Reader“ erzeugt. Die Kommunikation zwischen den Threads soll über zählende Semaphoren gelöst werden. Der „Writer“ hat die Aufgabe, eine stetig steigende Integerzahl nach Buffer zu schreiben. Zu beachten ist, dass Buffer eine Variable im kritischen Abschnitt ist! Der „Reader“ soll diese Variable lesen und den Ascii-Code sowie das entsprechende Zeichen ausgeben. Alle Threads sollen sich definiert beenden, wenn Buffer den Wert ‘q‘=112 erreicht hat.

36 Hello World mit zählendem Semaphor
Semaphore child_counter; Semaphore worlds_turn; main() { pthread_t thread1, thread2; char *message1 = "Hello"; char *message2 = "World"; semaphore_init( &child_counter ); semaphore_init( &worlds_turn ); semaphore_down( &worlds_turn ); // world goes second // child_counter now must be up-ed 2 times for a thread blocked on it to be released semaphore_decrement( &child_counter ); // value now 0 semaphore_decrement( &child_counter ); // value now -1 pthread_create( &thread1, pthread_attr_default, void *) &print_message_function, (void *) message1); semaphore_down( &worlds_turn ); pthread_create(&thread2, pthread_attr_default, (void *) &print_message_function, (void *) message2); semaphore_down( &child_counter ); // not really necessary to destroy since we are exiting anyway semaphore_destroy ( &child_counter ); semaphore_destroy ( &worlds_turn ); exit(0); }

37 Hello World mit zählendem Semaphor
void print_message_function( void *ptr ) { char *message; message = (char *) ptr; printf("%s ", message); fflush(stdout); semaphore_up( &worlds_turn ); semaphore_up( &child_counter ); pthread_exit(0); }

38 Deadlock und Liveness Livelock: Prozesse, die für immer busy sind, auf ein spin lock wartend. Deadlock: Prozesse, die für immer suspendiert (schlafend) an einer Condition variable oder einer Lock-Operation sind. Starvation: Prozesse, die für unbestimmte Zeit aufgeschoben sind. Liveness: Prozesse, die frei von Deadlock, Livelock und Starvation sind

39 Sema, Mutex, CV Mit allen kann man sehr leicht Deadlocks produziert werden Es gibt einige Regeln, um Deadlock-Situationen zu reduzieren und einen sensiblen Gebrauch von Semaphor, Mutex und CV zu sichern. Eine Möglichkeit, um Deadlocks zu verhindern ist z.B. die Verwendung des Priority Ceiling Protocols

40 Wie viele und wie groß Mutexe können einzelne Daten oder Programmbereiche schützen Wie dick sollte ein Mutex (d.h. der geschützte Bereich) sein? Wie viele Zustandsvariablen sollten kombiniert werden und durch ein Mutex geschützt werden. Mutexes bewirken, dass Threads warten. Das hat „Kontext switch“ zur Folge, der Zeit kostet. Wenige und große Mutexe sind gut.

41 Speisende Philosphen mit Mutexe

42 Aufgabe Auf der Basis von Posix-Mutexe und Posix-Conditions zählende Semaphore implementieren.

43 Fehlerbehandlung nie vergessen!
pthread_t some_thread; if ( pthread_create( &some_thread, ... ) == -1 ) { perror("Thread creation error"); exit(1); }

44 Thread beenden Beschreibung
Funktion Beschreibung pthread_cancel (thread_id) pthread_setcancelstate(stat, old) pthread_setcanceltype(typ, old) THREAD_CANCEL_ENABLE, THREAD_CANCEL_DISABLE pthread_testcancel() pthread_join pthread_cond_wait pthread_cond_timed_wait phread_testcancel sigwait Sem_wait

45 Thread terminieren Pthread_cleanup_push(func, arg)
Pthread_cleanup_pop(exec) sigwait Sem_wait


Herunterladen ppt "Vorlesung Echtzeitbetriebssysteme VI. Posix Übung"

Ähnliche Präsentationen


Google-Anzeigen