Betriebssysteme - Einleitung



Ähnliche Dokumente
Betriebssysteme - Einleitung

Gibt Daten im erweiterten Format aus. Dies beinhaltet die Angabe von Zugriffsrechten, Besitzer, Länge, Zeitpunkt der letzten Änderung und mehr.

Dämon-Prozesse ( deamon )

Systemprogrammierung I - Aufgaben zur Erlangung der Klausurzulassung für Informatiker und Wirtschaftsinformatiker

FILE *fp; char fname[100];... fp = fopen (fname, rb ); if( fp == NULL ) { perror( fopen );... } // Fehlernachricht auf letzten Fehler, der aufkam

Datensicherung. Beschreibung der Datensicherung

PROGRAMMIEREN MIT UNIX/LINUX-SYSTEMAUFRUFEN

Er musste so eingerichtet werden, dass das D-Laufwerk auf das E-Laufwerk gespiegelt

Binäre Bäume. 1. Allgemeines. 2. Funktionsweise. 2.1 Eintragen

Prozesse. Stefan Janssen. Alexander Sczyrba

Tapps mit XP-Mode unter Windows 7 64 bit (V2.0)

UNIX Dateirechte. Michael Hartmann. 7. Oktober Linux User Group Augsburg

Prozesse und Logs Linux-Kurs der Unix-AG

Einrichten einer Festplatte mit FDISK unter Windows 95/98/98SE/Me

Fachbericht zum Thema: Anforderungen an ein Datenbanksystem

Lizenzen auschecken. Was ist zu tun?

Um ein solches Dokument zu erzeugen, muss eine Serienbriefvorlage in Word erstellt werden, das auf die von BüroWARE erstellte Datei zugreift.

2. Word-Dokumente verwalten

Architektur Verteilter Systeme Teil 2: Prozesse und Threads

SFTP SCP - Synology Wiki

iphone-kontakte zu Exchange übertragen

4D Server v12 64-bit Version BETA VERSION

Mitarbeiter-Alarm. 1x Taster mit Kabel zum Anschluss an den seriellen Com-Port (optional) 1x Installationsprogramm auf CD 1x Lizenz

CA Übung Christian kann heute nicht kommen => ich bin heute da, Christian das nächste Mal wieder

ICS-Addin. Benutzerhandbuch. Version: 1.0

Die Dateiablage Der Weg zur Dateiablage

TechNote. Produkt: TWINFAX 7.0 (ab CD_24), TWINFAX 6.0 Modul: SMTP, T611, R3 Kurzbeschreibung: Briefpapier- und Mailbodyunterstützung

Bedienungsanleitung. Stand: Copyright 2011 by GEVITAS GmbH

Interprozesskommunikation

Schrittweise Anleitung zur Installation von Zertifikaten der Bayerischen Versorgungskammer im Mozilla Firefox ab Version 2.0

Systeme 1. Kapitel 6. Nebenläufigkeit und wechselseitiger Ausschluss

Updatehinweise für die Version forma 5.5.5

Speichern. Speichern unter

Prozessarchitektur einer Oracle-Instanz

Leitfaden zur ersten Nutzung der R FOM Portable-Version für Windows (Version 1.0)

OPERATIONEN AUF EINER DATENBANK

Verwendung des Terminalservers der MUG

MORE Profile. Pass- und Lizenzverwaltungssystem. Stand: MORE Projects GmbH

1 Vom Problem zum Programm

Backup der Progress Datenbank

2. Die eigenen Benutzerdaten aus orgamax müssen bekannt sein

Tutorial -

Handbuch PCI Treiber-Installation

Update von Campus-Datenbanken (FireBird) mit einer Version kleiner 9.6 auf eine Version größer 9.6

Objektorientiertes Programmieren mit Suse Linux

Betriebssysteme. Dipl.-Ing.(FH) Volker Schepper

Einrichtung des Cisco VPN Clients (IPSEC) in Windows7

3 ORDNER UND DATEIEN. 3.1 Ordner

ABB i-bus KNX. Software-Information. Melde- und Bedientableau. Typ: MT 701.2

SafeRun-Modus: Die Sichere Umgebung für die Ausführung von Programmen

Linux Prinzipien und Programmierung

Im Folgenden wird Ihnen an einem Beispiel erklärt, wie Sie Excel-Anlagen und Excel-Vorlagen erstellen können.

Zugriff auf Daten der Wago über eine Webseite

SICHERN DER FAVORITEN

DOKUMENTATION VOGELZUCHT 2015 PLUS

Outlook-Daten komplett sichern

Inhalt. 1 Einleitung AUTOMATISCHE DATENSICHERUNG AUF EINEN CLOUDSPEICHER

Was ist PDF? Portable Document Format, von Adobe Systems entwickelt Multiplattformfähigkeit,

Prozesse und Logs Linux-Kurs der Unix-AG

SJ OFFICE - Update 3.0

Artikel Schnittstelle über CSV

Lernwerkstatt 9 privat- Freischaltung

EasyWk DAS Schwimmwettkampfprogramm

Bilder zum Upload verkleinern

INSTALLATION VON INSTANTRAILS 1.7

Überprüfung der digital signierten E-Rechnung

Inkrementelles Backup

Computer Algebra Plan der Vorlesung. erstes Drittel: linux, emacs, L A TEX zweites Drittel: Sage als Taschenrechner letztes Drittel: Python für Sage

Wichtige Hinweise zu den neuen Orientierungshilfen der Architekten-/Objektplanerverträge

Monitore. Klicken bearbeiten

SANDBOXIE konfigurieren

2A Basistechniken: Weitere Aufgaben

Erstellen einer digitalen Signatur für Adobe-Formulare

Installation Blockdruck WEB. Version 3.1.1

Memeo Instant Backup Kurzleitfaden. Schritt 1: Richten Sie Ihr kostenloses Memeo-Konto ein

Leitfaden zum Sichern einer Festplatte als Image mit der System Rescue CD

Nutzung von GiS BasePac 8 im Netzwerk

IBM Software Demos Tivoli Provisioning Manager for OS Deployment

Anleitung: Webspace-Einrichtung

Datenübernahme von HKO 5.9 zur. Advolux Kanzleisoftware

KURZANLEITUNG CLOUD OBJECT STORAGE

Zählen von Objekten einer bestimmten Klasse

Eine Einführung in die Installation und Nutzung von cygwin

Daten-Synchronisation zwischen dem ZDV-Webmailer und Outlook ( ) Zentrum für Datenverarbeitung der Universität Tübingen

Step by Step Webserver unter Windows Server von Christian Bartl

Handbuch B4000+ Preset Manager

DriveLock 6. DriveLock und das Windows Sicherheitsproblem mit LNK Dateien. CenterTools Software GmbH

Nach der Anmeldung im Backend Bereich landen Sie im Kontrollzentrum, welches so aussieht:

1. Einschränkung für Mac-User ohne Office Dokumente hochladen, teilen und bearbeiten

Ordner Berechtigung vergeben Zugriffsrechte unter Windows einrichten

1. Zuerst muss der Artikel angelegt werden, damit später die Produktvarianten hinzugefügt werden können.

LabView7Express Gerätesteuerung über LAN in einer Client-Serverkonfiguration. 1. Steuerung eines VI über LAN

2 Die Terminaldienste Prüfungsanforderungen von Microsoft: Lernziele:

Adressen der BA Leipzig

TeamSpeak3 Einrichten

Handbuch USB Treiber-Installation

Einführung in die Programmierung

Meldung Lokale Anwendung inkompatibel oder Microsoft Silverlight ist nicht aktuell bei Anmeldung an lokal gespeicherter RWE SmartHome Anwendung

Drucken aus der Anwendung

Systemnahe Programmierung in C Übungen Jürgen Kleinöder, Michael Stilkerich Universität Erlangen-Nürnberg Informatik 4, 2011 U7.fm

Transkript:

Betriebssysteme - Einleitung... alois.schuette@h-da.de Version: WS2015-v1.0(6a9de72) Alois Schütte 12. Oktober 2015 1 / 99

Inhaltsverzeichnis In diesem Teil wird in die Thematik der Betriebssysteme eingeführt. 1 Überblick 2 Was ist ein Betriebssystem 3 Historische Betrachtung 4 Grundlegende Konzepte 5 6 Betriebssystemstrukturen 2 / 99

Überblick Überblick Software kann grob in zwei Arten eingeteilt werden: Systemsoftware, die den Rechner selbst steuert und Anwenderprogramme, die die Aufgaben der Anwender lösen. Das wichtigste Systemprogramm ist das Betriebssystem, es kontrolliert die Ressourcen des Rechners und ist Basis für die Entwicklung der Anwenderprogramme. 3 / 99

