Einführung in POSIX Threads

Ähnliche Dokumente
Einführung in POSIX-Threads. WS 08/09 Universität Innsbruck

PThreads. Pthreads. Jeder Hersteller hatte eine eigene Implementierung von Threads oder light weight processes

Threads Einführung. Zustände von Threads

Tafelübung zu BS 2. Threadsynchronisation

Tafelübung zu BS 2. Threadsynchronisation

Tafelübung zu BS 2. Threadsynchronisation

U9-3 Vergleich von Thread-Konzepten. U9-2 Motivation von Threads. U9-3 Vergleich von Thread-Konzepten (2) U9-1 Überblick

Softwaresysteme I Übungen Jürgen Kleinöder Universität Erlangen-Nürnberg Informatik 4, 2007 U9.fm

POSIX-Threads. Aufgabe 9 SP - Ü U10.1

I 7. Übung. I-1 Überblick. Besprechung Aufgabe 5 (mysh) Online-Evaluation. Posix Threads. Ü SoS I I.1

Shared-Memory Programmiermodelle

Betriebssysteme. Tafelübung 2. Thread-Synchronisation. Olaf Spinczyk.

Besprechung Aufgabe 1: Prozesse verwalten Fortsetzung Grundlagen C-Programmierung Aufgabe 2: Threadsynchronisation

Threads. Foliensatz 8: Threads Folie 1. Hans-Georg Eßer, TH Nürnberg Systemprogrammierung, Sommersemester 2015

U8-1 Motivation von Threads. U8-2 Vergleich von Thread-Konzepten. U8-2 Vergleich von Thread-Konzepten (2) Motivation

U8 POSIX-Threads U8 POSIX-Threads

Pthreads. David Klaftenegger. Seminar: Multicore Programmierung Sommersemester

Übungen zu Systemprogrammierung 1

Linux Prinzipien und Programmierung

Concurrency and Processes of Pthreads

POSIX Solaris thread management pthread_create thr_create pthread_exit thr_exit pthread_kill thr_kill pthread_self thr_self

Vorlesung Betriebssysteme I

Echtzeitbetriebssysteme (am Beispiel QNX) Dr. Stefan Enderle HS Esslingen

Übungen zu Systemprogrammierung 1 (SP1)

Übungen zu Systemprogrammierung 1 (SP1)

Übungen zu Systemnahe Programmierung in C (SPiC) Sommersemester 2018

Inhalt. Übungen zu Systemnahe Programmierung in C (SPiC) Inhalt. Motivation

Threads. Netzwerk - Programmierung. Alexander Sczyrba Jan Krüger

Aufgabe 9: recgrep rekursive, parallele Suche (letzte Aufgabe)

Übungen zu Systemnahe Programmierung in C (SPiC)

Übungen zu Systemnahe Programmierung in C (SPiC) Inhalt. Sebastian Maier (Lehrstuhl Informatik 4) Übung 10. Sommersemester 2016

Besprechung Aufgabe 5 (crawl) POSIX-Threads. Problem: UNIX-Prozesskonzept ist für viele heutige Anwendungen unzureichend

U6-1 Organisatories. U6-2 Motivation von Threads. U6-3 Vergleich von Thread-Konzepten. Organisatorisches

Multithread Programming

Betriebssysteme. Agenda. Tafelübung 3. Deadlock. Olaf Spinczyk.

Systemnahe Programmierung in C Übungen Jürgen Kleinöder, Michael Stilkerich Universität Erlangen-Nürnberg Informatik 4, 2011 U7.fm

Übungen zu Systemprogrammierung 1 (SP1)

Markus Klußmann, Amjad Saadeh Institut für Informatik. Pthreads. von Markus Klußmann und Amjad Saadeh. Pthreads

U7 POSIX-Prozesse U7 POSIX-Prozesse

Eine Mini-Shell als Literate Program

Klausur Betriebssysteme I

User-Level-Threads (Koroutinen) Realisierung von Threads auf Benutzerebene innerhalb eines Prozesses

PROGRAMMIEREN MIT UNIX/LINUX-SYSTEMAUFRUFEN

