Betriebssysteme Teil 7: Konzept der Threads

Ähnliche Dokumente
Architektur Verteilter Systeme Teil 2: Prozesse und Threads

Betriebssysteme Teil 6: Hardware-Schicht II

Betriebssysteme Kap B: Hardwaremechanismen

[6-1] Engelmann, Lutz (Hrsg.): Abitur Informatik Basiswissen Schule. Duden-Verlag, 2003, S.43-53, , , S.

Systeme I: Betriebssysteme Kapitel 4 Prozesse. Maren Bennewitz

Operating System Kernels

Dämon-Prozesse ( deamon )

Enterprise Computing Einführung in das Betriebssystem z/os. Prof. Dr. Martin Bogdan Prof. Dr.-Ing. Wilhelm G. Spruth WS2012/13

Betriebssystembau (BSB)

Interrupts. Funktionsprinzip. Funktionsprinzip. Beispiel in C

Grundlagen der Rechnerarchitektur. Ein und Ausgabe

Ein- Ausgabeeinheiten

Prüfung VO Betriebssysteme SS2008 / 7. Juli 2008

Name: ES2 Klausur Thema: ARM Name: Punkte: Note:

Was machen wir heute? Betriebssysteme Tutorium 2. Organisatorisches. Frage 2.1.a. Theorieblätter Abgabe. Antwort. Probleme mit OS/161?

Datentechnik. => Das Rechenergebnis ist nur dann sinnvoll, wenn es rechtzeitig vorliegt. Die Zeit muß daher beim Programmdesign berücksichtigt werden.

Rechnerarchitektur Atmega Vortrag Atmega 32. Von Urs Müller und Marion Knoth. Urs Müller Seite 1 von 7

Vortrag zum Seminar Konzepte und Techniken virtueller Maschinen und Emulatoren. Bruno Kleinert 20. Juni 2007

Domänenanalyse Threadverwaltung/Scheduling

Übersicht. Virtueller Speicher CPU-Modi Virtuelle Maschinen. ISM SS Teil 4/ProtectionI

ARM Cortex-M Prozessoren. Referat von Peter Voser Embedded Development GmbH

Paging. Einfaches Paging. Paging mit virtuellem Speicher

Sicheres C Programmieren in Embedded Systemen ARM II (ARM7TMDI [1] ) Wintersemester

B1 Stapelspeicher (stack)

OSEK-OS. Oliver Botschkowski. PG AutoLab Seminarwochenende Oktober AutoLab

VORSTELLUNG DER DIPLOMARBEIT

Neues vom STRIP Forth-Prozessor

Übersicht. UNIX-Dateisystem (ext2) Super-User unter Linux werden MSDOS: FAT16 und FAT32

Ausarbeitung im Rahmen der PG Autolab zum Thema: OSEK 1 -OS. geschrieben von Oliver Botschkowski

Grundlagen der Rechnerarchitektur

Design and Implementation of a Soft-error Resilient OSEK Real-time Operating System

Betriebssysteme. Teil 13: Scheduling

Der Scheduler von Windows Konzepte und Strategien

Prozesse und Prozessmanagement des BS. 1 Unterschied Prozess, Threads. 1.1 Prozess. 1.2 Threads

Embedded-Linux-Seminare. Linux als Betriebssystem

Die Ausführung geschieht über die sequentielle Abarbeitung der Instruktionen.

Moderne Betriebssysteme. Kapitel 8. Kapitel 8. Folie: 1. Multiprozessorsysteme. Autor: Andrew S. Tanenbaum

SC18IM700-Tester v Einleitung

Tag 2 Eingabe und Interrupts

Thread-Synchronisation in in Java. Threads Wechselseitiger Ausschluss Bedingte Synchronisation Beispiel: Warteschlangen

Verteilte Systeme. Verteilte Systeme. 5 Prozeß-Management SS 2016

Virtueller Speicher. SS 2012 Grundlagen der Rechnerarchitektur Speicher 44

Hardware Virtualisierungs Support für PikeOS

Vorl. 6: Single- und Multitasking

Übung Betriebssysteme 11

White Paper. Embedded Treiberframework. Einführung

Betriebssystembau. 7. Übung. Michael Engel Arbeitsgruppe Eingebettete Systemsoftware. Lehrstuhl für Informatik 12 TU Dortmund

Betriebssysteme BS-V SS Hans-Georg Eßer. Foliensatz V: Ulix: Interrupts und Faults Ulix: System Calls. Dipl.-Math., Dipl.-Inform.

System Monitoring mit strace. Systemcall tracing