Überblick Ein moderner Rechner besteht u.a. aus den Komponenten: ein oder mehrere Prozessoren, Hauptspeicher, Taktgeneratoren, Datenendgeräte, Plattenlaufwerke, Netzwerkkomponenten und DFÜ Einrichtungen. Insgesamt also eine komplexe Hardware. 4 / 99

Überblick Schichtenmodell Ziel der Systemprogrammierung ist es, die Anwender vor der Komplexität der Hardware zu bewahren. Um der Komplexität Herr zu werden, kann man Rechner in Schichten einteilen. Das Betriebssystem ist eine Softwareschicht oberhalb der Hardware, bedient alle Teile des Systems und stellt dem Anwender eine virtuelle Maschine bereit, die einfach zu verstehen und zu bedienen ist. SAP Office... Compiler Editoren Shells Betriebssystem Maschinensprache Mikroprogrammierung physikalische Geräte Systemprogramme Anwendungsprogramme Hardware 5 / 99

Was ist ein Betriebssystem Betriebssystem als virtuelle Maschine Rechner sind auf Maschinenebene umständlich zu programmieren. Soll ein Text auf Diskette gespeichert werden, muss man auf Maschinenebene die Struktur des Speichermediums (Spuren, Sektoren,...) und die Maschinenbefehle zum Positionieren des Lese/Schreibkopfes kennen. Ein Anwendungsprogrammierer soll sich mit solchen Details der physischen Geräte nicht kümmern. Er erwartet von einem Betriebssystem eine virtuelle Maschine, auf der er dateiorientiert Lesen und Schreiben kann. 6 / 99

Was ist ein Betriebssystem Betriebssystem als Ressourcenverwalter Heutige Rechner sind leistungsfähig genug, um mehreren Benutzern Zugriff auf angeschlossene Drucker, Modems oder Prozessoren zu geben. Damit unterschiedliche Benutzer die selben Ressourcen gleichzeitig nutzen können, sind Verwaltungsaufgaben erforderlich. Ein Betriebssystem übernimmt die Verwaltung der unterschiedlichen an den Rechner angeschlossenen Geräte und der Bestandteile des Rechners selbst, wie Hauptspeicher oder Prozessoren. 7 / 99

Historische Betrachtung Historische Betrachtung 1 Überblick 2 Was ist ein Betriebssystem 3 Historische Betrachtung Erste Generation: Röhrenrechner (1945-1955) Zweite Generation: Transistoren und Stapelsysteme (1955-1965) Dritte Generation: Multiprogrammierung (1965-1980) Vierte Generation: Personalcomputer (seit 1980) Netzwerk-Betriebssysteme und Verteilte Systeme 4 Grundlegende Konzepte 5 6 Betriebssystemstrukturen 8 / 99

Historische Betrachtung analytical engine Der erste digitale Rechner wurde von dem englischen Mathematiker Charles Babbage (1792-1871) entworfen. Seine analytical engine funktionierte nie, denn ihr rein mechanischer Aufbau ließen die Fertigung mit der erforderlichen Präzision damals nicht zu. Programmiert wurde in Assembler; es war die erste Turing-vollständige Programmiersprache. Dennoch, dieser Rechner wäre ohne Betriebssystem ausgekommen. Abbildung: analytical engine 9 / 99

Historische Betrachtung Erste Generation: Röhrenrechner (1945-1955) Erste Generation: Röhrenrechner (1945-1955) Der erste brauchbare digitale Rechner wurde im zweiten Weltkrieg entwickelt, die trotz ihre Größe (ein ganzer Raum voll mit Rohren, Kühlungen und E/A Geräten) weniger Rechenleistung vollbrachten als ein Smartphone. Diese Röhrenrechner wurden von wenigen Ingenieuren konzipiert, gebaut, programmiert, betrieben und gewartet. Die Programmierung erfolgte im Binärcode. Programmiersprachen und Betriebssysteme waren noch unbekannt. Die ersten Programme waren Berechnungen von Sinus- und Kosinustabellen. Anfang der fünfziger Jahre begann man, die Programme im Binärcode auf Lochkarten zu schreiben, damit sie wieder verwendbar waren. Abbildung: Zuse Z22 10 / 99

Historische Betrachtung Zweite Generation: Transistoren und Stapelsysteme (1955-1965) Zweite Generation: Transistoren und Stapelsysteme (1955-1965) Die Erfindung des Transistors führte dazu, dass Rechner zuverlässiger arbeiteten. Ab diesem Zeitpunkt wurde zwischen Entwicklern, Herstellern, Betreibern und Wartungspersonal unterschieden. Die Maschinen wurden in gesicherten Räumen aufgestellt, die von Bedienern (operator) betreut wurden. Wenn ein Rechenauftrag (job) ausgeführt werden sollte, musste ein Programmierer die Befehlsfolge auf Lochkarten stanzen und dem Operator übergeben. Der legte die Lochkarten in den Kartenleser und startete den Übersetzungslauf und anschliessend das Programm. Das Ergebnis (Ausdruck auf Papier) brachte der Operator in den Ausgaberaum, wo der Programmierer das Resultat abholen konnte. 11 / 99

Historische Betrachtung Zweite Generation: Transistoren und Stapelsysteme (1955-1965) Die hohen Investitionskosten zwangen die Entwickler dazu, nach effizienteren Wegen zu suchen. Der Stapelbetrieb (batch system) sollte die Systeme besser auslasten. Zuerst wurden die Aufträge im Eingaberaum gesammelt und dort auf einem Magnetband mit einem kleineren Rechner übertragen, der teilweise auch die Übersetzung (Fortran-Maschinenkode) vornahm. Diese Prozedur wurde wiederholt (etwa eine Stunde) bis genügend viele Aufträge auf dem Magnetband waren. Das erzeugte Eingabeband wurde dann zum eigentlichen Rechner gebracht. Dort wurde ein spezielles Programm gestartet (eigentlich der Vorfahre eines Betriebssystems), das das Eingabeband einlas, die Programme ausführte und das Ergebnis auf ein Ausgabeband schrieb. Der Operator brachte das Band dann zu einem weiteren Rechner, der die Ergebnisse ausdruckte. 12 / 99

Historische Betrachtung Zweite Generation: Transistoren und Stapelsysteme (1955-1965) Diese Abarbeitungsmethodik von Kontrollkarten war der Vorgänger heutiger Kommandointerpreter (Shells). Großrechner der zweiten Generation wurden weitgehend in Forschungseinrichtungen, etwa zur Lösung von Differentialgleichungen eingesetzt. Programmiert wurde in Fortran oder Assembler. In Banken und Versicherungen begann die EDV eine wichtige Rolle zu spielen der closed job Betrieb wurde etabliert. Typische Betriebssysteme waren FMS (Fortran Monitor System) und IBSYS, das IBM Betriebssystem des Großrechners 7094. Abbildung: Lochkartenstapel 13 / 99

Historische Betrachtung Dritte Generation: Multiprogrammierung (1965-1980) Dritte Generation: Multiprogrammierung (1965-1980) Anfang der sechziger Jahre hatten die meisten Computerhersteller zwei unterschiedliche und gegenseitig inkompatible Produktlinien: wortorientierte Grossrechner für den technisch-wissenschaftlichen Bereich und zeichenorientierte Rechner für den kommerziellen Bereich, die hauptsächlich für Bandsortierungen und zum Ausdrucken (Auszüge, Fälligkeitslisten) bei Banken. IBM versuchte daraufhin mit einem einheitlichen Konzept, dem System /360, beide Anwendungsbereiche durch eine Systemfamilie mit softwarekompatiblen Abbildung: IBM 360 Rechnern abzudecken. Dieses Konzept war sehr erfolgreich. Nachfolger dieser Familie (z.b. 370, 3090) sind z.t. noch heute bei Banken im Einsatz. 14 / 99

Historische Betrachtung Dritte Generation: Multiprogrammierung (1965-1980) Der Vorteil, ein Betriebssystem für alle Bereiche und Programme, das auf allen Rechnern der Familie lauffähig war, erwies sich im Laufe der Zeit als die größte Schachstelle: es entstand ein sehr großes, kaum mehr zu wartendes Betriebssystem. Die wichtigsten Schlüsseltechniken der Betriebssysteme dieser Generation waren/sind: 1 Mehrprogrammbetrieb, 2 Spooling und 3 Timesharing Betrieb. 15 / 99

