Tafelübung zu BSRvS 1 2. Prozesssynchronisation Olaf Spinczyk Arbeitsgruppe Eingebettete Systemsoftware Lehrstuhl für Informatik 12 TU Dortmund olaf.spinczyk@tu-dortmund.de http://ess.cs.uni-dortmund.de/~os/ http://ess.cs.tu-dortmund.de/de/teaching/ss2009/bsrvs1/ 1
Agenda Besprechung Aufgabe 1: At Fortsetzung Grundlagen C-Programmierung Aufgabe 2: Prozesssynchronisation Erzeuger-/Verbraucher-Problem Wiederholung Semaphore (P, V / wait, signal) System V Semaphore (sem*-funktionen) Gemeinsamer Speicher Makefiles BSRvS 1: U2 Prozesssynchronisation 2
Besprechung Aufgabe 1 Foliensatz Besprechung BSRvS 1: U2 Prozesssynchronisation 3
Grundlagen C-Programmierung Foliensatz C-Einführung (Folie 36-47) BSRvS 1: U2 Prozesssynchronisation 4
Erzeuger-/Verbraucher-Problem... mit einelementigem Puffer (Pseudocode!) Erzeuger/Verbraucher nicht synchronisiert Warum ist das ein Problem? Element gemeinsamerpuffer; /* Erzeuger und Verbraucher laufen parallel! */ erzeuger() { while (1) { Element e = produziereelement(); gemeinsamerpuffer = e; /* schreibe in gemeinsamen Puffer */ verbraucher() { while (1) { Element e; e = gemeinsamerpuffer; /* lies aus gemeinsamem Puffer */ verbraucheelement(e); BSRvS 1: U2 Prozesssynchronisation 5
Erzeuger-/Verbraucher-Problem... mit einelementigem Puffer (Pseudocode!) Erzeuger/Verbraucher nicht synchronisiert Elemente gehen verloren oder werden mehrfach verwendet! Element gemeinsamerpuffer; /* Erzeuger und Verbraucher laufen parallel! */ erzeuger() { while (1) { Element e = produziereelement(); gemeinsamerpuffer = e; /* schreibe in gemeinsamen Puffer */ verbraucher() { while (1) { Element e; e = gemeinsamerpuffer; /* lies aus gemeinsamem Puffer */ verbraucheelement(e); BSRvS 1: U2 Prozesssynchronisation 6
E. Dijkstra: Semaphor (semaphore) Eine nicht-negative ganze Zahl, für die zwei unteilbare Operationen definiert sind: P (hol. prolaag, erniedrige ; auch down, wait) -1 hat der Semaphor den Wert 0, wird der laufende Prozess blockiert ansonsten wird der Semaphor um 1 dekrementiert V (hol. verhoog, erhöhe ; auch up, signal) +1 auf den Semaphor ggf. blockierter Prozess wird deblockiert ansonsten wird der Semaphor um 1 inkrementiert Eine Betriebssystemabstraktion zum Austausch von Synchronisationssignalen zwischen nebenläufig arbeitenden Prozessen. BSRvS 1: U2 Prozesssynchronisation 7
Erzeuger-/Verbraucher-Problem... mit einelementigem Puffer mit zwei Semaphore synchronisiert, Operationen P/V (Pseudocode!) Element gemeinsamerpuffer; Semaphore sem_frei = 1, sem_belegt = 0; /* Erzeuger und Verbraucher laufen parallel! */ erzeuger() { while (1) { Element e = produziereelement(); P(sem_frei); gemeinsamerpuffer = e; /* schreibe in gemeinsamen Puffer */ V(sem_belegt); verbraucher() { while (1) { Element e; P(sem_belegt); e = gemeinsamerpuffer; /* lies aus gemeinsamem Puffer */ V(sem_frei); verbraucheelement(e); BSRvS 1: U2 Prozesssynchronisation 8
Erzeuger-/Verbraucher-Problem Was muss man ändern, wenn der Puffer mehr als ein Element aufnehmen kann (z.b. Ringpuffer)? Element gemeinsamerpuffer; Semaphore sem_frei = 1, sem_belegt = 0; /* Erzeuger und Verbraucher laufen parallel! */ erzeuger() { while (1) { Element e = produziereelement(); P(sem_frei); gemeinsamerpuffer = e; /* schreibe in gemeinsamen Puffer */ V(sem_belegt); verbraucher() { while (1) { Element e; P(sem_belegt); e = gemeinsamerpuffer; /* lies aus gemeinsamem Puffer */ V(sem_frei); verbraucheelement(e); BSRvS 1: U2 Prozesssynchronisation 9
Semaphore, semget(2) int semget(schlüssel, Anzahl, Optionen); Weiterverwenden oder Erzeugen von Semaphormengen Schlüssel = Schlüssel, der mit einer Semaphormenge im System assoziiert ist Anzahl = Anzahl der Semaphore in der Menge Optionen = IPC_CREAT, IPC_EXCL, Zugriffsrechte; mit bitweisem ODER ( ) verknüpft Semaphore werden nicht automatisch initialisiert! Rückgabewert: -1: Fehler bei der Ausführung (z.b. eine gesuchte Semaphormenge nicht gefunden), Fehlercode in errno abgelegt id: Identifier, der von den weiteren sem* Funktionen verwendet wird benötigte Header: sys/types.h, sys/ipc.h, sys/sem.h BSRvS 1: U2 Prozesssynchronisation 10
Semaphore, semget(2), Beispiel #include <sys/types.h> #include <sys/sem.h> #include <sys/ipc.h> #include <stdio.h> #include <errno.h> #define KEY 0xcaffee /* ein Schlüssel zur Suche nach Sem.-Mengen */ #define RIGHTS 0600 /* volle Zugriffsrechte für den Besitzer*/ int main() { int semid = 0; /* gibt es schon eine Semaphorenmenge zu diesem Schlüssel? */ semid = semget(key, 0, 0); if (semid < 0) { /* nein, also erzeuge eine Menge mit einem Semaphor... */ printf( create new semaphore\n ); semid = semget(key, 1, IPC_EXCL IPC_CREAT RIGHTS); if (semid < 0) { perror( semget ); return -1; return 0; pohl@host:~/# ipcs -s ------ Semaphore Arrays -------- key semid owner perms nsems 0xcaffee 13172741 pohl 600 1 pohl@host:~/# BSRvS 1: U2 Prozesssynchronisation 11
Semaphore, semctl(2) int semctl(semid, SemNr, Kommando,...); Initialisieren, Abfragen von Semaphoren Kommandos (Auswahl): SETVAL/GETVAL Setzen/Abfragen des Semaphor SemNr SETALL/GETALL Setzen/Abfragen aller Semaphore in der Menge IPC_RMID Freigeben einer Semaphormenge Rückgabewerte: -1: Fehler während der Ausführung, errno enthält Fehlercode >=0: je nach Kommando erwarteter Rückgabewert BSRvS 1: U2 Prozesssynchronisation 12
Semaphore, semctl(2) Beispiel #include <sys/types.h> #include <sys/sem.h> #include <sys/ipc.h> #include <stdio.h> #include <errno.h> #define KEY 0xcaffee /* ein Schlüssel zur Suche nach Sem.-Mengen */ int main() { int semid = 0, retval = 0; /* gibt es schon eine Semaphorenmenge zu diesem Schlüssel? */ semid = semget(key, 0, 0); if (semid > 0) { /* ja, dann initialisieren wir mit 1*/ retval = semctl(semid, 0, SETVAL, (int) 1); if (retval < 0) { perror( semctl SET ); return -1; retval = semctl(semid, 0, GETVAL); if (retval < 0) { perror( semctl GET ); return -1; printf( Semaphore hat Wert %d\n, retval); semctl(semid, 0, IPC_RMID); /* Semaphormenge entfernen */ return 0; BSRvS 1: U2 Prozesssynchronisation 13
Semaphore, semop(2) int semop(semid, *Operationen, AnzahlOPs); Kann eine oder mehrere Operationen auf den Semaphoren einer Semaphormenge ausführen Atomar entweder alle Operationen, oder keine! Rückgabewerte: -1: Fehler während der Ausführung, errno enthält Fehlercode 0: Ausführung erfolgreich Zentrale Struktur: struct sembuf struct sembuf { unsigned short sem_num; /* Semaphor Nr. */ short sem_op; /* Operation auf Semaphor */ short sem_flg; /* optionale Flags */ BSRvS 1: U2 Prozesssynchronisation 14
Semaphore, semop(2), Beispiel 3 Arten von Operationen definiert: sem_op > 0: addiere Wert von sem_op auf den aktuellen Wert des Semaphors Operation wird immer durchgeführt sem_op = 0: sog. wait-for-zero Schlafen bis Semphor = 0 sem_op < 0: - Semaphor >= sem_op Operation wird direkt fortgesetzt, und sem_op vom aktuellen Wert des Semaphors abgezogen - Semaphor < sem_op Operation muss warten Hiermit können wir Dijkstras P() und V() implementieren! struct sembuf sop; sop.sem_num = <Nummer innerhalb der Semaphore-Menge>; sop.sem_op = <siehe oben>; sop.sem_flg = <optionales Flag, siehe Manpage>; if (semop(semid, &sop, 1) == -1) { perror( semop ); exit(-1); BSRvS 1: U2 Prozesssynchronisation 15
Gemeinsamer Speicher 0x00000000 Prozess 1 Prozess 2 0x00000000 0x45a33b1c Shared Memory Segment 0x5516a388 0xffffffff 0xffffffff Systemaufrufe: shmget(2), shmctl(2), shmat(2), shmdt(2) Bereits in der Vorgabe zu A2! BSRvS 1: U2 Prozesssynchronisation 16
Makefiles Bauen von Projekten mit mehreren Dateien Makefile Informationen wie eine Projektdatei beim Bauen des Projektes zu behandeln ist # -= Variablen =- # Name=Wert oder auch # Name+=Wert für Konkatenation CC=gcc CFLAGS=-Wall -ansi -pedantic -D_XOPEN_SOURCE -D_POSIX_SOURCE # -= Targets =- # Name: <benötigte Dateien und/oder andere Targets> # <TAB> Kommando # <TAB> Kommando... (ohne <TAB> beschwert sich make!) all: program1 program2 # erstes Target = Default-Target program1: prog1.c prog1.h $(CC) $(CFLAGS) -o program1 prog1.c program2: prog2.c prog2.h program1 # Abhängigkeit: benötigt program1! $(CC) $(CFLAGS) -o program2 prog2.c BSRvS 1: U2 Prozesssynchronisation 17
Targets & Abhängigkeiten program1 program2 prog1.c prog1.h prog2.c prog2.h # target program1 program1: prog1.c prog1.h... # target program2 program2: prog2.c prog2.h program1... Vergleich von Änderungsdatum der Quell- und Zieldateien Quelle jünger? Neu übersetzen! make durchläuft Abhängigkeitsgraph Java-Pendant: Apache Ant BSRvS 1: U2 Prozesssynchronisation 18
Beispiel: Vorgabe von Aufgabe A2 # -= Variablen =- # Name=Wert oder auch # Name+=Wert für Konkatenation CC=gcc CFLAGS=-Wall -ansi -pedantic -D_XOPEN_SOURCE -D_POSIX_SOURCE # -= Targets =- # Name: <benötigte Dateien und/oder andere Targets> # <TAB> Kommando # <TAB> Kommando... (ohne <TAB> beschwert sich make!) all: primserv primeat # erstes Target = Default-Target primserv: primserv.c primshm.c primshm.h sync.c sync.h $(CC) $(CFLAGS) -o primserv primserv.c primshm.c sync.c primeat: primeat.c primshm.c primshm.h sync.c sync.h $(CC) $(CFLAGS) -o primeat primeat.c primshm.c sync.c clean: rm -f primserv primeat *.o test: all./primserv& sleep 2./primeat.PHONY: clean test # unechte (phony) Targets BSRvS 1: U2 Prozesssynchronisation 19
Makefiles und make(1) rhodos:~/a2 $ make clean rm -f primserv primeat *.o rhodos:~/a2 $ make gcc -Wall -ansi -pedantic -D_XOPEN_SOURCE -D_POSIX_SOURCE -o primserv primserv.c primshm.c sync.c gcc -Wall -ansi -pedantic -D_XOPEN_SOURCE -D_POSIX_SOURCE -o primeat primeat.c primshm.c sync.c rhodos:~/a2 $ touch primserv.c rhodos:~/a2 $ make gcc -Wall (...) -o primserv primserv.c primshm.c sync.c rhodos:~/a2 $ make make: Nothing to be done for `all'. rhodos:~/a2 $ touch sync.h rhodos:~/a2 $ make gcc -Wall (...) -o primserv primserv.c primshm.c sync.c gcc -Wall (...) -o primeat primeat.c primshm.c sync.c rhodos:~/a2 $ Makefiles ausführen mit make <target> bei fehlendem <target> wird das Default-Target (das 1.) ausgeführt Optionen - -f: Makefile angeben; make -f <makefile> - -j: Anzahl der gleichzeitig gestarteten Jobs, z.b. make -j 3 BSRvS 1: U2 Prozesssynchronisation 20