Die Präsentation wird geladen. Bitte warten

Die Präsentation wird geladen. Bitte warten

Beendigung von Prozessen Normale Beendigung –durch Ende von main –durch Aufruf von exit() Anormale Beendigung –Aufruf von abort() –interne oder externe.

Ähnliche Präsentationen


Präsentation zum Thema: "Beendigung von Prozessen Normale Beendigung –durch Ende von main –durch Aufruf von exit() Anormale Beendigung –Aufruf von abort() –interne oder externe."—  Präsentation transkript:

1 Beendigung von Prozessen Normale Beendigung –durch Ende von main –durch Aufruf von exit() Anormale Beendigung –Aufruf von abort() –interne oder externe Signale

2 Exit Status Jeder Prozess hat Exit Status, der an aufrufenden Prozess (meist shell) zurückgegeben wird. Exit Status undefiniert wenn: –automatische Rückkehr durch Ende von main() –Aufruf von return in main // ohne Angabe von Rückgabewert –Aufruf von exit; // ohne RetWert $? gibt exit status des letzten von shell gestarteten Kommandos zurück: echo $? Programmierer sollte immer Exit Status definieren: exit(0); return(0)

3 Verwaiste Kindprozesse Elternprozess wird beendet, bevor alle Kindprozesse beendet sind. init Prozess wird neuer Elternprozess.

4 Zombie-Prozesse Elternprozess kann mit wait bzw waitpid Beendigungsstatus (~exit status) von Kindprozess erfragen (und auf Kind warten wenn Kind läuft). Wenn Elternprozess nicht auf Beendigung von Kind wartet (mit wait), wie kann er (später) Beendigungsstatus von Kind erfahren? Lösung: zombie-Prozess: System speichert bei Beendigung von Prozess, auf den nicht gewartet wird, Informationen (u.a. exit stat, PID, CPU-Zeit)

5 Verhindern von Zombies Einbauen von Signalhandler der SIGCHLD abfängt und gleich wait() aufruft. Siehe Später! fork zweimal aufrufen: Programm kreirt Kindprozess der wiederum Kind kreirt bevor er sich beendet. Enkelprozess ist nun verwaist und bekommt init als Vater. init ruft bei Ende von Kind immer wait auf!

6 Warten auf Beendigung #includes: sys/types.h und sys/wait.h pid_t wait(int *status); pid_t waitpid(pid_t pid, int *status,int options) Verhalten der Funktionen: –Wenn ein Kind bereits früher beendet, kehrt wait(pid) sofort zurück; Rückgabewert: PID des beendeten Kinds –Rückkehr mit Fehler wenn keine Kinder existieren –Blockierung, wenn alle Kinder noch aktiv

7 Unterschiede wait/ waitpid wait wartet nur auf nächste Beendigung von beliebigem Kind kann aufrufenden Prozess blockieren waitpid wartet auf Beendigung von bestimmtem Kind mit Option kann Blockierung unterbunden werden

8 Beendigungsstatus int *status: call by reference Beendigungsstatus ist binärcodiert in Makros definiert, mit denen man Code interpretierten kann: WIFEXITED(status): liefert TRUE, wenn Kind sich normal beendet hat. WIFEXITSTATUS liefert Exit- Status. WIFSIGNALED TRUE, wenn Kind durch Signal (anormal) beendet. Mit WTERMSIG kann Signalnummer erfragt werden. WIFSTOPPED: Kind wurde angehalten. WSTOPSIG(status) liefert Nummer des Signals, das Prozess angehalten hat. (z.B ctrl-Z)

9 waitpid mit waitpid() kann auf bestimmten Prozess gewartet werden: pid_t waitpid(pid_t pid, int *status,int options) pid == -1: identisch zu wait=warten auf beliebigen Prozess pid > 0 auf Kind mit pid warten. ( pid < -1 Auf Kind warten, dessen ProzessgruppenID gleich |pid| ist ) options: Konstanten mit OR | verknüpfen: WNOHANG: Wenn Kind mit pid nicht verfügbar blockiert Prozess nicht sondern leifert 0 als Returnwert.