POSIX Echtzeit: Kernel 2.6 und Preempt-RT

Ringlicht-v3 - Frei konfigurierbares Ringlicht mit RS232 Anbindung. Kurzbeschreibung

Betriebssysteme Teil 16: Dateisysteme (Beispiele)

Round-Robin Scheduling (RR)

Dateisystem: Einführung

Dateisystem: Einführung

Teil VIII Von Neumann Rechner 1

Simple Scope. ecos-vertiefung. Florian Franzmann Tobias Klaus Peter Wägemann

Übungscomputer mit Prozessor Bedienungsanleitung

B.5 Prozessverwaltung B.5. Prozessverwaltung Prof. Dr. Rainer Manthey Informatik II 1

Die L4-Mikrokern. Mikrokern-Familie. Hauptseminar Ansätze für Betriebssysteme der Zukunft. Michael Steil. Michael Steil

Neue Funktionen im GUI für PC-DMIS V3.x 4.x Seite 1 von 8

CUDA. Jürgen Pröll. Multi-Core Architectures and Programming. Friedrich-Alexander-Universität Erlangen-Nürnberg Jürgen Pröll 1

Inhaltsverzeichnis. 2.4 Thread-Systeme. 2.1 Was ist ein Prozess? 2.2 Scheduling. 2.3 Interprozesskommunikation

Begriff: Scheduling Planung, Schedule Plan. Verplanung der CPU-Zeit an die Threads (bzw. Prozesse)

RealTime Linux. Paul Seidel Seminar Prozessteuerung und Robotik WS 08/09 Lehrstuhl BS und Middleware Prof. Polze Hasso-Plattner-Institut Potsdam

Microsoft ISA Server 2004

Programme werden durch den Ablauf eines oder mehrerer Prozesse (engl.: process, task) von einem Rechner abgearbeitet.

Grundlagen der Rechnerarchitektur

Timm M. Steinbeck und Arne Wiebalck Lehrstuhl für Technische Informatik Universität Heidelberg. Prozess-Monitoring auf CPU-Takt Ebene

CS2101 Nebenläufige und Verteilte Programme Bachelor of Science (Informatik)

PThreads. Pthreads. Jeder Hersteller hatte eine eigene Implementierung von Threads oder light weight processes

JX3-AI4 Versions-Update von V 1.02 auf V 1.03

AVR-8-Bit-Mikrocontroller Bootloader

Gliederung Hardware fuer die Zeitmessung Zeitmanagement auf Uniprozessorsystemen. Timing Measurements. Timo Schneider. 4.

Systeme 1. Kapitel 5. Scheduling

A Kompilieren des Kernels B Lineare Listen in Linux C Glossar Interessante WWW-Adressen Literaturverzeichnis...

SurefireKernel ÜBERSICHT SPEZIFIKATION. Sicherheitskernel DATASHEET

Beim Programmieren mit MMIX habt ihr vielleicht schon öfter eine der folgenden Fehlermeldungen von MMIXAL bekommen:

13. Übung mit Musterlösung

Rechnernutzung in der Physik. Betriebssysteme

BAV-Remote Skript L.Pagel

Realisierung: virtueller Prozessor: der reale Prozessor wird periodisch dem Programm zugewiesen Im Prozessor: durch Task-Status Segment (TSS)

Was machen wir heute? Betriebssysteme Tutorium 12. Organisatorisches. Frage 12.1.a. Programmieraufgaben Vorstellung. Antwort

Parallel-IO. Ports am ATmega128

Programmiersprache 1 (C++) Prof. Dr. Stefan Enderle NTA Isny

Assembler und Hochsprachen

Betriebssysteme KU - Einführungstutorium

Echtzeitbetriebssysteme (am Beispiel QNX) Dr. Stefan Enderle HS Esslingen

Die Mikroprogrammebene eines Rechners

AutiSta 9.6 AE Technische Informationen zur Auslieferung oh 1 / 8. AutiSta 9.6 Technische Informationen zur Auslieferung (T-IzA)

TCP/IP ASCII Schnittstelle Programmierhandbuch

Entwicklung eines Mac OS X Treibers für eine PCI-VME Interface Karte

Die Integration zukünftiger In-Car Multimedia Systeme unter Verwendung von Virtualisierung und Multi-Core Plattformen

Übung zu Grundlagen der Betriebssysteme. 10. Übung

USB in Embedded Systemen. Referat von Peter Voser Embedded Development GmbH

Embedded OS für ARM Cortex Microcontroller

Mainframe Internet Integration. Prof. Dr. Martin Bogdan Prof. Dr.-Ing. Wilhelm G. Spruth SS2013. WebSphere Application Server Teil 4

