Universität Bremen  
  Universität Bremen FB3 TZI BISS  
  AG BS > Lehre > SoSe 2009 > Deutsch
English
 

Betriebssysteme 2, Sommersemester 2009

 

Aktuelles


Termine

Vorlesung: 
Di. 10-12 Uhr, MZH 1400
(ab 7.04.09)
Prof. Dr. Jan Peleska
Übung: 
Mo. 10-12 Uhr, MZH 1380
(ab 20.04.09)
Florian Lapschies


Auf dieser Seite werden während des Semesters weiterführende Informationen sowie die jeweiligen Aufgabenzettel bereitgestellt. Wir bemühen uns, die Seite so aktuell wie möglich zu halten. Weitere Informationen zur Vorlesung:


Überblick

Betriebssysteme 2 umfasst folgende Themen:

Die Übungen vertiefen den Vorlesungsstoff durch praktische Anwendung der beschriebenen Konzepte.


Veranstaltungsinhalte

Session 1: Der Weg durchs Betriebssystem

  • System-Call Wrapper für den Einsprung in den Kernel: Makros _syscallx() mit x=0,1,2,4,5,6 in der unistd.h für die Konstruktion von System-Call Wrappern (Funktionsdeklaration plus Funktionskörper). Parameter(adress)übergabe in Registern. Spezifizierung des Systemcalls über das EAX-Register. Auslösen des Traps (=Softwareinterrupt) 0x80. Setzen von errno. Definition des Rückgabewertes. Prozessorinstruktionen SYSENTER/SYSEXIT als Alternative zum Interrupt.
    Hinweis: In neueren Kernelversionen gibt es die unistd.h nicht mehr. Stattdessen befinden sich entsprechende Makros in den C-Libraries (siehe sysdep.h aus der GNU C-Library 2.9)
  • Einstiegspunkt im Kernel (Datei arch/x86/kernel/entry_32.S): Einsprungadressen ENTRY(system_call) für Interrupt 0x80 und ENTRY(ia32_sysenter_target) für die SYSENTER-Instruktion. Retten der Register auf dem Stack. Aufruf von sys_SystemCallName() über die Tabelle sys_call_table (Datei arch/x86/kernel/syscall_table_32.S).
  • Für das Kopieren von Datenbereichen, die im Systemaufruf über Funktionszeiger identifiziert werden: copy_to_user() kopiert aus dem Kernelspace in den Userspace. copy_from_user() kopiert aus dem Userspace in den Kernelspace.
  • Beispiel: Systemaufruf gettimeofday(2) - Kernel-Implementierung mittels sys_gettimeofday() (Datei kernel/time.c). Hierzu weitere interessante Dateien:
    • kernel/timer.c: Funktionen void do_gettimeofday() und getnstimeofday().
    • include/linux/clocksource.h: Funktion static inline cycle_t clocksource_read() verweist über Funktionspointer auf die HW-abhängige Methode zum Lesen der Mikrosekunden-Zeiteinheiten.
    • arch/i386/kernel/tsc.c: Realisierung der Zeitabfrage über das Time Stamp Counter Register (TSC), siehe static struct clocksource clocksource_tsc und static cycle_t read_tsc() und static int __init init_tsc_clocksource().
    • include/asm-i386/msr.h: Definition des Assemblermakros rdtscll(), welches das TSC-Register ausliest.
  • Das aktuelle System Call Interface (SCI) von glibc und Linux unter Nutzung der Intel Instruktionen sysenter, sysexit (verfügbar etwa ab Kernel 2.6.20)

Hintergrundlektüre: Kernel command using Linux system calls von M. Tim Jones