Historische Betrachtung Dritte Generation: Multiprogrammierung (1965-1980) Mehrprogrammbetrieb Der Mehrprogrammbetrieb (multiprogramming) wurde entwickelt, nachdem festgestellt wurde, dass gerade bei E/A intensiven Anwendungen die CPU die meiste Zeit nicht ausgelastet war. Z.B. wartete die CPU auf das Terminieren eines Bandkopierens, bevor sie den nächsten Auftrag ausführte. Job5 Die Lösung bestand in der Aufteilung des Hauptspeichers in Speicherabschnitte auf Hardwareebene (jeweils mit Auftrag und Schutzmechanismen), die sicherstellen, dass Aufträge sich nicht gegenseitig beeinträchtigen. Job4 Job3 Job2 Job 1 Betriebssystem Hauptspeicher Abbildung: Mehrprogrammbetrieb mit 5 Jobs im Speicher 16 / 99

Historische Betrachtung Dritte Generation: Multiprogrammierung (1965-1980) Spooling Spooling (Simultaneous Peripheral Operation On Line) bezeichnet ursprünglich die unmittelbare Zwischenspeicherung von Aufträgen, die vom Kartenleser direkt auf Platten übertragen wurden. Wenn ein Auftrag terminierte, konnte das Betriebssystem einen neuen Auftrag sofort in den freien Speicherbereich laden und bearbeiten. Heute wird diese Technik noch im Bereich Ausgabe angewendet (Drucker Spooling). Die Rechner der dritten Generation waren zwar sehr leistungsfähig, sowohl im kaufmännischen als auch im technisch wissenschaftlichen Bereich. Die Verarbeitung war aber hauptsächlich Stapelverarbeitung: ein Fehler, der in einer Ausgabe (Liste) bemerkt wird, muss korrigiert werden und ein neuer Compile Auftrag muss eingeplant werden, dann erst kann die korrigierte Version in einem neuen Auftrag laufen. 17 / 99

Historische Betrachtung Dritte Generation: Multiprogrammierung (1965-1980) Dialogsysteme Der Wunsch nach schnelleren Reaktions- und Antwortzeiten brachte Dialogsystem (timesharing) hervor: jeder Benutzer ist online mit dem Rechner über ein eigenes Terminal verbunden. MULTICS Das MIT, Bell Labs und General Electric wollten eine Maschine entwickeln, mit der gleichzeitig mehrere hundert Benutzer arbeiten können sollten. Vorbild war das elektrische Versorgungssystem: ein elektrisches Gerät wird einfach an eine Steckdose angeschlossen. Obwohl das Projekt MULTICS (MULTiplexed Information and Computing Service) nie abgeschlossen wurde, sind aus ihm viele wichtige Ideen in die Informatik eingeflossen und Nachfolgeprojekte stark beeinflusst worden. 18 / 99

Historische Betrachtung Dritte Generation: Multiprogrammierung (1965-1980) Minicomputer Ein weiterer wesentlicher Faktor der Entwicklung der Rechner der 3. Generation bestand im Wachstum der Minicomputer, beginnend mit der DEC PDP-1 im Jahre 1961. Sie verfügte über 4k 18 Bit Worte, kostete nur 120.000 USD (ca. 5% einer IBM 7094) und war im Bereich numerische Berechnungen mit einer IBM 7094 vergleichbar, aber zu IBM inkompatibel. Die leistungsstärksten PDP Rechner (PDP-11) waren noch Ende der 80er Jahre in vielen Forschungsinstituten zu finden. Abbildung: Ken Thompson und Dennis Ritchie 1972 vor einer PDP-11 19 / 99

Historische Betrachtung Dritte Generation: Multiprogrammierung (1965-1980) UNICS, C Ein Mitarbeiter des MULTICS Projektes bei Bell Labs, Ken Thompson, schrieb eine abgemagerte Version als Einbenutzersystem. Brian Kernighan bezeichnete dieses System als UNICS (UNiplexed Information and Computing Service), später wurde es dann UNIX genannt. Ein anderer Informatiker bei Bell, Dennis Ritchi entwickelte die Programmiersprache C, um damit Unix neu zu schreiben. Ziel war es, ein Betriebssystem für den Mehrbenutzerbetrieb zu realisieren, das leicht auf unterschiedliche Rechner portierbar war. Bell Labs vergab Unix Lizenzen kostenlos an Hochschulen. Dadurch wurde es auf viele Plattformen portiert. Unix und die freie Version Linux ist das Betriebssystem, das auf den meisten Plattformen portiert wurde. Die Vorlesung wird sich stark an Unix orientieren, um Konzepte von Betriebssystemen zu erklären. 20 / 99

Historische Betrachtung Vierte Generation: Personalcomputer (seit 1980) Vierte Generation: Personalcomputer (seit 1980) Durch Fortschritte in der Entwicklung hochintegrierter Schaltkreise konnten Rechner konstruiert werden, die auch für Einzelpersonen erschwinglich waren. Der PC wurde mit Betriebssystemen und Oberflächen ausgestattet, die es auch Normalbenutzern ermöglichten, damit zu arbeiten. Zwei Betriebssysteme hatten sich für PCs durchgesetzt: MS-DOS von Microsoft für Intel-basierte Hardware und Unix für Systeme, die auf Motorolas 68000er Familie beruhten. MS-DOS entwickelte sich bezogen auf die Funktionalität immer mehr in Richtung Unix (Microsoft war zu dieser Zeit der führende Unix Lieferant [XENIX]). 21 / 99

Historische Betrachtung Netzwerk-Betriebssysteme und Verteilte Systeme Netzwerk-Betriebssysteme und Verteilte Systeme Seit Mitte der 80er Jahre wurden Rechner mehr und mehr vernetzt. Dadurch wurden Betriebssystemkonzepte erforderlich, die mit der Problematik des Datenaustausches, dem Verteilen von Aufgaben und der Kommunikation zwischen Prozessen, die auf unterschiedliche Knoten im Netz ablaufen, erforderlich. 22 / 99

Historische Betrachtung Netzwerk-Betriebssysteme und Verteilte Systeme Netzwerkbetriebssysteme Netzwerkbetriebssysteme erlauben einem Benutzer, Daten von anderen bekannten Rechnern zu verwenden und sich auf anderen bekannten Rechnern anzumelden. Dabei können die verschiedenen Rechner unterschiedliche Betriebssysteme haben. Die Transparenz beim Kopieren von Daten geschieht durch die Verwendung von Standardprotokollen (z.b. ftp). Die Netzwerkbetriebssysteme unterscheiden sich nicht von den Einprozessor Betriebssystemen; auf unterster Ebene ist lediglich eine Schicht, die über einen Netzwerkkontroller auf Netzressourcen zugreift. Weiterhin sind Programme erforderlich, um Remoterechner ansprechen zu können (telnet, rsh, ssh, sftp, scp). 23 / 99

Historische Betrachtung Netzwerk-Betriebssysteme und Verteilte Systeme Verteilte Betriebssysteme Verteilte Betriebssysteme spiegeln dem Benutzer ein Einprozessorsystem vor: der Benutzer weiß nicht, auf welchem Knoten im verteilten System sein Programm abläuft. Auf der Betriebssystemseite sind (im Gegensatz zu Netzwerkbetriebssystemen) hier neue komplexere Algorithmen erforderlich, die die Verteilung der Programmstücke auf verschiedene Prozessoren vornehmen, um viel paralleles Arbeiten zu ermöglichen. Die Problematik resultiert im Prinzip daraus, dass es keine zentrale Haltung von Zustandsinformation gibt. Der Entwurf eines verteilten Systems hat darüber hinaus zum Ziel, die Ausfallsicherheit und Fehlertoleranz des Gesamtsystems zu erhöhen: der Ausfall von Komponenten soll ohne Beeinträchtigung der Funktionalität gewährleistet werden können. Diese Verteilten Betriebssysteme werden hier nicht behandelt! 24 / 99

Grundlegende Konzepte Grundlegende Konzepte 1 Überblick 2 Was ist ein Betriebssystem 3 Historische Betrachtung 4 Grundlegende Konzepte Prozesse Dateien Kommandointerpreter 5 6 Betriebssystemstrukturen 25 / 99

Grundlegende Konzepte Prozesse Prozesse Ein Prozess ist ein Programm, das sich in der Ausführung befindet. Während ein Programm nur einmal auf der Festplatte gespeichert ist, dann dieses Programm mehrmals aufgerufen sein, also können von einem Programm mehrere Prozesse existieren. Ein Prozess besteht aus: dem Programmcode, den Programmdaten, dem Programmstack, dem Befehlszähler, dem Stackzeiger und Speicherinformationen, die zum Ablauf erforderlich sind. 26 / 99