Leseprobe. Kapitel 12:»Threads« Inhaltsverzeichnis. Index. Die Autoren. Leseprobe weiterempfehlen.

Aufgabenblatt 5 Musterlösung

Alle Fäden in der Hand

Operating Systems Principles. Eventund. Threadbasierte Programmierung

Homogene Multi-Core-Prozessor-Architekturen

Linux Prinzipien und Programmierung

Übungen zu Systemprogrammierung 2 (SP2)

e) Welche Aussage zu Speicherzuteilungsverfahren ist falsch?

fork () Hans-Georg Eßer, Hochschule München Betriebssysteme I, SS Prozesse (2/2) Folie 4

Aufgabenblatt 6 Musterlösung

Lehrstuhl für Datenverarbeitung. Technische Universität München. Leistungskurs C++ Multithreading

Lehrstuhl für Datenverarbeitung. Technische Universität München. Leistungskurs C++ Multithreading

Tafelübung zu BS 1. Prozesse, Shell

Lehrstuhl für Datenverarbeitung. Technische Universität München. Leistungskurs C++ Multithreading

(Ausnahmebehandlung)

Pthreads. David Klaftenegger. Seminar: Multicore Programmierung Sommersemester

Sequentielle Programm- / Funktionsausführung innerhalb eines Prozesses ( thread = Ausführungsfaden )

Globale Variablen Diverses. Globale Variablen. Globale Variablen

Aufgabe 6: palim rekursive, parallele Suche nach einer Zeichenkette

Parallele Prozesse. Prozeß wartet

Tafelübung zu BS 1. Prozesse, Shell

Was ist ein Prozess?

Tafelübung zu BSRvS 1 1. Prozesse, at

Testen nebenläufiger Objekte

Javakurs für Fortgeschrittene

Übungen zu Systemnahe Programmierung in C (SPiC) Inhalt. Moritz Strübe, Rainer Müller (Lehrstuhl Informatik 4) Sommersemester 2014

Tafelübung zu BSRvS 1 3. Kreuzung

Einführung in die Programmiersprache C

Inhaltsverzeichnis. Carsten Vogt. Nebenläufige Programmierung. Ein Arbeitsbuch mit UNIX/Linux und Java ISBN:

2A Basistechniken: Lösungen 2A.1 Wissens- und Verständnisfragen

Objektorientierte Programmierung

RTEMS- Echtzeitbetriebssystem

6 Speicherorganisation

Übung zu Grundlagen der Betriebssysteme. 10. Übung

Institut für Informatik der Ludwig-Maximilians-Universität München Systempraktikum Wintersemester 2009/2010 Prof. Dr. Dieter Kranzlmüller Dr. Thomas S

U3 UNIX-Signale U3 UNIX-Signale

1 Fehler in Bibliotheksfunktionen. 1 Überblick. 2 Ziele der Aufgabe. Besprechung der 1. Aufgabe

Was machen wir heute? Betriebssysteme Tutorium 3. Organisatorisches. Prozesskontrollblock (PCB) Programmieraufgaben. Frage 3.1.a

Einführung in die Programmiersprache C

G 5. Übung. G-1 Überblick. Besprechung 3. Aufgabe. Infos zur Aufgabe 5: fork, exec. Rechenzeiterfassung. Ü SoS I G.1

Systemprogrammierung

Programmiertechnik. Teil 4. C++ Funktionen: Prototypen Overloading Parameter. C++ Funktionen: Eigenschaften

Tafelübung zu BS 1. Prozesse, ToothBrush

Heap vs. Stack vs. statisch. 6 Speicherorganisation. Beispiel Statische Variablen. Statische Variablen

einlesen n > 0? Ausgabe Negative Zahl

Übungen zu Systemprogrammierung 1

Klausur Betriebssysteme I

Heap vs. Stack vs. statisch. 6 Speicherorganisation. Beispiel Statische Variablen. Statische Variablen

Linux Prinzipien und Programmierung

Programmierung mit C Zeiger

PROGRAMMIEREN MIT UNIX/LINUX-SYSTEMAUFRUFEN

Betriebssysteme. G: Parallele Prozesse. (Teil B: Klassische Problemstellungen, Mutual Exclusion, kritische Regionen)