Beispielklausur B MPGI 3

Übung I Echtzeitbetriebssysteme

Betriebssysteme. 4y Springer. Eine kompakte Einführung mit Linux. Albrecht Achilles. Mit 31 Abbildungen

Transkript:

Betriebssysteme Teil 7: Konzept der Threads 06.11.15 1

Übersicht I/O als langsamster Vorgang Threadwechsel mit der Resume-Operation Interrupts Scheduler Time Sharing 2

Motivation Die Geschwindigkeit der CPU und die der I/O-Geräte sind sehr unterschiedlich. Ein fiktives Beispiel: 1 Befehl sei mit 10 Takten durchgeführt, d.h. bei einer Taktrate von 1 GHz benötigt ein Befehl 10 ns. 1 Plattenzugriff liegt im günstigsten Fall bei 8ms (bei Laptop-Platten ca. 12ms). Dies bedeutet, dass die CPU während eines Plattenzugriffs in diesem Beispiel mind. 800.000 Instruktionen ausführen kann. Ziel: Um die CPU während der I/O-Wartezeit zu nutzen, sollte diese Wartezeit mit der Abarbeitung anderer Programme verbracht werden. 3

Das weitere Vorgehen Bevor erklärt werden kann, wie das Ziel der optimierten Benutzung der CPU erreicht wird, sind einige Vorarbeiten notwendig... Im folgenden wird schrittweise in immer besser werdenden Versionen das endgültige Konzept vorgestellt. Aber: In der Praxis kommen auch alle Zwischenversionen vor. 4

Durchführung von I/O-Vorgängen Ein I/O-Vorgang wird durch Beschreiben von Geräteregistern gestartet. Seine Beendigung wird durch Abfragen des Status-Registers vom Gerät erkannt. Dort zeigt das Busy-Bit an, ob der letzte Vorgang beendet ist. Polling = Befragung durch die CPU nach dem Status des Geräts bzw. nach der Beendigung des letzten I/O-Vorgangs Busy Waiting = Ausschließliches Polling durch die CPU Beschreiben der Geräteregister // Kommando an Gerät WHILE Busy-Bit = 1 DO ; // keine Operation OD Ergebnis auswerten 5

Beispiel Keyboard-Interface Busy-Bit Zitat aus http://www.lowlevel.eu/wiki/kbc 6

Beispiel SSD-Interface Auszug aus einem Datenblatt über SSD: www.micron.com 7

Treiber und Kernel I Das Lesen/Schreiben von einzelnen Worten bei Massenspeichern ist viel zu ineffizient: es werden daher immer Blöcke von mindestens 1 Kbyte pro Vorgang mit DMA verarbeitet. Das Veranlassen eines I/O-Vorgangs wird auch als Absetzen eines I/O-Kommandos bezeichnet. Treiber (Device Driver) = Komponenten im Kernel, die Zugriff auf die Geräte-Register haben und Eigenarten der Geräte gegenüber anderen Komponenten verdecken (Realisierung von Abstraktion) Die Treiber verdecken die speziellen Eigenschaften der Geräteregister (Aufbau, Kommandokodierung etc.) durch Realisierung einer einheitlichen Schnittstelle. Die Aufgabe der Treiber besteht darin, die I/O-Kommandos abzusetzen und deren Ergebnisse zu verarbeiten. 8

Treiber und Kernel II Kernel = Speicherresidenter Teil des Betriebssystems Der Kernel besteht aus Routinen bzw. Komponenten, die gemeinsam von den Threads und Prozessen benutzt werden. Diese Routinen bzw. Komponenten werden zum Beginn (Boot) in den RAM geladen und verbleiben dort bis zum Herunterfahren (Shutdown). 9

Das Problem und die Lösung Das Problem besteht darin, dass die I/O-Geräte um mindestens den "Faktor" 800.000 langsamer als die CPU sind, so dass die CPU mit dem Busy Waiting sehr viel Zeit vergeudet. Idee: Während des Wartens auf I/O arbeitet die CPU ein anderes Programm ab bis der I/O-Vorgang beendet ist; dann wendet sie sich wieder dem alten Programm zu. Dazu ist aber ein Programmoder Threadwechsel notwendig. Thread = Coroutine = Sequentiell arbeitendes Programm(teil) bestehend aus Code und Daten Threadwechsel = Änderung der Zuordnung von Thread zur CPU Der Name Thread stammt von der Idee eines Fadens, der die Spur des Programm Counters im Code symbolisiert. 10

