Minix - Ich habe was vor

Benutzeravatar
davidvajda.de
Site Admin
Beiträge: 1424
Registriert: Di Jul 18, 2023 8:36 pm
Wohnort: D-72072, Tübingen
Kontaktdaten:

Minix - Ich habe was vor

Beitrag von davidvajda.de »

Ich möchte jetzt folgendes machen, ich möchte keine Erklärung schreiben. Wer was nicht weiss, weiss es halt nicht. Es sind folgende Dinge
  1. Wir haben drei Teile
    1. Kernel

      Code: Alles auswählen

      kernel
    2. Speicherverwalter
    3. Dateisystem
  2. Wir müssen uns merken: Es gibt die üblichen Systemaufrufe, die sind quasi 1:1 im Kernel Und daneben haben wir
  3. Wichtig ist
    • Was wir vom Betriebssystem an Systemaufrufen besonders kennen
      1. alloc

        Code: Alles auswählen

        alloc ()
        , zum Beispie

        Code: Alles auswählen

        malloc (), realloc ()
      2. fork

        Code: Alles auswählen

        fork ()
      3. exec

        Code: Alles auswählen

        exec ()
      4. exit

        Code: Alles auswählen

        exit ()
      Das sind tatsächlich die Systemaufrufe.

      Hier halt realisiert, dass sie im OS selber tun.
    • gut, was wichtig ist: Wir haben für jeden Betriebssystem Prozess eine

      Code: Alles auswählen

      main.c
      
    • Der
      1. Code: Alles auswählen

        kernel
      sind jeweils in einem Prozess realisiert. Gut
    • Was die Register beim sind, sollte klar sein

      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
      1. Der

        Code: Alles auswählen

        IBM PC
        hat mit

        Code: Alles auswählen

        8086/8088
        einen Timer Baustein

        Code: Alles auswählen

        Intel 8253
        . 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 Speicherschutz

        dazu zeige ich folgenden Code

        Code: Alles auswählen

        extern struct proc {
        ...
        } [NR_TASKS+NR_PROCS]
        
        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.
      2. Gut, jetzt haben wir: für jeden Prozess quasi. Zeitlich, mit Timer. Dazu hat jeder Prozess eine Struktur. Die besteht unter anderem aus
        1. Code: Alles auswählen

          ax, bx, dx, cx, sp, bp, si, di, es, cs, ds, ss 
        2. Dazu Speichebereiche. wir haben ja:
          1. Code
          2. Daten
          3. Stack
          Jetzt haben wir die Register schon. aber: Wir haben. Segment-Anfang-Segment-Ende. Und jetzt haben wir mehrere Struktur
          1. Wir haben eine für den Prozess
          2. Wir haben welche, die die Speicherbereiche für den Prozess selber festlegen. die darin auftauchen. Weil das ist eine eigene Struktur
          3. 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 zeige ich kurz. Danach kommt der MM
      3. Code: Alles auswählen

        mm/mproc.h
        
        extern struct mproc {
        	struct mem_map mp_seg [NR_SEGS];
        	...
        } mproc [NR_PROCS]
        
        Das ist mal das Entscheide. Es gibt so viele wie

        Code: Alles auswählen

        NR_PROCS
        
        Und darn steht

        Code: Alles auswählen

        mem_map
        
        Und das enthält: Die

        Code: Alles auswählen

        mem_map
        
        ist in

        Code: Alles auswählen

        kernel/type.h
        
        definiert und geht so:

        Code: Alles auswählen

        struct mem_map {
        	vir_clicks mem_vir;
        	phys_clicks mem_phys;
        	vir_clicks mem_len
        };
        
        Das sind unsere Speicherbereiche pro Segment, eines Prozesses.
      4. Gut, die Register finden wir auch wieder. Jetzt haben wir genau da drunter definiert

        Code: Alles auswählen

        #define ES_REG		7
        #define DS_REG		8
        #define CS_REG		9
        #define SS_REG		10
        
        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 auf

        Code: 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];
        
        Gut unser Prozess ist

        Code: Alles auswählen

        mproc
        Und das ist ein Array - weil das sind viele Prozesse. Und. Die haben auch nur den Index Das mact den Task Switch so zu sagen aus. Und. In dem sind die Register in

        Code: Alles auswählen

        p_reg
        . Ds gucken wir uns an. Was wir vorher hatten.

        Wenn wir jetzt, die

        Code: Alles auswählen

        extern struct proc {
        ...
        } [NR_TASKS+NR_PROCS]
        
        angucken finden wir:

        Code: Alles auswählen

        extern struct proc {
        	int p_reg [NR_REGS];
        ...
        } [NR_TASKS+NR_PROCS]
        
        Und das sind genau unsere Register. Natürlich Integer - 2 Byte - ein Wort
        Gut.
      5. 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
        1. Code: Alles auswählen

           exec
        2. Code: Alles auswählen

           exit
        3. Code: Alles auswählen

           fork
        4. Code: Alles auswählen

           alloc
          für

          Code: Alles auswählen

           malloc
        5. Code: Alles auswählen

           signal
        6. Code: Alles auswählen

           break
        Jetzt sind das einzelne Interessante Sachen. Blos, die

        Code: Alles auswählen

        alloc.c
        
        ist besonders interessant. Das muss zu unserem Speicherverwalter. Das findet jetzt nicht im

        Code: Alles auswählen

        kernel
        
        sondern im 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 Alloc

        Bei Linux gibt es mehrere Konzepte
        1. 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
        2. 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
          Die bei Minix ist einfach. Der Speicher. Das sind

          Code: Alles auswählen

          1 MByte
          . Gut: Diesen kann ich zum Beispiel in Segmente von

          Code: Alles auswählen

          1 kByte
          zerlegen. Gut, das einfachste uf der Welt - ich habe 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 Minix
        Gut, blos, der Rest, erledigt

        Code: Alles auswählen

        fork, exec, exit, ...
        Allerdings: Wenn ich oder dann kommt ein neuer Prozess. Gut, wenn der kommt, dann braucht der Speicher. Der Speicher wird immer mit

        Code: Alles auswählen

        alloc
        beantragt und nicht nur das: Auch der Prozess selber kann

        Code: Alles auswählen

        alloc
        deswegen ist

        Code: Alles auswählen

        alloc
        das wichtigste und das spannenste. Und das hat zwei Komponenten
        1. Ich habe ja jetzt lauter Blöcke, das heisst, als für jeden Speicherbereich, sagen wir

          Code: Alles auswählen

          1024 Byte
          Jetzt brauche ich eine Liste, welcher ist belegt.
        2. 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
        Worauf ich hinaus, zeige ich am Ende. Was ich machen, was unterscheidet, von sonst. Ich werde jetzt diese Funktionen, der

        Code: Alles auswählen

        alloc
        vorstellen.

        Ach, ja was ich nicht gesagt habe:

        Code: Alles auswählen

        extern struct proc {
        	int p_reg [NR_REGS];
        ...
        	struct mem_map p_map [NR_SEGS];
        ...
        } [NR_TASKS+NR_PROCS]
        
        Das was neu da steht, sind unsere Speicherbereiche. Es war die Rede von

        Code: Alles auswählen

        mem_map
        
        Und das ist genau das. Wir haben die Register und wir haben den Speicherbereich. Die Maximale Anzahl ist

        Code: Alles auswählen

        NR_REGS
        NR_SEGS
        
        Gut, zum Alloc

        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);
        
Jetzt sind wir erst mal fertig. Jetzt machen wir folgendes: Erstens lehnen wir zurück. Also.
  • 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

    Code: Alles auswählen

    max_hole
    , wenn man sich das anguckt, dann klingt das logisch. Trotzdem wirkt die Funktion nicht so wichtig. Es gibt daneben Funktionen wie 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 Wort

    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

    Code: Alles auswählen

    malloc
    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

    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
Das wird hier passieren, drei Dinge
  1. Minix lernen
  2. Minix auswendig hinschreiben
  3. Eigene Speicherverwaltung jeden Tag
Und der Witz: Sie kennen meine Automaten. Jeden Tag ein Automat. Jeden Tag eine Übung zur Speicherverwaltung. der Unterschied: Speicherverwaltung findet hier in dem leeren Computer statt. Sie kann genauso im Array passieren. Das mache ich.
Antworten