10 IPC Mechanismen in Linux Signale message queues shared memory semaphore pipes

11 Signale Interrupts die von HW oder SW erzeugt werden, wenn bei Programmausführung Ausnahmesituationen auftreten: – z.b. Division durch 0 (SIGFPE) –Drücken von Strg-C –Zugriff auf unerlaubte Speicheradr. (segment violation: SIGSEGV) –Signale von der Funktion kill ( -TERM -KILL –9) –SW-Signale (z.b. Schreiben in pipe zu der kein Leser existiert oder Ablauf von Wecker (SIGALRM) in /usr/include/linux/signal.h sind Namen aller signale aufgelistet.

12 Einschub: Info GL: Funktionspointer Man kann einer Funktion eine andere Funkt. als Parameter mit call by refrc übergeben für generische Programme zuerst Typ für Header deklariern typedef int cmp(int x,int y); –Funktion hat zwei int als Parameter und gibt int zurück

13 Funktionspointer typedef int cmp(int x,int y); Extremus(cmp *cmpFunc; int mAr[LENGTH] { int ret; int extr = mAr[0]; for (i=1; i <LENGTH; i++) if (cmp(mAr[i],extr) == 0) ret = mAr[i]; return ret; } main() { ret =Extremus(min,mAr[LENGTH]); printf (“min %d”,ret); ret=Extremus( max,mAr[LENGTH]); printf (“max %d”,ret); int min(int x,int y) { if (x<y) return 0 else return 1; } int max(int x,int y) { if (x>y) return 0 else return 1; }

14 Signalkonzept Da Signale asynchron auftreten (man weiss nicht, wann Signal auftritt), kann man nicht Variable verwenden um Wert von signal abzufragen. Stattdessen: Einrichten eines Signalhandlers: Wenn bestimmtes Signal auftritt tue folgendes! Wenn bestimmtes Signal auftritt rufe Funktion auf! Angabe von Signalnummer und Funktion siehe Bild an Tafel void (*signal(int signr, void (*sighandler)(int)))(int); (nächste Folie einfacher!!)

15 Deklaration von signalhandler Vereinfachung durch typedef void sigfunk(int) statt void (*signal(int signr, void (*sighandler)(int)))(int); sigfunk *signal(int signr, sigfunk *sighandler); signr legt Nummer des Signals fest, für das man Signalhandler einrichtet. sighandler ist Funktion, die in Zukunft bei Auftreten von signr aufgerufen wird. Rückgabewert ist bisheriger sighandler oder SIG_ERR

16 Bsp: Abfangen von Signal für Division durch Null #include static void null_division(int sig); main ()..................... if (signal(SIGFPE,null_division) == SIG_ERR) printf(„signalhandler konnte nicht eingerichtet werden“);.............// Ab híer wird null_division aufgerufen wenn Signal gesendet! } void null_division(int sig) { // Für Dauer der Funktion müssen weitere SIGFPE ignoriert werden! signal(SIGFPE, SIG_IGN); printf(„division durch null“); }

17 sighandler Signal ignorieren (SIG_IGN): für alle signale auuser SIGKILL und SIGSTOP möglich. Achtung: ignorieren von „ernstzunehmenden“ signalen wie SIGSEGV kann zu Absturz führen Default-Aktion einrichten (SIG_DFL): für meisten Signale ist default Beendigung von Prozess Pointer auf Funktion, die aufzurufen ist, wenn Signal signr auftritt: –Meistens selbstgeschriebene Funktion.

18 SignalbehandlungsFunktionen –z.b. cleanup() wird aufgerufen, wenn Abbruchsignal geschickt. cleanup gibt Speicher frei, schliesst Dateien –z.b. Abfangen des Signals SIGCHLD, das bei Beendigung von Kindprozess an Vater gesendet wird. Hier sollte Vater waitpid aufrufen, um Zombie zu vermeiden.

19 Senden von Signal: kill int kill (pid_t pid,int signr); pid > 0 Signal signr wird an Prozess mit PID pid geschickt. ( pid < -1 Signal an ProzessgruppenID gleich |pid|) pid == -1: Broadcast Signale. An alle Prozesse int raise(int signr) schickt signal an sich selber

20 Probleme mit der signal Funktion Signalkonzept ist unzuverlässig: Erfragen des aktuellen Signalstatus ohne Änderung nicht möglich: if (sighandler=signal(SIGINT, SIG_IGN) !=SIG_IGN) signal(SIGINT,sighandler); Nach dem Abfragen des Signals wird automatisch die Default Aktion vom System eingerichtet

21 Problem: automatisch Default Aktion eingerichtet main {..... signal(SIGINT, sighandler) } int signalhandler { //kritische Stelle signal(SIGINT,sighandler);..... } Wenn bei krit. Stelle erneut SIGINT gesendet wird, wird default Aktion ausgeführt! Kommt allerdings selten vor (kurze Zeit in krit.Stelle). -> in Testphase unwahrscheinl. aber eventuell in produktivem Einsatz!

22 Probleme mit der signal Funktion cont‘ Man kann nicht Signal kurzzeitig blockieren, um es später zu bearbeiten. Möglichkeit: ignorieren. Dann weiss man nicht ob in Zwischenzeit Signal aufgetreten ist! Andere Mögl: sighandler tut nichts ausser globales flag zu setzten. Kann später abgefragt werden.

23 int sigint_flag =0; main {..... signal(SIGINT, sighandler ) // Prozess kann weiterarbeiten...... while (sigint_flag==0) //kritische Stelle pause(); } int signalhandler { sigint_flag=1; } Wenn bei krit. Stelle SIGINT gesendet wird, ist Signal verloren! Endlosschleife! Probleme mit der signal Funktion cont‘

24 Lösung der Probleme neues Signalkonzept Signalmengen sigaction() Signalmasken

25 Zeitschaltuhr int alarm(int seconds); wenn seconds abgelaufen sind, wird Signal SIGALRM gesendet. Wenn bei alarm-Aufruf eingeschaltete Uhr noch nicht abgelaufen ist wird diese durch neue ersetzt. Rückgabewert ist dann verbleibende Zeit Mit seconds =0 kann Wecker abgeschaltet werden. Typische Anwendung: Festlegen einer oberen Zeitgrenze für Aktionen die blockiert werden können (z.b. Lesen von Netzwerk)

26 IPC InterProcessCommunication Damit Prozess untereinander kommunizieren können gibt es verschiedene Mechanismen: –shared file (ineffizient!) –Pipes –FIFOs (named Pipes) –Stream Pipes –Message Queues –Semaphore –Shared Memory –Sockets –STREAMS

27 Pipes (Einschränkungen) 1.Pipes können nur zwischen Prozessen eingerichtet werdn die gemeinsame Vorfahren =(Gross)Eltern haben. –Normalerweise wird pipe von Elternprozess eingerichtet der dann mit fork() Kind kreirt, der dann pipe erbt 2.pipes sind halbduplex, d.h. Daten können immer nur in eine Richtung fliessen. –Prozess der Pipe zum Schreiben eingerichtet hat kann nicht aus pipe lesen (nur Prozess auf anderen Seite kann das) –Will man Duplex-Kommunikation braucht man 2 pipes

28 FIFOS und Stream Pipes FIFOS haben Einschränkung 1 nicht. Können also zwischen beliebigen Prozessen eingerichtet werden Stream Pipes haben Einschränkung 2 nicht. Sind also vollduplex. FIFOS bzw. Stream Pipes nur auf bestimmten UNIX-Derivaten!

29 Einrichten einer pipe int pipe(int fd[2]); fd[0] liefert Filedeskriptor zum Lesen aus pipe fd[1] liefert Filedeskriptor zum Schreiben in pipe fd[0]fd[1] Prozess A Kern

30 Pipe nach fork-Aufruf fd[0]fd[1] ElternProzess A Kern fd[0]fd[1] KindProzess A Normalerweise ruft man nach pipe fork auf, damit man mit Kind über pipe kommunizieren kann.

31 Elternprozess schreibt und Kind liest close(fd[0])fd[1] ElternProzess A Kern fd[0]close (fd[1]) KindProzess A Elternprozess schliesst Leseseite der pipe und Kindprozess Schreibseite der Pipe

32 Schliessen einer Seite Nach Schliessen einer Seite gelten folgende Regeln: Lesen aus Pipe, deren Schreibseite geschlossen wurde, nachdem alle Daten aus Pipe gelesen wurde, liefert read() 0 (für EOF). Schreiben in Pipe deren Leseseite geschlossen, liefert SIGPIPE. Sowohl beim Ignoriern als auch Abfangen des Signals (nach Rückkehr aus Signalhandler) liefert write Fehler.

33 Kommunikation von nicht verwandten Prozessen Da alle IPC Objekte ausser pipe auch zwischen nicht verwandten Prozessen möglich braucht man Kennungen (IDs) ID: wird vom System vergeben (wie PID), Schlüssel (key) wird vom Programmierer vergeben: –Wenn neues Objekt angelegt wird key angegeben. –Angabe von IPC_PRIVATE bewirkt anlegen von neuem Objekt

34 Kommunikation von nicht verwandten Prozessen Prozess A (Server) kreiert neues Objekt mit IPC_PRIVATE. Zurückgegebene Kennung wird an vereinbarte Stelle (z.b. Datei) gespeichert. Andere Prozess B,C.. (Clients) kann Kennung aus Datei lesen und mit Kennung auf IPC-Objekt zugreifen. Client und Server vereinbaren gemeinsamen Schlüssel (z.b. in header file). Server kreiert Objekt mit diesem key. Problem: key könnte schon vergeben sein.

35 Einrichten von neuem Objekt msgget, semget, shmgt int msgget(key_t key, int flag); –flag IPC_CREAT oder –IPC_EXCL: wenn Objekt mit key bereits existiert Fehlerrückgabe –Rückgabewert ist Kennung

36 Verbindung zu existierendem Objekt herstellen auch mit msgget(), jedoch IPC_CREAT nicht gesetzt! Anzeigen existierender IPC-Objekte und ihr Status: ipcs in Konsole/Shell Löschen mit ipcrm

37 message queues Nachrichtenwarteschlangen werden in Form von verketteten Liste vom System verwaltet Message besteht aus 3 Komponenten: –Message-Typ –Länge der Message ( size_t ) –Message-String

38 message queues Zum Senden von Nachricht: msgsnd() Zum Empfangen: msgrcv() Messages müssen nicht in der Reihenfolge gelesen werden in der sie gespeichert wurden! Durch Angabe von Typ in msgrcv() können messages „ausgelassen“ werden..

39 msgsnd int msgsnd(int id, const void *buffer, size_t length, int flag) size_t ist int oder long length legt Länge von Message-Text (buffer- sizeof(long) für Typ) fest id ist Kennung die von msgget zurückgegeben wurde buffer z.b. Zeiger auf struct my_msg { long mtype; char mtext[MAX] }

40 msgrcv() int msgrcv(int id, const void *buffer, size_t length, long type, int flag) length legt maximale Länge von Message-Text (buffer-sizeof(long) für Typ) fest. Fehler wenn msg grösser. type legt fest welche msg aus queue gelesen –type = 0: erste msg aus Queue (FIFO) –type > 0: 1. msg die den Typ type hat. –type < 0: 1.msg deren Typ kleinster wert ist der kleiner oder gleich absolut Betrag von type.

41 flag msgsnd blockiert normalerweise wenn queue voll. msgrcv blockiert wenn keine msg mit typ vorhanden. IPC_NOWAIT in flag: non-blocking function Sonst (bei Übung) flag=0 Verwendung von typ für Prioritäten Client-Server Anwendungen wenn nur eine msg queue zwischen client und server. PID kann als Typ zur Identifikation der clients verwendet werden.

42 Client Server Anwendungen Server stellt Dienst zur Verfügung. Clients können Dienst nutzen. Client sendet request(Anfrage) an Server Server bearbeitet und beantwortet request


Herunterladen ppt "Beendigung von Prozessen Normale Beendigung –durch Ende von main –durch Aufruf von exit() Anormale Beendigung –Aufruf von abort() –interne oder externe."

Ähnliche Präsentationen


Google-Anzeigen