Session 2: Linux-Kernel - Modifikation, Übersetzung, Installation

  • Linux Kernel 2.6.29.1 herunterladen.
  • Kernelquellen auspacken: als normaler User im eigenen Home-Verzeichnis
  • Ins Verzeichnis linux-2.6.29.1 wechseln.
  • Versionsnummer modifizieren, z.B. echo -bs2 > localversion-bs2.
  • Konfiguration, Übersetzung, Installation und weitere Aktionen mit make. Ein Überblick verschafft make help.
    Hinweis: Eventuell muss muss die Zielarchitektur mit ARCH=... angegeben werden (zum Beispiel: make ARCH=i386 menuconfig)
  • Übernahme einer bestehenden .config-Kernelkonfigurationsdatei mit make oldconfig
  • Kernel konfigurieren: make menuconfig
    Hinweis: Je mehr Features konfiguriert sind, desto länger dauert der Generierungsvorgang. Es ist sinnvoll, die Konfiguration für die Lösung der BS2-Aufgaben so klein wie möglich zu halten. Sollen Teile des Kernels als Modul kompiliert werden, die jedoch zum Booten benötigt werden ist eine Init-Ramdisk nötig.
  • Kernel-Sources nach Bedarf modifizieren.
  • Wenn neue Dateien (z.B. unter kernel/)angelegt werden, müssen die zugehörigen Objektdateien im Makefile des Verzeichnisses eingetragen werden.
  • Kernel übersetzen: make
  • als root: Kernel-Module installieren: make modules_install . Die Module landen dann in der Regel unter /lib/modules/.
  • als root: Kernel installieren: make install
    Hierdurch wird unter anderem das komprimierte Kernel-Image von arch/i386/boot/bzImage nach /boot kopiert. Ggf. ist auch noch per Hand das Anlegen einer Init-Ramdisk und das Erstellen eines neuen Eintrags in /boot/grub/menu.lst nötig. Auf jeden Fall sollte alles noch einmal kontrolliert werden.
  • Reboot unter Auswahl des neuen Kernels - hier hilft auch manchmal ein kleines Gebet ...
  • Um häufige Reboots zu vermeiden, ist der Emulator qemu nützlich.
    • Zur Konfiguration des Kernels für qemu kann die Datei .config übernommen werden.
    • Zum Starten eines selbstgebauten Kernels kann dieses Makefile genutzt werden.
      Das Makefile lädt automatisch ein vorinstalliertes Debian-Linux aus dem Netz und startet euren Kernel mit diesem System. Hierzu muss sich der übersetzte Kernel im Verzeichnis kernel/ befinden.
      Nützliche Makefile-Targets sind:
      • make run-kernel: Starten des Systems mit dem von euch gebauten Kernel.
      • make debug-kernel: Starten des Systems mit dem von euch gebauten Kernel, so dass er mit dem gdb debuggt werden kann.
      • make linux: Starten des Systems mit dem vorinstallierten Debian Linux-Kernel.


Session 3: Der O(1) Scheduler im Linux Kernel 2.6

  • Datenstrukturen: Runqueue mit active und expired Arrays
  • Einsprungpunkte für das Scheduling:
    • scheduler_tick(): Aufgerufen vom Timer Interrupt und von do_fork()
    • schedule(): Wird aufgerufen, wann immer Kernel-Code entscheidet, dass der zugehörige Prozess schlafen muss, sowie immer wenn Task Preemption stattfinden soll.
    • sched_fork(): Wird f&uumöl;r einen neu erzeugten Kindprozess aufgerufen.
  • Als ergänzende Literatur zu den Kernel-Quellen sched.c, sched.h wird der Artikel Understanding the Linux 2.6.8.1 CPU Scheduler von Josh Aas empfohlen, ausserdem [0], Kapitel 4.


Session 4: Scheduler Hierarchie und Completely Fair Scheduler im Kernel 2.6.29

  • Scheduler Klassen und die struct sched_class Struktur mit ihren vorgegebenen Funktionen, die von jeder Klasse zu implementieren sind.
  • Das Konzept der virtuellen Laufzeit (virtual runtime) die zwischen den rechenbereiten Prozessen fair ausgeglichen wird.
  • O(1)-Eigenschaft des RT-Schedulers (sched_rt.c) für die Klassen SCHED_FIFO und SCHED_RR.
  • Scheduling Gruppen, um Fairness zwischen Gruppen von Prozessen zu realisieren