Dynamische Speicherverwaltung

6 Speicherorganisation

Transkript:

Einführung in POSIX Threads Radu Prodan Institut für Informatik, Universität Innsbruck Verteilte und Parallele Systeme http://dps.uibk.ac.at 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 1

Motivation für Nebenläufigkeit Klassischer Ansatz in UNIX-Systemen Mehrere Prozesse durch fork()erzeugen Vorteil: Einfach Nachteile o Verbraucht Ressourcen COW (Copy-on-Write)-Verfahren reduziert diesen Verbrauch etwas o Aufwändiger Context-Switch o Nicht sehr flexibel Rückgabe von Daten des Kindprozesses, IPC 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 2

Threadidee Nur ein Prozess Aber mehrere Ausführungsstränge (Threads) Trennung o Prozess entspricht einer Einheit für die Ressourcenverwaltung o Thread entspricht einer Einheit für das Scheduling 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 3

Thread Eigenschaften Gemeinsame Ressourcen für alle Threads o Adressraum o Offene Dateien (Filedeskriptoren) o Signal-Handler und Einstellungen o Benutzer- und Gruppenkennung o Arbeitsverzeichnis Thread-eigene Elemente o Eigene Thread-Kennung (Thread-ID) o Eigene Kopie der Prozessorregister (einschließlich Programmzähler und Stackpointer) o Eigener Stack (für lokale Variablen und Rückgabeadressen) o Eigene Signalmaske o Eigene Priorität o Eigene errno-variable 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 4

Threads (Wiederholung) Arten von Threads o Benutzer-Threads (Green Threads) o Kernel-Threads (Native Threads) o Hybride Threads Anwendungen o GUIs (Interaktion und Berechnungen im Hintergrund trennen) o Server (z.b. ein Thread pro Aufruf) o Multiprozessormaschinen ausnutzen o Ereignisbasierte Systeme 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 5

Vorteile von Threads Schnelle Erzeugung o Auch für fein-körnige Parallelisierung von kleinen Codeteilen geeignet Schnelles Context-Switching Gemeinsamer Speicher für schnelle Kommunikation o Muss aber entsprechend synchronisiert werden Wenn ein Thread blockiert, können andere Threads weiterarbeiten o Nur bei Kernel-Threads Asynchrone Kommunikation mittels Threads Nachteile o Kein Speicherschutz o Threads eines Prozesses sind nicht voneinander geschützt 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 6

POSIX-Threads Der POSIX-Standard definiert eine einheitliche API für das Erzeugen und Manipulieren von Threads o Unabhängig von der Implementierung o Kann unterschiedlich sein (Kernel-Threads, Benutzer-Threads) Bibliotheken, die diese Threads implementieren, werden oft als Pthreads bezeichnet Verbreitung o UNIX-Systeme wie Linux oder Solaris o Auch Windows-Implementierung erhältlich Wichtige generelle Informationen o Alle Thread-Funktionen und Datentypen sind in pthread.h deklariert o Die Thread-Funktionen befinden sich nicht in der C-Standardbibliothek, sondern in libpthread.so o Daher muss man beim Linken lpthread angeben 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 7

Thread Erzeugen int pthread_create(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *), void *arg); Erzeugt und startet einen neuen Thread (Unterschied zu Java!) o Nach erfolgreicher Erzeugung beginnt dieser Thread asynchron mit seiner Ausführung Rückgabewert o 0 wenn ok, ansonsten Fehlercode (nicht -1 und nicht in errno gesetzt) thread zeigt auf die ID des neuen Threads attr spezifiziert bestimmte Attribute o Stackgröße, Priorität, o NULL wenn die Standardattribute benutzt werden start_routine ist die Funktion, die der Thread aufruft wenn er gestartet wird o Nur ein void * Argument arg 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 8

Zugriff auf Thread-ID pthread_t pthread_self(void); int pthread_equal(pthread_t t1, pthread_t t2); pthread_self retourniert die ID des aufrufenden Threads o Hier sind keine Fehler definiert o Ähnlich getpid() bei Prozessen pthread_equal gibt einen Wert ungleich 0 wenn die Threads t1 und t2 gleich sind, ansonsten 0 o Keine Fehler definiert 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 9