Zum Begriff des Prozesses und des Threads I Das führt zum Konzept der Threads und der Umschaltung zwischen Threads. In einem Thread läuft jeweils ein "Programm", das aus Code, Daten und dem CPU-Status besteht. 11

Zum Begriff des Prozesses und des Threads II Thread = Coroutine = Light weight Process = ein sequentiell ablaufender Teil innerhalb eines Prozesses Ein Prozess besteht aus gegenüber anderen Prozessen getrennten Code und Daten, dem eine CPU zugeordnet werden kann, ist also ein eigenständiges laufendes bzw. lauffähiges Programm. 12

Threadwechsel mit resume() I Es wird nun eine fiktive Operation namens resume() definiert, die immer dann benutzt wird, wenn die CPU abgegeben werden muss. Folgende Definition von resume(thread) beschreibt den Ablauf beim Wechsel ausgehend von einem Thread im User-Mode in einen anderen im User-Mode (Freistil-Notation): resume(thread P) ==> // Ablauf! Wechsel in den Kernel-Modus Rette PC und SR // aktueller Prozess Rette die allgemeinen Register ------------ // Wechsel Stelle allgemeine Register her Stelle PC und SR her Wechsel in den geretteten Modus // neuer Thread P TRAP RTT 13

Threadwechsel mit resume() II Threadwechsel mit resume(p): beide Threads arbeiten im User-Mode Nur während der Ausführung von resume() wird im Kernel-Modus gearbeitet. 14

Threadwechsel mit resume() III Jeder Thread wird mit einem Threaddeskriptor verwaltet: Aufbau des Threaddeskriptors: Identifikation Verbrauchte CPU-Zeit Priorität etc. Die Zusammenfassung aller Deskriptoren in einer Tabelle wird meist Threadtabelle genannt. Wir werden später nach der Einführung des Virtuellen Speichers von einem Processdeskriptor sprechen. Die Tabelle heißt dann Prozesstabelle. Im Linux-Umfeld wird der Begriff der Task als Oberbegriff von Thread und Prozess benutzt. 15

