Betriebssystembau: 1. Übung

Werbung
Betriebssystembau
1. Übung
Michael Engel
Arbeitsgruppe Eingebettete Systemsoftware
Lehrstuhl für Informatik 12
TU Dortmund
[email protected]
http://ess.cs.uni-dortmund.de/~me/
1
Betriebssystembau: Übungen
●
●
●
●
2 SWS „Tafel“übung (hier :-)) und 2 SWS Rechnerübung
Die Tafelübung vermittelt...
● Grundlagen der PC-Hardware-Architektur
● Grundwissen zur Bearbeitung der Rechnerübungen
● C++-Programmierkenntnisse
Die Rechnerübung... (Horst und Nils)
● dient zum Bearbeiten der Rechneraufgaben
● ist zum Fragen stellen da
● ...und ist der Ort zum Abgeben der Aufgaben!
Es gibt keine theoretischen Aufgaben!
Betriebssystembau: 1. Übung
© Olaf Spinczyk
2
Betriebssystembau: Übungen
●
Rechnerübung
● Aufgaben alle 2-3 Wochen, insgesamt 6 Aufgaben
● optional: 7. Aufgabe — eine OOStuBS-Anwendung
● Bearbeitung in Dreiergruppen
● Anwesenheit zu den Abgabeterminen erforderlich!
● Abgabe innerhalb der Gruppe soll reihum erfolgen
- Jeder Gruppenteilnehmer sollte also die Lösung zu
jeweils 2 der 6 Aufgaben demonstrieren und erläutern
- Demonstration auf echter PC-Hardware, nicht im
Emulator
● Bearbeitung der Aufgaben auch zu Hause möglich
- auf Linux oder mittels vmware-Image (debian)
Betriebssystembau: 1. Übung
© Olaf Spinczyk
3
Betriebssystembau: Einführung C++
●
Grundlage für die Rechnerübungen
●
Voraussetzungen:
● Programmierkenntnisse in einer objektorientierten
Sprache (z.B. Java)
●
Wir konzentrieren uns auf die Unterschiede zwischen Java
und C++
● ...und die kleinen Eigenheiten, auf die man achten muss,
wenn man C++ für Systemprogrammierung einsetzt...
Betriebssystembau: 1. Übung
© Olaf Spinczyk
4
Literatur
●
●
●
Es gibt jede Menge Bücher und Tutorials zu C++...
Eine gute Einführung (€ 17,95) ist
● Marko Meyer
„C++ programmieren im Klartext“
Pearson Verlag, ISBN 3-8273-7093-0
Kostenlos und gut – Folien und Skript von Prof. Ring, Uni
Siegen:
http://www.math.uni-siegen.de/ring/cpp.html
●
...und außerdem der Kurs „Von Java nach C++“ von Prof.
Müller und Frank Weichert, die Basis für diese Folien
http://ls12-www.cs.uni-dortmund.de/~marwedel/eda/Java2C.pdf
Betriebssystembau: 1. Übung
© Olaf Spinczyk
5
C++
●
Wie so üblich: „Hello, World“ in C++
#include <iostream>
int main() {
cout << "Hello, world" << endl;
return 0;
}
●
Java-Version:
import whatever.u.like.*;
class Test {
public static void main(String[] argv){
System.out.println(„Hello, world“);
}
}
Betriebssystembau: 1. Übung
© Olaf Spinczyk
6
C++ - Übersetzungsprozess
Betriebssystembau: 1. Übung
© Olaf Spinczyk
7
Sourcecode - Präprozessor
●
●
●
Zwei Dateiendungen:
● .cc — C++ Source Code
● .h — „Header Files“ mit Definitionen von Datentypen,
Konstanten, Präprozessor-Makros etc.
Die Endungen sind Konvention, aber nicht zwingend
● oft z.B. auch .cpp, .hpp o.ä.
Header-Files werden mit Hilfe des Präprozessors textuell
in .cc-Files integriert
● #include – Anweisung:
- #include <iostream> für System-Headerfiles
- #include „device.h“ für eigene Headerfiles
Betriebssystembau: 1. Übung
© Olaf Spinczyk
8
Sourcecode - Präprozessor
●
Weitere Präprozessorfunktionen:
Makrodefinition, z.B. für Konstanten:
- #define pi 3.1415926
- #define VGA_BASE 0xb8000
- ohne Semikolon am Ende!
● Bedingte Compilierung:
- #ifdef DEBUG
....
#endif
- #ifndef VGA_BASE
#define VGA_BASE 0xb8000
#endif
●
●
Der Präprozessor expandiert Makros im Source Code,
fügt Header-Files ein und erzeugt eine neue Textdatei, die
der Compiler vorgesetzt bekommt
Betriebssystembau: 1. Übung
© Olaf Spinczyk
9
Sourcecode - Präprozessor
●
Wichtige Anwendung für #define und #ifndef:
● Verhindern von mehrfacher Inklusion von HeaderDateien
- Header-Dateien dürfen wiederum Header-Dateien
inkludieren -> Ringschluss...
#ifndef __cgastr_include__
#define __cgastr_include__
#include "object/o_stream.h"
#include "machine/cgascr.h"
class CGA_Stream
/* Hier muesst ihr selbst Code vervollstaendigen */
{
/* Hier muesst ihr selbst Code vervollstaendigen */
};
#endif
Betriebssystembau: 1. Übung
© Olaf Spinczyk
10
Sourcecode - Compiler
●
Erzeugt aus vom Präprozessor vorverarbeitetem Source
Code eine Objektdatei (.o)
●
●
●
Diese ist i.a. nicht direkt ausführbar, da noch Referenzen auf
Funktionen oder Variablen in anderen Objektdateien enthalten sein
können
Der Compiler überprüft den Source Code auf Syntaxfehler
und erzeugt ggf.
● Fehlermeldungen (errors)
● Warnungen (warnings)
Eine Objektdatei wird nur bei fehlerfreier Compilierung
erzeugt
● Warnungen führen nicht zum Abbruch des
Übersetzungsvorgangs! Sie sollten aber beachtet
werden...
Betriebssystembau: 1. Übung
© Olaf Spinczyk
11
Sourcecode - Linker
●
●
●
Der Linker faßt eine Menge an Objektdateien (.o) sowie
bei Bedarf Libraries (.a, .so) zu einem ausführbaren
Programm zusammen:
● Auflösung von Referenzen
● Sortierung der einzelnen Teile der Objektdateien im
Speicherabbild der ausführbaren Datei
„Normalerweise“ gibt es zwei Link-Modi:
● dynamisch – Libraries werden erst zur Zeit der
Ausführung des Programms zum Objektcode geladen
und Referenzen darin aufgelöst
● statisch – Libraries werden zur Link-Zeit in ein komplett
ausführbares Programm integriert
Vor-/Nachteile beider Ansätze? Welcher davon ist für
unsere Systementwicklung geeignet?
Betriebssystembau: 1. Übung
© Olaf Spinczyk
12
Klassen in C++
●
Eine Klasse in C++ besteht aus
● Deklaration in Headerdatei (z.B. keyctrl.h)
class Keyboard_Controller
{
...
};
● und
Implementierungsdatei (keyctrl.cc)
#include “machine/keyctrl.h“
...
● Name
der .h/.cc-Files und Name der Klasse müssen
nicht übereinstimmen!
Betriebssystembau: 1. Übung
© Olaf Spinczyk
13
Aufbau der Headerdatei
●
Ausschnitt aus keyctrl.h:
class Keyboard_Controller
{
private:
unsigned char code;
unsigned char prefix;
...
public:
Keyboard_Controller ();
~Keyboard_Controller ();
// Attribute
// Konstruktor
// Destruktor
Key key_hit ();
// Methoden
void reboot ();
void set_repeat_rate (int speed, int delay);
...
};
Betriebssystembau: 1. Übung
© Olaf Spinczyk
14
Aufbau der Headerdatei
●
●
●
●
●
●
Beginn der Klassendefinition mit Schlüsselwort „class“
Klassen sind immer public
Attribute
● (Instanz-)Variablen dürfen bei der Deklaration nicht
initialisiert werden
Konstruktoren und Destrukturen
● Konstruktoren: Instanziierung von Objekten
● Destruktoren: Löschen instanziierter Objekte
Deklaration von Methoden
Klassendefinition wird mit Semikolon beendet!
Betriebssystembau: 1. Übung
© Olaf Spinczyk
15
Aufbau der Implementierungsdatei
●
●
Einbinden der Header-Datei mit #include
Durch den Klassennamen und den Bereichsoperator „::“
wird die Zugehörigkeit zur Klasse gekennzeichnet:
#include “keyctrl.h“
Keyboard_Controller::Keyboard_Controller ()
{
...
}
Keyboard_Controller::~Keyboard_Controller () {}
void Keyboard_Controller::reboot ()
{
...
}
Betriebssystembau: 1. Übung
© Olaf Spinczyk
16
C++-Konzepte
●
Kontrollstrukturen und Variablentypen in C++
●
Komplexe Datentypen (structs)
●
Zeiger (Pointer) und Referenzen
●
Vererbung und Mehrfachvererbung
●
Virtuelle Funktionen
●
Überladen von Operatoren
Betriebssystembau: 1. Übung
© Olaf Spinczyk
17
Kontrollstrukturen und Variablentypen
●
bedingte Anweisungen, Schleifen, Verbundanweisungen
(Blöcke)
●
●
sind identisch in C++ und Java!
In C++ sind „globale“ Funktionen möglich, in Java
dagegen müssen Methoden immer innerhalb einer Klasse
stehen
insbesondere lassen sich in C++ auch „normale“ C- und AssemblerFunktionen aufrufen
● ...und man kann C++-Funktionen als von C und Assembler aufrufbar
deklarieren mittels extern „C“ (wird für OOStuBS aber nicht benötigt)
● eine wichtige globale Funktion ist die „main“-Funktion :-)
●
Betriebssystembau: 1. Übung
© Olaf Spinczyk
18
Kontrollstrukturen und Variablentypen
●
Arrays (Felder) werden in C++ wie folgt definiert:
int a[4]; // oder
● int a[] = {1,2,3}; // mit Initialisierung
●
●
In C++ findet dabei keine Überprüfung der Array-Grenzen
zur Laufzeit statt!
●
Folge: die berüchtigten „Buffer Overflows“, bei denen z.B. über die
Grenzen von Arrays hinaus Werte (andere Variableninhalte,
Rücksprungadressen auf dem Stack etc.) überschrieben werden
- potentiell großes Sicherheitsproblem!
●
●
Variablen haben keine Default-Werte, müssen also immer
explizit initialisiert werden. Erfolgt das nicht, generiert der
Compiler eine warning (aber keinen error!)
Die Speicherverwaltung muss durch den Programmierer
erfolgen. Ein Garbage Collector wie in Java ist nicht
vorhanden
Betriebssystembau: 1. Übung
© Olaf Spinczyk
19
Typwandlung (type casting)
●
In C++ können Typen – wie in Java – explizit gewandelt werden:
● (Typ) Ausdruck
// in C++ und Java
● Beispiel:
int a=3;
double b=(double)a/2;
●
// b==1.5
Eine weitere Möglichkeit, die nur in C++ verfügbar ist:
● Typ(Ausdruck)
● Beispiel:
int a=3;
double b=double(a)/2;
// b==1.5
Betriebssystembau: 1. Übung
© Olaf Spinczyk
20
Wertebereiche
●
●
●
●
In C++ existieren vorzeichenbehaftete und nicht vorzeichenbehaftete
Typen (char, short, int, long), z.B.:
● int von -2^31 bis 2^31-1
● unsigned int von 0 bis 2^32-1
Bei arithmetischen Operationen erfolgt keine Überprüfung auf
Overflow bzw. Underflow! => Sicherheitsproblem!
● Beispiel:
unsigned int i = 0;
i = i - 1;
// i == 4294967295
Die Wertebereiche, die einzelne Variablentypen einnehmen können,
sind maschinenabhängig!
● z.B. kann ein int 32 oder 64 Bit „lang“ sein
Mittels „typedef“ lassen sich neue Namen für Datentypen definieren:
● typedef int Index;
Index a=3;
Betriebssystembau: 1. Übung
© Olaf Spinczyk
21
Komplexe Datentypen
●
enums: Aufzählungstypen
enum { caps_lock = 4, num_lock = 2, scroll_lock = 1 };
●
Oft Alternative zu #defines
structs: Benutzerdefinierte Datentypen
struct rechteck
{
int xp, yp;
int width, height;
int color;
...
};
●
Verwendung:
struct rechteck r;
r.xp = 100; r.yp = 200; r.width = 20; r.height = 40;
Betriebssystembau: 1. Übung
© Olaf Spinczyk
22
Pointer (Zeiger)
●
●
●
●
●
Ein Pointer ist eine Variable, deren Wert auf die
Speicheradresse einer Variablen, einer Struktur oder eines
Objekts zeigt
Der Pointer ist ein eigener Datentyp
Er ist typisiert (bezogen auf den Datentyp, auf den gezeigt
wird)
Durch Symbol „*“ gekennzeichnet
Beispiel:
● kein Pointer: int a;
● Pointer auf eine Integer-Variable: int *a;
Betriebssystembau: 1. Übung
© Olaf Spinczyk
23
Pointer (Zeiger)
●
●
●
●
Eine Adresse ist die Speicherstelle, die einer Variablen
oder einem Objekt zugeordnet ist
Bei der Betriebssystemprogrammierung kann dies auch
die Speicherstelle sein, an der ein bestimmtes Gerät
Speicher oder Kontrollregister einblendet — z.B. der
Bildschirmspeicher der Grafikkarte
Der Inhalt ist dann der Wert, der an einer Speicherstelle
gespeichert ist
Die Größe des Inhalts (in Bytes) ist vom jeweiligen
zugeordneten Datentyp abhängig
● z.B. 1 Byte für char, 2 Byte für short usw.
● Diese Größen sind in C/C++ architektur- und
compilerabhängig, also nicht portabel !!!
Betriebssystembau: 1. Übung
© Olaf Spinczyk
24
Pointer (Zeiger)
●
●
Ein Pointer ist also eine Variable, in der eine Adresse gespeichert
wird. Diese Variable hat den identischen Typ zu der Variablen, deren
Adresse in ihr gespeichert ist
Es existieren zwei Operatoren zu Pointern:
● Dereferenzierungsoperator „*“
- Gibt den Wert zurück, der an der Adresse gespeichert ist, auf die
die Pointervariable zeigt
● Referenzoperator „&“
- Liefert die zu einer Variablen gehörende Speicheradresse
int a;
int *adresse_von_a;
- // Entweder...
adresse_von_a = &a;
*adresse_von_a = 42;
- // oder einfach — mit selbem Ergebnis
a = 42;
Betriebssystembau: 1. Übung
© Olaf Spinczyk
25
Pointer (Zeiger): Beispiel
●
Code:
char *CGA_START = (char *)0xb8000;
char *pos;
int x=20, y=20;
pos = CGA_START + 2*(x + y*80);
*pos = 'Q';
●
char *pos;
definiert einen Pointer pos, der auf eine Variable vom Typ char zeigt
●
pos = (char *)CGA_START + 2*(x + y*80);
initialisiert den Pointer mit dem Wert, der dem Zeichen an Position (x,y) im
Bildschirmspeicher (Basisadresse CGA_START) entspricht
●
*pos = 'Q';
schreibt das ASCII-Zeichen (char) 'Q' in die Speicherzeille, auf die der Pointer
pos verweist
Betriebssystembau: 1. Übung
© Olaf Spinczyk
26
Pointer (Zeiger): Beispiel
char *CGA_START = (char *)0xb8000;
char *pos;
int x=20, y=20;
pos = CGA_START + 2*(x + y*80);
*pos = 'Q';
Betriebssystembau: 1. Übung
© Olaf Spinczyk
27
Referenzen als Parameter
●
Neben den normalen Referenzen der Art
int *a;
int b;
a = &b;
können Referenzen auch als Funktionsparameter auftauchen:
int& max(int& a, int& b) {
if (a>b) return a else return b;
}
●
Dies entspricht einem „call by reference“, d.h., es wird eine Referenz
auf die entsprechende Variable übergeben und auch zurückgegeben.
Der Aufruf erfolgt dann so:
int a=5, b=7;
max(a,b)++; // erhöht b um 1!
Betriebssystembau: 1. Übung
© Olaf Spinczyk
28
Einfache Vererbung
●
●
Klasse keyboard_interrupt erbt von Klasse interrupt
Vererbungsoperator „:“ (entspricht extends in Java)
interrupt.h:
keyboard_interrupt.h:
class interrupt {
...
}
#include „interrupt.h“
class keyboard_interrupt :
public interrupt {
public:
keyboard_interrupt();
~keyboard_interrupt();
}
Betriebssystembau: 1. Übung
© Olaf Spinczyk
29
Mehrfachvererbung
●
Klasse keyboard_interrupt erbt von Klassen interrupt und
keys:
keyboard_interrupt.h:
#include „interrupt.h“
class keyboard_interrupt : public interrupt, public keys {
public:
keyboard_interrupt();
~keyboard_interrupt();
}
Betriebssystembau: 1. Übung
© Olaf Spinczyk
30
Virtuelle Funktionen
●
●
Virtuelle Funktionen sind Funktionen einer Basisklasse.
Eine abgeleitete Klasse kann sie überschreiben und
übernimmt damit die Ausführung der Funktion für ihre
Klassenmitglieder.
●
●
●
das funktioniert auch mit nicht virtuellen Klassen...
Das Besondere an virtuellen Funktionen ist, dass das
Objekt selbst weiss, zu welcher abgeleiteten Klasse es
gehört und seine zugehörige Klassenfunktion ruft.
Nicht jede Funktion ist standardmäßig virtuell, es muss
explizit das Schlüsselwort „virtual“ verwendet werden! (im
Gegensatz zu Java)
Betriebssystembau: 1. Übung
© Olaf Spinczyk
31
Virtuelle Funktionen
●
Ausgabe:
„Derived“
●
ohne das virtual vor
void display():
„Base“
#include <iostream>
class base
{
public:
virtual void display()
{
cout<<”\nBase”;
}
};
class derived : public base
{
public:
void display()
{
cout<<”\nDerived”;
}
};
void main()
{
base *ptr = new derived();
ptr->display();
}
Betriebssystembau: 1. Übung
© Olaf Spinczyk
Virtuelle Destruktoren
●
●
Es gibt eine Faustregel, die besagt, dass jede Klasse mit
virtuellen Funktionen auch einen virtuellen Destruktor
haben soll.
Da ein nicht virtueller Destruktor nicht gewährleistet, dass
abgeleitete Klassen ordnungsgemäß abgebaut werden,
kann ein nicht virtueller Destruktor sogar so interpretiert
werden, dass der Autor ein Ableiten seiner Klasse nicht
vorgesehen hat und wohl auch nicht empfiehlt.
Betriebssystembau: 1. Übung
© Olaf Spinczyk
33
Überladen von Operatoren
●
●
●
Wurde in Java nicht realisiert
Operatoren funktionieren abhängig vom Datentyp, auf
dem sie operieren
Beispiel: Operator „+“
für int, float, double-Variablen wird die jeweilige Additionsfunktion für
den entsprechenden Zahlentyp aufgerufen
● für String-Objekte kann der „+“-Operator so überladen (=umdefiniert)
werden, dass er die beiden String-Operanden konkateniert
●
●
In OOStuBS: Operator „<<“
für int-Werte bewirkt der „<<“-Operator, dass der in der Variablen
enthaltene Zahlenwert um n Bits (2. Operand) nach links geschoben
wird — z.B. ist 2 << 3 == 16
● für Ausgabestreams ist der Operator ein Verkettungsoperator, siehe
„Hello World“:
●
●
cout << "Hello, world" << endl;
Operator „>>“ entsprechend für Eingabestreams
Betriebssystembau: 1. Übung
© Olaf Spinczyk
34
Überladen von Operatoren
●
●
Es können nur von der Sprache definierte Operatoren
überladen, die Definition neuer Operatoren ist nicht möglich
Unterstützt werden dabei
● unäre Operatoren:
+ - * & ~ ! ++ -- -> ->*
● binäre
Operatoren:
+ - * / % ^ & | << >>
+= -= *= /= %= ^= &= |= <<= >>=
< <= > >= == != && ||
, [] ()
new new[] delete delete[]
Betriebssystembau: 1. Übung
© Olaf Spinczyk
35
Überladen von Operatoren: Beispiel
●
Addition von ganzen Zahlen zu einem Datum:
class tDatum
{
public:
// ....
tDatum operator+(int Tage);
};
tDatum tDatum::operator+(int Tage)
{
// Berechnung des Datums
return *this;
}
Die Addition liefert ein neues Datum zurück und als rechter Operand
ist eine ganze Zahl zulässig. So kann das Datum durch einfache
Addition um 14 Tage weitergeschoben werden:
tdatum heute;
heute = heute + 14;
Betriebssystembau: 1. Übung
© Olaf Spinczyk
36
Systemprogrammierung in C++
●
Keine Laufzeitumgebung vorhanden!
●
●
man muss alles selber von Hand bauen...
Damit sind auch keine Objekte dynamisch instanziierbar!
kein „new“ und „delete“ möglich...
● ...woher soll auch die passende Speicherverwaltung dazu kommen?
●
●
Für Spezialisten... das geht auch nicht:
Exceptions
● Assertions
● runtime type information
●
●
Ein falscher Pointer kann das Ende sein...
der Rechner hängt und das war's
● keine „segmentation violation“, keine core dumps
●
Betriebssystembau: 1. Übung
© Olaf Spinczyk
37
Herunterladen