exit und cancel void pthread_exit(void *value_ptr); void pthread_cancel(pthread_t thread); pthread_exit terminiert die Ausführung des aktuellen Threads o Explizites Terminieren o Ein Thread terminiert immer, wenn er aus seiner Startroutine zurückkehrt value_ptr ist der Rückgabewert des Threads o Die von pthread_create aufgerufenen Funktion gibt void * zurück!! value_ptr kann von einem Thread, der pthread_join aufruft, benutzt werden pthread_cancel kann einen anderen Thread terminieren o Vorsicht! 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 10

pthread_join int pthread_join(pthread_t thread, void **value_ptr); Auf Threads, die noch nicht detach aufgerufen haben, kann man mit pthread_join warten o pthread_join führt dazu, dass der Aufrufer auf das Beenden des angegebenen Threads wartet o Ist vergleichbar mit waitpid bei Prozessen o Der Rückgabewert des beendeten Threads wird an die Adresse geschrieben auf die der Parameter value_ptr zeigt Wird nicht gewartet, dann bleiben die Thread-ID und der Exit-Status solange erhalten bis pthread_join aufgerufen wird (vergleiche Zombie-Prozess) pthread_join kann nur von einem Thread für eine bestimmte Thread-ID aufgerufen werden o Nachfolgende Aufrufe resultieren in einem Fehler (von 0 verschiedener Rückgabewert) 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 11

pthread_detach int pthread_detach(pthread_t thread); Thread ist entweder verknüpfbar (joinable) oder eigenständig (detached) Wenn ein Thread beendet wird (Beendigung der ausgeführten Funktion), gibt er normalerweise noch nicht seine Ressourcen frei Dafür muss man detach aufrufen o Dann werden die Thread-ID und der Exit-Status sofort freigegeben o Aufruf von pthread_join nicht mehr möglich Um einen Thread bereits bei seiner Kreierung als abgehängten Thread festzulegen, steht das Thread-Attribut PTHREAD_CREATE_DETACHED zur Verfügung Wird auf einen Thread schon gewartet, dann hat dieser Funktionsaufruf keine Auswirkung 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 12

Beispiel (1) #include <stdio.h> #include <pthread.h> struct data { int nr; int zeit; }; void *rennen(void *arg) { int meter = 0, sek; struct data *data_zgr = (struct data *) arg; while (meter < 50) { sleep(sek = rand()%3+1); data_zgr->zeit += sek; meter += 5; fprintf(stderr, "%*s%3d\n", data_zgr->nr * 15-7, " ", meter); } fprintf(stderr, "%*s\n", data_zgr->nr * 15, "--------"); } return arg; /* auch möglich: pthread_exit( arg ); */ 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 13

Beispiel (2) int main(void) { int i, j; pthread_t id[3]; struct data *renner_daten[3]; srand(time(null)); for (i=0; i<3; i++) renner_daten[i] = calloc( 1, sizeof(struct data) ); printf("%15s%15s%15s\n", "Laeufer 1", "Laeufer 2", "Laeufer 3"); printf("------------------------------------------------\n"); for (i=0; i<3; i++) { renner_daten[i]->nr = i+1; pthread_create(&id[i], NULL, &rennen, renner_daten[i]); } for (i=0; i<3; i++) pthread_join(id[i], (void **) &renner_daten[i]); Aufruf für Übersetzung (Programmname ist thread1.c) gcc lpthread o thread1 thread1.c } for (i=0; i<2; i++) for (j=i+1; j<3; j++) if (renner_daten[i]->zeit > renner_daten[j]->zeit) { struct data h = *renner_daten[i]; *renner_daten[i] = *renner_daten[j]; *renner_daten[j] = h; } fprintf(stderr, "Zieleinlauf:\n"); for (i=0; i<3; i++) fprintf(stderr, "%5d: Laeufer %d (%2d Sek.)\n", i+1, renner_daten[i]->nr, renner_daten[i]->zeit); return 0; 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 14