Grundlegende Konzepte Prozesse Der Prozessbegriff orientiert sich an den Erfordernissen von Timesharing Systemen, d.h. das Betriebssystem muss periodisch entscheiden, ob der aktuelle Prozess weiterlaufen darf, oder ob seine Zeitscheibe schon abgelaufen ist, er also suspendiert werden muss, um einem neuen Prozess die CPU zuteilen zu können. Wird ein Prozess suspendiert, so muss er bei der Reaktivierung im selben Zustand weitermachen können. Deshalb ist es erforderlich, sich den zuletzt ausgeführten Befehl vor der Suspendierung mit allen Umgebungsmerkmalen zu merken, z.b. die aktuell geöffneten Dateien, pro Datei den Stand des Lese/Schreib-Kopfes usw. 27 / 99

Grundlegende Konzepte Prozesse Prozesstabelle Diese Informationen werden i.a. in einer so genannten Prozesstabelle gespeichert. Sie besteht aus einem Feld von Strukturen, die die o.a. Informationen pro suspendiertem Prozess enthält. Damit besteht ein suspendierter Prozess aus: seinem Prozessadressraum (dem Speicher in dem Programm und Daten liegen) und seinem Eintrag in der Prozesstabelle. 28 / 99

Grundlegende Konzepte Prozesse zur Prozessverwaltung sind u.a. dafür verantwortlich, dass Prozesse erzeugt und terminiert werden, Speicher für einen Prozess angefordert und freigegeben wird, ein Prozess sich mit anderen unterhalten kann und die Ablaufumgebung von Prozessen definiert (Größe des Speichers, Anzahl max. geöffneter Dateien,...) wird. 29 / 99

Grundlegende Konzepte Prozesse Prozessbaum Wenn ein Benutzer mit dem Betriebssystem arbeitet, so werden seine Anfragen von der Shell bearbeitet. Dies ist ein Prozess, der die Kommandos des Benutzers liest, interpretiert und ausführt. Nehmen wir an, der Benutzer gibt ein Kommando zum Suchen einer Datei ein. Dann muss die Shell einen Prozess erzeugen, ihm den Code des Such-Kommandos überlagern, ihn ausführen und dann beenden. Durch das Erzeugen von Prozessen (Kindern), ausgehend von einem Erzeuger-Prozess (Vater), entsteht eine Baumstruktur von Prozessen. Abbildung: Prozessbaum 30 / 99

Grundlegende Konzepte Dateien Dateien Dateien werden in Betriebssystem systematisch auf Plattenbereiche verteilt. existieren zum Öffnen, Lesen, Schreiben und Schließen einer Datei. Dateien in Linux werden organisiert, indem man Verzeichnisse anlegen kann. Ein Verzeichnis ist ein Katalog mit Inhalten, die aus Dateien und selbst wieder Verzeichnissen bestehen können. Somit hat auch das Dateisystem eine baumartige Struktur. Wenn ein Benutzer sich in Linux anmeldet, so ist er automatisch in seinem Heimatverzeichnis (home directory). Die Daten kann er sich dort selbst organisieren. Abbildung: Dateibaum 31 / 99

Grundlegende Konzepte Dateien Zugriffsrechte Der Zugriff auf Dateien ist in Linux durch einen neunstelligen binären Zugriffscode, der jeder Datei zugeordnet ist, geschützt. Dieser Code besteht aus jeweils 3 Bit für den Eigentümer, die Gruppe zu der der Eigentümer gehört und für alle anderen. Rechte können pro Kategorie vergeben werden: Lesen (r), Schreiben (w) und Ausführen (x). Somit hat eine Datei, die nur der Benutzer Lesen, Schreiben und Ausführen und die Gruppenmitglieder nur Lesen dürfen, die Zugriffsmaske " rwx r-- --- " (entspricht oktal 740). Für Verzeichnisse bedeutet das x-bit, dass im Katalog gesucht werden darf. (sticky bit und set user id bit später) 32 / 99

Grundlegende Konzepte Dateien mounten Wenn ein Betriebssystem hochgefahren wird, so wird i.a. zuerst das Dateisystem, auf dem sich das Betriebssystem selbst befindet, verfügbar gemacht. Es befindet sich normalerweise auf der Festplatte. Daneben existieren auswechselbare Speichermedien z.b. USB-Laufwerke oder CD Laufwerke. Auf solchen Medien kann man ein auswechselbares Dateisystem (mounted file system) anlegen. Es wird in das Hauptdateisystem eingehängt und ist ab diesem Zeitpunkt normal über den Pfadnamen erreichbar. Abbildung: mounten 33 / 99

Grundlegende Konzepte Kommandointerpreter Kommandointerpreter Das Betriebssystem ist der für die Ausführung von n verantwortliche Teil der Systemsoftware. Wie im Schichtenmodell gezeigt, ist der Kommandointerpreter nicht Bestandteil des Betriebssystems. Jedes Kommando, das der Benutzer absendet, bewirkt, dass die Shell einen Kindprozess erzeugt, der das Kommando ausführt. Die Shell wartet, bis der Kindprozess terminiert ist. Dann wird wieder ein Prompt geschrieben. Soll ein Programm im Hintergrund ablaufen, so wird das Kommando mit dem Zeichen & abgeschlossen. 34 / 99

1 Überblick 2 Was ist ein Betriebssystem 3 Historische Betrachtung 4 Grundlegende Konzepte 5 Prozessverwaltung Signale Dateiverwaltung Katalogverwaltung Schutzmechanismen Zeitverwaltung Ablaufumgebung von Prozessen 35 / 99

Die vorgestellten sind an der Implementierung in Unix orientiert. In anderen Betriebssystemen sind die Umsetzungen zwar teilweise anders, das Prinzip ist aber das gleiche. bilden die Schnittstelle zur Hardware, auf dem das Betriebssystem abläuft. Deshalb sind grosse Teile eines Systemaufrufs in Assembler programmiert. Um sie für Programmierer nutzbar zu machen, wird oft eine C-Bibliothek bereitgestellt. Systemaufruf zum Lesen einer Datei ist read. count = read(file, buffer, nbytes); Mit drei Parametern von read wird beschrieben, welche Datei gelesen werden soll, wohin die Leseoperation das Ergebnis ablegen soll und wieviele Bytes aus der Datei gelesen werden sollen. 36 / 99

Beispiel simplecat.c 1 # define BUFSIZE 512 2 int main (){ 3 char buf [ BUFSIZE ]; 4 int n; 5 while (( n = read (0, buf, BUFSIZE )) > 0) <- 6 write (1, buf, n); <- 7 } Das Programm wird von Shellebene aus mit dem Kommando $ cc simplecat.c -o simplecat übersetzt. Die resultierende ausführbare Datei simplecat kann nun verwendet werden, um Dateien zu lesen, etwa durch: $ simplecat < /etc/passwd Im Folgenden werden nun die wichtigsten kurz für Prozessverwaltung, Signale, Dateiverwaltung, Katalog- und Dateisystemverwaltung, Schutzmechanismen und Zeitverwaltung vorgestellt. 37 / 99

Prozessverwaltung Prozessverwaltung Ein Prozess besitzt einen Adressraum, in dem er abläuft. Der Prozessraum ist in mehrere Teile (Segmente) aufgeteilt. Der Programmcode befindet sich im Textsegment. Im Datensegment sind globale Objekte abgelegt, dann folgt der Heap für dynamische Objekte. Der Stack ist zur Speicherung lokaler Objekte und für Rücksprungadressen bei Funktionsaufrufen nötig. Stack und Heap wachsen aufeinander zu. Stack Heap Daten Textsegment FFFF 0000 38 / 99

Prozessverwaltung fork Ein Prozess wird erzeugt, in dem ein Eltern Prozess durch den Systemaufruf fork einen Kind Prozess erzeugt. Der Aufruf erzeugt eine exakte Kopie des Originalprozesses (Kind=Clone des Vaters), einschließlich aller Dateideskriptoren, Register usw. Stack CC00 Stack DD00 Nach dem fork werden beide Prozesse unterschiedliche Aktivitäten übernehmen. Zum Zeitpunkt des fork haben alle Variablen die gleichen Werte, nach dem fork wirken sich Änderungen der Variablen nur noch im jeweiligen Prozess aus. Heap Daten pid= fork() Vater AA01 Heap Daten pid= fork() Kind CC01 39 / 99

Prozessverwaltung fork Der fork Aufruf gibt einen Wert zurück, durch den im Programm unterschieden werden kann, ob der Code des Kindes oder des Vaters gemeint ist. 0 ist der Kindprozess, >0 der Wert ist die Prozessidentifikation (pid) des Kindprozesses für den Eltern Prozess. <0 Ein Rückgabewert von fork, der kleiner als 0 ist, zeigt an, dass kein neuer Prozess erzeugt werden konnte (Fehler). Stack Heap Daten pid= fork() Vater CC00 AA01 Stack Heap Daten pid= fork() Kind DD00 CC01 40 / 99

