- Wir haben drei Teile
- Kernel
Code: Alles auswählen
kernel
- Speicherverwalter
Code: Alles auswählen
mm
- Dateisystem
Code: Alles auswählen
fs
- Kernel
- Wir müssen uns merken: Es gibt die üblichen Systemaufrufe, die sind quasi 1:1 im Kernel
- Fork und Exit:
Code: Alles auswählen
mm/forkexit.c
- Exec:
Code: Alles auswählen
mm/exec.c
- Break:
Code: Alles auswählen
mm/break.c
- Signal:
Code: Alles auswählen
mm/signal.c
- Alloc:
Code: Alles auswählen
mm/alloc.c
-
Code: Alles auswählen
mm/mproc.h
-
Code: Alles auswählen
mm/main.c
-
Code: Alles auswählen
mm/forkexit.c
-
Code: Alles auswählen
mm/exec.c
-
Code: Alles auswählen
mm/break.c
-
Code: Alles auswählen
mm/signal.c
-
Code: Alles auswählen
mm/alloc.c
- Fork und Exit:
- Wichtig ist
- Was wir vom Betriebssystem an Systemaufrufen besonders kennen
- alloc , zum Beispie
Code: Alles auswählen
alloc ()
Code: Alles auswählen
malloc (), realloc ()
- fork
Code: Alles auswählen
fork ()
- exec
Code: Alles auswählen
exec ()
- exit
Code: Alles auswählen
exit ()
Hier halt realisiert, dass sie im OS selber tun. - alloc
- gut, was wichtig ist: Wir haben für jeden Betriebssystem Prozess eine
Code: Alles auswählen
main.c
- Der
Code: Alles auswählen
mm
Code: Alles auswählen
kernel
Code: Alles auswählen
fs
- Was die Register beim sind, sollte klar sein
Code: Alles auswählen
8086
Code: Alles auswählen
ax bx dx cx sp bp si di cs ds es ss
- Jetzt haben wir, was das betrifft, ein prägnante Sachen. Erstens
- Der hat mit
Code: Alles auswählen
IBM PC
einen Timer BausteinCode: Alles auswählen
8086/8088
. Der wird Software Mässig so verwendet, wie ihn die Hardware anbietet. Der Witz ist: Dieser Timer-Baustein ist notwendig um den Task-Switch zu vollziehen. Wir haben zwei Timer-Interrupts. Die werden genauso verwendet, vom OS. Das Minix ist ein Multitasking betriebssystem, wenn auch ohne SpeicherschutzCode: Alles auswählen
Intel 8253
dazu zeige ich folgenden Code
Diese Struktur im Kernel entspricht 1:1 unseren nachher ausgeführten Prozessen. Die Prozesse des Betriebssystem heissen Tasks. Es geht jetzt um den Kontext-Switch.Code: Alles auswählen
extern struct proc { ... } [NR_TASKS+NR_PROCS]
- Gut, jetzt haben wir: für jeden Prozess quasi. Zeitlich, mit Timer. Dazu hat jeder Prozess eine Struktur. Die besteht unter anderem aus
-
Code: Alles auswählen
ax, bx, dx, cx, sp, bp, si, di, es, cs, ds, ss
- Dazu Speichebereiche. wir haben ja:
- Code
- Daten
- Stack
- Wir haben eine für den Prozess
- Wir haben welche, die die Speicherbereiche für den Prozess selber festlegen. die darin auftauchen. Weil das ist eine eigene Struktur
- Wir haben Strukturen im Arbeitsspeicher, die nicht für den Prozess den Bereich festlegen - sondern: Die für jeden freien Block im Speicher - Speicher anbieten, oder als belegt markieren
-
- Das ist mal das Entscheide. Es gibt so viele wie
Code: Alles auswählen
mm/mproc.h extern struct mproc { struct mem_map mp_seg [NR_SEGS]; ... } mproc [NR_PROCS]
Und darn stehtCode: Alles auswählen
NR_PROCS
Und das enthält: DieCode: Alles auswählen
mem_map
ist inCode: Alles auswählen
mem_map
definiert und geht so:Code: Alles auswählen
kernel/type.h
Das sind unsere Speicherbereiche pro Segment, eines Prozesses.Code: Alles auswählen
struct mem_map { vir_clicks mem_vir; phys_clicks mem_phys; vir_clicks mem_len };
- Gut, die Register finden wir auch wieder. Jetzt haben wir genau da drunter definiert
Gut die Register, müssen aber bei jedem Task-Switch neu geladen werden. Deswegen sind sie für jeden Prozess gespeichert. Und das in dem seiner Struktur. Aber die hat wieder eine Unterstruktur und das ist ein mit einem Array realisiert. Deswegen haben wir hier die Defines für die einzelnen Wörter für die Register. Gut. Diese zeigen aufCode: Alles auswählen
#define ES_REG 7 #define DS_REG 8 #define CS_REG 9 #define SS_REG 10
Gut unser Prozess istCode: Alles auswählen
mproc[i].p_reg [ES_REG]; mproc[i].p_reg [DS_REG]; mproc[i].p_reg [CS_REG]; mproc[i].p_reg [SS_REG];
Und das ist ein Array - weil das sind viele Prozesse. Und. Die haben auch nur den IndexCode: Alles auswählen
mproc
Das mact den Task Switch so zu sagen aus. Und. In dem sind die Register inCode: Alles auswählen
i
. Ds gucken wir uns an. Was wir vorher hatten.Code: Alles auswählen
p_reg
Wenn wir jetzt, die
angucken finden wir:Code: Alles auswählen
extern struct proc { ... } [NR_TASKS+NR_PROCS]
Und das sind genau unsere Register. Natürlich Integer - 2 Byte - ein WortCode: Alles auswählen
extern struct proc { int p_reg [NR_REGS]; ... } [NR_TASKS+NR_PROCS]
Gut. - Damit ist erst Mal, der Teil erledigt. Der zweite Teil, der wichtig ist, der als erstes auffällt, ist der Speicherverwalter. Und der Speicherverwalter habe ich gesagt hat
-
Code: Alles auswählen
exec
-
Code: Alles auswählen
exit
-
Code: Alles auswählen
fork
- für
Code: Alles auswählen
alloc
Code: Alles auswählen
malloc
-
Code: Alles auswählen
signal
-
Code: Alles auswählen
break
ist besonders interessant. Das muss zu unserem Speicherverwalter. Das findet jetzt nicht imCode: Alles auswählen
alloc.c
sondern imCode: Alles auswählen
kernel
Das wichtige ist: Dieser Speicherverwalter muss ziemlich, was leisten. Ich sage ziemlich, weil die Sache ist schnell verstanden. Schnell verstanden, aber nicht gemerkt. Das wird das Konzept der Sache sein. Die Sache richtet sich mehr an mich und wer mitmachen will, soll mitmachen. Blos: Das ist nicht unlogisch - natürlich, aber fitzelig. Und wer sich da verfitzelt, der wird 20x hingucken. Und trotzdem: Auch, wenn nicht, das sollte man lernen. Sage ich gleich was. Während der erklärte Rest, bisher - so wenig es im Gegensatz zum Rest-Code aussieht, der zwar grösser ist, aber, wo das das entscheidende ist - womit der Rest schnell geht - das ist der Witz. Kommt jetzt das AllocCode: Alles auswählen
mm
Bei Linux gibt es mehrere Konzepte
- Paging - das heisst, wir haben eine Pagetable. Angenommen der Computer ist im Rohzustand. Ich habe das schon gemacht. Aber eine Pagetable, vom Prozessor her, lässt sich relativ einfach verwenden
- Das Memory Management - ist mehr als das Paging - sondern die Buddy Speicherverwaltung. Vom Prinzip ist die nicht schwer zu verstehen. Aber wir sollten wissen
- Es gibt kompliziertere nach dem Buddy System
- Wir sollten wenn wir programmieren, komplizierte Formationen im Kopf haben und einfache
. Gut: Diesen kann ich zum Beispiel in Segmente vonCode: Alles auswählen
1 MByte
zerlegen. Gut, das einfachste uf der Welt - ich habeCode: Alles auswählen
1 kByte
Blöcke, die entweder belegt sind oder nicht. Und hintereinander liegen. Wiederum fast ein Array. Das nächste wäre ich könnte die verknüpfen. Ich kann mir alles mögliche ausdenken. Das einfachste ist MinixCode: Alles auswählen
1024
Allerdings: Wenn ichCode: Alles auswählen
fork, exec, exit, ...
oderCode: Alles auswählen
fork
dann kommt ein neuer Prozess. Gut, wenn der kommt, dann braucht der Speicher. Der Speicher wird immer mitCode: Alles auswählen
exec
beantragt und nicht nur das: Auch der Prozess selber kannCode: Alles auswählen
alloc
deswegen istCode: Alles auswählen
alloc
das wichtigste und das spannenste. Und das hat zwei KomponentenCode: Alles auswählen
alloc
- Ich habe ja jetzt lauter Blöcke, das heisst, als für jeden Speicherbereich, sagen wir Jetzt brauche ich eine Liste, welcher ist belegt.
Code: Alles auswählen
1024 Byte
- Dann ist es aber komplizierter. Ich habe noch die Slots. während die eine Liste belegt, welcher belegt ist, liefert die andere Liste, quasi welche der Elemente der Liste, oder so, das gucken wir gleich nach belegt ist
vorstellen.Code: Alles auswählen
alloc
Ach, ja was ich nicht gesagt habe:
Das was neu da steht, sind unsere Speicherbereiche. Es war die Rede vonCode: Alles auswählen
extern struct proc { int p_reg [NR_REGS]; ... struct mem_map p_map [NR_SEGS]; ... } [NR_TASKS+NR_PROCS]
Und das ist genau das. Wir haben die Register und wir haben den Speicherbereich. Die Maximale Anzahl istCode: Alles auswählen
mem_map
Gut, zum AllocCode: Alles auswählen
NR_REGS NR_SEGS
Code: Alles auswählen
#define NR_HOLES 128 #define NIL_HOLE (struct hole *) 0 PRIVATE struct hole { phys_clicks h_base; phys_clicks h_len; struct hole *h_next; } hole [NR_HOLES] PRIVATE struct hole *hp, *hole_head; PRIVATE struct hole *free_slots; PUBLIC phys_clicks alloc_mem (phys_clicks clicks); PUBLIC free_mem (phys_clicks base, phys_clicks clicks); PRIVATE del_slot (register struct hole *prev_ptr, register struct hole *hp); PRIVATE merge (register struct hole *hp); PUBLIC phys_clicks max_hole (); PUBLIC mem_init (phys_clicks clicks);
Jetzt sieht das so aus:
Code: Alles auswählen
#define NR_HOLES 128 #define NIL_HOLE (struct hole *) 0 PRIVATE struct hole { phys_clicks h_base; phys_clicks h_len; struct hole *h_next; } hole [NR_HOLES] PRIVATE struct hole *hp, *hole_head; PRIVATE struct hole *free_slots; PUBLIC phys_clicks alloc_mem (phys_clicks clicks) { register struct hole *hp, *prev_ptr; phys_clicks old_base; hp = hole_head; while (hp != NIL_HOLE) { if (hp->h_len >= clicks) { old_base = hp->h_base; hp->h_base += clicks; hp->h_len -= clicks; } if (hp->h_len != 0) return (old_base) del_slot (prev_ptr, hp); return old_base; } prev_ptr = hp; hp->h_next; } PUBLIC free_mem (phys_clicks base, phys_clicks clicks) { register struct *hole, *new_ptr, *prev_ptr; if ((new_ptr = free_slots) == NIL_HOLE) panic (...) new_ptr->h_base = base; new_ptr->h_len = clicks; free_slots = new_ptr->h_next; hp = hole_head; if (hp == NIL_HOLE || base <= hp->h_base) { new_ptr->h_next = hp; hole_head = new_ptr; merge (new_ptr); return; } while (hp != NIL_HOLE) { prev_ptr = hp; hp = hp->h_next; } new_ptr->h_next = prev_ptr->h_next; prev_ptr->h_next = new_ptr; merge (prev_ptr); } PRIVATE del_slot (register struct hole *prev_ptr, register struct hole *hp) { if (hp == hole_head) hole_head = hp->h_next; else prev_ptr->h_next = hp->h_next; hp->h_next = free_slots; free_slots = hp; } } PRIVATE merge (register struct hole *hp) { register struct *next_ptr; if ((next_ptr = hp->h_next) == NIL_HOLE) return; if (hp->h_base + hp->h_len == next_ptr->h_base) { hp->h_len += next_ptr->h_len; del_slot (hp, next_ptr); } else { hp = hp->h_next; } if ((next_ptr = hp->h_next) == NIL_HOLE) return; if (hp->h_base + hp->h_len == hp->h_base) { hp->h_len += next_ptr->h_len; del_slot (hp, next_ptr); } } PUBLIC phys_clicks max_hole (); PUBLIC mem_init (phys_clicks clicks);
-
- Der
- Was wir vom Betriebssystem an Systemaufrufen besonders kennen
- Das entscheidende ist, es gibt einen Minix-Quelltext und den sollte man können
- Es gibt einen Linux-Quelltext, der ist einfach zu verstehen, wenn man Minix versteht
- Man muss zweiteren als Erweiterung und Modifikation von ersterem Begreifen. Es gibt eigentlich extrem viele Wege Programme in den Computer zu bringen. Und was die machen ist unser Problem. Sie tun meist das, was wir für sinnvoll halten. Was an Programm - 16 GByte Arbeitsspeicher möglich ist, ist eine Menge. Allerdings ergeben - viele dieser Programm für uns keinen Sinn. Das ist Psychologie - Programme haben was mit uns zu tun
- Ein Teil dieser Programme sind Betriebssysteme - auch hier gäbe es rechnerisch, eine Menge Möglichkeiten eines zu schreiben. UNIXe sind alle verwandt. Und: Ich sage so: Es gibt eine Menge möglicher Modifikationen, Verbesserungen, Erweiterungen, irgendwo sind sie sich ähnlich, sind fast alle Betriebssysteme ähnlich
- Darauf will ich nicht hinaus. Ich will nur sagen: Minix sollte man zu 100% kennen. Zu 100% heisst, erstens: Man kennt jede Funktion und jede heisst jede. Das , wenn man sich das anguckt, dann klingt das logisch. Trotzdem wirkt die Funktion nicht so wichtig. Es gibt daneben Funktionen wie
Code: Alles auswählen
max_hole
die für alle der Tasks realisiert sind. Die muss man alle kennen. Das geht. Das hat nichts mit Shakespeare zu tun. Und trotzdem: es gibt Leute, die den runter beten können und das mit gutem Grund - weil sie die Sache im Kopf haben - können sie trotzdem jedes WortCode: Alles auswählen
getc
Das möchte ich haben: Jedes Wort - Trotzdem: Ich würde sagen: Wie lernt man so etwas: Shakespeare hat gewisse höhen und tiefen. Aber die Menschliche Sprache hat ein Problem. Weil sie nicht in Gegenständen denkt, die sie gliedert - sondern etwas über Personen mitteilt, ist sie eine ständige Folge von Worten. Das ist bei Dingen nicht so und ein Betriebssystem ist ein Ding. Ein Ding zu definieren heisst, eine Unterstruktur zu definieren und das hat wieder Unterstruktur.
Und das was man machen muss: Man muss eine Funktion nicht so lernen: Wie sieht der Inhalt aus - Das muss man doch. Man muss zwei Wege unterscheiden
- Der erste Weg ist sagen wir: Der Speicherverwalter. Hier gibt es alloc. Sie kennen Mathematiker. Sie liefern ihnen Theorie und Übungsaufgaben. Dabei müssen die komplette Theorie kennen, und die übungsaufgaben zu einem Thema komplett
- Also, muss man den Speicherverwalter und das Alloc des Speicherverwalters komplett lernen. Alloc zu lernen, heisst, zu 100% wissen, was in alloc passiert. Das heisst nicht, was passiert, sondern wie die Funktion aussieht
- Daneben gibt es alle anderen Funktionen. Diese nicht einfach von innen her lernen. Man muss erst Mal jede Funktion kennen. Wichtiger als die Funktion von innen, ist die Funktion von aussen. Wir wollen immer alles verstehen. Meisten, wenn wir was nicht verstehen, ist weil die Begriffe nicht kennen. Unsere Begriffe sind die Funktionen. die Funktionen selber rufen Funktionen auf. Deswegen wäre es gut, die Funktionsnamen zu kennen
- Also, Speichermanager komplett, besonders komplett alloc und dann die Funktionen all
- Der nächste Teil ist das Dateisystem. Das sollte man auch kennen - das hat allerdings Zeit. Das Dateisystem kann an das Ende rutschen. Das ist erst Mal nicht von Bedeutung. Dabei ist das Dateisystem nicht das hauptsächlich entscheidende, und macht trotzdem den grössten Teil des Codes aus
- Denken wir an Betriebssysteme, denken wir an grossen Code. Und die Wahrheit ist, ich habe die Funktionen gerade abgeschrieben. Und sage: Na ja, was mir auffällt, das wichtigste vom wichtigsten, wird noch mal kleiner, wenn man erkennt, ein grossteil sind Kommentare
- Das nächste ist. Und darauf will ich hinaus
- Es gibt unendlich viele Programme, davon ergeben viele keinen sinn. Die einen sinn ergeben, sind immer noch unendlich viele.
- Es gibt unendlich viele Programme, davon ergeben viele keinen sinn. Die einen sinn ergeben, sind immer noch unendlich viele. Und davon sind einige Betriebssysteme. Und es gibt eine Unmenge. Und trotzdem
- Minix ist das fulminanteste Beispiel. Es macht keinen sinn, wenn man selber - und ich meine, wenn man keinen Bootloader schreibt, der ein Programm lädt, das ist auch ein thema, aber was anderes. Man kennt viele Möglichkeiten, Speicherbereiche zu verwalten. Wir stellen uns ein Array vor. Das Array wird in Segmente unterteilt und wir stellen uns jede Möglichkeit vor, das zu verwalten. Dann ist trotzdem nicht gut, wenn man Minix nicht kennt. Es gibt andere Möglichkeiten. Aber Minix steht für sich. Das heisst, das muss man auch kennen - Wort für Wort
- Was ich machen will, ich bete jeden der Teile runter. Also, das ist nicht nur Übung
- Das ist erstens Übung
- Auswendig wissen
- Aber
- Es gibt somit die Möglichkeit das zu wissen. Und die Möglichkeit, ein Array - in Segmente auf zu teilen und zu verwalten
- Es geht nicht darum eine Menge auf zu stellen, das heisst, alle Möglichkeiten
- Es geht um Übung
- Das heisst, ich denke mir das alles aus, an Möglichkeiten und mache dazu Übungen, im eigenen Sinne oder mehr an Minix orientiert. Ich kann eine Liste nehmen oder ich kann ein Array wiederum nehmen
- Minix verwendet Listen. Weil Listen normalerweise verwenden, das aber nicht da ist, weil das ja Programmiert wird. Braucht man Listen, die eigentlich Arrays sind. Deswegen gibt es
Code: Alles auswählen
malloc
Code: Alles auswählen
slots
- Ich möchte, wenn ich übe, eine Übung nicht so machen, das sie ein Mal gemacht
- Das entscheidende: Ich möchte das Minix üben, und lernen. Das muss sitzen, aber: Ich mache dazu jeden Tag die Übung das hin zu schreiben, damit es sitzt. Daneben mache ich übungen, zum Allgemeinen. Der Witz ist. Wenn ich Minix übe, reicht das nicht ein Mal. Meine Übung auch nicht, ich mache sie jeden Tag
- Minix lernen
- Minix auswendig hinschreiben
- Eigene Speicherverwaltung jeden Tag