Ausgabe (Beispiele) Laeufer 1 Laeufer 2 Laeufer 3 ------------------------------------------ 5 5 5 10 10 10 15 15 15 20 20 20 25 25 25 30 30 30 35 35 35 40 45 40 50 -------- 40 45 45 50 -------- 50 -------- Zieleinlauf: 1: Laeufer 2 (14 Sek.) 2: Laeufer 3 (17 Sek.) 3: Laeufer 1 (20 Sek.) Laeufer 1 Laeufer 2 Laeufer 3 ------------------------------------------ 5 5 5 10 10 10 15 15 20 25 15 20 25 30 20 30 35 35 25 40 45 40 30 50 -------- 45 35 50 -------- 40 45 50 -------- Zieleinlauf: 1: Laeufer 1 (17 Sek.) 2: Laeufer 2 (20 Sek.) 3: Laeufer 3 (24 Sek.) 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 15

Nebenläufigkeitsprobleme Zugriff auf gemeinsame Ressourcen (z.b. Dateien oder globale Variablen) muss synchronisiert werden Ansonsten kann es zu Synchronisationsproblemen kommen o Race conditions o Dafür gibt es Mutexe o Es existiert eine eigene Bibliothek dafür Wenn mehrere Threads mit der gleichen Funktion aufgerufen werden, dann führen sie den gleichen Code aus Wenn Funktionen in Threads aufgerufen werden, dann muss immer überprüft werden, ob diese thread-safe sind 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 16

Mutual Exclusion Für die Synchronisation von Threads stehen sogenannte Mutexe zur Verfügung Mutexe erlauben es, Codeteile für andere Threads zu sperren, wenn ein Thread diesen Codeteil gerade ausführt Zwei Arten o Statische Mutexe o Dynamische Mutexe 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 17

Statische Mutexe int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_unlock(pthread_mutex_t *mutex); int pthread_mutex_trylock(pthread_mutex_t *mutex); Ein Mutex hat den Datentyp pthread_mutex_t Ist eine Mutex-Variable statisch definiert, dann muss man diese mit der Konstante PTHREAD_MUTEX_INITIALIZER initialisieren pthread_mutex_lock o Sperrt einen Mutex oder blockiert solange, bis der Mutex freigegeben wird pthread_mutex_unlock o Gibt den Mutex wieder frei pthread_mutex_trylock o Sperrt einen Mutex, wenn es frei ist o Kehrt sofort wieder mit einem entsprechenden Fehlercode (EBUSY) zurück, wenn der Mutex schon gesperrt ist 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 18

Dynamische Mutexe int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr); int pthread_mutex_destroy(pthread_mutex_t *mutex); Dynamische Mutexe werden zum Beispiel benötigt, wenn sich die Mutex-Variable in einer Struktur befindet, zu der man sich mittels malloc einen Speicherbereich allozieren lassen möchte pthread_mutex_init initialisiert den Mutex mutex mit den in mutexattr festgelegten Attributen o Wird bei den Attributen NULL angegeben, dann werden die voreigenstellten Attribute verwendet pthread_mutex_destroy löscht einen mit pthread_mutex_init angelegten Mutex und gibt die belegten Ressourcen wieder frei 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 19

Einsatz von Mutexe Codeteile, die durch einen Mutex geschützt werden, sollte nur so groß sein, wie notwendig Mutexe machen ein Programm langsamer o Sperren und Freigeben kosten Zeit Mutexe serialisieren die Programmausführung o Wenn Threads häufig auf Mutexe zugreifen, werden Sie meist auch viel Zeit beim Zugreifen und Freigeben verbringen o Es sollten nur notwendige Teile geschützt werden o Mehreren kleinere Codeteile durch mehrere verschiedene Mutexe schützen Praktische Vorgehensweise o Zuerst größere Codeteile durch Mutexe schützen o Schrittweise Verkleinerung der Codeteile, wenn die Performanz Probleme bereitet 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 20

