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

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

Embed Size (px)

Citation preview

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

Beendigung von Prozessen

• Normale Beendigung – durch Ende von main– durch Aufruf von exit()

• Anormale Beendigung– Aufruf von abort()– interne oder externe Signale

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

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)

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

Verwaiste Kindprozesse

• Elternprozess wird beendet, bevor alle Kindprozesse beendet sind.

• init Prozess wird neuer Elternprozess.

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

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)

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

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!

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

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

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

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

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

Beendigungsstatus• int *status: call by reference• Beendigungsstatus ist binärcodiert• in <sys/wait.h> 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)

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

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.

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

IPC Mechanismen in Linux

• Signale• message queues• shared memory• semaphore• pipes

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

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.

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

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

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

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 0else return 1;

}int max(int x,int y) {

if (x>y) return 0else return 1;

}

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

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!!)

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

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

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

Bsp: Abfangen von Signal für Division durch Null

#include <signal.h>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“);}

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

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.

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

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.

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

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

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

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

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

Problem: automatisch Default Aktion eingerichtet

main {.....signal(SIGINT, sighandler)

}int signalhandler {

//kritische Stellesignal(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!

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

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.

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

int sigint_flag =0;main {.....

signal(SIGINT, sighandler) // Prozess kann weiterarbeiten......while (sigint_flag==0)

//kritische Stellepause();

}int signalhandler {

sigint_flag=1;}• Wenn bei krit. Stelle SIGINT gesendet wird, ist

Signal verloren! Endlosschleife!

Probleme mit der signal Funktion cont‘

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

Lösung der Probleme

• neues Signalkonzept• Signalmengen• sigaction()• Signalmasken

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

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)

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

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

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

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

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

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!

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

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

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

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.

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

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

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

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.

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

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

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

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.

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

Einrichten von neuem Objekt

• msgget, semget, shmgtint msgget(key_t key, int flag);– flag IPC_CREAT oder – IPC_EXCL: wenn Objekt mit key bereits

existiert Fehlerrückgabe– Rückgabewert ist Kennung

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

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

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

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

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

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..

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

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 aufstruct my_msg {

long mtype;char mtext[MAX]

}

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

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.

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

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.

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

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