Prozessverwaltung Beispiel fork.c 1 # include <stdio.h> 2 int main (){ 3 int childpid ; 4 if (( childpid = fork ()) == -1) { <- 5 fprintf ( stderr," can 't fork \n"); 6 exit (1); 7 } else if ( childpid == 0) { /* child process */ 8 fprintf ( stdout, " child : child pid = %d, parent pid = %d\n", 9 getpid (), getppid ()); 10 exit (0); 11 } else { /* parent process */ 12 fprintf ( stdout, " parent : child pid = %d, parent pid = %d\n", 13 childpid, getpid ()); 14 exit (0); 15 } 16 } 41 / 99

Prozessverwaltung In welcher Reihenfolge erscheinen die Ausgaben? 42 / 99

Shell Prozessverwaltung Ein reales Beispiel, bei dem ein Prozess erzeugt wird, ist die Shell. Für jedes Kommando, das aus der Shell heraus ausgeführt wird, wird von der Shell ein eigener Prozess erzeugt. Dabei dupliziert sich die Shell durch fork. Im Kind (subshell) wird der Shell Code mit dem Code des auszuführenden Kommandos überlagert (exec). Der Vater wartet (wait) bis der so erzeugte Kind-Prozess terminiert (exit). Somit kann in der Umgebung des Kindprozesses, das echo Kommando ausgeführt werden. 43 / 99

Prozessverwaltung Shell-Programmgerüst geruest.cpp 1 void read_command ( char *com, char ** par ){ 2 fprintf ( stdout, "$ "); 3... 4 return ; 5 } 7 int main (){ 8 int childpid ; 9 int status ; 10 char command [20]; 11 char * parameters [60]; 12 while (1) { 13 read_command ( command, parameters ); 14 if (( childpid = fork ()) == -1) { <- Kind (subshell) erzeugen 15 fprintf ( stderr," can 't fork \n"); 16 exit (1); 17 } else if ( childpid == 0) { /* child */ 18 execvp ( command, parameters ); <- command wird im Kind ausgeführt 19 exit (0); <- Kind terminiert 20 } else { /* parent process */ 21 waitpid ( childpid, & status, WUNTRACED WCONTINUED ); <- Vater wartet 22 } 23 } 44 / 99

Prozessverwaltung execvp Der execvp-systemaufruf bewirkt, dass das Textsegment des Prozesses mit dem Kode des auszuführenden Programms überlagert wird. Der erste Parameter ist das auszuführende Programm. Der zweite Parameter ist ein Array mit Zeigern auf das auszuführende Programm und die Programmargumente. Dabei muss der letzte Eintrag des Arrays NULL sein. parameters echo Hallo Jenni NULL execvp("echo", "Hallo", Jenni") Stack Heap Daten Kode von echo Kind DD00 CC01 Abbildung: execvp 45 / 99

Prozessverwaltung exit - wait Mittels wait und exit können Vater und Kind kommunizieren. exit hat einen Parameter, den so genannten Exitstatus. Dies ist ein Integerwert zwischen 0 und 255. Konvention in Unix ist, dass ein Exitstatus von Null bedeutet, dass die Aktion erfolgreich ausgeführt werden konnte. Jeder andere Wert wird als Fehler angesehen. Dieser Status wird dem Elternprozess in der Variablen status des wait Aufrufs mitgegeben. Soll z.b. eine Kommunikation zwischen Kind und Eltern stattfinden, so kann das Kind z.b. durch exit(4); dem Elternprozess die Nachricht 4 übergeben. Der Elternprozess wird durch child-pid = wait(&status); dann die Information in der Variablen status sehen. 46 / 99

Prozessverwaltung Im Wert von Status sind unterschiedliche Informationen kodiert. Neben dem exit-wert des Kindes, sind Informationen über die Terminierung des Kindes abgelegt. Um diese Informationen auszulesen existieren C-Macros. Macro WIFEXITED(status) WIFSGNALES(status) WIFSTOPPED(status) Beschreibung true, wenn status vom Kind gesetzt und das Kind normal beendet wurde. Dann kann man durch WE- XITSTATUS(status) die niederwertigen 8 Bit auslesen, die den Exit-Wert des Kindes beinhalten. true, wenn status vom Kind gesetzt und das Kind abnormal beendet wurde, durch signal. Dann kann man durch WTERMSIG(status) die Signalnummer, die das Kind beendet hat, abfragen. true, wenn status vom Kind gesetzt und das Kind gerade gestoppt wurde. Dann kann man durch WSTOPSIG(status) die Signalnummer, die das Kind gestoppt hat, abfragen. Der Aufruf von wait() bewirkt, dass der Vaterprozess blockiert, bis irgend ein Kinder beendet sind (waitpid später). 47 / 99

Prozessverwaltung Beispiel I status.c 1 # include <stdio.h> 2 # include <sys / wait.h> 3 main () 4 { 5 int childpid ; 6 int status ; 7 if (( childpid = fork ()) == -1) { 8 fprintf ( stderr," can 't fork \n"); 9 exit (1); 10 } else if ( childpid == 0) { /* child process */ 11 fprintf ( stdout, " child : exit 4\ n"); 12 sleep (20); <- Kind 13 exit (4); 14 } else { /* parent process */ 15 if ( wait (& status )!= childpid ) { <- Vater 16 fprintf ( stderr, " wait error \n"); 17 exit (1); 18 } 19 fprintf ( stdout, " parent : status %d\n", status ); 20 /* check status */ 21 if ( WIFEXITED ( status )) { <- Macros 22 fprintf ( stdout, " parent : normal termination of child, status %d\n", 48 / 99

Prozessverwaltung Beispiel II status.c 23 WEXITSTATUS ( status ) ); 24 } else if ( WIFSIGNALED ( status )) { 25 fprintf ( stdout, " parent : abnormal term. of child, status %d\n", 26 WEXITSTATUS ( status ) ); 27 fprintf ( stdout, " parent : abnormal term. of child, signal number %d\n" 28 WTERMSIG ( status ) ); 29 } else if ( WIFSTOPPED ( status )) { 30 fprintf ( stdout, " parent : child stopped, status %d\n", 31 WEXITSTATUS ( status ) ); 32 fprintf ( stdout, " parent : child stopped, signal number %d\n", 33 WSTOPSIG ( status ) ); 34 } 35 exit (0); 36 } 37 } > ulab 49 / 99

Prozessverwaltung Hörsaalübung Welche Ausgabe erzeugt das u.a. Programm? fork.c 1 # include <stdio.h> 2 # include <unistd.h> // wg. getpid 3 int x = 0; 4 int main () { 5 fork (); 6 fork (); 7 fork (); 8 printf (" pid =%d x=%d\n", getpid (), x ++); 9 } ulab 50 / 99

Prozessverwaltung waitpid Mit wait() kann der Elternprozess nur auf die Beendigung irgend eines Kindes warten. Soll der Vater hingegen auf die Beendigung eines bestimmten Prozesses warten, dann ist waitpid() zu verwenden. Mit dem ersten Argument von waitpid(pid wpid, int *status, int options) legt man fest, worauf gewartet werden soll: pid Beschreibung -1 auf beliebigen Prozess warten äquivalent zu wait() pid auf die Beendigung von pid warten 0 auf Beendigung warten, dessen Prozessgruppen-ID gleich der Prozessgruppen-ID des aufrufenden Prozesses ist. < 1 auf Beendigung warten, dessen Prozessgruppen-ID gleich der Prozessgruppen-ID des aufrufenden Prozesses ist. 51 / 99

Prozessverwaltung waitpid Das dritte Argument options von waitpid(pid wpid, int *status, int options) bestimmt das Verhalten von waitpid(). Folgende Konstanten können dabei verwendet werden Konstante WNOHANG WUNTRACED WIFSTOPPED Beschreibung Der aufrufende Prozess wird nicht blockiert, wenn der Prozess ppidnnoch nicht beendet wurde bzw. noch nicht im Zombie-Status ist. (waitpid() liefert in diesem Fall 0 zurück). Status des angehaltenen Kindprozesses, der mit pid spezifiziert wurde. liefert 1 zurück, wenn es sich beim Rückgabewert um die PID des angehaltenen Kindprozesses handelt. Im folgenden Beispiel wird nicht auf die Terminierung des 1. Kindes gewartet, es wird ein zweites Kind erzeugt, auf dessen Beendigung gewartet wird. 52 / 99