Deadlocks und Backoff-Algorithmus Wenn zwei Threads gegenseitige Abhängigkeiten haben, dann können Deadlocks auftreten o Threads versuchen nacheinander die gleichen Mutexe zu sperren o Die Sperrreihenfolge unterscheidet sich, es gibt aber zyklische Abhängigkeiten Eine einfache Möglichkeit für die Vermeidung von Deadlocks bietet der sogenannte Backoff-Algorithmus o Erster Mutex wird mit pthread_mutex_lock gesperrt o Alle nachfolgenden Mutexe versucht man mit pthread_mutex_trylock zu sperren o Ist ein Mutex gesperrt, dann werden rückwärts alle zuvor gesperrten Mutexe wieder freigegeben 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 21

Beispiel: ohne Mutex #include <pthread.h> #include <stdio.h> #define ANZAHL 100000 /* eventuell höher setzen */ int zaehler; /* wird durch die Threads hochgezählt */ void *zaehl_thread(void *); int main(void) { pthread_t id1, id2; pthread_create(&id1, NULL, &zaehl_thread, NULL); pthread_create(&id2, NULL, &zaehl_thread, NULL); pthread_join(id1, NULL); pthread_join(id2, NULL); fprintf(stderr, "zaehler = %d\n", zaehler); return 0; } void *zaehl_thread(void *unbenutzt) { int i, zahl; for (i=0; i < ANZAHL; i++) { zahl = zaehler; fprintf(stderr, "\r %7d", zahl+1); zaehler = zahl + 1; } } Beispiel für Ausgabe: 48756... Thread 1074792800 fertig (zaehler = 48755) 100708... Thread 1075845472 fertig (zaehler = 100708) zaehler = 100708 fprintf(stderr, "... Thread %d fertig (zaehler = %d)\n", (int) pthread_self(), zaehler); return NULL; 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 22

Beispiel: mit Statischem Mutex #include <pthread.h> #include <stdio.h> #define ANZAHL 100000 /* eventuell höher setzen */ int zaehler; /* wird durch die Threads hochgezählt */ pthread_mutex_t zaehler_mutex = PTHREAD_MUTEX_INITIALIZER; void *zaehl_thread(void *); int main(void) { pthread_t id1, id2; pthread_create(&id1, NULL, &zaehl_thread, NULL); pthread_create(&id2, NULL, &zaehl_thread, NULL); pthread_join(id1, NULL); pthread_join(id2, NULL); fprintf(stderr, "zaehler = %d\n", zaehler); return 0; } void *zaehl_thread(void *unbenutzt) { int i, zahl; for (i=0; i<anzahl; i++) { pthread_mutex_lock(&zaehler_mutex); zahl = zaehler; fprintf(stderr, "\r %7d", zahl+1); zaehler = zahl + 1; pthread_mutex_unlock(&zaehler_mutex); } } Beispiel für Ausgabe: 165106... Thread 1074792800 fertig (zaehler = 165106) 200000... Thread 1075845472 fertig (zaehler = 200000) zaehler = 200000 fprintf(stderr, "... Thread %d fertig (zaehler = %d)\n", (int)pthread_self(), zaehler); return NULL; 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 23

Bedingungsvariablen Mutexe dienen nur zur Synchronisation des Zugriffs auf gemeinsame Ressourcen Manchmal möchte man beim Warten darüber informiert werden, dass eine bestimmte Bedingung eingetreten ist, durch die der Thread fortfahren kann Mit Bedingungsvariablen kann der Thread auf das Eintreten einer Bedingung warten Eine Bedingungsvariable hat immer den Typ pthread_cond_t Es gibt zwei Arten o Statische Bedingungsvariablen o Dynamische Bedingungsvariablen Ähnlich zu wait/notify in Java 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 24

Statische Bedingungsvariable Müssen mit PTHREAD_COND_INITIALIZER initialisiert werden int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); o Der aufrufende Thread muss den Mutex mutex gesperrt haben o Er gibt den Mutex frei und wartet, bis die Bedingungsvariable cond erfüllt ist o Beim Zurückkehren wird wieder automatisch die Sperre eingerichtet int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime); o Wartet nur eine bestimmte Zeitspanne, danach wird wieder die Sperre eingerichtet int pthread_cond_signal(pthread_cond_t *cond); o Weckt einen wartenden Thread (für cond) auf (nach Priorität sortiert) int pthread_cond_broadcast(pthread_cond_t *cond); o Weckt alle Threads auf, die auf cond warten 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 25