Thread-Tabelle I struct Thread { // 1. Version int id; // Identifikation des Threads ThreadLayout data; // Datensegment } Thread threadtbl[max_threads] Die Threads erhalten eine eindeutige Nummer dies könnte auch der Index in der Tabelle threadtbl[] sein. Das Thread-Layout beschreibt den Bereich des Threads im RAM, d.h. wo der Datenbereich im RAM liegt. 16

Thread-Tabelle II - ThreadLayout struct ThreadLayout {// 1. Version address min; // Untere Adresse address max; // Obere Adresse address SP; // Wert von SP } Jeder Thread hat eigene Werte von SP und SL, aber auch eigene Grenzen. Im ThreadLayout-Deskriptor werden diese Werte abgespeichert. Hinzu kommt noch der Rest des CPU-Status. 17

Speicher-Layout für mehrere Threads I Thread 1 Thread 2 Thread N Mehrere Threads werden zu einem Prozess zusammengefasst 18

Speicher-Layout für mehrere Threads II Prozess...... Thread- Verwaltung Code Daten Thread1 Daten Thread2 Daten ThreadN...... 0 max In der Thread-Verwaltung befinden sich die Thread-Tabelle. Der Code wird von allen Threads innerhalb eines Prozesses gemeinsam benutzt. In den unteren Adressen außerhalb der Prozesse liegen die Trap- Vektoren und in den oberen Adressen die Geräte-Register. Diese Aufteilung des RAMs betrifft hier die gesamte Maschine. Wenn der vordere und der hintere Teil entfernt werden, ist es auch die Struktur von mehreren Threads innerhalb eines Prozesses bzw. Programms. 19

Implementierung von resume() I - Beispiel User-Mode resume(thread P){ push(p) trap #1 pop() } Kernel-Mode Trap-Handler#1(){ push(alle-register-ohne-sp) store SP in ThreadDescriptor get new Threaddeskriptor P set new values of SP Alle-Register-ohne-SP= pop() rtt } Die Implementierung von resume() erfolgt in zwei Teilen: der erste für den User-Mode, der zweite für den Kernel-Mode. Der Parameter P der Name des neuen Threads wird vor dem Trap auf den Stack gebracht und anschließend dort wieder entfernt. 20

Implementierung von resume() II - Beispiel User-Mode resume(thread P){ push(alle-register-ohne-sp) store SP, SL in ThreadDescriptor get new Threaddeskriptor P set new values of SP Alle-Register-ohne-SP= pop() } Es ist aber auch möglich ohne die beiden CPU-Modi eine Thread- Umschaltung zu realisieren. Das ist dann der Fall, wenn innerhalb eines Programms bzw. Prozesses über Bibliotheksroutinen Threads realisiert werden. 21

Implementierung von resume() III - Beispiel Die Zuordnung des CPU-Modus zu den Komponenten 0 max... Thread- Verwaltung Code Daten Thread1 Daten Thread2 Daten ThreadN... In dieser Version läuft die Verwaltung der Threads innerhalb des Kernels. Die Threads selbst laufen im User-Mode (User-Threads). Der Kernel selbst kann auch selbst in mehreren Threads realisiert sein, so dass die Thread-Verwaltung auch Kernel-Threads verwaltet. 22

Implementierung von resume() IV (User-)Stack (User-)Stack (User-)Stack <- SP <- SP <- SP Situation direkt vor dem Trap nach Aufruf von resume() Situation zum Beginn der Trap-Handlers Situation bei Abgabe der Kontrolle 23

Implementierung von resume() V (User-)Stack (User-)Stack Situation während der Ausführung des neuen Threads <- SP Situation vor Annahme der Kontrolle des neuen Threads 24

Beispiel-Situation Thread 1 Thread 2 Thread 3 Thread N <- SP <- SP <- SP <- SP schläft schläft läuft schläft 25

Systemaufruf I - Wiederholung Ein Systemaufruf ist das Aufrufen einer Routine des Kernels, die im Kernel-Modus der CPU abläuft. Ein Systemaufruf ist vollkommen analog zum Aufruf einer Routine so wie bisher behandelt. Warum ist der Kernel-Modus erforderlich? Ausführen von I/O-Instruktionen bzw. der Zugriff auf die Geräte- Register ist nur in diesem Modus möglich. (Das zweite stimmt nicht ganz, denn der Zugriff auf hohe Adressen wäre auch im User-Modus möglich ) 26

Systemaufruf II resume() betrifft den Fall, dass ein Threadwechsel aus einem laufenden Thread zu einem anderen laufenden Thread stattfinden soll. Ein Systemaufruf ohne Threadwechsel läuft sehr ähnlich zu resume() ab, nur dass eine Routine ausgeführt wird: Syscall(Name, Parameter) = // Verkürzter Ablauf! Wechsel in den Kernel-Modus Rette PC und SR // aktueller Thread Rette die allgemeinen Register // aktueller Thread... Führe den Systemaufruf Name mit Parameter aus... Stelle allgemeine Register her // aktueller Thread Stelle PC und SR her // aktueller Thread Wechsel in den User-Modus TRAP RTT 27

Das Ganze mit Busy Waiting (Ablauf) Syscall(Name, Parameter) = // Verkürzter Ablauf! Wechsel in den Kernel-Modus Rette PC und SR // aktueller Thread Rette die allgemeinen Register // aktueller Thread... Beschreiben der Geräteregister // Kommando an Gerät WHILE Busy-Bit = 1 DO ; // keine Operation OD Ergebnis auswerten... Stelle allgemeine Register her // aktueller Thread Stelle PC und SR her // aktueller Thread Wechsel in den User-Modus TRAP RTT 28

Implementierung von Systemaufrufen User-Mode syscall(int N,Param) = { push(n) trap #1 pop() } Kernel-Mode Trap-Handler#1() = { push(alle-register-ohne-sp) Get Syscallnumber N Get Parameter Call N(Parameter) Alle-Register-ohne-SP= pop() rtt } Die Ähnlichkeiten zum resume() sind nicht zufällig, denn das resume() ist eigentlich ein Syscall... 29

Threadwechsel innerhalb des Systemaufrufs Wenn nun ein Syscall mit I/O, z. B. Lesen von einer Platte, durchgeführt wird, wird der erste Teil des resume() durch den Syscall ausgeführt. Der Threadwechsel findet dann im Code des Systemaufrufs statt, wobei die Operation continue(thread) die 2. Hälfte des resume() realisiert. Nun kann ein Threadwechsel bei jedem Warten realisiert werden: // während des Syscalls im Kernel Setzen der Geräteregister // Absetzen eines Kommandos WHILE Busy-Bit = 1 DO continue(thread) // CPU abgeben OD Ergebnis auswerten... 30

continue() I continue() ist eine Variante von resume(), bei der initial keine Trap-Instruktion ausgeführt wird. Es wird daher das Stacklayout einer Trap-Instruktion nachgebaut, so dass dieselbe Stack- Struktur am Ende entsteht. continue() wird als Subroutine aufgerufen, so dass das übliche Stacklayout eines Routinenaufrufs entsteht. Nach Beendigung von continue(thread P) wird der Thread P direkt nach dessen letzten continue() fortgesetzt. RTT continue(thread P) = // Verkürzter Ablauf! Rette PC und SR // explizit im Kernel Rette die allgemeinen Register // aktueller Thread ------------ // Wechsel Stelle allgemeine Register her Stelle PC und SR her // aktueller Thread Wechsel in den geretteten Modus // neuer Thread P 31

continue() II ein Beispiel Start (1) (3) User- Mode (5) Kernel- Mode (7) (9) Prozesswechsel innerhalb von Systemaufrufen (3): Wechsel Kernel- -> User-Mode, (5), (7), (9) Wechsel innerhalb des Kernel-Modes 32

continue() III War die letzte Aktion des zukünftigen Threads T ein continue(), so wird im Kernel weiter gemacht, eben nach dessen continue(). Hierbei entsteht eine kleine Schwierigkeit: continue() selbst wurde als eine Subroutine aufgerufen, was stört. Daher wird der Stackaufbau des Calls beseitigt. War die letzte Aktion des zukünftigen Threads T ein resume(), so wird in dessen User-Mode weiter gemacht. Das wird dadurch erleichtert, dass für beide Fälle derselbe Stackaufbau benutzt wird. Der zukünftige CPU-Modus wird durch den alten auf dem Stack geretteten SR-Wert bestimmt; dieser Wert wird von der RTT- Instruktion zur Bestimmung des zukünftigen CPU-Modus verwendet. 33

continue() IV <- SP <- SP <- SP So sieht die Stack-Struktur eines schlafenden Threads aus Stack-Struktur eines schlafenden Threads, wenn vorher resume() aufgerufen wurde Stack-Struktur eines schlafenden Threads, wenn vorher continue() aufgerufen wurde Das ist der CPU-Status ohne den Stackpointer 34

continue() V Alter Thread T Aktueller Thread <- SP continue(thread T){ return-adr:= pop() push(sr) push(return-adr) push(alle-register-ohne-sp) store SP in ThreadDescr. --- Wechsel get new Threaddeskriptor of T set new values of SP Alle-Register-ohne-SP= pop() rtt } <- SP Aus trap heraus Stackaufbau zum Zeitpunkt des Wechsels 35

continue() VI Thread T <- SP Aktuell <- SP continue(thread T){ return-adr:= pop() push(sr) push(return-adr) push(alle-register-ohne-sp) store SP in ThreadDescr. --- Wechsel get new Threaddeskriptor of T set new values of SP Alle-Register-ohne-SP= pop() rtt } Direkt nach RTT beim Aufrufer vom continue() Nach der Thread-Abgabe 36

Bemerkungen Wird kein I/O gemacht, so sollten sich die Prozesse in dieser Version gegenseitig per resume() aufrufen (so wie zum Anfang). resume() wird wie ein Syscall ohne I/O behandelt. Syscalls ohne I/O rufen nie continue() auf. Alle Syscalls mit I/O rufen immer continue() auf. continue() aktiviert einen anderen Thread, der im Kernel ist. Aber: alle schlafenden Threads sind immer im Kernel. Warum? 37

Busy Waiting mit continue() (Ablauf) Syscall(Name, Parameter) = // Verkürzter Ablauf! Wechsel in den Kernel-Modus Rette PC und SR // aktueller Thread Rette die allgemeinen Register // aktueller Thread... Beschreiben der Geräteregister // Kommando an Gerät WHILE Busy-Bit = 1 DO continue(thread) // CPU abgeben OD Ergebnis auswerten... Stelle allgemeine Register her // aktueller Thread Stelle PC und SR her // aktueller Thread Wechsel in den User-Modus TRAP RTT Warum immer noch die While-Schleife? Weil der Thread vorzeitig die Kontrolle bekommen kann. 38

Scheduler I Doch es gibt ein Problem: Der Thread, der zu warten beginnt, d.h. der continue-aufrufer, muss wissen, welche Threads existieren und welcher von diesen weiter arbeiten soll. Lösung: Es wird dann zu einem Thread Namens Scheduler gewechselt, der den nächsten Thread bestimmt. 39

Scheduler II Der Scheduler wird in einem eigenen Thread innerhalb des Kernels realisiert, der für folgende Aufgaben zuständig ist: in der Threadtabelle mitschreiben, welche Threads existieren, vermerken, welcher Thread zu welchem Anteil die CPU schon bekommen hat, vermerken des Thread-Zustandes (state): Running: Thread hat gerade die CPU Ready: Thread wartet auf CPU Waiting: Thread wartet auf Beendigung von I/O Sleeping: Thread benötigt irgendwann später die CPU, ändern des Threadzustands entsprechend einer neuen Situation, zuteilen der CPU dem nächsten Thread. 40

Scheduler III Scheduler = Thread, der die Zuteilung der CPU zu den Threads nach einem Verfahren (Scheduling Strategie) realisiert In der Threadtabelle werden dann die dafür notwendigen Informationen untergebracht. struct Thread { // 2. Version int id; // Identifikation des Threads int state; // Zustand ThreadLayout data; // Datensegment } Thread threadtbl[max_threads] 41

Scheduler IV Start User-Mode Kernel-Mode Thread Scheduler while true do look_for_next; continue(thread); od; (2), (4), (6), (8), (10) Thread p1 Thread p2...... (1) (3)...... (5) (9) continue(scheduler);...... continue(scheduler);... (7) continue(scheduler);...... continue(scheduler);... [Verkürzte Version: das continue(scheduler) ist Teil eines Syscalls Der Scheduler selbst arbeitet permanent im Kernel-Modus] 42

Da sind noch zwei kleine Probleme... 1. Es ist immer noch das Polling notwendig - Nachsehen, ob der I/O-Vorgang beendet ist. Dies ist zwar schon reduziert, aber noch vorhanden. 2. Eine Endlosschleife in einem Thread ohne ein Syscall/Continue bringt das gesamte System zum Absturz, da nur mit der Beendigung des Ganzen dieser eine Thread beendet werden kann. Besser ist, dass nur der Thread mit der Endlosschleife getrennt von den übrigen beendet wird. 43

Unterbrechungen (Interrupts) Wenn ein I/O-Gerät seine Operation beendet hat, sendet es über den Bus ein Signal, das von der CPU als Unterbrechung (Interrupt) behandelt wird. Empfängt die CPU dieses Signal, so passiert folgendes: Die Ausführung der aktuellen Instruktion wird beendet. Anstatt die nächste Instruktion auszuführen, wird eine Art Trap- Instruktion ausgeführt. Die Behandlung im Kernel besteht in der Ausführung einer für diesen Interrupt speziellen Routine, dem Interrupt-Handler (Unterbrechungsbehandler). Dieser tut folgendes: Feststellen, welches Gerät die Unterbrechung signalisierte In der Threadtabelle alle Threads, die auf diese Unterbrechung warten, in den Zustand Ready bringen 44

Ablauf beim Interrupt - Version 1 Interrupt Handler = // Ablauf! Wechsel in den Kernel-Modus // wie bei Trap-Instruktion Rette PC und SR Rette die allgemeinen Register Bestimme signalisierendes Gerät Setze wartende Prozesse auf Ready Stelle allgemeine Register her // alte Werte Stelle PC und SR her // alte Werte Wechsel in den geretteten Modus INT RTI In dieser Version wird das aktuell laufende Programm lediglich unterbrochen und nach dem Ändern der Threadtabelle wieder fortgeführt, d.h. es findet kein Threadwechsel statt. Der Interrupt kann auftreten, wenn die CPU im User-Modus ist oder auch im Kernel-Modus! Dieses Verfahren hat den Nachteil, dass Threads, die auf den Interrupt lange gewartet haben, noch weiter warten müssen. 45

Ablauf beim Interrupt - Version 2 INT Interrupt Handler = // Ablauf! Wechsel in den Kernel-Modus // gilt auch für den Kernel Rette PC und SR Rette die allgemeinen Register Bestimme signalisierendes Gerät Setze wartende Prozesse auf Ready continue(scheduler) In dieser 2. Version wird direkt nach dem Bearbeiten der Threadtabelle zum Scheduler gewechselt, der dann entscheidet, mit welchem Thread es weiter geht. Der Scheduler durchsucht bei jedem seiner Aufrufe die Threadtabelle nach Threads im Zustand "Ready" bzw. "Running", um einen von diesen auszuwählen. Alle anderen Threads werden von ihm ignoriert. Das heißt, dass ein Signal/Interrupt die Bedeutung eines resume(scheduler) hat, wenn der Thread im User-Modus war, oder eines continue(scheduler), wenn der Thread im Kernel-Modus war. 46

Die beiden Behandlungsmöglichkeiten Thread P1 Thread P1 Thread P2 47

Warten auf I/O mit Interrupts I Der Thread setzt sich freiwillig auf eine Warteliste, indem er in den Zustand "Waiting" geht. Ein I/O-Deskriptor beschreibt das Gerät bzw. den Vorgang, auf den der Thread wartet. Wenn der Interrupt-Handler dieses Geräts aktiviert wird, werden alle mit dessen I/O-Deskriptor wartenden Threads in den Status "Ready" gesetzt. Thread Pn... Beschreiben der Geräteregister; wait(i/o-descriptor); Ergebnis auswerten;... struct I/O-Descriptor { Address SR-Register; int Busy-Bit-Number; } PROC wait(i/o-descriptor) Setze Thread Status("Waiting"); Setze I/O-Descriptor in IO-Tabelle; continue(scheduler); 48

Warten auf I/O mit Interrupts II Wir kennen nun zwei Tabellen: die Threadtabelle und die Tabelle mit den I/O-Desriptoren. Die I/O-Descriptoren sind fest den Geräten zugeordnet. In ihnen wird ja der Aufbau der Geräte-Register beschrieben. In der Threadtabelle wird daher ein Verweis auf den Descriptor vorgesehen, der beim Warten gesetzt wird: struct Thread { // 3. Version int id; // Identifikation des Threads int state; // Zustand ptr I/O-Descriptor // Verweis auf das Gerät ThreadLayout data; // Datensegment } 49

Priorisierung von Interrupts I - Controller Ein externer Baustein, der über Geräte-Register angesprochen wird, priorisiert. Die CPU konfiguriert über Geräteregister, mit welcher Priorität die I/O-Bausteine behandelt werden. Der Interrupt-Controller signalisiert, dass irgendein Interrupt vorliegt. Dann sieht die CPU nach, von welcher Quelle dieser kam. Die CPU hat zwei besondere Instruktionen: enable_interrupts lass Unterbrechungen zu disable_interrupts Sperre alle Unterbrechungen 50

Priorisierung von Interrupts II - Level Jedes Gerät ist an eine Leitung angeschlossen, die einer Priorität zugeordnet ist. Die CPU hat einen internen Level, dessen Wert im Status-Register gehalten wird. Hat der externe Interrupt einen höheren Wert als der interne Level, so wird der Interrupt akzeptiert, ansonsten ignoriert. Beispiel: Es gibt 7 Levels: 0..7. Den Geräten wird einer dieser Levels zugeordnet. Hat die CPU den Level 7, so kommt kein Interrupt durch. 51

Priorisierung von Interrupts III Wenn der Interrupt-Handler läuft... sind im ersten Fall alle Interrupts ausgeschaltet hat die CPU im zweiten Fall den Level des Interrupts Das bedeutet, dass der Interrupt-Handler nicht oder nur durch Interrupts höherer Priorität unterbrochen werden kann. Selbstverständlich kann der Interrupt-Handler dieses Verhalten ändern. Daher: Die Interrupt-Handler sollten immer nur so kurz wie nur möglich die Interrupts verhindern, da ansonsten Interrupts verloren gehen könnten. 52

Damit ist das erste Problem gelöst... Durch den Mechanismus des Interrupts entfällt vollkommen das Polling und damit auch das Busy Waiting. Ein auf I/O wartender Thread wird erst dann aktiviert, wenn der I/O-Vorgang beendet ist. Besser geht es nicht mehr. Was aber passiert, wenn alle Threads auf I/O warten? Ganz einfach: dann findet der Scheduler keinen Thread, den er aktivieren kann und durchsucht in einer Endlosschleife seine Tabelle bis er selbst unterbrochen wird. Dieser unterbrechende Interrupt-Handler setzt dann einen der Threads auf "Ready" und aktiviert den Scheduler; der findet dann, was er sucht. (Dazu muss der Handler heraus bekommen, ob der Scheduler unterbrochen wurde, denn dann kann er direkt den Scheduler aktivieren.) 53

Time Sharing Verfahren Mit den vorgestellten Mechanismen lassen sich alle Nachteile bis auf die "Ungerechtigkeiten" bei der CPU-Vergabe sowie bis auf den Fall der Endlosschleifen beseitigen. Das letztere Problem wird durch einen Timer gelöst. Ein Timer ist ein spezielles I/O-Gerät, das nach einer eingestellten Zeit bzw. in zyklischen Abständen einen Interrupt sendet. Dieser wird immer mit dem Aktivieren des Schedulers aus dem Handler heraus behandelt. Zeitscheibe = Dauer zwischen zwei Timer-Interrupts Nach Ablauf einer Zeitscheibe kann damit der Scheduler die CPU an einen anderen Thread geben, unabhängig davon, ob ein I/O- Vorgang erfolgt ist oder nicht. 54

Nach dieser Anstrengung etwas Entspannung... 55