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

Betriebssysteme 2, Sommersemester 2010

 

Aktuelles

  • Prüfungstermine:
    Die Prüfungen finden am 02.08. statt. Als Ausweichtermin steht der 5.08. zur Verfügung. Um einen Prüfungstermin zu bekommen, schreibt Ihr bitte eine Email an Florian und teilt ihm mit, ob Ihr ein Fachgespräch oder eine mündliche Prüfung machen möchtet und ob Euch die angebotenen Termine passen.

Termine

Vorlesung: 
ab dem 13.04.10, Di. 10-12 Uhr, MZH 5210, Prof. Dr. Jan Peleska 
Übung: 
ab dem 19.04.10, Mo. 10-12 Uhr, MZH 1090, 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 in der Form 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/time/timekeeping.c: Funktionen void do_gettimeofday() und getnstimeofday() und Verweis über Funktionspointer auf die HW-abhängige Methode zum Lesen der Mikrosekunden-Zeiteinheiten in timekeeping_get_ns().
    • arch/x86/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().
    • arch/x86/include/asm/msr.h: Definition des Makros rdtscll(), welches das TSC-Register mit der rdtsc-Assemblerinstruktion in __native_read_tsc() 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)
Der Weg durch den Kernel als anschauliches Sequenzdiagram.

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


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

  • Linux Kernel 2.6.33.2 herunterladen.
  • Kernelquellen auspacken: als normaler User im eigenen Home-Verzeichnis
  • Ins Verzeichnis linux-2.6.33.2 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/x86/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: Systemaufrufe: semop() (IPC-Paket) und fork()

semop()

  • Alle Systemaufrufe aus dem IPC-Paket (Semaphoren, Shared Memory, Message Queues) werden über den sys_ipc()-Aufruf im Kernel behandelt. Beispielsweise bietet die aktuelle Version der glibc für semop() folgenden Wrapper:

    int semop (semid, sops, nsops)
    int semid;
    struct sembuf *sops;
    size_t nsops;
    {
    return INLINE_SYSCALL (ipc, 5, IPCOP_semop, semid, (int) nsops, 0, CHECK_N (sops, nsops));
    }

  • sys_i386_32.c definiert sys_ipc(). Dort wird je nach angefordertem Dienst (Semaphoren, Shared Memory, Message Queues) in weitere sys_xyz()-Funktionen verzweigt.
  • semop() wird in sys_semtimedop() behandelt.
  • Die eigentlichen DOWN und UP-Operationen werden ggf. auf mehreren Semaphoren gleichzeitig (atomar) durchgeführt. Dies geschieht in try_atomic_semop.
  • Nach UP-Operationen muss ggf. ein wartender Prozess wieder rechenbereit gemacht werden. Dies erfolgt in update_queue(). Dort wird der aufzuweckende Prozess bestimmt und mittels wake_up_sem_queue() rechenbereit geschaltet. Der Standard-Dienst des Kernels hierzu ist wake_up_process().
  • Bei vergeblich versuchten DOWN-Operationen wird der semop() aufrufende Prozess in den Wartezustand versetzt. Sein neuer Zustand ist TASK_INTERRUPTIBLE: Schlafend, kann aber durch ein Unix-Signal wieder aufgeweckt werden. Der Prozess wird in eine Semaphorgruppen-spezifische Warteschlange eingetragen. schedule() wird aufgerufen, um einen Kontextwechsel einzuleiten. Der Prozess wird später als Resultat einer update_queue()-Durchführung aufgeweckt und setzt seine Arbeit hinter dem schedule()-Aufruf fort.