Statische Bedingungsvariable: Schlechtes Beispiel #include <pthread.h> #include <stdio.h> #include <unistd.h> #include <string.h> #define AMPEL_ZAHL 4 #define AMPEL_AUSGABE { \ int i; \ for (i = 0; i < AMPEL_ZAHL; i++) \ fprintf(stderr, "%10s", farbe[i]); \ fprintf(stderr, "\n"); \ } pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; char farbe[ampel_zahl][10]; void *ampel(void *kennung); int main(void) { pthread_t id[ampel_zahl]; int i; for (i = 0; i < AMPEL_ZAHL; i++) { strcpy(farbe[i], "rot"); pthread_create(&id[i], NULL, &ampel, &i); } pthread_cond_signal(&cond); pthread_exit(null); } 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 26

Statische Bedingungsvariable: Schlechtes Beispiel (2) void *ampel(void *kennung) { int i, nr = *(int *) kennung; for (i = 1; i <= 2; i++) { pthread_mutex_lock(&mutex); pthread_cond_wait(&cond, &mutex); } sleep(1); strcpy(farbe[nr], "gelb"); AMPEL_AUSGABE sleep(1); strcpy(farbe[nr], "gruen"); AMPEL_AUSGABE sleep(1); strcpy(farbe[nr], "gelb"); AMPEL_AUSGABE sleep(1); strcpy(farbe[nr], "rot"); pthread_mutex_unlock(&mutex); pthread_cond_signal(&cond); } return NULL; Ausgabe: gelb rot rot rot gruen rot rot rot gelb rot rot rot rot gelb rot rot rot gruen rot rot rot gelb rot rot rot rot gelb rot rot rot gruen rot rot rot gelb rot rot rot rot gelb rot rot rot gruen rot rot rot gelb gelb rot rot rot gruen rot rot rot gelb rot rot rot rot gelb rot rot rot gruen rot rot rot gelb rot rot rot rot gelb rot rot rot gruen rot rot rot gelb rot rot rot rot gelb rot rot rot gruen rot rot rot gelb 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 27

Dynamische Bedingungsvariable Wie bei Mutexe o Wenn sich zum Beispiel die Bedingungsvariable in einer Struktur befindet int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *attr); o Initialisierung der Bedingungsvariable cond o attr legt die Attribute fest (NULL für Standardattribute) int pthread_cond_destroy(pthread_cond_t *cond); o Löscht Bedingungsvariable o Gibt belegte Ressourcen wieder frei 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 28

Abbruchaufforderungen von Threads Kein Abbruch o Aufforderungen werden ignoriert, bleiben aber bestehen (später reagieren) o pthread_setcancelstate(pthread_cancel_disable, ) o pthread_setcancelstate(pthread_cancel_enable, ) Abbruch mit Verzögerung o Thread läuft weiter und wird erst später (Abbruchpunkt) abgebrochen o pthread_setcanceltype(pthread_cancel_deferred, ) Asynchroner Abbruch o Thread beendet sich unmittelbar nach dem Empfang der Abbruchaufforderung o pthread_setcanceltype(pthread_cancel_asynchronous, ) 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 29

Abbruchfunktionen von Threads int pthread_cancel(pthread_t id); o Schickt Abbruchaufforderung an einen Thread void pthread_testcancel(void); o Prüft ob Abbruchaufforderungen vorliegen o Realisiert eigene Abbruchpunkte (weitere existieren bei bestimmten Funktionsaufrufen) o Wenn ja, dann wird abgebrochen 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 30

Exit-Handler Eine Thread kann einen Exit-Handler installieren oder deinstallieren Der Exit-Handler wird zum Beispiel für Aufräumarbeiten verwendet pthread_cleanup_push(void (*routine) (void *), void *arg); o Installiert Funktion routine mit dem Argument arg pthread_cleanup_pop(int execute); o Entfernt zuletzt installierten Exit-Handler o Ist execute = 0, dann wird der Handler nur entfernt o Ist execute!= 0, dann wird der Handler ausgeführt und entfernt 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 31

