Das klingt ja schon ziemlich cool, was du alles hast. Ich glaube, wir haben ein wirklich würdiges neues Mitglied gewonnen.
Ähm, danke
(...Schleimer!!!
)
Wie Svenska schon sagte, ist dein (vor allem virtueller Speicher) ja doch sehr begrenzt und ich nehme an, volle 16k brauchst du nicht für deinen Mikrokernel. Hast du dir schonmal überlegt, ob du nicht vielleicht auch noch ein paar Treiber in die Page 0 legen willst? Speicherschutz hast du ja sowieso nicht, insofern brauchst du Kernel- und Usermode-Pages auch nicht strikt zu trennen. Dann hättest du Page 0 für Kernel und Treiber (die zufälligerweise auch bevorzugte RPC-Ziele sein werden, d.h. du kannst dir evtl. ein paar Bankswitches sparen, weil die Treiber sowieso immer da sind), und die restlichen drei Pages für Anwendungsprogramme.
Genau, außerdem kann sich eine Anwendung auch mehr Pages reservieren und nur die einblenden, die es braucht. Die Kernel-Page werde ich dann wahrscheinlich mit Allround-Funktionen wie Multiplikation, Division, Zahlenformatierung und solchem Kram vollpacken, damit die Anwendungen weniger mit sich rumschleppen müssen und die Kernel-Page nicht so brachliegt.
Wenn jeder Task wenigstens eine Page braucht, dann kriegst du keine 256 Tasks in dein eines Megabyte und verschwendest wertvollen Speicher. Ich würde die Größe der Tabelle dynamisch halten, wenigstens beim Kernelstart Speicher für die mit dem verbauten Speicher maximal mögliche Taskzahl allozieren, oder besser vielleicht eine verkettete Liste von kleineren Tabellen mit 16 oder 32 Tasks.
Das stimmt nicht ganz. Es können sich auch mehrere Tasks eine Page teilen, was dann Multithreading ergibt. Natürlich mit jeweils eigenem Stack, aber das ist ja kein Problem. Man kann auch z.B. die Kernelpage in alle Banks einblenden, womit man effektiv keine Page hat. Dann muss der Code halt in der Kernel-Page mit ausgeführt werden, wie es bereits beim Kernel Task passiert.
Bitte entschuldige die Erbsenzählerei, aber auf Deutsch heißt es "physische Speicherverwaltung", das hat nichts mit Physik zu tun.
Pffff
Ich gehe davon aus, dass normale Code-, Export- und Stackpage in der Regel genau dieselbe Page sind, wenn das Programm mit 16k auskommt? Für Treiber einfacher Hardware dürfte das oft der Fall sein.
Genau, dann wird diese Page einfach mehrmals eingeblendet. Kann man ja machen.
Was hast du vor zu machen, wenn ein Treiber gerade einen RPC verarbeitet und ein anderer Prozess auch einen RPC auf den Treiber machen will?
Entweder ich blockiere per _KRNL_start_atomic (böööse in präemptivem Multitasking, ich weiß, brauchte ich jedoch an kritischen Stellen), ooooder ich arbeite mit Messages. Ich glaube, Messages wären hier die bessere Form der IPC.
In tyndur habe ich die Erfahrung gemacht, dass beide Optionen Probleme mit sich bringen: Entweder man blockiert einfach und lässt den Aufrufer warten, bis der Treiber fertig ist. Irgendwie schafft man es immer, Rekursion reinzubringen (Treiber A benutzt eine Funktion von Treiber B und der benötigt wieder Treiber A - typisches Beispiel für A ist das VFS) und das ist dann eben ein Deadlock. Oder man unterbricht den RPC und behandelt den neuen zuerst, das macht die Bedingungen für einen Deadlock dann ein bisschen komplizierter und kann zu interessanten Effekten führen, weil Dinge in einer unerwarteten Reihenfolge bearbeitet werden (unser Tastaturtreiber hat mal die Tastendrücke rückwärts abgearbeitet, wenn man zu schnell tippt...).
Puh... Das hängt dann aber mehr mit der geschickten Programmierung der Treiber zusammen, als mit dem grundlegenden Konzept von IPC und Treiberschnittstellen. Vielleicht könnte man in den Treibern eine Art "Job Queuing" machen - eine Komponente schickt eine Message mit einem Job an einen Treiber und sobald diese an der Reihe ist, wird sie bearbeitet und eine weitere Message verschickt. Benötigt man dann innerhalb des Treibers ein Ergebnis eines anderen Treibers (z.B. Dateisystemtreiber fragt Festplattentreiber nach nem Datenblock), könnte man den Job zurück in die Warteschlange einreihen und dann fortsetzen, wenn der angefragte Treiber fertig ist. "Jobs" könnten einfache Messages in der ganz normalen Message Queue sein. Ich glaube, so mach ich das, wenn ihr nicht gerade gravierende Einwände dagegen habt.
Egal für welche IPC-Möglichkeit du dich genau entscheidest, sind das vermutlich die entscheidenden Fragen, die du dir stellen musst. Den Ringpuffer von Jidder mag ich zum Beispiel auch, aber das ist aus diesem Blickwinkel nicht viel anders als RPC zu blockieren, wenn du nicht anfangen willst, mehrere Ringpuffereinträge parallel abzuarbeiten.
Man könnte es doch so machen: Eine Message im Puffer, die an der Reihe wäre, aber nicht abgearbeitet werden kann (z.B. warten auf anderen Treiber), wird einfach wieder ans Ende des Puffers geschrieben.
Du brauchst irgendeine zentrale Stelle, die eine Zuordnung von Treiber (nach Namen oder einer fest definierten ID in der Art 1 = VFS, 2 = Tastatur, 3 = ...) zu PID vornimmt. Ich würde dafür den Kernel vorschlagen, so dass man über einen Syscall an die Information kommt.
Und da jeder Treiber mehrere Interfaces zur Verfügung stellen kann (könnte), würde ich dort am besten einfach die Interfaces auflisten. Dann steht in dieser Tabelle z.B.:
00: 02 01 05 <--- PID 2, Interface 1 ist Typ 5 (z.B. PS/2-Interface)
01: 02 02 04 <--- PID 2, Interface 2 ist Typ 4 (z.B. RS232-Interface)
02: 06 01 0F <--- PID 6, Interface 1 ist Typ 15 (z.B. Grafikbibliothek)
...
Somit würde die Trennung der einzelnen Gerätefunktionen in deren Treibern erfolgen und für jedes Gerät wäre exakt ein Treiber zuständig. Braucht man irgendeine Funktion, sucht man sich einfach das passende Interface raus. Auch Softwarebibliotheken (aka DLLs in Windows) könnte man so sehr einfach und vorallem Platzsparend realisieren, zusammen mit RPCs.
Ja, bitte! Und der zentimeterhohe Drahtverhau macht die Sache sicher nur noch beeindruckender.
Für Hirnverknotungen hafte ich nicht
http://s23.postimg.org/xscwc7957/PICT0160.jpghttp://s22.postimg.org/qcf25v469/PICT0161.jpgOberseite der Platine: Links mittig sitzt die Z80-CPU (Z84C0020PEC). Diese wird mit einem Takt von 17 MHz aus dem ATTiny24 darunter versorgt. Wahlweise kann man mit dem Jumper über dem 1 MHz Quarzoszillator auf 1 MHz Taktung von diesem oder auf 4 kHz Taktung aus dem BIOS umschalten. Das BIOS ist der Chip rechts neben der CPU (ist ein ATMega32), steht sogar drauf. Darüber befindet sich das 1 MB RAM-Modul, welches aus 2x 512 kB 50ns-SRAM zusammengesetzt ist. Links oben beim RAM befindet sich die Spannungsregelung (12V über das steckbare Modul, 5V über einen Linearbrater mit Kühlkörper). Am linken Rand der Platine befinden sich einige Logik-ICs zur Timing-Steuerung für I/O-Zugriffe, welche auch einen Timeout bei nicht reagierender Hardware beinhaltet. Auf der rechten, später angebauten Lochrasterplatine ist die Memory-Banking-Logik... Die ICs nummeriert von 0 bis 3 schalten bei einem Zugriff die Nummer der Page in der entsprechenden Bank auf den Bus durch (sind 74HCT244 Bustreiber), die 4 ICs dazwischen wählen den passenden Bustreiber aus (je nach Adressbereich eine andere Bank) und der große Chip dazwischen (ein ATMega8515) nimmt Bank-Switch-Anforderungen von der CPU entgegen und füttert die 74HCT244 mit passenden Page-Nummern. Der Eingang des mit 0 beschrifteten 74HCT244 ist einfach auf 0V geschaltet - ich hatte keinen Port mehr am ATMega8515 frei und brauchte sowieso eine Kernel-Page. Mit einem kleinen SRAM anstatt dieser Riesen-Logik hätte ich zwar kleinere Pages hinbekommen, jedoch ließ sich einfach kein schnelles SRAM (<15ns) auftreiben, wodurch ich leider bei 16kB-Pages bleiben musste. Unten am Motherboard sind dann die PCIe-artigen Steckplätze für die Peripherie. Momentan steckt dort nur meine glorreiche RS232-PS/2-Kombikarte drin. Je nach Adresse wird dort über den 74LS154 (4-zu-16-Dekoder) die IORQ-Leitung eines der maximal 15 Steckplätze aktiviert. Momentan sind nur 2 davon verdrahtet, was aber später nicht mehr ausreichen wird.
Na dann programmiere ich mal meine Messages.
Gruß
Jonathan