Prozessverwaltung Beispiel I waitpid.c 1 # include <unistd.h> 2 # include... 4 int main ( void ) { 5 pid_t pid1, pid2 ; 6 int status ; 7 switch ( pid1 = fork ()) { 8 case -1: 9 perror (" fork ()"); 10 return EXIT_FAILURE ; 11 case 0: // child 1 12 printf (" child 1: %d\n", getpid ()); 13 sleep (10); 14 printf (" child 1: %d terminated \n", getpid ()); 15 break ; 16 default : // parent code 17 if ( waitpid ( pid1, NULL, WNOHANG )!= 0) { <- wartet nicht! 18 perror (" waitpid ()"); return EXIT_FAILURE ; 19 } 20 printf (" --- parent will not block, creating new child ---\n"); 21 switch ( pid2 = fork ()) { 22 case -1: 53 / 99

Prozessverwaltung Beispiel II waitpid.c 23 perror (" fork ()"); 24 return EXIT_FAILURE ; 25 case 0: // child 2 26 printf (" child 2: %d\n", getpid ()); 27 sleep (5); 28 printf (" child 2: %d terminated \n", getpid ()); 29 break ; 30 default : // parent code 31 if ( wait ( NULL )!= pid2 ) { <- wartet! 32 perror (" waitpid ()"); return EXIT_FAILURE ; 33 } 34 printf (" --- parent can continue ---\n"); 35 sleep (20); 36 printf (" --- parent terminated ---\n"); 37 } 38 } 39 return EXIT_SUCCESS ; 40 } 54 / 99

Signale Signale Signale sind das Äquivalent im Bereich Software zu Interrupts im Bereich Hardware. Wenn ein Signal zu einem Prozess gesendet wird und der Prozess das Signal nicht annimmt, dann wird der Prozess vom Betriebssystem automatisch entfernt. Um sich vor diesem Automatismus schützen zu können, kann sich ein Prozess durch den Systemaufruf signal auf das Eintreffen von Signalen vorbereiten. Dazu muss er eine Signalbehandlungroutine bereitstellen. In einem Betriebssystem gibt es mehrere Signalarten. Die meisten Signale werden durch Ereignisse, die von der Hardware ausgelöst werden, erzeugt. 55 / 99

Signale Signale Die nachfolgende Tabelle listet die wichtigsten Signalarten auf. Nummer Konstante Bedeutung 1 SIGHUP Hang up, Modemunterbrechung 2 SIGINT DEL Taste 3 SIGQUIT Quit Signal von Tastatur 4 SIGILL Nicht erlaubte Instruktion 5 SIGTRAP Unterbrechung für Testzwecke 8 SIGFPE Gleitkommaüberlauf 9 SIGKILL Abbruch 10 SIBBUS Busfehler 11 SIGSEGV Segmentfehler 12 SIGSYS Ungültige Argumente bei Systemaufruf 13 SIGPIPE Ausgabe auf Pipe ohne Leser 14 SIGALARM Alarm 15 SIGTERM Softwareerzeugtes Endesignal 16 frei frei 56 / 99

Signale Beispiel Eine Endlosschleife wird auf das Eintreffen vom Signal SIGINT, ausgelöst durch CTR-C reagieren, indem es einen Text ausgibt. signal.c 1 # include <stdio.h> 2 # include <signal.h> 4 void handler ( int ) { 5 printf (" handler \n"); 6 return ; 7 } 9 int main () { 10 signal ( SIGINT, handler ); /* CTR -C handled */ 11 while (1) { 12 printf (" main \n"); 13 sleep (2); 14 } 15 } Wenn ein Programm so geschrieben ist, dass es alle Signale ignoriert, könnte es nie abgebrochen werden. Deshalb gibt es das Signal SIGKILL. Dieses Signal kann nicht per Signalhandler abgefangen werden. ulab: kill -9 57 / 99

Signale alarm Im Bereich Echtzeitanwendungen muss ein Betriebssystem in der Lage sein, Prozesse nach einer gewissen Zeit zu informieren, dass bestimmte Dinge zu erledigen sind. Der Systemaufruf alarm hat einen Parameter, der die Anzahl Sekunden angibt, nach denen das Signal SIGALARM erzeugt werden soll. timer.c 1 void handler ( int ) { 2 printf (" Bye \n"); 3 exit (0); 4 } 6 main () { 7 char str [80]; 8 signal ( SIGALRM, handler ); 9 while (1) { 10 alarm (5); /* start timer */ 11 printf ("> "); 12 fgets (str, 80, stdin ); 13 printf ("%s\n", str ); 14 } 15 } 58 / 99

Dateiverwaltung Dateiverwaltung - create Das nachfolgende Beispielprogramm simpletouch.c demonstriert den Systemaufruf creat. Das Programm legt die als Aufrufparameter anzugebende Datei mit den Zugriffsrechten 0640 an. simpletouch.c 1 # include <stdio.h> 2 main ( int argc, char * argv []) { 3 int fd; 4 if ( argc!= 2) { 5 fprintf ( stderr, " usage : %s file \n", argv [0]); 6 exit (1); 7 } 8 if (( fd=creat ( argv [1],0640)) < 0) { 9 fprintf ( stderr, " create error \n"); 10 exit (2); 11 } 12 } Achtung: wenn die Datei bereits existiert, wird sie überschrieben! 59 / 99

Dateiverwaltung open Bevor eine Datei bearbeitet werden kann, muss sie geöffnet werden. Dazu existiert der Systemaufruf open, der neben dem Namen noch die Art des Zugriffs (Lesen, Schreiben oder beides) benötigt. Das folgende Programm liest die als Parameter anzugebende Datei und schreibt den Inhalt auf die Standardausgabe. cat.c 1 # define BUFSIZE 512 2 main ( int argc, char ** argv ) { 3 int fd; 4 int n; 5 char buf [ BUFSIZE ]; 6 if ( argc!= 2) { /* check usage */ 7 fprintf ( stderr, " usage %s file \n", argv [0]); 8 exit (1); 9 } 10 if (( fd=open ( argv [1], O_RDWR )) < 0) { /* open file */ 11 fprintf ( stderr, " open error \n"); 12 exit (2); 13 } 14 while (( n = read (fd, buf, BUFSIZE )) > 0) /* read and write file */ 15 write (1, buf, n); 16 } Achtung: wenn die Datei bereits existiert, wird sie uberschrieben! 60 / 99

Dateiverwaltung Wahlfreier Zugriff - fseek Der wahlfreie Zugriff auf Dateien wird durch den Systemaufruf lseek realisiert. fseek(file, no-bytes, start); hat drei Parameter: 1 einen Filedescriptor, der die zu bearbeitende Datei definiert, 2 den Offset, der die Position des Lese/Schreibkopfes in Byte relativ zum Ausgangspunkt definiert und 3 den Ausgangspunkt für die Positionierung des Lese/Schreibkopfes SEEK SET=Dateianfang SEEK END=Dateiende SEEK CUR=aktuelle Position Damit kann man ein Programm, das eine Datei (angefangen am Dateiende, d.h. rekursiv) liest einfach programmieren. 61 / 99

fseek revcat.c Dateiverwaltung 1 main ( int argc, char ** argv ) { 2 int fd; 3 int pos ; 4 char buf [1]; 6 if ( argc!= 2) { /* check usage */ 7 fprintf ( stderr, " usage %s file \n", argv [0]); 8 exit (1); 9 } 10 if (( fd=open ( argv [1], O_RDWR )) < 0) { /* open file */ 11 fprintf ( stderr, " open error \n"); 12 exit (2); 13 } 14 if (( pos=lseek (fd, -1, SEEK_END )) == -1) { /* pos == # bytes in file */ 15 fprintf ( stderr, " lssek error \n"); 16 exit (1); 17 } 18 while (pos >=0) { /* read and write file */ 19 read (fd, buf, 1); 20 write (1, buf, 1); 21 lseek (fd, --pos, SEEK_SET ); <- pos vom Anfang an 22 } 23 printf ("\n"); 24 } 62 / 99

Dateiverwaltung fseek Im folgenden Programm hole.c wird der Systemaufruf lseek verwendet, um ein Loch in einer Datei zu erzeugen. hole.c 1 int main () { 2 int fd; 3 char buf1 [] = " abcdefghijklmnop "; 4 char buf2 [] = " ABCDEFGHIJKLMNOP "; 6 if (( fd= creat (" file. hole ",0640)) < 0) { 7 fprintf ( stderr, " create error \n"); exit (1); 8 } 9 if (( write (fd, buf1, 16))!= 16) { /* offset now 16 */ 10 fprintf ( stderr, " buf1 write error \n"); exit (2); 11 } 12 if (( lseek (fd, 32, SEEK_SET )) == -1) { /* offset now 32 */ 13 fprintf ( stderr, " lseek error \n"); exit (3); 14 } 15 if (( write (fd, buf2, 16))!= 16) { /* offset now 48 */ 16 fprintf ( stderr, " buf2 write error \n"); 17 exit (4); 18 } 19 exit (0); 20 } 63 / 99