Notwendige POSIX-Erweiterungen Verwendung von Pthreads hat auch Auswirkung auf das traditionelle UNIX-Prozesskonzept fork o Ruft ein Thread fork auf, dann existiert im Kindprozess nur der Thread, der fork aufrief o Thread des Kindprozesses hat den selben Zustand wie der Thread im Elternprozess Hat zum Beispiel die gleichen Mutexe gesperrt o Deadlock-Problematik bei Mutex-Variablen Anderer Thread hält eine Sperre auf einen Mutex Dieser Thread ist aber nach dem fork im Kindprozess nicht vorhanden Lösung o Spezielle Funktion für das Registrieren von Handler-Funktionen o Hier gibt man meist die in prepare geschlossenen Mutexe wieder frei 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 32

pthread_atfork pthread_atfork (void (*prepare) (void), void (*parent) (void), void (*child) (void)); prepare o Wird vom Elternprozess vor der Erzeugung des neuen Prozesses aufgerufen o Hier sperrt man meist die betreffenden Mutexe parent o Wird vom Elternprozess aufgerufen, unmittelbar bevor fork zurückkehrt o Die von prepare gesperrte Mutexe werden freigegeben child o Wird vom Kindprozess aufgerufen, unmittelbar bevor der fork-aufruf zurückkehrt o Meist identisch zu parent (Mutexe freigeben) Mehrere Aufrufe o prepare in LIFO-Ordnung o parent und child in FIFO-Ordnung 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 33

exec und exit exec o Ersetzt den ganzen Prozess o Threads werden automatisch beendet o Keine Exit-Handlers werden ausgeführt o Mutexe und Bedingungsvariablen verschwinden auch (außer das pshared-attribut ist gesetzt) exit o Alle Threads im entsprechenden Prozess werden beendet o Wenn sich der main-thread beendet, werden auch alle Threads beendet 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 34

Thread-Sichere und Reentrant Funktionen C89-Funktionen wurden noch ohne Thread-Implementierung entwickelt Die meisten sind aber mittlerweile Thread-sicher gemacht worden o Keine Prototyp-Änderung o Können auch bei Threads verwendet werden Bei manchen Funktionen war eine Thread-sichere Reimplementierung bei Beibehaltung der externen Schnittstelle nicht möglich o Daher wurden Thread-sichere Varianten eingeführt, die das Suffix _r am Ende ihres Namens haben o Beispiele: strtok_r, rand_r, etc. o Diese Funktionen sind nicht nur Thread-sicher sondern auch reentrant, da sie keine internen statischen Puffer verwenden o Aufrufer muss Pufferadresse mitgeben, an die dann die Informationen geschrieben werden 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 35

Signale Jeder Thread kann eine eigene Signalmaske haben o Setzen mit pthread_sigmask o Beim Kreieren erbt ein Thread die Signalmaske des Threads, der ihn kreiert o Vom main-thread können alle Threads erben Signal-Handler gelten aber prozessweit für alle Threads Signale, die von der Hardware geschickt werden, werden immer dem Thread geschickt, der sie auslöst Andere Signale werden einem beliebigen Thread geschickt o An Threads können Signale mit pthread_kill geschickt werden o Signale, die Auswirkung auf das Prozessverhalten haben, wirken sich bei pthread_kill immer auf den ganzen Prozess aus z. B. SIGKILL beendet den ganzen Prozess o Um auf das Eintreffen eine Signals zu warten, stehen die sigwait, sigwaitinfo (setzt auch errno), sigtimedwait (setzt auch errno) zur Verfügung 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 36

Weitere Synchronisierungsmöglichkeiten Semaphore o Wurde schon in der IPC-Vorlesung kurz genannt o In diesem Fall die Semaphore aus semaphore.h Read/Write-Sperren o Mehrere gleichzeitige Leser erlaubt o Nur ein Schreiber erlaubt Barrier o Synchronisationspunkt, der erst dann freigegeben wird, wenn eine bestimmte Anzahl von Threads an dieser Barriere angekommen sind Spinlocks o Zur Synchronisation auf Multiprozessorsystemen 04.05.2012 R. Prodan, Betriebssysteme, Sommersemester 2012 37