UNIX-Schnittstelle ================== 1. Prozesse =========== |
next | back | 2017 - 1 |
Alle Beispiel-Quellen mittels SVN unter: https://svn.informatik.hu-berlin.de/svn/unix2014/Prozesse 1.Vorbemerkungen ---------------- Was ist ein Prozess???? 1. Menge von Befehlen und Daten einschließlich der aktuellen Werte der Prozessorregister. (virtuelle Maschine) 2. Tupel von Informationen, das die Arbeit der CPU in jedem Zeitpunkt vollständig charakterisiert. Problem: Abgrenzung eines Prozesses, Initialzustand. 3. UNIX: Ein Programm, das ausgeführt wird. Initialzustand ist in einem File gespeichert. Ein Prozess muss Systemressourcen wie Speicher und die CPU haben. UNIX unterstützt die Illusion von der gleichzeitigen Arbeit von Prozessen. (Prozess = ausführbare Instanz eines Programms) Problem: Prozess 0 -init- im UNIX??? |
next | back | 2017 - 2 |
Betrachtungsweise von Prozessen ------------------------------- - Prozess als aktives Element zur Beschreibung des Organisations- prinzips eines Betriebssystems auf einem Rechner und eines Algorithmus (Programms) Prozesse: - werden geboren, leben und sterben - sind in ihrer Zahl variabel - können Ressourcen belegen und freigeben - können einander beeinflussen - können zusammenarbeiten - können in Konflikt geraten (sich blockieren) - Ressourcen teilen - voneinander abhängig sein - können parallel arbeiten (voneinander unabhängig sein) (gleichberechtigt) - können hierarchisch voneinader abhängig sein - Prozess als passives Element, auf das die aktiven Elemente (Prozessor und Peripherie) wirken. Ein Prozess erscheint als Datenstruktur. UNIX-Datenstrukturen für einen Prozess: 1. Codesegment (Anfangszustand im File, sonst im Speicher oder geswappt) 2. Datensegment (Anfangszustand im File, sonst im Speicher oder geswappt) 3. Stacksegment (im Speicher oder geswappt) 4. Eintrag in der proc-Liste (immer im Speicher) Headerfile: sys/proc.h 5. user-Struktur (u-Struktur)(im Speicher oder geswappt) Headerfile: sys/user.h |
next | back | 2017 - 3 |
proc-Struktur ------------- Identifiers: p_pid Prozessnummer pp_pid Elternprozessnummer p_pgrp Prozessgruppenidentifier p_uid Useridentifier p_gid Gruppenidentifier p_suid effektiver Useridentifier p_sgid effektiver Gruppenidentifier Schedulinginformationen: p_flag Flags SLOAD - Prozess im Hauptspeicher SSYS - Prozess vom System erstellt (Swapper, Page-Daemon) SLOCK - Prozess wird gerage ausgelaggert (geswappt) SSWAP - Rueckkehr nach dem Auslaggern STRC - Prozess wird "getraced" SWTED - Prozess wird "getraced" SOUSIG - Prozess benutzt alten Signalmechanismus SULOCK - Prozess darf nicht geswappt werden SPAGE - Prozess wartet auf eine Seite SKEEP - Kernel fordert den Prozess SOWEUPC- Prozess wartet auf CPU-Zeit für Systemruf SOMASK - Signalmaske Wiedererstellen SWEXIT - Prozess wird beendet SPHYSIO- Physische E/A-Opertion wird ausgeführt SVFORK - vfork-Prozess SNOVM - Child besitzt VS des Elternprozesses vfork() SVFDONE- Child hat VS des Elternprozesses zurückgegeben vfork() |
next | back | 2017 - 4 |
SPAGI - Prozess hat Seiten des VM aus dem Dateisytem STIMO - TIMEOUT während sleep() SSEL - Prozess sobald wie möglich auswählen SLOGIN - login-Prozess p_stat Status des Prozesses SSLEEP - Warten auf ein Ereignis SRUN - Prozess lauffähig SIDL - Prozess bei der Erzeugung SZOMB - Zombi-Prozess SSTOP - Prozess gestoppt p_pri aktuelle Priorität PSWP - Swapping Priorität (0) PINOD - Priorität während des Wartens auf eine Inode (10) PRIBIO - Priorität beim Warten auf Platten-E/A (20) PWAIT - Priorität beim Warten auf Ressourcen (30) PLOCK - Priortät beim Warten auf das Locken einer Reseource (35) PSLEP - Priorität beim Warten auf ein Signal (40) PUSER - Normale User-Priorität (50) p_nice User nice-Priorität p_cpu neue CPU-Zeit p_userpri berechnet Userpriorität p_slptime Summe der Sleep-Zeiten Speichermanagment: p_textp Pointer zur Datenstruktur für das Datensegment p_pObr Pointer zur Pagetable p_szpt Zahl der Eintragungen in der Pagetable p_addr Adresse der user-Struktur (u-Struktur) p_swaddr Adresse der user-Struktur während der Prozess geswappt ist |
next | back | 2017 - 5 |
Synchronisation: p_wchan Prozess auf den gewartet wird Signalbehandlung: p_sig Maske für hängende Signale p_sigignore Maske für zu ignorierende Signale p_sigcatch Maske für einzufangende Signale Ressourcenberechnung: p_rusage Zeiger zur Ressourcenstruktur p_quota Zeiger zur Plattennutzungsstruktur Zeitgebermanagment: p_time Echtzeittimer Verkettungen in der proc-Struktur: p_link, p_rlink Verkettung in der Run-Queue bzw. Sleep-Queue p_nxt Allgemeine Verkettung (frei, besetzt, Zombi) p_pptr Elternprozess (p_parent) p_cptr 1.Kind (p_child) p_ysptr linker Bruder p_osptr rechter Bruder |
next | back | 2017 - 6 |
Prozess A | ^ | | p_cptr| |p_pptr | | | | V | +---------------Prozess B<---------------+ | +------------> ^ | | | | | | | | | c_ptr| | p_pptr |p_pptr p_pptr | | | | | | | | | | p_osptr | p_osptr | p_osptr V | ---------> | ------------> | -------------> Prozess E Prozess D Prozess C +--- <-------- <------------ | p_ysptr p_ysptr | | p_ysptr V Abbildung: Hierarchie der Prozess-Gruppen |
next | back | 2017 - 7 |
user-Struktur (u-Struktur) --------------------------- - Ausführungsstatus user-Modus, kernel-Modus, Register, Adressen - Status der Systemrufe - Deskriptortabellen (E/A, Netzwerk, ...) - Abrechnungsinformationen - Ressourcensteuerung - Kernel Process Stack - u-Struktur wird geswappt, 2-6 KB |
next | back | 2017 - 8 |
Statuswechsel eines Prozesses (einfaches Modell) ------------------------------------------------ +----------------+ | running | +----------------+ | | ^ 1| 2| |3 V V | +---------+ +------------+ | blocked |------>| ready | +---------+ 4 +------------+ 1. Prozess wartetet auf ein externes Ereignis ( read, write, Signal, ...) 2. Scheduler übergibt die Steuerung an einen anderen Prozess (Priorität) 3. Prozess erhält Steuerung vom Scheduler (Priorität) 4. Externes Ereignis ist eingetroffen. Prozess kann wieder aktiviert werden. |
next | back | 2017 - 9 |
Statuswechsel eines Prozesse in der Realität --------------------------------------------- usermode (1) <--------------------------+ ^ | | | | +----------->Interrupthandler | | | | exit V V | Zombie (9)<------ kernelmode (2)----------->verdrängt (7)-+ | ^ +--------------+ | Scheduling | warten | V V sleep (4)---------->ready (3)<------+ im Speicher im Speicher | | ^ | | | | | | fork (8) | | | | | | V V | sleep (6) ---------> ready (5)<---+ ausgelagert ausgelagert Abbildung: Prozesszustände und mögliche Übergänge |
next | back | 2017 - 10 |
1. Prozess arbeitet im "user mode". Kein Zugriff auf systeminterne Daten. Jederzeit unterbrechbar. 2. Prozess arbeitet im "kernel mode". Zugriff auf systeminterne Daten. Nicht unterbrechbar!? 3. Prozess ist bereit. Wartet auf Prozessorzuteilung. 4. Prozess im HS, schläft weil er auf ein Ereignis wartet. 5. Prozes ist bereit weiter zu arbeiten, aber ist ausgelagert. wartet auf Einlagerungen durch Swapper. 6. Prozess wartet auf Ereignis und der Swapper hat ihn aus dem HS entfernt (auf Platte) 7. Prozess wurde verdrängt (Zeitgeberinterrupt) 8. Prozess wurde durch fork() erzeugt 9. Prozess hat einen Systemruf exit ausgeführt und wartet auf die Beendigung(Zombi). |
next | back | 2017 - 11 |
Prozess-Scheduling ------------------ Früher: Kein Problem - Batchjobs, mehrere Partitionen, Partitionen mit fester Priorität Jetzt: Problematisch - Kombinationen von Batchjobs mit Dialogprozessen, Timesharing Problem: Wer darf wann den Prozessor wie lange benutzen? Kriterien: 1. Fairplay: Sichern, dass jeder Prozess einmal die CPU bekommt. 2. Effektivität: Auslastung der CPU mit 100%. 3. Anwortzeitverhalten: Minimieren der Antwortzeiten für interaktive Prozessen. 4. Durchsatz: Maximum von Batchjobs pro Stunde. 5. Verweilzeit: Minimieren der Zeit, die der Batchjob im Rechner benötigt. Schedulingverfahren ------------------- 1. Round Robin Scheduling 2. Mehrfach Schlangen (multiple queues) 3. Shortest Job First 4. Zeit-Überwachte-Steuerung (apriori scheduling) 5. Zweistufiges Scheduling |
next | back | 2017 - 12 |
1. Round Robin Scheduling ------------------------- alt, einfach, faire, oft benutzt Auf einer Prozesschlange basierend. +---+---+---+---+---+ Der am weitesten vorn stehende Prozess | A | B | C | D | E | erhält die Steuerung. Alle Prozesse +---+---+---+---+---+ haben gleiche Zeitquanten. ^ | aktiv Wenn das Zeitquantum des aktiven Prozesses | abgelaufen ist, wird er vorn entfernt und V hinten wieder angestellt. Alle anderen +---+---+---+---+---+ Prozesse rücken einen Platz nach vor. | B | C | D | E | A | +---+---+---+---+---+ aktiv | Wird der aktive Prozess blockiert ohne V das sein Zeitquantum abgelaufen ist, +---+---+---+---+---+ wird der nächste Prozess in der Schlange | B | C | D | E | A | aktiv. Der blockierte Prozess bleibt vorn. +---+---+---+---+---+ Er wird wieder aktiv, sobald die ^ Blockierung nicht mehr vorliegt. | blocked z.B.: DOS/IBM für IBM/360 - 1968 |
next | back | 2017 - 13 |
Interessante Frage: Wie gross ist das Zeitquantum zu wählen? Prozesswechselzeit!!!!!!!! z.B. 2 ms Quantum % Verwaltungszeit Wartezeit bei 5 Prozessen 8 ms 20% 32 ms 18 ms 10% 72 ms 98 ms 2% 392 ms 5 sec 0% 20 sec --> Je kürzer die Quanten - je geringer die Effektivität Je grösser die Quanten - je grösser die Reaktionszeit übliche Zeitquanten: 100 - 200 ms |
next | back | 2017 - 14 |
2. Mehrfach Schlangen (multiple queues) --------------------------------------- Problem: Viel Zeit wurde für das Ein-/Auslagern von Prozessen verbraucht. Es war sinnvoll CPU-intensive Prozesse länger im Speicher zu halten und grössere Quanten zu ver- teilen. Queue-Köpfe +--------------+ +-+-+-+-+ | Priorität 1 |<----| | | | | +--------------+ +-+-+-+-+ | Priorität 2 |<----| | | | arbeitsfähige +--------------+ +-+-+-+-+-+ Prozesse | Priorität 3 |<----| | | | | | +--------------+ +-+-+-+-+-+ | Priorität 4 | +--------------+ | Priorität 5 | +--------------+ Priorität Quanten 1 1 2 2 3 4 4 8 5 16 Wenn ein Prozess sein Quantum verbraucht hat, wird er in die folgende Prioritätenklasse eingeordnet. Dadurch wird die Zahl der Swappvorgänge stark verringert gegenüber "round robin". Kurze Jobs werden stark bevorteilt. |
next | back | 2017 - 15 |
Problem: Prozesse die abwechselnd interaktiv und CPU-intensiv sind werden bezüglich ihrer Reaktionszeit immer langsamer. Lösung: Bei einer Terminaleingabe wird die Priorität auf 1 gesetzt. Kluge Leute können diesen Algorithmus "austricksen" und damit andere Leute ernsthaft behindern. |
next | back | 2017 - 16 |
3. Shortest Job First --------------------- Prinzip für Batch-Job-Systeme Der kürzeste Job wird als erster realisiert. Beispiel: Job A: 8 min (a) Job B: 4 min (b) Job C: 4 min (c) Job D: 4 min (d) Jobreihefolge: B C D A interessant: mittlere Verweilzeit eines Jobs??? (4*b + 3*c + 2*d + a) / 4 = ( 16 + 12 + 8 + 8) / 4 = 11 Dieses Verfahren ist auch gut für interaktive Prozesse geeignet, wenn jede Kommandoausführung ein Prozess (Job) ist. Problem: Woher weiss man vorher, welches der kürzeste Prozess ist???? Lösung: Schätzen, Messen und Rechnen (gewichtete Summen) innerhalb von Takten Prozess erhält S0 sec im 0.Takt Prozess verbraucht T0 sec im 0.Takt Prozess erhält S1 = T0 sec im 1. Takt Prozess verbraucht T1 sec im 1.Takt Prozess erhält S2 = a*T0 + (1-a)*T1 sec im 2.Takt usw. Sn = a*Tn-2 + (1-a)*Tn-1 sec im n.Takt. für a = 1/2 ergeben sich folgende Schätzungen: S1 = T0 S2 = T0/2 + T1/2 S3 = T0/4 + T1/4 + T2/2 S4 = T0/8 + T1/8 + T2/4 + T3/2 S5 = T0/16 + T1/16 + T2/8 + T3/4 + T4/2 Damit wird die Vergangenheit eines Prozesses unterdrückt. a= 1/2 erweist sich damit als günstig (auch gut berechenbar) |
next | back | 2017 - 17 |
4. Zeit-Überwachte-Steuerung (apriori scheduling) -------------------------------------------------- Ziel: gleichverteilte CPU-Zeit über die gesamte Sitzung eines Nutzers dazu notwendig: Login-Zeit, Anzahl der Nutzer, bisher verbrauchte Zeit Prioritätsbestimmung: verbrauchte CPU-Zeit Priorität = ---------------------------- Login-Zeit ----------------- Anzahl der Nutzer 0.5 - Prozess verbrauchte weniger als ihm zustand, hohe Priorität 2.0 - Prozess verbrauchte mehr als ihm zustand, niedrige Priorität. Der Prozess mit dem kleinsten Quotienten erhält die höchste Priorität. |
next | back | 2017 - 18 |
5. Zweistufiges Scheduling -------------------------- Problem: Nicht alle Prozesse die arbeiten wollen, haben Platz im Hauptspeicher. ---> Prozesse müssen durch einen Scheduler geswappt werden. ----> es existieren zwei Scheduler: a) für Prioritätssteuerung der Prozesse im HS (verwaltet kleine Quanten) realisiert Strategien 1. bis 4. b) für das Swappen (verwaltet grosse Quanten) Kriterien für Swapping: 1. Wie lange ist der Prozess im HS bzw.Platte? 2. Wieviel CPU-Zeit hat der Prozess verbraucht? 3. Wie gross ist der Prozess? 4. Wie hoch ist die Priorität des Prozesses? |
next | back | 2017 - 19 |
Scheduling unter UNIX --------------------- multilevel Feedback-Queue Prioritätsebene Prozesse ^ +-------------------------+ S | 0 | Swapper |-----O Y | +-------------------------+ S |20 | Platten-I/O |-----O---O---O T | +-------------------------+ E |30 | Wait for Buffer |-----O---O M | +-------------------------+ | | Wait for Inodes |-----O - | +-------------------------+ | +-------------------------+ |40 | Wait for TTY-Input |-----O---O----O---O---O | +-------------------------+ | | Wait for TTY-Output |-----O | +-------------------------+ | | Wait for exit of child |-----O | +-------------------------+ | +-------------------------+ B |50 | User level 0 |-----O<--------------+ E | +-------------------------+ | N | | User level 1 |-----O----O----O-----O U | +-------------------------+ T | | | Z | +-------------------------+ E | | User level n |-----O R | +-------------------------+ Priorität |
next | back | 2017 - 20 |
Prioritätsberechnung: p_cpu - Schätzung der aktuellen CPU-Auslastung des Prozesses p_nice - Nutzerspezifischer Wichtungsfaktor -20<=p_nice<=20 - negative Werte steigern die Priorität PUSER - Prioritätskonstante, die garantiert, dass die Nutzerpriorität nicht höher ist als die niedrigste Systempriorität p_userpri = PUSER + p_cpu / 4 + 2 * p_nice Diese Berechnung erfolgt in gewissen Zeitabständen Bei jeder Berechnung wird p_cpu halbiert Beispiel ohne Nice, PUSER=60: Intervall Prozess A Prozess B Prozess C nice=0 nice=0 nice=0 60 60 60 PUSER+2*nice userpri cpu userpri cpu userpri cpu 0 60 0 60 0 60 0 x 60 0 0 1 75 30 60 0 60 0 0 x 60 0 2 67,5 15 75 30 60 0 0 0 x 60 3 63,75 7,5 67,5 15 75 30 x 60 0 0 4 76,9 33,6 63,75 7,5 67,5 15 0 x 60 0 5 68,4 16,8 76,9 33,6 63,75 7,5 0 0 x 60 Prozessreihenfolge: A B C A B C |
next | back | 2017 - 21 |
p_userpri = PUSER + p_cpu / 4 + 2 * p_nice Beispiel mit Nice, PUSER=60: Intervall Prozess A Prozess B Prozess C nice=0 nice=-10 nice=5 60 40 70 PUSER+2*nice userpri cpu userpri cpu userpri cpu 0 60 0 40 0 70 0 0 x 60 0 1 60 0 55 30 70 0 0 x 60 0 2 60 0 62,5 45 70 0 x 60 0 0 3 75 30 51,25 22,5 70 0 0 x 60 0 4 67,5 15 60,625 41,25 70 0 0 x 60 0 5 63,75 7,5 65,312 50,625 70 0 x 60 0 0 Prozessreihenfolge B B A B B A |
next | back | 2017 - 22 |
1.2 Systemrufe zur Prozessteuerung ================================== Prozessidentifikation Erzeugung eines neuen Prozesses Prozessbeendigung Programmaufruf Änderung der Identität eines Prozesses |
next | back | 2017 - 23 |
1.2.1 Prozessidentifikation --------------------------- Jeder Prozess hat eine eindeutige Prozessnummer (nicht negative Integerzahl) Bestimmte Prozesse haben feste Prozessnummern. z.B. 0 - sched oder swapper 1 - init (Elternprozess aller Prozesse) Prozessnummer muss man für verschiedene Aktionen wissen!!! #include <sys/types.h> #include <unistd.h> pid_t getpid(void) Gibt die Nummer des eigenen Prozesses zurück. pid_t getppid(void) Gibt die Nummer des Elternprozesses zurück. uid_t getuid(void) Gibt den wirklichen (real) UID des Prozesses zurück. uid_t geteuid(void) Gibt den effektive UID des Prozesses zurück. gid_t getgid(void) Gibt den wirklich (real) GID des Prozesses zurück. gid_t getegid(void) Gibt den effektiven GID des Prozesses zurück. |
next | back | 2017 - 24 |
Aktueller Wertebereich für pid,uid und gid Linux 2.4 pid 0 .. 32.000 gid,uid: 0 .. 65.000 Linux 2.6 pid 0 .. 1.000.000.000 gid,uid: 0 .. 4.000.000.000 Solaris 2.8 pid 0 .. 30.000 gid,uid 0 .. 2.147.483.647 Beispiele getuid, geteuid ------------------------- Prozesse/p1.c Prozesse/p2.c |
next | back | 2017 - 25 |
1.2.2 Erzeugung eines Prozesses ------------------------------- #include <sys/types.h> für System V #include <unistd.h> pid_t fork(void) Erzeugen eines neuen Prozesses, der in all seinen Eigenschaften und Zugriffsrechten dem alten Prozess entspricht (Kopie des alten) Achtung bei der Benutzung von Threads: Hier verhält sich fork je nach benutzter Thread-Art (SOLARIS, POSIX) unterschiedlich. Unterschiede zwischen Eltern- und Kindprozess: - Rückkehrkode des child-processes ist 0 Rückkehrkode des parentprocess ist PID des child-process - child-process hat andere PID und andere PPID - eigenes Stack-Segment, eigenes Daten-Segment - child-process: - prozesspezifische Zeitangaben auf 0 - Semaphoreoperation clears - Process-Locks, Text-Segment-Locks und File-Locks aufgehoben. - keine hängenden Signale - eventuell Threads |
next | back | 2017 - 26 |
Gleich bei Eltern- und Kindprozess: "fast alles" - Filedescriptoren - UID, EUID, GID, EGID, PGID - Environment - Close-on-exec Flag - Signalbehandlung (Signalroutinen) - Set-user-ID-Bit, Set-group-ID Bit - Profiling Status - Nice-Value, Scheduler-Class - Shared Memory - Working Directory, Root Directory - File Mode Creation Mask - Resourcenlimits - Controling Terminal - Offene Files (am gleichen Zugriffspunkt) fork() liefert als Ergebnis: 0 - child-process >0 - PID des child-process im parent-process <0 - Fehler, child-process konnte nicht erzeugt werden (kein Platz in der proc-Tabelle, Nutzerressourcen erschöpft) |
next | back | 2017 - 27 |
Filezugriff nach fork --------------------- Parent Prozess u-stuct fd0 -------------------> file table 0 +-> vnode-table 43 ^ file status flags | v-node information fd1 ----------+ current file offset | i-node | | v-node ptr ------+ current file size fd2 --------------+ | | | | v | | +-------> file table 1 +-> vnode-table 46 | ^ | file status flags | v-node information | | | current file offset | i-node | | | v-node ptr ----- + current file size Child Prozess | | | u-struct | | | | | | fd0 ------+ | | | | fd1 ----------+ | v fd2 --------------+---> file table 2 +-> vnode-table 56 file status flags | v-node information current file offset | i-node v-node ptr --------+ curren file size |
next | back | 2017 - 28 |
Filezugriff von Prozessen auf ein File ohne Vererbung ----------------------------------------------------- Prozess 1 table entry fd0 fd1 fd2 fd3 ------------>filetable 3 file status flags current-file offset v-node ptr -----------+--> vnode-table 63 ^ v-node information | i-node Prozess 2 | current file size table entry | fd0 | fd1 | fd2 | fd3 ------------>filetable 3 | file status flags | current-file offset | v-node ptr ----------+ |
next | back | 2017 - 29 |
#include <sys/types.h> für System V #include <unistd.h> pid_t int fork1(void) Erzeugen eines neuen Prozesses. Funktionalität analog fork(). Neuer Systemruf nur für Solaris. Variante von fork(). bis Solaris10: -lthread: alle Threads werden gedoppelt. -lpthread: nur der aktuelle Thread wird gedoppelt. ab Solaris10: nur der aktuell Thread wird gedoppelt. Rückkehrkode: wie fork() ------------------------------------------------------------ #include <sys/typedef.h> pid_t int vfork(void) Erzeugen eines neuen Prozesses. Funktionalität analog fork(). Aber ohne Kopie des Datensegmentes und des Stacksegmentes. Kindprozess benutzt Daten- und Stacksegment des Elternprozesses. Seiteneffekte, wenn Kindprozess mehr als EXEC macht!!!!! Rückkehrkode: wie fork() |
next | back | 2017 - 30 |
#include <sys/types.h> für System V #include <unistd.h> pid_t int forkall(void) Erzeugen eines neuen Prozesses. Funktionalität analog fork(). Sehr neuer Systemruf für Solaris 10. Es werden alle momentan laufenden Threads kopiert. Rückkehrkode: wie fork() ------------------------------------------------------------ Verhalten von fork() bei der Benutzung von Threads: fork vfork fork1 forkall Linux a,d+s a - - Solaris8/9 SOLARIS a,d+s t t,d+s - (-lthread) POSIX t,d+s t t,d+s (-lpthread) Solaris10 t,d+s t t,d+s a,d+s Beispiele für fork und vfork: ----------------------------- Prozesse/fork1.c Prozesse/fork2.c Prozesse/vfork1.c, Prozesse/fork-test.c, Prozesse/forkv-test.c |
next | back | 2017 - 31 |
1.2.3 Prozessbeendigung ----------------------- Arten der Prozessbeendigung: 1. Normales Prozessende a) return im Hauptprogramm ----> exit() b) Aufruf von exit - Abarbeitung von mit int atexit(void (*func)(void)) definierten privaten exit-Routinen (ANSI-C) - E/A-Endebehandlungen - Aufruf von _exit c) _exit() Aufruf UNIX-spezifische Prozessendebehandlung 2. Abnormales Prozessende a) Aufruf der Funktion abort void abort(void) diese Funktion erzeugt ein Signal SIGABRT (POSIX.1, ANSI C) siehe Stevens: Program 10.18 b) Ende über Signalbehandlung |
next | back | 2017 - 32 |
#include <stdlib.h> void exit(int exitcode) ANSI-C libc #include <unistd.h> void _exit(int exitcode) POSIX.1 Systemruf Reguläres beenden eines Prozesses. Die niederwertigen 8 Bits von "exitcode" werden als Resultat an den Parentprozess übergeben. Dateien werden abgeschlossen. Probleme bei der Übergabe des Resultats: a) Parentprozess wartet mit wait() ---> kein Problem b) Parentprozess wartet nicht: a) Parentprozess existiert nicht: Resultat wird an init-Prozess übergeben. b) Parentprozess existiert noch: Resultat wird in der proc-Tabelle gespeichert. Alle anderen Informationen über den Prozess werden gestrichen. Es entsteht ein "Zombie-Prozess". Dieser wird gestrichen wenn der Parentprozess das Resultat mit wait() abholt. Nebenwirkungen: SIGHUB für alle Prozesse der Prozessgruppe |
next | back | 2017 - 33 |
Beispiele für exit: ------------------- Prozesse/myexit.c Prozesse/myexit1.c Prozesse/myexit2.c |
next | back | 2017 - 34 |
Warten auf Prozessende eines Kindprozesses ------------------------------------------ Wenn ein Prozess beendet oder gestoppt wird, wird ein Terminationkode gebildet. Dieser Kode beinhaltet den Exitkode bzw. die Signalnummer, die das Beenden bzw. das Stoppen des Kindprozesses bewirkte. Der Kode wird in der proc-Table gespeichert. Systemrufe: pid_t wait(int *statloc) POSIX.1, SVR4, BSD 4.3 pid_t waitpid(pid_t pid, int *statloc, int options) POSIX.1, SVR4, BSD 4.3 Warten auf das Ende oder das Stoppen (Stop-Signal) eines Kindprozesses des aktuellen Prozesse. |
next | back | 2017 - 35 |
#include <sys/types.h> #include <sys/wait.h> pid_t wait(int *statloc)) Wartet auf das erste Ende eines beliebigen Kindprozesses, sofern der aktuelle Prozess einen Kindprozess besitzt. *statloc enthält den Terminationkode, wenn statloc != NULL Terminationkode: <high order 8 bits> <low order 8 bits> Kindprozess durch Signal gestoppt: <Signalnummer><0x7F> exit, _exit: <niederwertigen 8 Bit des Exitkodes><0x00> Kindprozess durch Signal abgebrochen: <0x00><Signalnummer> Kindprozess durch Signal abgebrochen und Dump erzeugt: <0x00><Signalnummer + 0x80> Rückkehrkode: >=0 - Prozessnummer des Kindprozesses < 0 - Fehler, kein Kindprozess vorhanden oder falscher Parameter |
next | back | 2017 - 36 |
#include <sys/types.h> #include <sys/wait.h> pid_t waitpid(pid_t pid, int *statloc, int options) Bedingtes warten auf das Prozessende eines spezifizierten Prozesses. pid > 0 - warten auf das Ende des Prozesses mit der PID == pid pid == -1 - warten auf das Ende eines Kindprozesse (entspricht wait()) pid == 0 - warten auf das Ende eines Kindprozesses der eigenen Prozessgruppe pid < -1 - warten auf jeden Prozess mit der ProzessengruppenID==|pid| *statloc enthält den Terminationkode (siehe wait()) options: WCONTINUED - warten auf das Ereignis WNOHANG - auf das Ereignis nicht warten WUNTRACED - nur gestoppte Prozesse melden, die noch nicht gemeldet wurden Rückkehrkode: >=0 - Prozessnummer des Kindprozesses < 0 - Fehler, kein Kindprozess vorhanden oder falscher Parameter |
next | back | 2017 - 37 |
Weiter Systemrufe für das Warten auf Kindprozesse -------------------------------------------------- Voraussetzungen: #include <sys/types.h> #include <sys/wait.h> #include <sys/time.h> #include <sys/resource.h> pid_t wait3(int *statloc, int options, struct rusage *rusage) SVR4, BSD 4.3 pid_t wait4(pid_t pid, int *statloc, int options, struct rusage *rusage) BSD 4.3 *rusage: Ressourcenangaben über den beendeten oder gestoppten Prozess (CPU-Zeit, Hauptspeicherbedarf, Seitenfehler, Signale, ...) wait3() entspricht wait() unter der Berücksichtigung von options. wait4() entspricht waitpid(). |
next | back | 2017 - 38 |
1.2.4 Programmausführung ------------------------- exec() umfasst eine Familie von Systemrufen, die zum Laden und Starten eines neuen Programms innerhalb eines Prozesses dient. Grundfunktionen: -exec() überlagert im aufrufenden Prozess das aktuelle Codesegment und das Datensegment. -Das Stacksegment wird zurückgesetzt -Eröffnete Dateien bleiben eröffnet. -Ignorierte Signale bleiben ignoriert. -Alle anderen Signale werden zurückgesetzt. Folgende prozesspezifischen Werte bleiben unverändert: nice-Wert Schedulerwerte Filecreationmask PID Semaphore Ressourcelimits PPID Session ID Controlterminal PGID traceflag Prozesssignalmaske Alarmzeit Workingdirectory hängende Signale Prozesszeit Rootdirectory Rückgabewerte: - keiner, wenn alles ok <0 bei Fehlern |
next | back | 2017 - 39 |
EXEC-Systemrufe: #include <unistd.h> int execl(const char *pathname, const char *arg0,...(char *) 0); Feste Anzahl von Parametern int execv(const char *pathname, char *const argv[]); variable Anzahl von Parametern int execle(const char *pathname, const char *arg0, ..., (char *) 0, char *const envp[]); wie execl, aber mit setzen der Umgebungsvariablen int execve(const char *pathname, char *const argv[], char *const envp[]); wie execv, aber mit setzen der Umgebungsvariablen int execlp(const char *filename, const char *arg0,..., (char *) 0); wie execl, aber Programm wird gesucht int execvp(const char *filename, char *const argv[]); wie execv, aber Programm wird gesucht |
next | back | 2017 - 40 |
Beispiel für exec: exec1.c, echoall.c ------------------ Prozesse/echoall.c Prozesse/exec1.c Prozesse/pwd-test.c |
next | back | 2017 - 41 |
1.2.5 Änderung von ID's von Prozessen -------------------------------------- Folgende ID's sind änderbar: UID - User ID SUID - Saved User ID GID - Group ID EUID - Effective User ID EGID - Effective Group ID PGID - Processgroup ID SID - Session ID Systemrufe zum Abfragen von ID's: getuid(), geteuid(), getgid(), getegid(), getpid(), getppid(), getsid(), getprgp() Systemrufe zum Ändern von ID's: setuid(), seteuid(), setreuid(), setgid(), setegid(), setregid(), setpgid(), setpgrp(), setsid() |
next | back | 2017 - 42 |
#include <sys/types.h> #include <unistd.h> int setuid(uid_t uid); Setzen des UID's für den aktuellen Prozess. int setgid(gid_t gid); Setzen des GID's für den aktuellen Prozess. setuid() wirden unter folgenden Bedingungen erfolgreich abgearbeitet: 1. aktueller UID == 0 (root): setuid() setzt aktuellen UID auf uid und EUID auf uid. 2. aktueller UID != 0 und (uid == UID oder uid == SUID): setuid() setzt EUID auf uid. setgid() arbeitet analog. Rückkehrkode: 0 - ok <0 - Fehler |
next | back | 2017 - 43 |
#include <sys/types.h> #include <unistd.h> int seteuid(uid_t uid); Setzen des effektiven UID's für den aktuellen Prozess. int setegid(gid_t gid); Setzen des effektiven GID's für den aktuellen Prozess. seteuid() wirden unter folgenden Bedingungen erfolgreich abgearbeitet: 1. aktueller UID == 0 (root): seteuid() setzt aktuellen EUID auf uid. 2. aktueller UID != 0 und (uid == UID oder uid == SUID): seteuid() setzt EUID auf uid. setegid() arbeitet analog. Rückkehrkode: 0 - ok <0 - Fehler |
next | back | 2017 - 44 |
#include <sys/types.h> #include <unistd.h> int setreuid(uid_t ruid, uid_t euid); BSD 4.3 Setzen von UID und EUID. int setregid(gid_t rgid, gid_t egid); BSD 4.3 Setzen von GID und EGID. Der Superuser kann sowohl uid und euid gleichzeitig setzen. Der normale User kann lediglich einen Wechsel zwischen EUID und UID veranlassen. Hiermit können Programme, die mit S-Bit laufen zwischen priveligierten Modus und User-Modus hin- und herschalten. Rückkehrkode: 0 - ok <0 - Fehler |
next | back | 2017 - 45 |
#include <sys/types.h> #include <unistd.h> pid_t setpgid(pid_t pid, pid_t pgid) Setzen des Pozessgruppen ID's für den Prozess pid auf den Wert pgid. Wenn pid==0, ist der aktuelle Porzess gemeint. Wenn pgid==0, wird der PGID des mit pid spezifierten Prozess benutzt. Prozessgruppen werden für Signalverteilung und zur Synchronisation des Zugriffs auf das Controling Terminal benuzt. Rückkehrkode: 0 - ok <0 - Fehler pid_t setpgrp(void) Der aktuelle Prozess wird Prozessgruppenführer. Der Prozess- gruppen ID und der aktuelle Session ID ergeben sich aus dem PID des aktuellen Prozesses. Identisch mit setpgid(0,0). Rückkehrkode: Nummer der aktuellen Prozessgruppe pid_t setsid(void) Der aktuelle Prozess wird Gruppenführer einer neuen Session. Prozessgruppe ohne Controling Terminal. Der aktuelle SID ergibt sich aus dem aktuellen PID. Rückkehrkode: Nummer der aktuellen Sessiongroup |
next | back | 2017 - 46 |
Übersichten ----------- Ändern der User-IDs (UID, EUID, SUID) bei exec() und setuid() ID | exec | setuid(nuid) | S-Bit=0 | S-Bit=1 | superuser | user --------+------------+-----------------|-----------+---------- UID | - | - | nuid | - EUID | - | ID vom Programm | nuid | nuid SUID | EUID | UID | nuid | - UID - Wirklicher UID (Real UID) EUID - Effektiver UID, bestimmt Zugriffsrechte SUID - Geretteter EUID |
next | back | 2017 - 47 |
Funktionen, die unterschiedliche User-IDs setzen setreuid(ruid,euid) setuid(uid) seteuid(uid) root root root | | | | | | | | +----------+ | +-----------+ | | | | | | | | +----------------------+ | +-----------|------------+ | | | | | | | +-----------+ | | | +------------+ | | | | | | V V V V V V REAL ---setreuid:u---> EFFEKTIVER <--setrueid:u ---- SAVED UID <--setreuid:u---- UID <-- exec mit S-Bit --> UID | ^ ^ | | | | | | | | | | | | | +- setuid:u,seteuid:u ----+ +-- setuid:u,seteuid:u ---+ :u - normaler Nutzer |
next | back | 2017 - 48 |
Beispiel getuid, geteuid: ------------------------- Prozesse/changeuid.c Prozesse/pruids.c Prozesse/setid.c Prozesse/setidn.c |
back | 2017 - 49 |