Dateiverwaltung fseek Ein Aufruf bewirkt: $ hole file. hole $ ls -l fil * -rw -r----- 1 as users 48 Nov 1 19:11 file. hole $ od -c file. hole 00 a b c d e f g h i j k l m n o p 20 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 Loch 40 A B C D E F G H I J K L M N O P 60 $ 64 / 99

Dateiverwaltung Dateistatus - stat Die Informationen über eine Datei, der so genannte Dateistatus, kann durch die abgefragt werden. stat und fstat liefern Information über Dateien, lstat über Links. Alle Aufrufe geben eine Struktur vom Typ stat zurück. 1 struct stat { 2 dev_t st_dev ; /* ID of device containing file */ 3 ino_t st_ino ; /* inode number */ 4 mode_t st_mode ; /* protection */ 5 nlink_t st_nlink ; /* number of hard links */ 6 uid_t st_uid ; /* user ID of owner */ 7 gid_t st_gid ; /* group ID of owner */ 8 dev_t st_rdev ; /* device ID ( if special file ) */ 9 off_t st_size ; /* total size, in bytes */ 10 blksize_t st_blksize ; /* blocksize for file system I/O */ 11 blkcnt_t st_blocks ; /* number of 512B blocks allocated */ 12 time_t st_atime ; /* time of last access */ 13 time_t st_mtime ; /* time of last modification */ 14 time_t st_ctime ; /* time of last status change */ 15 }; 65 / 99

Dateiverwaltung Beispiel - lstat simplefile.c 1 int main ( int argc, char ** argv ) { 2 int i; 3 struct stat buf ; 4 char * ptr ; 5 if ( argc!= 2) { /* check usage */ 6 fprintf ( stderr, " usage %s file \n", argv [0]); exit (1); 7 } 8 if ( lstat ( argv [1], & buf ) < 0) { /* get file info */ 9 fprintf ( stderr, " lstat error \n"); exit (2); 10 } 11 /* print file info */ 12 if ( S_ISREG ( buf. st_mode )) ptr = " regular "; 13 else if ( S_ISDIR ( buf. st_mode )) ptr = " directory "; 14 else if ( S_ISCHR ( buf. st_mode )) ptr = " character special "; 15 else if ( S_ISBLK ( buf. st_mode )) ptr = " block special "; 16 else if ( S_ISFIFO ( buf. st_mode )) ptr = " fifo "; 17 else if ( S_ISLNK ( buf. st_mode )) ptr = " link "; 18 else ptr = " unknown mode!"; 19 printf ("%s: %s\n", argv [1], ptr ); 20 } 66 / 99

Dateiverwaltung Pipes Pipes sind ein Kommunikationsmedium, das es erlaubt, dass Prozesse in FIFO Manier kommunizieren. Eine Pipe ist dabei eine Pseudodatei, die die Kommunikationsdaten temporär beinhaltet. In Unix können durch folgende Kommandofolge zwei Dateien sortiert und in einer Datei zusammengeführt werden: cat file2 file2 sort Hierbei werden zwei Prozesse erzeugt: der erste (cat) schreibt seine Ausgabe in die Pipe, der zweite (sort) liest die Standardeingabe aus der Pipe. cat datei sort 67 / 99

Dateiverwaltung pipe Der Systemaufruf pipe erzeugt eine Pipe und gibt zwei Dateideskriptoren zurück, einen zum Lesen und einen zum Schreiben. Der Mechanismus wird nun am Beispiel eines Programmes gezeigt, bei dem ein Vaterprozess einem Kind Informationen über eine Pipe sendet. int p[2]; pipe(p); /* p[1]=schreibende, p[0]=leseende */ 68 / 99

Dateiverwaltung Beispiel - pipe I pipe.c 1 /* Example : Child Father */ 2 # define BUFSIZE 20 3 main () { 4 int pid, status ; 5 int p [2]; 6 char buf [ BUFSIZE ]; 7 if ( pipe (p)!= 0) { <- create pipe 8 fprintf ( stderr, " pipe error \n"); 9 exit (1); 10 } 11 switch ( pid = fork ()) { 12 case -1: /* fork failed */ 13 fprintf ( stderr, " cannot fork \n"); 14 exit (2); 15 case 0: /* child : write into pipe */ 16 { int i; 17 close (p [0]); /* close read end of pipe */ <- close read end 18 /* create example data */ 19 for (i= getpid (); i >0; i - -) { 20 sprintf (buf, "%d\n", i); 21 write (p[1], buf, BUFSIZE ); <- write into pipe 69 / 99

Dateiverwaltung Beispiel - pipe II pipe.c 22 sleep (2); /* just to have more time to see it with " ps -el" 23 } 24 close (p [1]); 25 exit (0); 26 } 27 default : /* father : read from pipe */ 28 { int length ; 29 close (p [1]); /* close write end of pipe */ <- close write end 30 do { 31 length =read (p[0], buf, BUFSIZE ); <- read from pipe 32 fprintf ( stdout, "%s", buf ); 33 } while ( length >0); 34 close (p [0]); 35 while ( wait (& status )!= pid ); 36 exit (0); 37 } 38 } 39 } 70 / 99

Dateiverwaltung named pipes Sollen zwei unabhängige Prozesse über eine Pipe kommunizieren, muss die Pipe außerhalb des Adressraums der beiden Prozesse abgebildet werden. Eine named Pipe (FIFO) wird erzeugt durch den Systemaufruf: int mknod(char *pathname, int mode, int dev); Dabei ist pathname, ein Dateiname von Unix, der Name des FIFO. Das Argument mode definiert den Zugriff (read, write mit Rechten für owner, group, others). mode wird logisch OR verknüpft mit dem Flag S IFIFO (aus < sys/stat.h >), um auszudrücken, dass mknode einen FIFO erzeugen soll. dev wird für FIFOs ignoriert (relevant für andere Objekte). FIFOs können auch durch das Unix Kommando mknod erzeugt werden: /bin/mknod name p 71 / 99

Dateiverwaltung named pipes Sollen zwei unabhängige Prozesse über eine Pipe kommunizieren, muss die Pipe außerhalb des Adressraums der beiden Prozesse abgebildet werden. Eine named Pipe (FIFO) wird erzeugt durch den Systemaufruf: int mknod(char *pathname, int mode, int dev); Dabei ist pathname, ein Dateiname von Unix, der Name des FIFO. Das Argument mode definiert den Zugriff (read, write mit Rechten für owner, group, others). mode wird logisch OR verknüpft mit dem Flag S IFIFO (aus < sys/stat.h >), um auszudrücken, dass mknode einen FIFO erzeugen soll. dev wird für FIFOs ignoriert (relevant für andere Objekte). FIFOs können auch durch das Unix Kommando mknod erzeugt werden: /bin/mknod name p 71 / 99

Dateiverwaltung named pipe - Beispiel Client/Server cat Eine Client-Server Anwendung, bei der ein Client einen Dateinamen von stdin einliest und den Namen auf einen Kommunikationskanal schreibt. Der Server liest den Dateinamen vom Kommunikationskanal, öffnet die Datei, liest den Inhalt und schreibt ihn über einen Kommunikationskanal zurück. Der Client erwartet den Inhalt der Datei vom Kommunikationskanal (FIFO) und gibt ihn über stdout aus. 72 / 99

Dateiverwaltung named pipe - Beispiel Client/Server cat (Server) server.c fifo.h 1 # define MAXBUFF 1024 2 main () { 3 int readfd, writefd ; 4 /* Create the FIFOs, then open them - one for 5 reading and one for writing. */ 6 if ( ( mknod ( FIFO1, S_IFIFO PERMS, 0) < 0) 7 && ( errno!= EEXIST )) 8 perror (" can 't create fifo : FIFO1 "); 9 if ( ( mknod ( FIFO2, S_IFIFO PERMS, 0) < 0) 10 && ( errno!= EEXIST )) { 11 unlink ( FIFO1 ); 12 perror (" can 't create fifo : FIFO2 "); 13 } 14 if ( ( readfd = open ( FIFO1, 0)) < 0) 15 perror (" server : can 't open read fifo : FIFO1 "); 16 if ( ( writefd = open ( FIFO2, 1)) < 0) 17 perror (" server : can 't open write fifo : FIFO2 "); 19 server ( readfd, writefd ); 20 close ( readfd ); 21 close ( writefd ); 22 exit (0); 23 } 73 / 99

