Tafelübung zu BSRvS1 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/teaching/ss2008/bsrvs1/exercises/ 1
Agenda Besprechung Aufgabe 1: Wecker Fortsetzung Grundlagen C-Programmierung Aufgabe 2: Prozesssynchronisation Semaphore (sem* Funktionen) Gemeinsamer Speicher (shm* Funktionen) Makefiles Signalhandler BSRvS1: Übung 02 Prozesssynchronisation 2
Besprechung Aufgabe 1 Foliensatz Besprechung BSRvS1: Übung 02 Prozesssynchronisation 3
Grundlagen C-Programmierung Foliensatz C-Einführung BSRvS1: Übung 02 Prozesssynchronisation 4
Semaphore, semget(2) int semget(schlüssel, Anzahl, Optionen); Abfragen oder Erzeugen von Semaphoren 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 BSRvS1: Übung 02 Prozesssynchronisation 5
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:~/# BSRvS1: Übung 02 Prozesssynchronisation 6
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 Id 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 BSRvS1: Übung 02 Prozesssynchronisation 7
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; BSRvS1: Übung 02 Prozesssynchronisation 8
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 */ BSRvS1: Übung 02 Prozesssynchronisation 9
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 void p() { struct sembuf sop; sop.sem_num = 0; /* Semaphor 0 */ sop.sem_flg = 0; sop.sem_op = -1; /* Dekrement */ if(semop(semid, &sop, 1) == -1) { perror( semop p ); exit(-1); void v() { struct sembuf sop; /* Semaphor 0 */ sop.sem_num = 0; sop.sem_flg = 0; sop.sem_op = 1; /* Inkrement */ if(semop(semid, &sop, 1) == -1) { perror( semop v ); exit(-1); BSRvS1: Übung 02 Prozesssynchronisation 10
Gemeinsamer Speicher 0x00000000 Prozess 1 Prozess 2 0x00000000 0x45a33b1c Shared Memory Segment 0x5516a388 0xffffffff 0xffffffff BSRvS1: Übung 02 Prozesssynchronisation 11
Gem. Speicher, shmget(2) int shmget(schlüssel, Größe, Optionen); Sehr ähnlich zu semget(2) Schlüssel = Schlüssel, der mit Shared-Memory-Segment assoziiert ist Größe = Größe des gemeinsamen Speicherbereiches Optionen = IPC_CREAT, IPC_EXCL, Zugriffsrechte; mit bitweisem ODER ( ) verknüpft Der Speicher wird nicht automatisch initialisiert! Rückgabewert: -1: Fehler bei der Ausführung (z.b. ein gesuchter gemeinsamer Speicher ist nicht verfügbar), Fehlercode in errno abgelegt id: Identifier, der von den weiteren shm* Funktionen verwendet wird benötigte Header: sys/ipc.h, sys/shm.h BSRvS1: Übung 02 Prozesssynchronisation 12
Gem. Speicher, shmget(2), Beispiel #include <sys/shm.h> #include <sys/ipc.h> #include <stdio.h> #include <errno.h> #define KEY 0xc0ffee /* ein Schlüssel für das Shared-Memory-Seg. */ #define RIGHTS 0600 /* volle Zugriffsrechte für den Besitzer*/ int main() { int shmid = 0; /* Größe, abhängig von den ausgetauschten Daten */ int size = sizeof(int[3]); /* gibt es schon den gemeinsamen Speicher? */ shmid = shmget(key, 0, 0); if (shmid < 0) { /* nein, also erzeugen wir uns einen... */ printf( create shared memory segment\n ); shmid = shmget(key, size, IPC_EXCL IPC_CREAT RIGHTS); if (shmid < 0) { perror( shmget ); return -1; return 0; pohl@host:~/# ipcs -m ------ Shared Memory Segments -------- key shmid owner perms nattach 0xc0ffee 69566495 pohl 600 0 pohl@host:~/# BSRvS1: Übung 02 Prozesssynchronisation 13
Gem. Speicher, shmctl(2) int shmctl(shmid, Operation, *Puffer); Operationen auf dem Gemeinsamen Speicher ausführen IPC_STAT: Abfrage von Statusinformationen (wird in den übergebenen Puffer vom Typ struct shmid_ds geschrieben) IPC_SET: Ändern von Nutzer/Gruppe, Berechtigungen (Daten aus Puffer) IPC_RMID: Freigeben des gemeinsamen Speichers Rückgabewerte: -1: Fehler bei der Ausführung, Fehlercode in errno >0: Abhängig von der jeweiligen Operation void shm_free() { if (shmctl(shmid, IPC_RMID, NULL) == -1) { perror( shmctl free ); exit (-1); BSRvS1: Übung 02 Prozesssynchronisation 14
Gem. Speicher, shmat(2) void *shmat(shmid, Adresse, Optionen); Gemeinsamen Speicher in den lokalen Adressraum einbinden ShmId = die von shmget zurückgelieferte Id Adresse = Adresse an der das Shared-Memory-Segment eingebunden werden soll, wenn NULL System wählt Adresse automatisch Optionen = optionale Flags benötigt zusätzlich sys/types.h Rückgabewerte: (void*) -1: Fehler bei der Ausführung, Fehlercode in errno >0: Adresse, an der das Segment eingebunden wurde BSRvS1: Übung 02 Prozesssynchronisation 15
Gem. Speicher, shmdt(2) int shmdt(adresse); Gemeinsamen Speicher aus dem lokalen Adressraum aushängen Adresse = Adresse, an der das Shared-Memory-Segment in den lokalen Adressraum eingebunden ist Rückgabewerte: -1: Fehler bei der Ausführung, Fehlercode in errno 0: Aushängen des gemeinsamen Speichers war erfolgreich BSRvS1: Übung 02 Prozesssynchronisation 16
Gem. Speicher, shm* Beispiel #include <sys/shm.h>... #define KEY 0xc0ffee /* ein Schlüssel für das Shared-Memory-Seg. */ #define RIGHTS 0600 /* volle Zugriffsrechte für den Besitzer*/ int main() { int shmid = 0, *data = NULL, size = sizeof(int[3]); shmid = shmget(key, 0, 0); if (shmid < 0) { perror( shmget ); return -1; data = (int*) shmat(shmid, NULL, 0); /* Gem. Speicher einhängen */ if (data == (void*) -1) { perror( shmat ); return -1; data[0] = 1; data[1] = 2; data[2] = 3; if (shmdt(data) == -1) { /* Gem. Speicher aushängen */ perror( shmdt ); return -1; return 0; BSRvS1: Übung 02 Prozesssynchronisation 17
Gem. Speicher, shm* Beispiel #include <sys/shm.h>... #define KEY 0xc0ffee /* ein Schlüssel für das Shared-Memory-Seg. */ #define RIGHTS 0600 /* volle Zugriffsrechte für den Besitzer*/ int main() { int shmid = 0, *data = NULL, size = sizeof(int[3]); shmid = shmget(key, 0, 0); if (shmid < 0) { perror( shmget ); return -1; data = (int*) shmat(shmid, NULL, 0); if (data == (void*) -1) { perror( shmat ); return -1; printf( data 1-3: %d, %d, %d\n, data[0], data[1], data[2]); if (shmdt(data) == -1) { perror( shmdt ); return -1; if (shmctl(shmid, 0, IPC_RMID) == -1) { /* Gem. Speicher freigeben */ perror( shmctl rm ); return -1; return 0; pohl@host:~/#./test data 1-3: 1, 2, 3 pohl@host:~/# BSRvS1: Übung 02 Prozesssynchronisation 18
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 CFLAGS+=-Werror -ansi -pedantic -D_POSIX_SOURCE # -= Targets =- # Name: <benötigte Dateien und/oder andere Targets> # <TAB> Kommando # <TAB> Kommando... test_1: test_1.c test_1.h # erstes Target = Default Target $(CC) $(CFLAGS) -o test_1 test_1.c test_2: test_1 test_2.c test_2.h # test_2 braucht test_1! $(CC) $(CFLAGS) -o test_2 test_2.c clean: rm -f test_1 test_2 # Verwendung von Shell-Befehlen BSRvS1: Übung 02 Prozesssynchronisation 19
Targets & Abhängigkeiten test_1 test_2 test_1.c test_1.h test_2.c test_2.h # target test_1 test_1: test_1.c test_1.h... # target test_2 test_2: test_1 test_2.c test_2.h... Vergleich von Änderungsdatum der Quell- und Zieldateien Quelle jünger? Neu übersetzen! make durchläuft Abhängigkeitsgraph BSRvS1: Übung 02 Prozesssynchronisation 20
Makefiles und make(1) pohl@host:~/# ls Makefile test_1.c test_1.h test_2.c test_2.h pohl@host:~/# make gcc -Wall -Werror -ansi -pedantic -D_POSIX_SOURCE -o test_1 test_1.c pohl@host:~/# make clean rm -f test_1 test_2 pohl@host:~/# make test_2 gcc -Wall -Werror -ansi -pedantic -D_POSIX_SOURCE -o test_1 test_1.c gcc -Wall -Werror -ansi -pedantic -D_POSIX_SOURCE -o test_2 test_2.c pohl@host:~/# Makefiles ausführen mit make <target> bei fehlendem <target> wird das Default-Target ausgeführt Optionen -f: Makefile angeben; make -f <makefile> -j: Anzahl der gleichzeitig gestarteten Jobs; make -j 3 BSRvS1: Übung 02 Prozesssynchronisation 21
Signalhandler, sigaction(2) int sigaction(signalnr, *neueakt, *alteakt); Im eigenen Prozess Signale verarbeiten (SIGINT, SIGSTOP, etc.) Parameter SignalNr = das Signal das behandelt werden soll *neueakt = Pointer auf struct sigaction mit den Informationen zum neuen Handler (Eingabe!) *alteakt = Pointer auf struct sigaction mit den Informationen zum vorherigen Handler (Ausgabe!), ignoriert bei Übergabe von NULL Benötigt <signal.h> Rückgabewerte: -1: Fehler bei der Ausführung, Fehlercode in errno 0: Erfolgreiche Ausführung BSRvS1: Übung 02 Prozesssynchronisation 22
Signalhandler struct sigaction { void (*sa_handler)(int); /* Zeiger auf die Handler-Funktion */ sigset_t sa_mask; /* Ignoriere Signale bei Behandlung */ int sa_flags; /* Optionen */ void (*sa_restorer)(void); /* veraltet -> IGNORIEREN */ #include <signal.h>... /* die Handler-Funktion für unser Signal */ void handle_abbruch(int sig) { printf("sigint gefangen: %d!\n", SIGINT==sig); exit(2); int main() { struct sigaction action; action.sa_handler = &handle_abbruch; /* die Adresse des Handlers angeben */ sigemptyset(&action.sa_mask); /* keinerlei Signale ignorieren */ if (sigaction(sigint, &action, NULL)) { /* SIGINT Handler registrieren */ perror( sigaction ); return -1; /* Rückgabewert!= 0 => Fehler! */ while(1); return 0; BSRvS1: Übung 02 Prozesssynchronisation 23