fork()

  • Architekturabhängiger Einsprungpunkt sys_fork() verzweigt direkt nach do_fork() unter Übergabe der Registerinhalte (Struktur struct pt_regs).
  • do_fork() behandelt die Erzeugung von "echten" Kindprozessen, sowie die Erzeugung von Clones. Light-Weight Processes (LWPs) sind ein Spezialfall der Clones: Das Cloning ermöglicht prinzipiell, folgende ressourcen mit dem Elternprozess zu teilen:
    • Virtuelle Speichertabelle
    • Dateisystem (Struktur fs in der Prozesstabelle)
    • Offene Dateien (Struktur files_struct)
    • Signal Handler
    • Tracing
    • Wakeup bei mm_release
    • Elternprozess des den Clone auslösenden Prozesses
    • Thread-Gruppe
    • Namespace-Gruppe
    • SEM_UNDO Semantik
    • Thread Local Storage (TLS)
    • TID des Elternprozess
    • UTS-Namensgruppe
    • IPCS
    • User Namespace
    • PID-Namespace
    • Network Namespace
    • IO Kontext
  • Die eigentliche Arbeit von do_fork() wird in copy_process() erledigt. Danach sind die wesentlichen Datenstrukturen für das Kind aufgesetzt. do_fork() bestimmt die als Rückgabewert von fork() zu vergebende virtuelle PID und aktiviert den neuen Prozess oder Clone über wake_up_new_task().
  • copy_process() erledigt folgende Arbeiten:
    • Erzeugung eines neuen Prozesstabelleneintrags (struct task_struct) für das Kind: Viele Einträge werden einfach durch Kopie vom Elternprozess übernommen; danach werden die Kond-spezifischen Daten eingetragen.
    • Aufbau eines neuen Stacks für das Kind
    • Je nach Status der Clone-Flags werden
      • Semaphore-Undo-information
      • files_struct
      • fs
      • Signal Handler
      • Anliegende Signale
      • Memory Map
      • Namespaces
      • IO-Kontext
      kopiert, oder es wird die Refernz auf die entsprechende Struktur des Elternprozess beibehalten.
    • Die Architekturabhängige Funktion copy_thread() Initialisiert Register und Stackpointer.
    • Die neue interne PID wird vergeben.
    • Das Kind wird unter seinen Geschwistern als neues Kind des Parent eingetragen.

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

  • Die zentrale Scheduler-Funktion schedule() führt die Kontextwechsel durch. Bei der Auswahl des nächsten Prozesses, der den CPU core erhalten soll, bedient sich schedule() der untergeordneten Scheduler-Klassen, die entsprechende Dienstfunktionen bereitstellen müssen.
  • 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
  • Funktion scheduler_tick() wird vom Clock Interrupt Handler aufgerufen. Die Scheduler-Klassen bieten Dienstfunktion task_tick() an, um die beim Time Tick notwendigen Funktionen innerhalb der Klasse auszuführen.
    • Bei der RT-Scheduler-Klasse ist dies in task_tick_rt() realisiert
    • Für SCHED_FIFO-Prozesse hat task_tick_rt() keine Auswirkung, es sei denn, dass die Beschränkung der genutzten CPU-Bandbreite konfiguriert wurde.
    • Bei SCHED_RR-Prozessen wird die Zeitscheibe dekrementiert. Bei Ablauf der Zeitscheibe wird der aktive Prozess ans Ende seiner Runqueue umgehängt, bekommt dabei wieder eine "frische" Zeitscheibe konstanter Länge (100ms).
    • Das Need-Resched-Bit wird dann gesetzt, so dass schedule() bei nächster Gelegenheit den Kontextwechsel vornimmt.
  • Funktion sched_fork() wird bei Erzeugung eines neuen Prozesses mittels fork()/clone() aufgerufen. Die Verwaltungsdaten für die Scheduling-Entity werden initialisiert.
  • Rot-Schwarz-Bäme für die Completely Fair Scheduler Klasse
    • Binärer Baum mit Knotenmarkierung rot/schwarz
    • Nutzt Parent-, Left-, Right-Pointer, die auf NIL zeigen, falls kein "echter" Parent-, Left-, Right-Knoten mehr existiert.
    • Garantiert einen Grad von Balancierung, der allerdings schwächer ist als derjenige von AVL-Bäumen: Bei Rot-Schwarz-Bäumen ist kein Pfad von der Wurzel zum Blatt mehr als doppelt so lang wie ein anderer.
    • Dieser Grad von Balancierung wird durch Einhaltung der Rot-Schwarz-Eigenschaften beim Einfügen und Löschen erreicht:
      • Jeder Knoten ist rot oder schwarz
      • Jedes NIL-Blatt ist schwarz
      • Wenn ein Knoten rot ist, dann sind alle seine Kinder schwarz
      • Für jeden Knoten n gilt: Jeder von n ausgehende Pfad, der erst bei einem NIL-Blatt endet, besitzt dieselbe Anzahl schwarzer Knoten.
    • Rot-Schwarz-Bäume mit k Knoten haben eine maximale Höhe von 2lg(k+1).
    • Zum Einfügen und Löschen werden die üblichen Algorithmen für binäre Bäume verwendet. Danach muss aber ggf. der Baum neu balanciert werden, um die Rot-Schwarz-Eigenschaften wieder herzustellen. Dies erfolgt mit Hilfe von Links- und Rechts-Rotationen, sowie durch Umfärbung von Knoten.
    • Siehe Pseudocode für Rot-Schwarz Einfügen und [18; pp. 263].

    Beispiel aus dem Tutorium für eine einfache Scheduling-Klasse.


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

    • Dateisysteme sind typischerweise in Kernel-Modulen implementiert. Die __init()-Funktion eines solchen Moduls ruft den Kernel-Dienst int register_filesystem(struct file_system_type * fs) (filesystems.c) auf und übergibt dabei die Adresse einer ausgefüllten file_system_type-Struktur (fs.h). Unter den Komponenten dieser Struktur befindet sich der Funktionszeiger auf die get-superblock Funktion
      int (*get_sb) (struct file_system_type *, int, const char *, void *, struct vfsmount *);
      welche jedes Dateisystem mit dieser Signatur anbieten muss, damit Mounts auf Instanzen dieses Dateisystems durchgeführt werden können.
    • Die konkrete Instanz eines Dateisystems nach dem Mount-Vorgang wird im Kernel durch die Struktur struct vfsmount (mount.h) repräsentiert.
    • 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 Modules, 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. Verfügbar unter PDF-Files
    Einführung in die Treiber Entwicklung für den Linux Kernel
    [18] Thomas H. Cormen, Charles E. Leiserson and Ronald L. Rivest: Introduction to Algorithms., The MIT Press, Cambridge Massachusetts, McGraw-Hill Book Company, New York, 1999
    Gute Beschreibung des Rot-Schwarz-Baum Algorithmus


    Aufgabenblätter

    Als Grundlage zur Bearbeitung der Übungsblätter dient der Linux Kernel 2.6.33.2.

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