Dateiverwaltung named pipe - Beispiel Client/Server cat (Server) 1 server ( int readfd, int writefd ) { 2 char buff [ MAXBUFF ]; 3 char errmesg [256]; 4 int n, fd; extern int errno ; 6 /* Read filename from the IPC descriptor. */ 7 if ( (n = read ( readfd, buff, MAXBUFF )) <= 0) 8 perror (" server : filename read error "); 9 buff [n] = '\0 '; /* null terminate filename */ 11 if ( ( fd = open ( buff, 0)) < 0) { 12 /* Error. Format an error message and send 13 it back to the client. */ 14 sprintf ( errmesg, ": can 't open fifo \n"); 15 strcat ( buff, errmesg ); n = strlen ( buff ); 16 if ( write ( writefd, buff, n)!= n) 17 perror (" server : errmesg write error "); 18 } else { 19 /* Read the data from the file and write to 20 the IPC descriptor. */ 21 while ( (n = read (fd, buff, MAXBUFF )) > 0) 22 if ( write ( writefd, buff, n)!= n) 23 perror (" server : data write error "); 24 if (n < 0) perror (" server : read error "); 25 } 26 } 74 / 99

Dateiverwaltung named pipe - Beispiel Client/Server cat (Client) client.c fifo.h 1 main () { 2 int readfd, writefd ; 4 /* Open the FIFOs. We assume the server 5 has already created them. */ 6 if ( ( writefd = open ( FIFO1, 1)) < 0) 7 perror (" client : can 't open write fifo : FIFO1 "); 8 if ( ( readfd = open ( FIFO2, 0)) < 0) 9 perror (" client : can 't open read fifo : FIFO2 "); 10 client ( readfd, writefd ); 11 close ( readfd ); 12 close ( writefd ); 14 /* Delete the FIFOs, now that we 're finished. 15 */ 16 if ( unlink ( FIFO1 ) < 0) 17 perror (" client : can 't unlink FIFO1 "); 18 if ( unlink ( FIFO2 ) < 0) 19 perror (" client : can 't unlink FIFO2 "); 20 exit (0); 21 } 75 / 99

Dateiverwaltung named pipe - Beispiel Client/Server cat (Client) 1 client ( int readfd, int writefd ) { 2 char buff [ MAXBUFF ]; 3 int n; 5 /* Read the filename from standard input, 6 write it to the IPC descriptor. */ 7 printf (" File to print : "); 8 if ( fgets ( buff, MAXBUFF, stdin ) == NULL ) 9 perror (" client : filename read error "); 11 n = strlen ( buff ); 12 if ( buff [n -1] == '\n') 13 n - -; /* ignore newline from fgets () */ 14 if ( write ( writefd, buff, n)!= n) 15 perror (" client : filename write error " );; 17 /* Read the data from the IPC descriptor and 18 write to standard output. */ 19 while ( (n = read ( readfd, buff, MAXBUFF )) > 0) 20 if ( write (1, buff, n)!= n) /* fd 1 = stdout */ 21 perror (" client : data write error "); 22 if (n < 0) 23 perror (" client : data read error "); 24 } 76 / 99

Katalogverwaltung Katalogverwaltung Durch den Systemaufruf link kann eine physische Datei unter mehreren Namen, auch in unterschiedlichen Verzeichnissen, erscheinen. Eine Änderung über den Zugriff mit einem Namen wirkt sich immer auf das reale Dateiobjekt aus. Um dies zu realisieren, wird eine Datei in Unix eindeutig identifiziert durch ihre inode-zahl. Ein Verzeichnis ist eine Datei, die eine Anzahl von Paaren (inode, Name im ASCII Code) enthält. Durch einen Link wird dann ein neuer Eintrag im Verzeichnis erzeugt, der zum selben inode einen zusätzlichen Namen einführt. Durch unlink kann man Links wieder löschen. Dabei wird nur der Eintrag aus dem Verzeichnis entfernt; die Datei bleibt erhalten. 77 / 99

Katalogverwaltung sync In Unix werden die zuletzt benutzten Blöcke in einem Cache (Zwischenspeicher) abgespeichert. Dadurch wird ein erneutes Zugreifen schneller, da dann nicht mehr auf die Festplatte zugegriffen werden muss. Wenn ein Fehler auftaucht, bevor die Platte wirklich beschrieben (durch write Systemaufruf) wird, so gehen Daten verloren und ein inkonsistentes Dateisystem wäre die Folge. Deshalb wird periodisch vom Betriebssystem der sync Systemaufruf ausgeführt; er schreibt die Blöcke im Cache auf die Platte. Beim Hochfahren eines Unix Systems wird ein Programm update als Hintergrundprozess gestartet, der alle 30 Sekunden einen sync Aufruf durchführt. 78 / 99

fsync Katalogverwaltung Während sync den gesamten Cache synchronisiert, kann man mit dem Systemaufruf fsync die Blöcke einer Datei im Cache synchronisieren (z.b. bei Datenbanksystemen bei commit ). fsync.c 1 int main ( int argc, char ** argv ) { 2 int fd; 3 if ( argc!= 2) { 4 fprintf ( stderr, " usage %s file \n", argv [0]); exit (1); 5 } 6 if (( fd=open ( argv [1], O_RDWR )) < 0) { 7 fprintf ( stderr, " open error \n"); exit (2); 8 } 10 /* manipulation of file... */ 12 if ( fsync ( fd) < 0) { 13 fprintf ( stderr, " sync error \n"); exit (3); 14 } 15 } 79 / 99

Katalogverwaltung chdir, chroot Um vom aktuellen Verzeichnis in ein anderes Verzeichnis zu wechseln, existiert der Systemaufruf chdir. Nachdem das aktuelle Verzeichnis mit dem Systemaufruf chdir( /home/schuette/tmp ) geändert wurde, werden die folgenden Aufrufe (create, open), bei denen kein vollständiger Pfadname angegeben ist, immer im tmp-verzeichnis Dateien angelegt, bzw. geöffnet. Ein Systemaufruf, mit dem das Root-Verzeichnis bestimmt werden kann, ist chroot. Er wird verwendet, um etwa auf Webservern virtuelle Root-Verzeichnisse für unterschiedliche Benutzergruppen definieren zu können. 80 / 99

Schutzmechanismen Schutzmechanismen In Linux besitzt eine Datei einen Besitzer und 12 Schutzbits in 4 Gruppen. Ausführungsmodi Benutzer Gruppe Rest [sgd] rwx rwx rwx s=setuid r=read g=setgpid w=write d=directory x=execute Durch den Systemaufruf chmod kann die Zugriffsmaske für eine Datei gesetzt werden. Die Maske wird dabei oktal angegeben. So bedeutet 0644 z.b.: 0 = 000 Programmaufruf mit Rechten des Aufrufers 6 = 110 rw- 4 = 100 r 4 = 100 r 81 / 99

Schutzmechanismen chmod Das folgende Programm setzt die Zugriffsrechte einer Datei (2.Parameter) so wie es die Maske (1. Parameter) angibt. simplechmod.c 1 # include <stdio.h> 2 main ( int argc, char ** argv ) { 3 int mask ; 4 if ( argc!= 3) { 5 fprintf ( stderr, " usage %s mask file \n", argv [0]); 6 exit (1); 7 } 8 sscanf ( argv [1],"%o",& mask ); 9 if (( chmod ( argv [2], mask )) < 0) { 10 fprintf ( stderr, " chmod error \n"); 11 exit (2); 12 } 13 } 82 / 99

Schutzmechanismen setuid Wenn ein Programm gestartet wird, so hat es die Rechte, die der Aufrufer des Programms hat. Deshalb kann ein Programm dem Superuser gehören (etwa das Programm rm=remove file)), aber der Aufrufer kann nur die Dateien löschen, für die er die Rechte hat. Mit dem setuid ( setgid ) Bits kann man erreichen, dass ein Programm mit den Rechten des Besitzers (Gruppe) (setuid/setgid=1) abläuft und nicht wie normal mit den Rechten des Aufrufers (setuid/setgid=0). Dies wird benötigt, um zum Beispiel die Passwort-Datei durch die Ausführung des Kommandos passwd abzuändern. Dazu hat das Kommando (=Datei) das setuid-bit gesetzt. Beispiel: passwd 83 / 99

Schutzmechanismen access Wird ein Programm mit dem setuid-bit ausgestattet, so kann es nicht prüfen, ob der Aufrufer die Rechte auf eine Datei hat, da das Programm selbst ja durch das setuid-bit alle Rechte hat. Um per Programm auf die Zugriffsrechte des realen Benutzers zugreifen zu können, existiert der Systemaufruf access. Der erste Parameter von access ist die zu prüfende Datei, der zweite der Zugriffsmodus, der geprüft werden soll. 84 / 99