Session 5: Das virtuelle Dateisystem (Virtual File System VFS)

  • Das folgende Klassendiagramm zum VFS stellt die Zusammenhänge zwischen den Datenstrukturen her, die beim Ausführen eines auf Dateien oder Verzeichnissen operierenden Systemaufrufs ausgewertet bzw. erzeugt werden - vom Prozesstabelleneintrag des aufrufenden Prozesses bis zur die Datei/das Verzeichnis repräsentierenden Inode-Instanz im Kernel.
  • Die vier Unix-Abstraktionen in Bezug auf Dateisysteme: Dateien, Verzeichnisse, Inodes, Mount Points
  • Die primären Objekttypen des VFS:
    • Superblock Objekt (struct super_block) - repräsentiert ein Dateisystem nach dem Mount.

      Die zugehörigen Operationen werden aus dem Superblock-Objekt via Referenz auf eine Instanz von struct super_operations entnommen. Diese Operationen unterstützen alle Aktivitäten, welche das gesamte Dateisystem betreffen, z.B.: Zurückschreiben des Superblocks auf die Platte (write_super()), Initialisieren eines neuen Inodes bei Erzeugung einer neuen Datei/eines neuen Verzeichnisses in diesem Dateisystem (alloc_inode()), Zurückschreiben aller im Kernel vorhandenen geänderten Daten (Superblock, Dateien oder Verzeichnisse mit Inodes und Nutzdaten) des Dateisystems auf die Platte (sync_fs()). Die Operationen lassen sich in solche einteilen, die nur auf die Dateisystemrepräsentation im Kernel einwirken (z.B. alloc_inode(),put_inode() [Inode wird im Kernel freigegeben]) und solche, die auf das Dateisystem auf der Platte einwirken (z.B. sync_fs(), delete_inode()[Inode wird im Dateisystem auf der Platte gelöscht]). Das Superblock Objekt und die Superblock-Operationen sind in include/linux/fs.h deklariert.

    • Inode Objekt (struct inode) - repräsentiert eine Datei. Verzeichnisse sind unter Unix spezielle Dateien, daher wird auch ein Directory über einen Inode repräsentiert.

      Die auf dem Inode wirkenden Operationen werden über eine Referenz auf eine Dateisystemtyp-spezifische Instanz von struct inode_operations gefunden. Inode-Operationen betreffen immer die ganze Datei (nie den Dateiinhalt), z.B. Öffnen/neu Anlegen der Datei in einem vorgegebenen Verzeichnis, Erzeugen und Löschen von harten und symbolischen Links, Umbenennen. Das Inode Objekt und die Inode-Operationen sind in include/linux/fs.h deklariert.

    • Dentry Objekt - repräsentiert einen Verzeichniseintrag, genauer, einen Pfadabschnitt, der ein Verzeichnis oder eine Datei in einem Verzeichnis repräsentiert.

      Auch Mount-Points werden durch Dentry-Objekte repräsentiert. Im Pfad /mnt/d1/d2/file1.txt werden /, mnt, d1, d2, file1.txt im Kernel durch jeweils ein Dentry-Objekt repräsentiert. Die auf einem Dentry-Objekt wirkenden Operationen werden über eine Referenz auf eine Instanz von struct dentry_operations gefunden. Die Operationen ermöglichen beispielsweise das Vergleichen von Verzeichnis- oder Dateinamen (wichtig z.B. für Dateisysteme, bei denen Groß- und Kleinschribung nicht unterschieden wird), sowie das Löschen eines Verzeichniseintrags (was gleichzeitig das zugehörige Dentry-Objekt invalidiert. Dentry-Objekte haben keine direkte Entsprechung im Dateisystem auf der Platte: Dort ist die Beziehung zwischen Verzeichnissen und Unterverzeichnissen oder Dateien in den Nutzdaten der Verzeichnisse repräsentierenden Dateien codiert. Dentry-Objekte werden in einem Kernel-Cache gehalten, um schnelle Pfadaufl&öuml;sungen zu ermöglichen. Das Dentry Objekt und die Dentry-Operationen sind in include/linux/dcache.h deklariert.

    • File Objekt - repräsentiert eine mit einem Prozess assoziierte geöffnete Datei.

      Die zum File-Objekt gehörigen Operationen sind in struct file_operations zusammegefasst. Sie unterstützen alle Aktivitäten, die den Dateiinhalt betreffen, auf welchen ein Prozess zugreift. Beispiele sind llseek() (Positionieren des Lese-/Schreibzeigers), read(), write(). Das File Objekt und die IFile-Operationen sind in include/linux/fs.h deklariert.

  • Als ergänzende Literatur sind die unten angegebenen Bücher [3] (gibt einen sehr guten Überblick der zu Grunde liegenden Konzepte des VFS; die Darstellung ist von Linux unabhängig und bezieht sich auf Unix im Allgemeinen) und[0,1,2] (Linux-spezifische Details) zu empfehlen.


Session 6: Second and Third Extended File Systems - ext2/ext3

  • Die Struktur von Ext2 Plattenpartitionen: Sonderrolle des Bootblocks - Blockgruppen - Blockgruppenaufteilung in Superblock, Blockgruppendeskriptor, Datenblock-Bitmap, Inode-Bitmap, Inode-Tabelle, Datenböcke - Inode Struktur - Codierung von Directory-Inhalten in den Datenblöcken der Directory-Files.
  • Journalling im Ext3 Filesystem Die 3 journalling modes
    • Writeback: Nur die Metadaten kommen ins Log. Damit können Nutzdaten abgeschlossener write()-Operationen nach einem Crash verloren sein, aber die Konsistenz des Dateisystems nach der Recovery (=Einspielen offener Transaktionen aus dem Journal in die Partition) ist gesichert.
    • Ordered: Das Commit für die Metadaten im Journal wird erst gegeben, nachdem die Nutzdaten auf die Disk geschrieben wurden. Dadurch sind vor einem Crash vergrösserte Dateilängen nur dann nach der Recovery sichtbar, wenn auch der korrekte Dateninhalt am Dateiende eingetragen ist. Write()-Operationen, die innerhalb einer Datei stattfinden, ohne ihre Länge zu verändern, können genau wie beim Writeback-Mode bei einem Crash verloren gehen bzw. zu einem inkonsistenten Zustand der Nutzdaten führen, wenn vor dem Crash nur ein Teil der Aktualisierung tatsächlich auf den Nutzdaten in der Partition realisiert wurde.
    • Journal: Metadaten und Nutzdaten werden ins Log geschrieben, so dass sowohl das Dateisystem konsistent bleibt, als auch alle abgeschlossenen write()-Operationen nach einem Crash rekonstruierbar sind.
  • Vergleich des Ext3-Journalling mit Logging und Transaktionsmanagement bei Datenbanksystemen

  • Als Literatur empfehlen wir [2; pp. 495] und [4]


Session 7: Interrupts und Interrupt Handling

  • Nebenbemerkungen:
    • direkter Zugriff auf Hardware-Devices, ihre Register (I/O Ports) und ggf. ihren zusätzliche Speicher (I/O Memory) mittels
      outb(), inb(), outw(), inw(), outl(), inl()
    • Polling versus Interrupts
    • Betrieb von Interface Devices ohne Interrupt Handling durch (1) zyklisches Auslesen der Statusregister und ggf. nachfolgenden Lese-/Schreibaufträgen an das Device, (2) DMA Devices ohne Interrupterzeugung, (3) Dual-ported RAM Devices
  • Synchrone Interrupts (Traps und Exceptions)
  • Asynchrone Interrupts - von externen Devices erzeugt
  • Vom HW-Interrupt bis zum Interrupt Handler: Interrupt am Device - Interrupt Controller - Interrupt lines zur CPU - do_IRQ()-Schnittstelle - Interrupt Vector - Interrupt Handler (= Interrupt Service Routine ISR, Top-Half) - Monitoring über /proc/interrupts
  • Registrierung von Interrupt Handlern durch Device Driver
  • ISR Interface
  • ISR Context im neuen Linux vom Prozesscontext verschieden - insbesondre mit eigenem Stack
  • Shared IRQs von Devices, welche die selbe Interrupt Line benutzen, Identifikation der zuständigen ISR
  • Sperren/Freigeben von Interrupts
  • Reentrant ISR sind unter Linux nicht erforderlich
  • Kernel Entropy Pool und der Beitrag von Interrupts zur Erzeugung "echter" Zufallszahlen
  • Bottom-halves zur Entlastung des Interrupt Handlers durch Verlagerung nicht zeitkritischer Aktivitäten in
    • Softirqs,
    • Taskletts,
    • Work Queues
  • Als Literatur empfehlen wir [0; pp. 75-118]


Session 8: Treiberentwicklung unter Linux

  • Als Literatur empfehlen wir [17]
  • Framework für Linux Kernel Mdules, die Treiber realisieren, siehe [17; Chapter 2].
  • Klassifikation der Treiber in Character/Block/Network Device Drivers
  • Major/Minor Numbers zur Identifikation von Treibern und den zugeordneten Hardwareschnittstellen (anwendbar für Character und Block Devices) - dynamische Vergabe der Major/Minor Numbers - Zuordnung über /proc/devices. Siehe [17; Chapter 3]
  • Die Realisierung von Treiberoperationen als Ausprägungen des virtuellen Dateisystems - Herstellung der Verbindung zwischen API-Aufruf (z.B. read(), write()) und Treiberfunktionen über File Structure, File Operations, Inode Structure - Kombination der (Treiber,Device)-spezifischen Zustandsdaten mit den Standardinformationen über Treiber, welche im Inode abgelegt werden (Devicenummer dev_t i_rdev; und Kernel-Datenstrukturen für (Character-) Devices struct cdev cdev;) - Standard-Entwurfsmuster für open(), read(), write(). Siehe [17; Chapter 3]
  • Das Beispiel eines Character Device Drivers ohne echten Hardware-Zugriff (Beispiel 'Scull' aus [17]) ist unter scull.tgz zu finden. Diese Beispiel erläutert das zugrunde liegende Framework für Linux Character Device Driver auf hervorragende Weise.


Literatur

Für die Lehrveranstaltung sind die folgenden Literaturangaben relevant, wobei speziell [0], [1], [2], [3] und [4] den Vorlesungstoff vertiefen.

[0] Robert Love: Linux Kernel Development, Second Edition, Novell Press, Indianapolis, USA, 2005. Elektronisch verfügbar als Safari Book Online unter Link zum Buch in der SUUB
[1] M. Beck, H. Böme, M. Dziadzka, U. Kunitz, R. Magnus, C. Schröter, D. Verworrner: Linux Kernel-Programmierung -- Algorithmen und Strukturen der Version 2.2, 5. Auflage. Addison-Wesley, 1999
[2] D.P. Bovet, M. Cesati: Understanding the Linux kernel, 1st edition. O'Reilly & Associates, 2001. Elektronisch verfügbar als Safari Book Online unter Link zum Buch in der SUUB
[3] U. Vahalia: Unix Internals - The New Frontiers, Prentice Hall 1996.
Dieses Buch geht zu den einzelnen Themenbereichen mehr in die Tiefe als Tanenbaum oder Stallings: Wenn diese beiden Bücher nicht mehr genug Details verraten, lohnt es sich, einen Blick in den Vahalia zu werfen.
[4] Wolfgang Maurer: Linux Kernelarchitektur. Konzepte, Strukturen und Algorithmen von Kernel 2.6, Hanser (2005).
siehe folgende WWW Referenz
[5] A. Tanenbaum: Modern Operating Systems, 2nd edition. Prentice Hall, 2001
[6] A. Tanenbaum: Moderne Betriebssysteme, Hanser 1995
[7] A. Tanenbaum, A. S. Woodhull: Operating Systems: Design and Implementation, 2nd edition. Prentice Hall, 1997.
Dies ist eine erweitere Fassung des 1. Teils von [5] bzw. [6].
[8] A. Tanenbaum: Distributed Operating Systems, Prentice Hall 1995.
Dies ist eine erweiterte und aktualisierte Fassung des 2. Teils von [5] bzw. [6].
[9] V. Toth: Programming Windows 98/NT Unleashed, Sams Publishing, 1998.
Eine umfangreicher Überblick über die Systemprogrammierung unter Windows 98 und Windows NT inkl. CD-ROM mit Beispielen.
[10] W. Stallings: Operating Systems - Internals and Design Principles, Prentice Hall 1998.
Diese Buch ist eine Alternative zu den Büchern von Tanenbaum. Es werden ebenfalls alle wichtigen Standardthemen, auch in bezug auf verteilte Systeme, behandelt.
[11] W.R. Stevens: Unix Network Programming, Prentice Hall 1990.
Eine sehr detaillierte Einführung in die Systemprogrammierung unter UNIX anhand ausführlicher Beispiele. Insbesondere wird auf die Standard Internet Protokolle eingegangen sowie auf Interprozesskommunikationsmechanismen aber auch Remote Login sowie RPCs werden behandelt. Inzwischen gibt es eine überarbeitete zweibändige Ausgabe von 1998.
[12] C.A.R. Hoare: Communicating Sequential Processes, Prentice Hall 1985.
Das Standardwerk zu CSP.
[13] A.W. Roscoe: The Practice and Theory of Concurrency, Prentice Hall 1998.
Eine modernisierte Einführung in CSP und FDR.
[14] J. Peleska: Formal Methods and the Development of Dependable Systems, Christian-Albrechts-Universität zu Kiel 1996.
In dieser Habilitationsschrift befindet sich u. a. die Spezifikation d er HP-UX Access Control Lists (S. 149ff). Eine Postscript-Version liegt zum Download lokal auf den Seiten der Universität Bremen.
[15] S. Maxwell: Linux Core Kernel Commentary, The Coriolis Group, 1999
Kernel-Kommentierungen
[16] Krzysztof R. Apt and Ersnt-Rüdiger Olderog: Verification of Sequential and Concurrent Programs., Springer, 1991
Vollständiger Beweis der Fairness und Universalität der Schedulers aus Session 4
[17] J. Corbet, A. Rubini and G. Kroah-Hartman: Linux Device Drivers., O'Reilly, 2005
Einführung in die Treiber Entwicklung für den Linux Kernel


Aufgabenblätter

 
   
Autor: jp
 
  AG Betriebssysteme, Verteilte Systeme 
Zuletzt geändert am: 2. November 2022   Impressum