Lowlevel

Lowlevel => Lowlevel-Coding => Thema gestartet von: FlashBurn am 19. September 2010, 17:48

Titel: Shared Memory
Beitrag von: FlashBurn am 19. September 2010, 17:48
Ich bin gerade über einen Bug in meinem Shared Memory Code gestolpert und bin mir bei einer Sache nicht ganz sicher wie man es am besten macht.

Mein Shared Memory funktioniert so, das ein Prozess einen Bereich als SharedMemory erstellen kann (sprich der Speicher dafür muss schon im Prozess vorhanden sein). Will er (der Prozess) anderen Prozessen den Zugriff auf den Speicher geben, muss er einen "SharedAllow"-Syscall aufrufen, dem er die Adresse des Shared Memory und des Ziel-Prozesses übergibt.
Der Zielprozess ruft dann einen "SharedMap"-Syscall auf mit der ID des SharedMemory, hat er seine Arbeit erledigt ruft er den "SharedUnmap"-Syscall auf.

Mit dem letztgenannten Syscall (SharedUnmap) hab ich dann so meine Probleme. Im Moment habe ich nen Counter wie oft ein Shared Memory Bereich irgendwo gemappt ist und nur wenn dieser Counter "0" wird, wird die ganze Strukur (die die Daten zur Beschreibung des Shared Memory beinhaltet) wieder freigegeben/gelöscht.
Nur kann es jetzt die Situation geben, das ein anderer Prozess noch gar nicht an der Reihe war (zwecks Scheduling-Strategie) und den Speicher noch nicht mappen konnte. Dadurch würde diese Shared Memory nicht mehr existieren und der Empfänger würde nen Fehler zurückbekommen.
Das ist natürlich unschön. Wenn ich sage das ein Shared Memory solange exisitiert bis alle Prozesse, die in der "Allow"-Liste stehen, diesen auch einmal gemappt und dann wieder geunmappt haben, mache ich mir sorgen um die Sicherheit bzw. würde ich das dann fast schon als Speicherleck sehen.

Was meint ihr wie man das lösen sollte?
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 20. September 2010, 12:18
Ich denke ich werde es so lösen, wie ich es momentan schon habe, sprich wenn kein Prozess mehr den Speicher gemappt hat, dann wird die SharedMemory Struktur gelöscht. Denn würde ich das nicht machen, wäre es ganz einfach mein OS zum stillstand zu bringen, in dem man einfach nen SharedMemory Bereich erstellt, nen anderen Prozess auf die Allow-Liste packt, den Bereich unmappt und dann wieder nen neuen Bereich erstellt und von vorne anfängt, so lange bis kein physischer Speicher mehr vorhanden ist.

Man (als Prozess der den SharedMemory erstellt) muss dann halt nur sicher stellen, das man es irgendwie bei der Kommunikation so macht, das der SharedMemory erst geunmappt wird, wenn der Ziel-Prozess diesen auch gemappt hat.
Titel: Re:Shared Memory
Beitrag von: bluecode am 20. September 2010, 17:02
Das ist natürlich unschön. Wenn ich sage das ein Shared Memory solange exisitiert bis alle Prozesse, die in der "Allow"-Liste stehen, diesen auch einmal gemappt und dann wieder geunmappt haben, mache ich mir sorgen um die Sicherheit bzw. würde ich das dann fast schon als Speicherleck sehen.
Ich seh da kein Speicherleck, der Prozess braucht ja nicht den Weg über shared-memory gehen um dein "Leck" zu erzeugen, er kann einfach wie wild Speicher allozieren und nie mehr freigeben.
Ich würde lieber den Weg mit der Allow-Liste einschlagen, denn IPC ist schon so schwer genug, da macht es keinen Spaß auch noch auf so ein Timing-Problem Rücksicht zu nehmen (va. wenn es in keinster Weise irgendein anderes Problem löst). Aber ich würde natürlich einen noch-nicht-angenommenen Shared-memory Bereich dem Prozess als verbrauchter Speicher anrechnen. Damit wird dein Shared-memory "Speicherleck" einfach nur zu einem normalen "Speicherleck".

Worauf du aber achten solltest, wenn du jeden Speicherbereich zu shared-memory machen kannst, ist, dass du nicht einfach mit dem anderen Prozess Dinge sharest, die du eigentlich nicht sharen wolltest, nur weil sie auf der selben Page liegen.
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 20. September 2010, 17:16
Zitat von: bluecode
Worauf du aber achten solltest, wenn du jeden Speicherbereich zu shared-memory machen kannst, ist, dass du nicht einfach mit dem anderen Prozess Dinge sharest, die du eigentlich nicht sharen wolltest, nur weil sie auf der selben Page liegen.
Das ist ein Problem des App-Programmierers und nicht des OS. Denn SharedMemory läuft nunmal nur über Pages.

Zitat von: bluecode
Ich seh da kein Speicherleck, der Prozess braucht ja nicht den Weg über shared-memory gehen um dein "Leck" zu erzeugen, er kann einfach wie wild Speicher allozieren und nie mehr freigeben.
Wenn ich mal davon ausgehe, das ich irgendwann Swappen kann, dann kannst du mein OS nur ärgern, wenn der Speicher auch nicht ausgelagert wird und das ist nur bei SharedMemory der Fall.

Zitat von: bluecode
Ich würde lieber den Weg mit der Allow-Liste einschlagen, denn IPC ist schon so schwer genug, da macht es keinen Spaß auch noch auf so ein Timing-Problem Rücksicht zu nehmen
Oder ich müsste mir noch was einfallen lassen, so dass man Pages von einem Prozess zu einem anderem Prozess verschieben/schicken kann. Dieser Speicher würde dann auch beim Sender "ausgetragen" werden.
Titel: Re:Shared Memory
Beitrag von: bluecode am 20. September 2010, 18:57
Zitat von: bluecode
Worauf du aber achten solltest, wenn du jeden Speicherbereich zu shared-memory machen kannst, ist, dass du nicht einfach mit dem anderen Prozess Dinge sharest, die du eigentlich nicht sharen wolltest, nur weil sie auf der selben Page liegen.
Das ist ein Problem des App-Programmierers und nicht des OS. Denn SharedMemory läuft nunmal nur über Pages.
Natürlich läuft das über Pages. Aber ich fand es sinnvoller spezielle Bereiche (die komplett vom Heap entkoppelt sind) für shared-memory vorzusehen, d.h. man kann sowas schon nicht mit new/malloc allozieren, sondern ruft da schon den Kernel auf. Ich find das mit malloc/new irgendwie sinnlos, da das free/delete beim Freigeben sowieso irgendwas shared-memory spezifisches machen muss (die Regionen werden ja nach dem Versenden irgendwie vom Kernel verwaltet) und man es dann auch nicht einfach wieder in die Freispeicherliste (oder was auch immer) eintragen kann, da ein anderer Prozess das ganze noch nutzt. Soll heißen, die Heapverwaltungsstrukturen auf einer shm Page sind für shared-memory überflüssig und im schlimmsten Fall kann dadurch ein anderer Prozess meinen Heap kaputtmachen. Abgesehen davon ist es für den Anwendungsenwickler einfach nur fricklig irgendwas mit align 4096 über malloc/new zu allozieren.
Wie gesagt, meine Lösung war das komplett von der Heapverwaltung zu entkoppeln.

Zitat
Zitat von: bluecode
Ich seh da kein Speicherleck, der Prozess braucht ja nicht den Weg über shared-memory gehen um dein "Leck" zu erzeugen, er kann einfach wie wild Speicher allozieren und nie mehr freigeben.
Wenn ich mal davon ausgehe, das ich irgendwann Swappen kann, dann kannst du mein OS nur ärgern, wenn der Speicher auch nicht ausgelagert wird und das ist nur bei SharedMemory der Fall.
Warum sollte das mit shared-memory nicht funktionieren, va. warum nicht in dem von dir beschriebenen Szenario?

Zitat
Zitat von: bluecode
Ich würde lieber den Weg mit der Allow-Liste einschlagen, denn IPC ist schon so schwer genug, da macht es keinen Spaß auch noch auf so ein Timing-Problem Rücksicht zu nehmen
Oder ich müsste mir noch was einfallen lassen, so dass man Pages von einem Prozess zu einem anderem Prozess verschieben/schicken kann. Dieser Speicher würde dann auch beim Sender "ausgetragen" werden.
Ich hab das mit dem verschicken in lightOS implementiert. Das ist so implementiert, dass man mit einer IPC-Message einen shared-memory Bereich mitschicken kann. Möglich war dabei, dass der Speicher aus dem Sender geunmappt wird (also ein kompletter Ownershiptransfer) oder Read-only oder Read-Write zu versenden.
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 20. September 2010, 20:33
Zitat von: bluecode
Wie gesagt, meine Lösung war das komplett von der Heapverwaltung zu entkoppeln.
Irgendwo müssen wir uns gegenseitig missverstanden haben, denn das es nichts mit der Heapverwaltung zu tun hat ist klar. Das ist bei mir genauso, aber ich weiß worum es geht, nämlich das man erstmal den Speicher zum sharen braucht.
Dafür würde ich dann sagen, ruft das Programm halt den selben Syscall auf wie malloc auch (um 1 oder mehrere Pages zu allozieren).

Zitat von: bluecode
Warum sollte das mit shared-memory nicht funktionieren, va. warum nicht in dem von dir beschriebenen Szenario?
Weil ich bei SharedMemory grundsätzlich sage, das es nicht geswappt wird. Denn dann müsste ich im average-case in 2 und im worst-case in noch mehr Prozessen in den PageTables rumspielen und das wird verdammt schwierig, weil auch mein Kernel immer nur die PageTables sieht von dem Prozess der gerade läuft.
Swappen wird sowieso interessant zu implementieren, da ja die Treiber und die VFS Sachen alle im UserSpace laufen (werden).

Zitat von: bluecode
Ich hab das mit dem verschicken in lightOS implementiert. Das ist so implementiert, dass man mit einer IPC-Message einen shared-memory Bereich mitschicken kann. Möglich war dabei, dass der Speicher aus dem Sender geunmappt wird (also ein kompletter Ownershiptransfer) oder Read-only oder Read-Write zu versenden.
Naja, da ich ja nur fixed-size Nachrichten habe (haben möchte) müsste ich mal sehen, ob ich das als Nachricht oder irgendwie anders machen würde.
Eventuell als ne spezielle Art von Nachricht, nur wie ich das dem Empfänger signalisiere wüsste ich jetzt spontan nicht. Denn was in so einer Nachricht steht ist bei mir nicht festgelegt und von daher habe ich auch nicht sowas wie nen Msg-Code mit dem man die Nachricht bzw. deren Inhalt identifizieren könnte.
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 22. September 2010, 17:34
Ich habe mir überlegt eine spezielle Nachricht bzw. einen speziellen Sender zu verwenden um Speicher von einem Prozess zu einem anderen zu senden.

Ich wollte mir jetzt nur mal euren "Segen" holen ob man das so machen kann/sollte oder nicht.

Bei mir kann es als Sender-ID keine "0" geben und diesen Sender wollte ich dann für spezielle Nachrichten verwenden. Sprich als Sender-ID bekommt der Receiver ne "0" und in der Nachricht wäre der erste 32bit-Wert ein Nachrichten-Code und ich würde diese speziellen Nachrichten für irgendwelche System-Nachrichten nutzen.
In diesem speziellen Fall wäre der Code halt das man Speicher von einem anderen Prozess bekommen hat und der zweite 32bit-Wert wäre die Adresse wo dieser Speicher hingemappt wurde.

Soweit so gut. Was mache ich aber, wenn im Adressraum des Receivers nicht genug virtueller Speicher vorhanden ist um den Speicher zu mappen?
Da das ganze ja asynchron wäre, könnte man dem Sender nicht mal sagen, dass der Speicher nicht gemappt werden konnte, das wäre ja noch nicht mal weiter Schlimm.
Aber was passiert mit dem Speicher der gemappt werden sollte, im Adressraum des Senders ist er nicht mehr und in den Adressraum des Receivers passt er nicht rein, also weg damit?
Eine Möglichkeit wäre, die Nachricht (irgendwie) synchron zu machen, sprich der Sender sollte (er muss nicht) auf eine Nachricht des Receivers warten. Ich (als Kernel) würde dann also eine Nachricht an den Sender schicken, wenn der Speicher gemappt werden konnte, das alles Ok ist. Wenn der Speicher nicht gemappt werden konnte, steht halt in der Nachricht der Code für "konnte nicht gemappt werden" drin und die Adresse wo ich (als Kernel) den Speicher wieder zurück gemappt habe.
Gut wäre eine Lösung, was passiert jetzt aber, wenn der virtuelle Speicher des Sender inzwischen auch so voll ist das der Speicher nicht wieder zurück gemappt werden kann?

Wie ihr seht habe ich ein Problem mit der Fehlerbehandlung bzw. ob es richtig ist den Speicher der versendet werden soll im Fehlerfall einfach wieder freizugeben als hätte es ihn nie gegeben.
Titel: Re:Shared Memory
Beitrag von: bluecode am 22. September 2010, 18:31
hm, du könntest ja beim Senden schon den virtuellen Speicher im anderen Prozess reservieren (ohne zu mappen, falls das zuviel Probleme macht) und dann dem Sender zurückliefern, dass zumindest das geklappt hat. Dann kann der sich überlegen ob er es später nochmal versucht oder einfach aufgibt.

Aber eigentlich sollte das bei einem Hobby-OS eher kein Problem werden, denke ich. :wink:
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 22. September 2010, 18:54
Zitat von: bluecode
hm, du könntest ja beim Senden schon den virtuellen Speicher im anderen Prozess reservieren (ohne zu mappen, falls das zuviel Probleme macht) und dann dem Sender zurückliefern, dass zumindest das geklappt hat.
Da wären wir wieder bei dem Problem das ich vom Sender aus nicht in die PageTables (bzw. meine anderen Datenstrukturen die dafür verwendet werden) reingucken kann und dann kommt noch hinzu, wenn ich den virtuellen Speicher reservieren kann, dann kann ich ihn auch mappen (es geht ja nur um ein "Stück" was groß genug ist, das der Speicherbereich, der gesendet werden soll, reinpasst).

Zitat von: bluecode
Aber eigentlich sollte das bei einem Hobby-OS eher kein Problem werden, denke ich.
Ist zwar richtig, sollte aber trotzdem irgendwie behandelt werden.
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 22. September 2010, 19:04
Hallo,


also ich würde in dem Sende-Syscall erst den Speicher ins Ziel mappen und danach erst beim Sender unmappen. Wenn das mappen beim Ziel nicht klappt dann kommt der Syscall mit einem Error-Code zurück und der Speicher ist noch beim Sender.
Physischer Speicher ohne Besitzer ist IMHO ein Bug im Kernel. ;)


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 22. September 2010, 19:19
Zitat von: erik
also ich würde in dem Sende-Syscall erst den Speicher ins Ziel mappen und danach erst beim Sender unmappen. Wenn das mappen beim Ziel nicht klappt dann kommt der Syscall mit einem Error-Code zurück und der Speicher ist noch beim Sender.
Wenn ich das als eigenen Syscall machen (also nicht über den sendMsg Syscall) dann dürfte das selbst bei meinem IPC System hinzubekommen sein.

Problematisch ist halt nur, dass das ganze dann halt blockierend wäre, was ich eher unschön finde, aber das könnte man ja über ein Flag lösen!

Zitat von: erik
Physischer Speicher ohne Besitzer ist IMHO ein Bug im Kernel.
Da hast du mich dann nicht ganz verstanden, wenn der Speicher nicht gemappt werden konnte, wird er einfach freigegeben als wenn es ihn nie gegeben hätte ;)
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 22. September 2010, 21:10
Hallo,


Wenn ich das als eigenen Syscall machen (also nicht über den sendMsg Syscall) dann dürfte das selbst bei meinem IPC System hinzubekommen sein.
Warum sollte das nicht gleich der SendMSG-Syscall machen?

Problematisch ist halt nur, dass das ganze dann halt blockierend wäre, was ich eher unschön finde, aber das könnte man ja über ein Flag lösen!
Der Syscall soll doch nur so lange blockieren bis das ummappen erledigt ist, so lange wird das schon nicht dauern.

Da hast du mich dann nicht ganz verstanden, wenn der Speicher nicht gemappt werden konnte, wird er einfach freigegeben als wenn es ihn nie gegeben hätte
Das ist aber auch nicht gerade höflich wenn Speicher einfach so verschwindet. Was ist wenn der Client es noch mal probieren will? Dann muss er neuen Speicher allozieren und neu mit Daten befüllen, das stelle ich mir unpraktisch vor.


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 22. September 2010, 21:31
Zitat von: erik
Warum sollte das nicht gleich der SendMSG-Syscall machen?
Naja, wird schwierig, weil zusätzliche Parameter (die bei den meisten Aufrufen total Sinnlos wären) und wie gesagt macht sich das über nen eigenen Syscall einfacher (zumindest die Idee die ich gerade im Kopf dazu habe).

Zitat von: erik
Der Syscall soll doch nur so lange blockieren bis das ummappen erledigt ist, so lange wird das schon nicht dauern.

Das ist aber auch nicht gerade höflich wenn Speicher einfach so verschwindet. Was ist wenn der Client es noch mal probieren will? Dann muss er neuen Speicher allozieren und neu mit Daten befüllen, das stelle ich mir unpraktisch vor.
Ich habe mir das nochmal genau überlegt, an und für sich, fordert der Client ja den Speicher an, d.h. er weis wie groß der Bereich ist, soll heißen er wird das nur machen wenn er auch genug freien virtuellen Speicher hat.
Daher kann man sagen, das der Fall das es nicht gemappt werden kann eher selten bis gar nicht auftreten sollte (was man aber bei Multithreading nicht so einfach sagen kann).
Was das Blockieren betrifft, das sollte (unter den selben Betrachtungspunkten wie Oben, sprich der Client wartet darauf) eigentlich auch nicht lange dauern (wie du schon sagtest).

Sprich ich werd es trotzdem mit nem Flag machen, das der Sender nicht blockiert und ich werde nen eigenen Syscall dafür machen (ist einfach einfacher ;) und performanter).

Jetzt komme ich dem zero-copy immer näher ;)
Titel: Re:Shared Memory
Beitrag von: bluecode am 23. September 2010, 10:33
Zitat von: bluecode
hm, du könntest ja beim Senden schon den virtuellen Speicher im anderen Prozess reservieren (ohne zu mappen, falls das zuviel Probleme macht) und dann dem Sender zurückliefern, dass zumindest das geklappt hat.
Da wären wir wieder bei dem Problem das ich vom Sender aus nicht in die PageTables (bzw. meine anderen Datenstrukturen die dafür verwendet werden) reingucken kann und dann kommt noch hinzu, wenn ich den virtuellen Speicher reservieren kann, dann kann ich ihn auch mappen (es geht ja nur um ein "Stück" was groß genug ist, das der Speicherbereich, der gesendet werden soll, reinpasst).
Um virtuellen Speicher zu allozieren muss man nicht in die PageTables schauen. Dafür wird man wohl sowieso andere geeignete Datenstrukturen verwenden, z.B. Buddy. Es läuft doch niemand (zumindest hoffe ich das) wie bei einer Bitmap durch die PageTables und sucht nach einem passend großen Stück virtuellen Speichers...

Zitat
Daher kann man sagen, das der Fall das es nicht gemappt werden kann eher selten bis gar nicht auftreten sollte
Der Fall tritt ja schon allein, weil es nur ein Hobby-OS ist, wohl eher nicht auf. Oder werden da fette Datenbanken oä. auf der 32Bit Version laufen. :-P
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 23. September 2010, 10:46
Zitat von: bluecode
Um virtuellen Speicher zu allozieren muss man nicht in die PageTables schauen. Dafür wird man wohl sowieso andere geeignete Datenstrukturen verwenden, z.B. Buddy. Es läuft doch niemand (zumindest hoffe ich das) wie bei einer Bitmap durch die PageTables und sucht nach einem passend großen Stück virtuellen Speichers...
Ich benutze weder Buddy noch gehe ich die PageTables durch, aber wie gesagt, diese Datenstrukturen sind Prozessbezogen und nur dann "sichtbar" wenn der Prozess auch läuft. Man kann auf diese Daten nicht von einem anderem Prozess aus zugreifen.

Zitat von: bluecode
Der Fall tritt ja schon allein, weil es nur ein Hobby-OS ist, wohl eher nicht auf. Oder werden da fette Datenbanken oä. auf der 32Bit Version laufen.
Auch hier wieder, nur weil es auf einem HobbyOS unwahrscheinlich ist/erscheint, sollte man den Fall trotzdem "bearbeiten" können.
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 23. September 2010, 11:48
Hallo,


Der Fall tritt ja schon allein, weil es nur ein Hobby-OS ist, wohl eher nicht auf. Oder werden da fette Datenbanken oä. auf der 32Bit Version laufen.
Was soll das denn Bitte bedeuten? Das auf einem Hobby-OS nicht auch mal sehr speicherintensive Programme gestartet werden? Du kennst doch bestimmt 7z und die LZMA-Library, das ist ein ziemlich speicherintensives Programm welches sich wohl sehr leicht portieren lässt.


Jetzt komme ich dem zero-copy immer näher
Das sehe ich noch nicht, solange die Clients sich extra neuen Speicher holen müssen (weil dieser ja wegen dem IPC weggenommen wird) und ihn extra mit Daten befüllen müssen (also kopieren) nur um IPC machen zu können bist Du IMHO noch nicht bei Zero-Copy! Oder habe ich was übersehen?
Außerdem sehe ich da eine Menge Syscalls für die Clients: Speicher holen, IPC antriggern, Speicher mitgeben, Antwort abholen und eventuell noch Speicher zurückholen.


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 23. September 2010, 13:08
Zitat von: erik
Das sehe ich noch nicht, solange die Clients sich extra neuen Speicher holen müssen (weil dieser ja wegen dem IPC weggenommen wird) und ihn extra mit Daten befüllen müssen (also kopieren) nur um IPC machen zu können bist Du IMHO noch nicht bei Zero-Copy! Oder habe ich was übersehen?
Außerdem sehe ich da eine Menge Syscalls für die Clients: Speicher holen, IPC antriggern, Speicher mitgeben, Antwort abholen und eventuell noch Speicher zurückholen.
Um es mal am Bsp. des HDD-Treibers zu "zeigen".

Du musst Speicher holen damit der HDD-Kontroller weis wo er die Daten hinladen soll (das wirst du bei dir wohl auch machen müssen).
Hat er die Daten dann in diesen Speicher geladen, wird er mit einem Syscall per Nachricht zum VFS-Service geschickt.
Wie dieser dann die Daten an den Clienten weiterreicht ist erstmal unwichtig, denn da müsste man viele Fälle betrachten (nur wenige Bytes, eine ganze Datei, die gemappt werden könnte, ...).

Ich denke du versuchst dein IPC auf mein IPC abzubilden ;)

Bei meinem IPC schickst du keinen "Speicher" mit wo die Daten reinkommen, du bekommst ihn und das RPC bei mir auch mit einem Syscall gemacht wird, weist du ja eigentlich.
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 23. September 2010, 14:19
Hallo,


Du musst Speicher holen damit der HDD-Kontroller weis wo er die Daten hinladen soll (das wirst du bei dir wohl auch machen müssen).
Also bei mir gibt der Client immer den Speicher mit, egal ob gelesen oder geschrieben werden soll.

Wie dieser dann die Daten an den Clienten weiterreicht ist erstmal unwichtig, denn da müsste man viele Fälle betrachten (nur wenige Bytes, eine ganze Datei, die gemappt werden könnte, ...).
Bis auf das Datei-Mapping (was auch was völlig anderes als fread/fwrite ist) geht alles bei mir über den selben Mechanismus, egal ob nur 3 Bytes oder ganze 3 GBytes, egal ob ausgerichtet oder nicht.
One way fits all! :-D

Ich denke du versuchst dein IPC auf mein IPC abzubilden ;)
Etwas vielleicht, ich versuche zumindest die Möglichkeiten meines Konzepts auch in Deinem Konzept wieder zu finden, aber eben nur mit wenig Erfolg. Sorry.

Bei meinem IPC schickst du keinen "Speicher" mit wo die Daten reinkommen, du bekommst ihn
Das deckt sich aber nicht mit fread (das bekommt einen Pointer wo die Daten hin sollen und liefert nicht einen Pointer zurück wo das System die Daten hingelegt hat) und auch sonst keinem der üblichen SW-Paradigmen, da wirst Du wohl viel kopieren müssen in Deiner libc. Bei fwrite wird auch ein Pointer mitgegeben aber die Daten verschwinden nicht sondern bleiben dem Prozess unverändert erhalten. Ausgerichtet sind die Pointer für fread/fwrite üblicherweise auch nicht, wenn Du also immer ganze Pages mitgeben willst dann hast Du Probleme die dann auch wieder auf kopieren hinauslaufen.
Sorry, aber Zero-Copy sehe ich in Deinem Konzept nicht. Was nicht heißt das ich Dein Konzept auch sonst wie schlecht finde, es ging mir heute nur um das Zero-Copy.


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 23. September 2010, 14:37
Zitat von: erik
Sorry, aber Zero-Copy sehe ich in Deinem Konzept nicht. Was nicht heißt das ich Dein Konzept auch sonst wie schlecht finde, es ging mir heute nur um das Zero-Copy.
Ich denke wir sollten uns darauf einigen, das es unter Paging kein "immer zero-copy möglich" gibt! Man macht halt das beste daraus.

Zitat von: erik
Das deckt sich aber nicht mit fread (das bekommt einen Pointer wo die Daten hin sollen und liefert nicht einen Pointer zurück wo das System die Daten hingelegt hat) und auch sonst keinem der üblichen SW-Paradigmen, da wirst Du wohl viel kopieren müssen in Deiner libc.
Naja, bei nem Mikrokernel der mehr mikro ist als das was man heute als solche bezeichnet (z.B. Haiku) wird man um einiges an kopieren nicht vorbei kommen.

Wie gesagt, sehe ich auch mit Paging-Tricks, wie dem reinmappen und nur den Rand kopieren, nicht das man das irgendwie "zufriedenstellend" lösen könnte.

Zitat von: erik
Bis auf das Datei-Mapping (was auch was völlig anderes als fread/fwrite ist) geht alles bei mir über den selben Mechanismus, egal ob nur 3 Bytes oder ganze 3 GBytes, egal ob ausgerichtet oder nicht.
Nicht dass das hierher gehören würde, aber mich würde mal interessieren wie du folgendes löst.

FILE *file= fopen("foobar","r");
char buffer[512], *ptr= &buffer;

for(uint32t x= 0; x < 512/4; x++) {
 fread(ptr,sizeof(uint32t),1,file);
 fseek(file,12,SEEK_CUR);
 ptr+= sizeof(uint32t);
}
Mir geht es bei diesem (sinnlosen??) Bsp darum, dass du ja immer den Speicher mitgibts wo die Daten reinsollen, wie bekommst du aber die Daten aus dem Sektor, der zu der Stelle in der Datei gehört, ohne kopieren in den Buffer?
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 23. September 2010, 15:09
Hallo,


Ich denke wir sollten uns darauf einigen, das es unter Paging kein "immer zero-copy möglich" gibt! Man macht halt das beste daraus.
[.....]
Wie gesagt, sehe ich auch mit Paging-Tricks, wie dem reinmappen und nur den Rand kopieren, nicht das man das irgendwie "zufriedenstellend" lösen könnte.
Also ich würde schon sagen dass das mit "nur den Rand kopieren" recht zufriedenstellend ist, besser geht es eben nur mit Paging nicht. Insofern hat man zumindest ein "fast immer ohne kopieren". Alternativ könnte man auch definieren das einem dieses kleine Sicherheitsrisiko egal ist und immer komplette Pages gemappt werden so das man dann doch "immer ohne kopieren" bekommt.

Mir geht es bei diesem (sinnlosen??) Bsp darum, dass du ja immer den Speicher mitgibts wo die Daten reinsollen, wie bekommst du aber die Daten aus dem Sektor, der zu der Stelle in der Datei gehört, ohne kopieren in den Buffer?
Nichts leichter als das! :-D
Ich lasse den Sektor in 3 verschiedene Bereiche vom AHCI-Controller reinlesen. Der interessante Teil kommt in die User-Daten und der Rest in einen 512Byte kleinen Trash-Buffer (welcher im Treiber liegt und immer ignoriert wird).
Selbst wenn die 4 Bytes z.B. an Datei-Offset 1021 liegen und an Speicher-Offset 4095 sollen und dort ne Pagegrenze ist und die Pages nicht zusammenhängend im physischen Speicher liegen ist das kein Problem. Die ersten 509 Bytes (der insgesamt 1024 Bytes von der HDD) kommen in den Trash-Buffer, das nächste Byte kommt ans Ende der ersten User-Page, anschließend 3 Bytes an den Anfang der zweiten User-Page (das in diesen 3 Bytes die Sektorgrenze ist spielt für AHCI keine Rolle) und zum Schluss noch 511 Bytes in den Trash-Buffer.
Das dieses Beispiel natürlich vom Cache im VFS abgefangen wird hab ich jetzt mal ignoriert, es geht uns doch ums Zero-Copy-Prinzip.


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 23. September 2010, 15:20
Zitat von: erik
Also ich würde schon sagen dass das mit "nur den Rand kopieren" recht zufriedenstellend ist, besser geht es eben nur mit Paging nicht.
Ich weis das es nicht gerade deine lieblings Aufgabe ist ;) aber ich habe da so meine Verständnis Probleme und wenn die weg sind, könnte ich mich fast zu einem dynamic-size IPC hinreißen lassen.

Also ich habe folgendes Problem:

Client will 10000bytes aus einer Datei gelesen bekommen, erstellt den Buffer über malloc und der hat die Adresse (die virtuelle bei ihm im AdressSpace) 0x1000200.
Der HDD-Treiber lädt die Datei aber 4KB align in den Speicher, also z.B. 0x4000000.
Wenn ich die Daten jetzt mappen wollte, dann geht das aber nicht, da ja der PageOffset beim Clienten 0x200 ist und beim Treiber 0x0. Die Daten sind also "verschoben" oder unausgerichtet und daher geht das Mappen (nach meinem Verständnis) nicht und es muss kopiert werden.

Ich denke mal das ich da irgendwas nicht richtig verstanden habe, denn irgendwie muss das ja funktionieren.
Titel: Re:Shared Memory
Beitrag von: kevin am 23. September 2010, 16:55
Das deckt sich aber nicht mit fread (das bekommt einen Pointer wo die Daten hin sollen und liefert nicht einen Pointer zurück wo das System die Daten hingelegt hat) und auch sonst keinem der üblichen SW-Paradigmen, da wirst Du wohl viel kopieren müssen in Deiner libc.
Der Sinn und Zweck von fread() ist, dass kopiert wird. Das Zeug in stdio.h sind gepufferte I/O-Funktionen. Für Performance wird daher auch unter anderen Systemen niemand fread() nehmen, sondern Funktionen der nativen API, z.B. read() unter Unix.
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 23. September 2010, 17:10
Hallo,


Das deckt sich aber nicht mit fread (das bekommt einen Pointer wo die Daten hin sollen und liefert nicht einen Pointer zurück wo das System die Daten hingelegt hat) und auch sonst keinem der üblichen SW-Paradigmen, da wirst Du wohl viel kopieren müssen in Deiner libc.
Der Sinn und Zweck von fread() ist, dass kopiert wird. Das Zeug in stdio.h sind gepufferte I/O-Funktionen. Für Performance wird daher auch unter anderen Systemen niemand fread() nehmen, sondern Funktionen der nativen API, z.B. read() unter Unix.
Ja, Recht hast Du. Aber read hat (im wesentlichen) die selben Parameter wie fread. Es geht mir darum das die Semantik, auf der read und fread aufbauen, sich durchaus mit Zero-Copy umsetzen lässt. Das es ne Reihe an Ebenen gibt in denen gecached wird (Cache bedeutet eben immer kopieren) ist ganz klar aber ich hab doch ausdrücklich gesagt das ich das mal ignoriere.


Client will 10000bytes aus einer Datei gelesen bekommen, erstellt den Buffer über malloc und der hat die Adresse (die virtuelle bei ihm im AdressSpace) 0x1000200.
Soweit so gut.

Der HDD-Treiber lädt die Datei aber 4KB align in den Speicher, also z.B. 0x4000000.
Warum benutzt der HDD-Treiber ein Aligment? Dafür gibt es doch gar keinen Grund. Und wieso lädt der HDD-Treiber einfach so Daten irgendwo hin? In meinem Konzept soll der Treiber nur dann etwas tun wenn er dazu explizit beauftragt wird und auch den passenden Speicherbereich gestellt bekommt, meine Treiber machen kein malloc für die Nutzdaten. Ließ Dir Bitte noch mal mein Posting (http://forum.lowlevel.eu/index.php?topic=2586.msg28963#msg28963) durch wo ich das mit dem Buffet erklärt habe, dort muss man schon einen Teller mitbringen damit das Personal da was drauf legen kann.

Wenn ich die Daten jetzt mappen wollte
Also was jetzt, mappen oder lesen?
Lesen ist wenn der Client Speicher hat (egal ob per malloc auf dem Heap oder auf dem Stack-Frame oder als normales statisches Array) und möchte das dort irgendwelche Daten rein gelegt werden. Wenn wir mal kurz die Ränder außer Acht lassen dann wird der Speicher für die Dauer des gesamten (aber kurzen) IPC-Vorgangs zusätzlich auch in den passenden Service gemappt, dort befüllt und zum Schluss beim Service wieder freigegeben. Im Client war dieser Speicher die ganze Zeit normal anwesend. Nachdem das fread damit fertig ist kann die Applikation mit dem Speicher bzw. den Daten darin machen was sie will.
Bei richtigem File-Mapping wird eine Datei in den Adressraum der Applikation eingeblendet und der Service registriert sich als eine Art Page-Fault-Handler für diesen Bereich und managed so dass Lesen der Daten von HDD in den Speicher der Applikation wenn zum ersten mal auf eine bestimmte Page zugegriffen wird. Dieser Speicher wird aber eben von einer bestimmten Funktion in einer User-Mode-Library in der Applikation (welche dann eben spezielle Syscalls benutzt) zur Verfügung gestellt und kann daher immer passend ausgerichtet sein. Wenn man eine so gemappte Datei wieder schließen möchte dann muss der Service alle modifizierten Pages auf die HDD zurückschreiben, meldet seinen Page-Fault-Handler ab und danach kann dann der spezielle Speicher in der Applikation wieder freigegeben werden.
Das zweite Szenario hat schon einen deutlich höheren Schwierigkeitsgrad und sollte unabhängig von den Basis-Mechanismen (fread/fwrite) später implementiert werden.

Falls ich Dich völlig missverstanden hab dann formuliere Bitte Deine Fragen etwas deutlicher.


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: kevin am 23. September 2010, 17:20
Ja, Recht hast Du. Aber read hat (im wesentlichen) die selben Parameter wie fread. Es geht mir darum das die Semantik, auf der read und fread aufbauen, sich durchaus mit Zero-Copy umsetzen lässt. Das es ne Reihe an Ebenen gibt in denen gecached wird (Cache bedeutet eben immer kopieren) ist ganz klar aber ich hab doch ausdrücklich gesagt das ich das mal ignoriere.
Es geht mir doch nicht darum, read und fread gegenüberzustellen, sondern fread und native Funktion. FlashBurns native Funktion könnte ja anders aussehen.

Was natürlich nichts dran ändert, dass eine Funktion, die einen neuen Puffer zurückgibt, für viele Sachen nicht so richtig gut geeignet ist...
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 23. September 2010, 18:37
Zitat von: taljeth
FlashBurns native Funktion könnte ja anders aussehen.
Im Moment glaube ich, um mir die Sache einfacher zu machen, würde ich als native Funktionen nur komplette Pages durch die Gegend schieben. D.h. fread müsste sich die Daten dann daraus kopieren in den Buffer des Clienten.
Das ist aber nur eine Idee!

Zitat von: taljeth
Was natürlich nichts dran ändert, dass eine Funktion, die einen neuen Puffer zurückgibt, für viele Sachen nicht so richtig gut geeignet ist...
Ich gehe mal davon aus, das du mehr Erfahrung in der Anwendungsprogrammierung hast als ich (was nicht besonders schwer ist).
Aber warum ist das nicht so gut geeignet?

Zitat von: erik
Warum benutzt der HDD-Treiber ein Aligment? Dafür gibt es doch gar keinen Grund.
Grund wäre das ich noch nie so einen Treiber geschrieben habe ;)

Ich dachte halt das es so abläuft, das der VFS-Service nen "Auftrag" an den HDD-Treiber gibt bestimmte Sektoren zu laden und er bekommt entweder die Pages mit den Sachen zurück oder nen Fehler. So in der Art:
void *loadLBA(uint64t lba, uint32t count)
Das ist halt meine Weise "zu denken".

Zitat von: erik
Lesen ist wenn der Client Speicher hat (egal ob per malloc auf dem Heap oder auf dem Stack-Frame oder als normales statisches Array) und möchte das dort irgendwelche Daten rein gelegt werden. Wenn wir mal kurz die Ränder außer Acht lassen dann wird der Speicher für die Dauer des gesamten (aber kurzen) IPC-Vorgangs zusätzlich auch in den passenden Service gemappt, dort befüllt und zum Schluss beim Service wieder freigegeben. Im Client war dieser Speicher die ganze Zeit normal anwesend. Nachdem das fread damit fertig ist kann die Applikation mit dem Speicher bzw. den Daten darin machen was sie will.
Klingt erstmal super!

Mein Problem damit wäre, wenn wir die Ränder mal nicht außer acht lassen, dann würde ich das per SharedMemory in den Treiber mappen. Also könnte der Treiber Daten aus dem Client lesen (nagut er könnte sie so oder so überschreiben, also wäre das egal, obwohl wenn da Passwörter stehen und das der Ethernet-Treiber wäre?). Ich habe einfach ein Problem mit dem Mappen des Buffers des Clienten in einen Treiber ohne das der Treiber die Daten des Clienten lesen kann und ohne das ich mir was anderes als SharedMemory dafür einfallen lasse.

Zitat von: erik
Bei richtigem File-Mapping wird eine Datei in den Adressraum der Applikation eingeblendet und der Service registriert sich als eine Art Page-Fault-Handler für diesen Bereich und managed so dass Lesen der Daten von HDD in den Speicher der Applikation wenn zum ersten mal auf eine bestimmte Page zugegriffen wird. Dieser Speicher wird aber eben von einer bestimmten Funktion in einer User-Mode-Library in der Applikation (welche dann eben spezielle Syscalls benutzt) zur Verfügung gestellt und kann daher immer passend ausgerichtet sein. Wenn man eine so gemappte Datei wieder schließen möchte dann muss der Service alle modifizierten Pages auf die HDD zurückschreiben, meldet seinen Page-Fault-Handler ab und danach kann dann der spezielle Speicher in der Applikation wieder freigegeben werden.
Interessant! Ich will/muss mich ja nicht gleich damit beschäftigen, aber ich mache mir gerade Gedanken wie man das mit dem PageFault-Handler löst, aber wie du schon geschrieben hast, dafür benötigt man dann noch Syscalls.
Aber wie machen sowas richtige Mikrokernel die außer IPC nicht viel mehr anbieten?
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 23. September 2010, 18:59
Hallo,


Was natürlich nichts dran ändert, dass eine Funktion, die einen neuen Puffer zurückgibt, für viele Sachen nicht so richtig gut geeignet ist...
Eben!

Natürlich kann FlashBurn native Funktionen nach eigenen Geschmack bauen wie er will aber wenn er wirklich ein read baut das einen Pointer zurück gibt dann weicht er damit so weit vom üblichen Weg ab das er daraus kaum einen Nutzen ziehen kann (wenn er Programme portieren möchte und diese dann sein natives API benutzen sollen würde er große Teile dieser Programme komplett neu schreiben müssen).

Worum es mir eigentlich geht ist klar zu machen warum sein bisheriges IPC-Konzept kein Zero-Copy für die klassische read/fread-Semantik ermöglicht.

Ich könnte jetzt noch schreiben das man sich bei sowas schon möglichst gut an den vorhandenen Wegen orientieren soll aber von jemandem mit meinen Segmentierungsplänen wäre so ein Statement einfach nicht glaubwürdig. ;)


void *loadLBA(uint64t lba, uint32t count)Das ist halt meine Weise "zu denken".
Wo her hast Du diese Denkweise? Das ist nicht so wirklich der übliche Weg für solche Funktionen.

Klingt erstmal super!
Wenn ich Dich weiterhin so gut überzeugen kann dann baust Du am Ende auch noch ein IPC-System das auf PopUp-Threads bassiert. ;)

wenn wir die Ränder mal nicht außer acht lassen
Dann hast Du entweder ein kleines Sicherheitsproblem oder Du musst maximal 2 Pages doch kopieren (aber nur auf der obersten Ebene zur eigentlichen Applikation, alle anderen Ebenen darunter können immer ohne kopieren auskommen, einmal kopieren reicht schließlich).

Aber wie machen sowas richtige Mikrokernel die außer IPC nicht viel mehr anbieten?
Ich denke mal das Abquatschen zwischen Client und Service läuft weiterhin über IPC ab und das Erstellen des virtuellen Speichers beim Client erfordert eben für beide Seiten einen zusätzlichen Syscall, erst muss der Service das vorbereiten und einen Handler registrieren und dann kann der Client, mit einer extra ID/Handle o.ä. (in der Antwort beim IPC-Vorgang enthalten), diesen Speicher einbinden und bekommt einen Pointer in seinen virtuellen Adressraum. Ob das wirklich so funktionieren kann weiß ich nicht, ich hab da noch nicht drüber nachgedacht, echtes File-Mapping ist bei meinem Projekt noch in sehr weiter Ferne.


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: kevin am 23. September 2010, 19:02
Ich gehe mal davon aus, das du mehr Erfahrung in der Anwendungsprogrammierung hast als ich (was nicht besonders schwer ist).
Aber warum ist das nicht so gut geeignet?
Naja, was ist, wenn du nur einen Teil eines (schon vorhandenen) Objekts/Puffers einlesen möchtest?

Aber selbst wenn du neue Objekte haben willst, die aber langlebig sein sollen, wäre es wahrscheinlich ein bisschen übertrieben, jedem davon eine komplette Page zuzuweisen.
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 23. September 2010, 19:26
Zitat von: erik
Natürlich kann FlashBurn native Funktionen nach eigenen Geschmack bauen wie er will aber wenn er wirklich ein read baut das einen Pointer zurück gibt dann weicht er damit so weit vom üblichen Weg ab das er daraus kaum einen Nutzen ziehen kann (wenn er Programme portieren möchte und diese dann sein natives API benutzen sollen würde er große Teile dieser Programme komplett neu schreiben müssen).
Das ist jetzt etwas mit dem ich nicht gerechnet hätte ;)

Ich gehe darauf jetzt mal ein. Sagen wir mal die meisten OpenSource Programme findest du im Linux/Posix Bereich. Dann würde das heißen, das man nur noch Posix kompatible Systeme schreiben sollte und damit kommt es effektiv zu einem Stillstand!

Mein Punkt ist, das wenn man etwas neues/anderes/besseres machen will, muss man leider bestimme Sachen neu schreiben!

Wenn man danach geht, müsste ich mein System auf fork und co aufbauen. Was ich aber nicht mache.

Im Endeffekt implezierst du damit, das gerade Hobby OSs eigentlich nur ein Rewrite von Linux sein könnten.

Zitat von: taljeth
Naja, was ist, wenn du nur einen Teil eines (schon vorhandenen) Objekts/Puffers einlesen möchtest?
Sorry ich versteh die Frage nicht bzw. was du meinst :(

Zitat von: taljeth
Aber selbst wenn du neue Objekte haben willst, die aber langlebig sein sollen, wäre es wahrscheinlich ein bisschen übertrieben, jedem davon eine komplette Page zuzuweisen.
Richtig. Wir waren ja aber insbesondere bei Dateien und dem HDD-Treiber und da finde ich mein Konzept nicht so fehl am Platz.
Bei Netzwerk-Sachen sieht das leider schon wieder anders aus. Da würde ich das Ganze wahrscheinlich durch eine Pipe jagen. Das wäre dann wieder kein zero-copy, sondern 2x Copy, aber gerade beim Netzwerk-Zeugs muss doch eh kopiert werden (um die eigentlichen Daten vom Protokoll zu trennen, oder?).

Zitat von: erik
Wenn ich Dich weiterhin so gut überzeugen kann dann baust Du am Ende auch noch ein IPC-System das auf PopUp-Threads bassiert.
Naja, sowas ähnliches habe ich ja eh geplant ;)

Bei mir würde nicht jede Nachricht nen neuen Thread aufmachen, sondern jeder Port würde durch einen Thread bearbeitet werden (was natürlich kein muss ist).

Zitat von: erik
Dann hast Du entweder ein kleines Sicherheitsproblem oder Du musst maximal 2 Pages doch kopieren (aber nur auf der obersten Ebene zur eigentlichen Applikation, alle anderen Ebenen darunter können immer ohne kopieren auskommen, einmal kopieren reicht schließlich).
Ok, ein Sicherheitsproblem der Größe möchte ich dann nicht.

Sorry, wenn ich jetzt wie ein dummer Newbie klinge, aber wie du dir das mit dem kopieren von max 2 Pages (den "Randpages") vorstellst müsstest du mir mal ganz genau erklären.

Ich könnte mir nur vorstellen, das du, außer bei den beiden Randpages, immer direkt in den Speicher des Clienten schreibst und für die beiden Randpages allokierst du Speicher, schreibst da die Daten rein und lässt die Daten aus diesen beiden Pages dann in die Randpages des Clienten kopieren. Soweit richtig?

Bei mir hapert es jetzt an der Umsetzung, sprich wie man sowas machen könnte.

Zitat von: erik
Ich denke mal das Abquatschen zwischen Client und Service läuft weiterhin über IPC ab und das Erstellen des virtuellen Speichers beim Client erfordert eben für beide Seiten einen zusätzlichen Syscall, erst muss der Service das vorbereiten und einen Handler registrieren und dann kann der Client, mit einer extra ID/Handle o.ä. (in der Antwort beim IPC-Vorgang enthalten), diesen Speicher einbinden und bekommt einen Pointer in seinen virtuellen Adressraum.
Mein Lieblingsbsp. ist hier der L4. Der bietet wirklich außer IPC und Thread-Management nicht viel mehr an (soweit ich es verstanden habe, bzw. habe ich nichts dazu gefunden). D.h. auch kein Mapping von Speicher in andere Prozesse, was er anbietet ist das "Versenden" von ganzen Pages als Nachrichten.
Eventuell kommt auch genau daher mein denken, du als Treiber versendest die Daten (daher bekommt man einen Pointer zurück).
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 23. September 2010, 21:53
Hallo,


Das ist jetzt etwas mit dem ich nicht gerechnet hätte ;)
Dann ist ja gut das wir das rechtzeitig angesprochen haben. ;)

Mein Punkt ist, das wenn man etwas neues/anderes/besseres machen will, muss man leider bestimme Sachen neu schreiben!
Ganz recht! Das wird mich auch erwarten, wobei ich hoffe mit Compiler/Linker/Assembler wenigstens einen Großteil dieser Arbeit erledigt zu haben.

Wenn man danach geht, müsste ich mein System auf fork und co aufbauen. Was ich aber nicht mache.
So etwas wie fork könnte ich mit meinen Segmenten gar nicht so einfach realisieren. In so fern wird auch mein OS sicher nicht viel mit POSIX zu tun haben, ich möchte die Kompatibilität auf der libc-Ebene erreichen und bin der Meinung das ich damit gute Chancen habe.

Im Endeffekt implezierst du damit, das gerade Hobby OSs eigentlich nur ein Rewrite von Linux sein könnten.
Also so habe ich das wirklich nicht gemeint, ganz ehrlich. Aber wenn ich mir mein Statement noch mal durchlese dann hast Du nicht so ganz unrecht. Ich denke ein eigenes OS zu schreiben bedeutet immer einen gewissen Grad an Inkompatibilität zu akzeptieren und auch dafür etwas zu programmieren. Die Frage ist nur wie weit man das treiben möchte, also mit wie vielen der überlieferten Traditionen man brechen möchte

Zitat von: taljeth
Aber selbst wenn du neue Objekte haben willst, die aber langlebig sein sollen, wäre es wahrscheinlich ein bisschen übertrieben, jedem davon eine komplette Page zuzuweisen.
Richtig. Wir waren ja aber insbesondere bei Dateien und dem HDD-Treiber und da finde ich mein Konzept nicht so fehl am Platz.
Hm, also da muss ich taljeth aber doch zustimmen. File-I/O nur in ganzen Pages machen zu können dürfte schon ziemlich ungünstig werden. Mal abgesehen davon das Du damit die Möglichkeit verbaust größere Pages benutzen zu können und mit steigendem Speicherbedarf der Programme wird das auch ein steigendes Performance-Problem.

aber gerade beim Netzwerk-Zeugs muss doch eh kopiert werden (um die eigentlichen Daten vom Protokoll zu trennen, oder?).
Ach Quatsch, auch bei Netzwerk muss man nicht immer kopieren, zumindest nicht beim Senden (beim Empfangen wird man schon immer kopieren müssen weil man ja nicht vorher weiß wie groß ein Ethernet-Frame ist oder für welche Applikation es bestimmt ist). Beim Abschicken von einigen kBytes bis etlichen MBytes mit einem einzelnen send-Aufruf könnte der TCP-Treiber den einen Datenblock als ein Array vieler gleich großer Häppchen betrachten und zu jedem Häppchen in einem weiteren Array je einen TCP-Header anlegen, der IP-Treiber macht das selbe und der Ethernet-Treiber auch so das zum Schluss pro Ethernet-Frame 4 Häppchen aus 4 verschiedenen Arrays vom Ethernet-Controller zusammengesammelt werden. Dank dem tollen Scatter/Gather ist das gar kein Problem und wenn der Ethernet-Controller beim Senden auch noch selbstständig alle Prüfsummen (im IP-Header und im TCP-Header) mit einfügen kann dann muss keine der SW-Schichten die Nutzdaten auch nur anfassen. Das ganze erfordert zwar ein klein wenig mehr an Verwaltungsaufwand aber da der TCP/IP-Stack keine Prüfsummen mehr berechnen muss dürfte das unterm Strich immer noch eine deutliche Reduktion bei der CPU-Belastung ergeben (viele der besseren Ethernet-Controller können dieses TCP-Offloading heutzutage und das bringt wirklich eine spürbare CPU-Entlastung).

Zitat von: erik
Wenn ich Dich weiterhin so gut überzeugen kann dann baust Du am Ende auch noch ein IPC-System das auf PopUp-Threads bassiert.
Naja, sowas ähnliches habe ich ja eh geplant ;)

Bei mir würde nicht jede Nachricht nen neuen Thread aufmachen, sondern jeder Port würde durch einen Thread bearbeitet werden (was natürlich kein muss ist).
Also gerade ähnlich sind unsere Konzept nun wirklich nicht. Da möchte ich noch mal die Metapher mit der Post benutzen: In Deinem Konzept muss jemand zur Post hingehen um ein Päckchen abzuholen und in meinem Konzept kommt ein Postbote zu mir und bringt das Päckchen. Der Nachteil in Deinem Konzept ist das Du jede Menge Threads brauchst um möglichst jede Message möglichst schnell abholen zu können (Du schickst also eine Menge Leute zur Post damit bei jeder Zustellung immer möglichst sofort einer "hier" schreiben kann wenn es heißt "Päckchen für XYZ") wogegen bei mir vom Kernel immer dann ein frischer Thread erstellt wird wenn es tatsächlich gilt eine Message zuzustellen (die Post erstellt genau so viele Postboten wie auch wirklich Päckchen unterwegs sind). Wenn Du versuchst mit weniger Threads auszukommen wird einiges an Messages erst mal gequeued werden müssen (es schreit also nicht immer jemand sofort "hier" wenn ein Päckchen kommt und in der Post muss zwischengelagert werden). Im Extremfall könnte es Dir passieren das Du pro Service 3 Leute (eigentlich willst Du ja weniger als 1 Person pro Service was zu einer spürbaren Unterbesetzung führen kann) zur Post schickst aber der Service XYZ momentan so beliebt ist das die 3 Leute es nicht mal annähernd schaffen die vielen Päckchen zu verarbeiten und zu allem Übel sitzen für den Service ABC 3 Leute gelangweilt in der Post rum ohne wirklich arbeiten zu müssen. Für die Services LMO, UVW und MFG sitzen auch noch nutzlose Leute in der Post die zwar ab und an mal was machen dürfen aber ansonsten nur Platz kosten. Ich hoffe meine Metapher war diesmal deutlich genug.

Ok, ein Sicherheitsproblem der Größe möchte ich dann nicht.
Also 2 Pages kopieren.

Sorry, wenn ich jetzt wie ein dummer Newbie klinge, aber wie du dir das mit dem kopieren von max 2 Pages (den "Randpages") vorstellst müsstest du mir mal ganz genau erklären.
Zu bestimmen ob die erste Page kopiert werden muss ist ganz einfach indem man prüft ob der Pointer page-aligned ist. Bei der letzten Page muss man sich eben ausrechnen wo die Message zu Ende ist und dort auch prüfen ob das Ende genau auf eine Page-Granze fällt. Der einzigste Sonderfall ist wenn die Message nur eine oder zwei Pages groß ist.

Mein Lieblingsbsp. ist hier der L4. Der bietet wirklich außer IPC und Thread-Management nicht viel mehr an (soweit ich es verstanden habe, bzw. habe ich nichts dazu gefunden). D.h. auch kein Mapping von Speicher in andere Prozesse, was er anbietet ist das "Versenden" von ganzen Pages als Nachrichten.
Der L4 ist sicher ein schönes akademisches Beispiel aber wirklich tauglich sind meiner persönlichen Meinung nach nicht alle der dort verwendeten Konzepte. Auch ich hab mir vieles über den L4 durchgelesen aber mehr als ein gute Inspirationsquelle ist er für mich nicht, schon allein deshalb weil ich Segmente mit beliebiger Größe vererben kann und nicht auf starre Page-Größen festgenagelt bin.


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: kevin am 23. September 2010, 22:52
So etwas wie fork könnte ich mit meinen Segmenten gar nicht so einfach realisieren. In so fern wird auch mein OS sicher nicht viel mit POSIX zu tun haben, ich möchte die Kompatibilität auf der libc-Ebene erreichen und bin der Meinung das ich damit gute Chancen habe.
Es tut mir ja leid, dir diese schlechte Nachricht zum wiederholten Male überbringen zu müssen, aber fork() ist der libc und wird von POSIX-Programmen fleißig benutzt. ;)

Zitat
Zitat von: taljeth
Aber selbst wenn du neue Objekte haben willst, die aber langlebig sein sollen, wäre es wahrscheinlich ein bisschen übertrieben, jedem davon eine komplette Page zuzuweisen.
Richtig. Wir waren ja aber insbesondere bei Dateien und dem HDD-Treiber und da finde ich mein Konzept nicht so fehl am Platz.
Hm, also da muss ich taljeth aber doch zustimmen. File-I/O nur in ganzen Pages machen zu können dürfte schon ziemlich ungünstig werden. Mal abgesehen davon das Du damit die Möglichkeit verbaust größere Pages benutzen zu können und mit steigendem Speicherbedarf der Programme wird das auch ein steigendes Performance-Problem.
Wir sollten vielleicht auch erstmal klarstellen über welche Schnittstelle wir überhaupt reden. Die Schnittstelle zwischen Blockgerät und Dateisystem kann ohne weiteres Page-Alignment erfordern, denke ich. Nur die Schnittstelle zum Anwenderprogramm sollte halt etwas flexibler sein.
Titel: Re:Shared Memory
Beitrag von: Svenska am 24. September 2010, 02:41
Naja, es gibt neben fork() ja auch vfork(), was in Eriks Fall wohl einfacher implementierbar sein dürfte (=> µCs ohne MMU haben vfork() und kein fork() ). Oft (fork()/exec() ) sollte man statt fork() nur vfork() benutzen und teilweise wird das auch schon getan. Und eine Möglichkeit der Prozesserstellung braucht man sowieso: Ob man da fork() oder eher CreateProcess() implementiert, bleibt jedem selbst überlassen.

Was die ganze Zero-Copy-Geschichte angeht: Man kann auch einfach den Kopf in den Sand stecken und immer kopieren, wenn der Speicher nicht ausgerichtet ist. Vergleichsweise einfach zu implementieren und - wenn die Anwendung selbst darauf achtet, ausgerichteten Speicher zu verwenden - auch schnell.

Im einfachsten Fall gibt malloc() nur ausgerichteten Speicher raus und wenn die Anwendung dann zig MB von Platte da reinlesen will, ist das schnell. Wenn Anwendung aber erstmal ein Byte Kopfdaten einfügt - Pech gehabt.

Später, wenns läuft, kann man sich immernoch eine Platte über die Randfälle machen. Ich seh da zumindest noch nicht so recht durch, wie man das lösen könnte. Und es ist spätfrüh.

Gruß,
Sebastian
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 24. September 2010, 09:36
Zitat von: erik
Hm, also da muss ich taljeth aber doch zustimmen. File-I/O nur in ganzen Pages machen zu können dürfte schon ziemlich ungünstig werden. Mal abgesehen davon das Du damit die Möglichkeit verbaust größere Pages benutzen zu können und mit steigendem Speicherbedarf der Programme wird das auch ein steigendes Performance-Problem.
Da habe ich mich dann vielleicht ein wenig unglücklich ausgedrückt, ich meine natürlich ein vielfache von 4KB Pages, das würde dann auch große Pages wie 4MB Pages einschließen (die ich ohne hin unterstützen möchte).

Zu der Netzwerk-Geschichte. Ich seh schon, ich sollte erstmal einfach weiter mein Konzept umsetzen bzw. einfach mal anfangen ein paar wichtige Treiber (PCI-Busmanager, IDE, SATA) schreiben, damit ich sowas wie Scatter/Gather kennenlerne. Eventuell überdenke ich dann mein Konzept nochmal bzw. entwickle ein neues.
Problem bei solchen Sachen ist halt, wenn man es nicht kennt kann man es auch nicht mit einplanen.
Die meisten Probleme und Schwierigkeiten fallen einem eh erst auf wenn man es dann versucht zu implementieren oder es benutzen muss.

Zu deinen Popup-Threads. Wenn ich dich da richtig verstanden habe, wird für jede Nachricht ein neuer Thread erstellt? Das ist etwas was ich eigentlich nicht möchte, denn das läuft ja wieder auf ähnliche Probleme wie mit dem Signalhandler hinaus.
Bei meinem Konzept werden verdammt viele Threads blockieren, aber das ist ja gewollt. Ich will nicht jedes Mal nen neuen Thread erstellen (weil soviel Speicher frisst der dann auch nicht) und außerdem will ich es auf einen Thread pro Port und nicht pro Nachricht beschränken.
Bei mir werden auch verdammt viele Ports benutzt werden. Damit will ich dann das parallele Arbeiten erreichen.

Zitat von: erik
Zu bestimmen ob die erste Page kopiert werden muss ist ganz einfach indem man prüft ob der Pointer page-aligned ist. Bei der letzten Page muss man sich eben ausrechnen wo die Message zu Ende ist und dort auch prüfen ob das Ende genau auf eine Page-Granze fällt. Der einzigste Sonderfall ist wenn die Message nur eine oder zwei Pages groß ist.
Ich meinte da eigentlich was anderes. Ich bin in meinem Kopf so beschränkt ;) das ich halt Probleme habe mir vorzustellen wie man es implementiert.
Sprich man würde ne Liste mit den Pages haben, die man mappen möchte und dann noch zusätzlich ne Datenstruktur wo drin steht wo was kopiert werden muss und wo man den Buffer dafür findet.

Zitat von: erik
Der L4 ist sicher ein schönes akademisches Beispiel aber wirklich tauglich sind meiner persönlichen Meinung nach nicht alle der dort verwendeten Konzepte. Auch ich hab mir vieles über den L4 durchgelesen aber mehr als ein gute Inspirationsquelle ist er für mich nicht, schon allein deshalb weil ich Segmente mit beliebiger Größe vererben kann und nicht auf starre Page-Größen festgenagelt bin.
So mikro wie der L4 will ich meinen Kernel ja auch nicht machen, aber ist halt nen gutes Bsp. wie ein "richtiger" Mikrokernel aussehen kann.
Titel: Re:Shared Memory
Beitrag von: kevin am 24. September 2010, 10:27
Naja, es gibt neben fork() ja auch vfork(), was in Eriks Fall wohl einfacher implementierbar sein dürfte (=> µCs ohne MMU haben vfork() und kein fork() ). Oft (fork()/exec() ) sollte man statt fork() nur vfork() benutzen und teilweise wird das auch schon getan. Und eine Möglichkeit der Prozesserstellung braucht man sowieso: Ob man da fork() oder eher CreateProcess() implementiert, bleibt jedem selbst überlassen.
Wenn du ein CreateProcess() und kein fork() hast, dann ist das für native Programme schön und gut. Aber wenn du POSIX-Programme portieren möchtest (was erik wohl vorhat), dann bringt dir das alles nicht, denn die benutzen eben fork(). Und vfork() deckt davon nur die uninteressantesten Fälle ab, die eh kaum vorkommen. Das bedeutet, du musst beim Portieren jeden einzelnen Aufruf von fork() patchen.

Noch dazu sagt meine Manpage vfork(3p):
Zitat
Conforming applications are recommended not to depend on vfork(), but to use fork() instead. The vfork() function may be withdrawn in a future version.
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 24. September 2010, 10:59
Zitat von: taljeth
Das bedeutet, du musst beim Portieren jeden einzelnen Aufruf von fork() patchen.
Ist das nicht genau das was ihr bei euch mit dem gcc gemacht habt oder war das was anderes?
Titel: Re:Shared Memory
Beitrag von: kevin am 24. September 2010, 11:41
gcc benutzt intern nochmal eine Abstraktion, die auch mit was CreateProcess-artigem recht gut funktioniert, insofern hat sich der Aufwand dort in Grenzen gehalten.
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 24. September 2010, 11:52
Hallo,


Es tut mir ja leid, dir diese schlechte Nachricht zum wiederholten Male überbringen zu müssen, aber fork() ist der libc und wird von POSIX-Programmen fleißig benutzt. ;)
Ich weiß das fork/exec-Pärchen kann ich definitiv nicht anbieten, da werde ich grundsätzlich Portierungsarbeit leisten müssen. Ich gehe aber davon aus das fork/exec überwiegend wie CreateProcess benutzt wird und zwischen fork und exec geregelt wird welche Ressourcen alles an den neuen Prozess vererbt werden sollen bzw. spezielle Pipes usw. erst erstellt werden. Ich werde zwar diesen Code-Abschnitt gegen was eigenes ersetzen müssen aber die gewünschte Funktionalität werde ich wohl überwiegend mit meiner libc bieten können.
Auch clone oder vfork werde ich nicht anbieten können, das widerspricht einfach meiner Speicher-Management-Philosophie. Aber z.B. pthread möchte ich anbieten und hoffe einfach das möglichst viele Multi-Threading-Programme das auch benutzen.

Wir sollten vielleicht auch erstmal klarstellen über welche Schnittstelle wir überhaupt reden. Die Schnittstelle zwischen Blockgerät und Dateisystem kann ohne weiteres Page-Alignment erfordern, denke ich. Nur die Schnittstelle zum Anwenderprogramm sollte halt etwas flexibler sein.
Klar sollten die tiefer liegenden Schichten ruhig mit Page-Aligmend arbeiten aber das der Speicher von unten nach oben gereicht wird (also als Rückgabewert von read) ist IMHO trotzdem recht ungeschickt.


Wenn ich dich da richtig verstanden habe, wird für jede Nachricht ein neuer Thread erstellt?
Genau, ich will das genau so viele Threads dafür da sind wie auch wirklich benötigt werden (nicht mehr und nicht weniger) und das die Threads auch genau dort sind wo sie benötigt werden.

Das ist etwas was ich eigentlich nicht möchte, denn das läuft ja wieder auf ähnliche Probleme wie mit dem Signalhandler hinaus.
Welche Probleme?

Bei meinem Konzept werden verdammt viele Threads blockieren, aber das ist ja gewollt. Ich will nicht jedes Mal nen neuen Thread erstellen (weil soviel Speicher frisst der dann auch nicht)
Wie viel Speicher das kostet kannst nur Du sagen aber für mein OS möchte ich nicht das unmengen an schlafenden Threads rumgammeln.

und außerdem will ich es auf einen Thread pro Port und nicht pro Nachricht beschränken.
Das beutet das Deine Services nicht sehr belastbar sind. Was ist wenn 5 Programme gleichzeitig aus Dateien lesen möchten? Wenn die alle im Cache im VFS wären könnte das alles parallel passieren. Stell Dir mal einen Compiler-Lauf auf einem Multi-Core-System vor, jede Compiler-Instanz möchte eine Quell-Code-Datei und einige Header-Dateien lesen (gerade die Header-Dateien dürften recht schnell im Cache liegen). Es wäre schade wenn die Compiler-Instanzen quasi serialisiert würden nur weil sie nicht parallel an die Dateien im VFS-Cache kommen.

Bei mir werden auch verdammt viele Ports benutzt werden. Damit will ich dann das parallele Arbeiten erreichen.
Eine Art Load-Balancing? Hm, das stelle ich mir ziemlich schwierig vor. Wenn VFS z.B. 8 gleichwertige Ports anbietet wie soll der Client entscheiden welchen er nimmt?


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 24. September 2010, 12:27
Zitat von: erik
Das beutet das Deine Services nicht sehr belastbar sind. Was ist wenn 5 Programme gleichzeitig aus Dateien lesen möchten? Wenn die alle im Cache im VFS wären könnte das alles parallel passieren. Stell Dir mal einen Compiler-Lauf auf einem Multi-Core-System vor, jede Compiler-Instanz möchte eine Quell-Code-Datei und einige Header-Dateien lesen (gerade die Header-Dateien dürften recht schnell im Cache liegen). Es wäre schade wenn die Compiler-Instanzen quasi serialisiert würden nur weil sie nicht parallel an die Dateien im VFS-Cache kommen.
Ich hatte das glaub ich in dem anderen Thread schonmal erklärt.

Ich habe einen allgemein bekannten Port (bzw. kann man ihn halt rausfinden) wo du dich als Client registrierst. Dann öffnet der Service nen neuen Port und über diesen wird dann kommuniziert.
Damit habe ich dann einen Port pro Prozess/Thread (je nach dem ob es sich lohnt einen Port pro Thread zu machen) und die Anfragen an diese Ports sollen serialisiert werden.
Was bei meinem Konzept eventuell nach hinten losgehen kann, sind die vielen Stacks die dadurch entstehen, aber wenn ich das richtig umsetze (und der Code, der im UserSpace ausgeführt wird, nicht alzu verschwenderisch ist) läuft das auf 8KB (4KB Kernel- + 4KB UserStack) hinaus.

Was ich mir bei deinem Konzept eher unglücklich vorstelle ist, dass du mehrere Nachrichten von ein und dem selben Prozess bekommst und alle bis auf einer deiner Popup-Threads müssen dann blockieren (da sie alle auf irgendeine gleiche Datenstruktur zugreifen). Das ist erstmal nicht so schlimm, aber wenn sie eh blockieren dann kann man alle Anfragen eines Prozesses/Threads doch gleich serialisieren und spart sich so die unnötigen Context-Switches.
Wimre wolltest du doch deine Popup-Threads so implementieren, das praktisch eine bestimmte Anzahl an Threads schon fix und fertig irgendwo rumliegt, mitsamt Stacks!? Wo ist da dann der Unterschied zu meinen blockierten Threads?

Was bei dir natürlich passieren kann ist, dass du mehrere Anfragen eines Prozesses bekommst und diese können sogar parallel abgearbeitet werden und dann vielleicht noch auf mehreren CPUs/Cores dann wird das natürlich schneller gehen als bei mir.
Aber ich will halt abwägen wo es Sinn macht einen Port pro Thread zu haben (z.B. VFS-Service) und wo es mehr Sinn macht nur einen Port pro Prozess zu haben (z.B. App-Server). Damit sollte ich auch eine gute Serilisation hinbekommen und könnte sogar sehr vernünftig C++ einsetzen.

Zitat von: erik
Wie viel Speicher das kostet kannst nur Du sagen aber für mein OS möchte ich nicht das unmengen an schlafenden Threads rumgammeln.
Seien wir mal ehrlich was wird denn dein OS die meiste Zeit machen, idlen oder? Es ist eigentlich normal das fast alle Threads/Prozesse blockieren und so gut wie keiner arbeitet.
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 24. September 2010, 21:27
Hallo,


gcc benutzt intern nochmal eine Abstraktion, die auch mit was CreateProcess-artigem recht gut funktioniert, insofern hat sich der Aufwand dort in Grenzen gehalten.
Ich denke mal das trifft auf die meisten Programme zu die wollen das man sie portieren kann.
Leider muss ich vermuten das die Core-Utils nicht in diese Kategorie gehören. :(


Dann öffnet der Service nen neuen Port und über diesen wird dann kommuniziert.
Also Du möchtest für jeden Client in Deinem Service einen extra Port aufmachen und auch einen extra Thread starten, verstehe ich das richtig? Bei welcher Aktion des Clients wird ein neuer Port im Service geöffnet? Z.B. bei jedem open? Ich bin mir nicht ganz sicher aber ich kann mir gut Vorstellen das Deine Services vor lauter Threads und Ports geradezu überquellen. Wie viel Speicher kostet eigentlich so ein Port (Verwaltungsstrukturen usw.) und wie aufwendig sind die Syscalls zum erstellen bzw. löschen eines Ports? Meine Services sollen genau einen Port (bei mir nenne ich das Message-Target) haben oder auch 2 wenn synchrones RPC und asynchrones IPC angeboten werden soll. Das wird IMHO völlig reichen.

Damit habe ich dann einen Port pro Prozess/Thread (je nach dem ob es sich lohnt einen Port pro Thread zu machen) und die Anfragen an diese Ports sollen serialisiert werden.
Das würde bedeuten wenn ein Web-Server mit vielen Threads arbeitet um möglichst viele Verbindungen parallel zu betreiben würden in Deinem TCP-Service auch ne Menge an Ports und Threads existieren, selbst wenn die Daten nur so dahin tröpfeln, verstehe ich das richtig?

Was bei meinem Konzept eventuell nach hinten losgehen kann, sind die vielen Stacks die dadurch entstehen, aber wenn ich das richtig umsetze (und der Code, der im UserSpace ausgeführt wird, nicht alzu verschwenderisch ist) läuft das auf 8KB (4KB Kernel- + 4KB UserStack) hinaus.
Erstmal besteht ein Thread aus mehr als nur seinem Stack, da gibt es noch Verwaltungsstrukturen im Kernel und Listen für den Scheduler und sicher noch einiges anderes. Zum anderen, wozu brauchst Du pro (User-Mode-)Thread einen eigenen Kernel-Stack? In meinem System wollte ich einen Kernel-Stack pro CPU, wird das bei x86 irgendwie anders gemanaged?

Was ich mir bei deinem Konzept eher unglücklich vorstelle ist, dass du mehrere Nachrichten von ein und dem selben Prozess bekommst und alle bis auf einer deiner Popup-Threads müssen dann blockieren (da sie alle auf irgendeine gleiche Datenstruktur zugreifen).
Auf was für eine gleiche Datenstruktur sollten die Threads in einem Service gleichzeitig (und vor allem für die ganze Verarbeitungszeit) zugreifen? Wenn wir mal vom VFS ausgehen der 10 gleichzeitige Anfragen zum Lesen von Dateien bekommt und 8 davon aus dem Cache bedient werden können dann geschieht etwa folgendes: Alle PopUp-Threads prüfen gleichzeitig ob die Daten im Cache vorrätig sind und müssen dazu in eine spezielle Semaphore (wo zwar beliebig viele Reader gleichzeitig aber immer nur ein Writer alleine rein darf) und das können alle parallel tun ohne sich genenseitig zu behindern (sind ja alles Reader), auch das kopieren der Nutzdaten aus dem Cache kann von den 8 Threads parallel getan werden (es findet ja keine gegenseitige Beeinflussung statt). Die 2 Threads die doch den Dateisystemtreiber bemühen müssen tun das auch erst mal parallel und im Dateisystemtreiber sehe ich ebenfalls keine Notwendigkeit das ein Thread den anderen völlig blockiert. Sicher muss man mal kurze Code-Abschnitte serialisieren aber das sind dann wirklich nur sehr kleine Teilstücke.

Wimre wolltest du doch deine Popup-Threads so implementieren, das praktisch eine bestimmte Anzahl an Threads schon fix und fertig irgendwo rumliegt, mitsamt Stacks!? Wo ist da dann der Unterschied zu meinen blockierten Threads?
Ja, ich möchte da einen Pool haben wo die Stacks und Verwaltungsstrukturen erstmal auf Vorrat liegen. Der Unterschied ist das ich diesen Vorrat für jeden Service benutzen kann der gerade Aufträge bekommt und das nicht starr festgelegt ist. Auch in meinem Konzept wird es eine Menge Threads geben aber die Threads sind dort wo auch Arbeit verrichtet wird und gammeln nicht in irgendwelchen blockierenden Syscalls rum. Was mich an Deinem Konzept stört ist das Du für jeden Service eine Reihe an Threads auf Vorrat erstellst ohne dabei berücksichtigen zu können wie viel Arbeit für die einzelnen Services momentan tatsächlich anfällt. Bei einem gerade sehr beliebten Service (wenn z.B. viele Compiler-Instanzen gleichzeitig ne Menge Dateien einlesen wollen) könnte sogar ein großzügig bemessener Thread-Vorrat im VFS-Service noch zu knapp sein und in anderen Situationen liegen die vielen Threads nur nutzlos rum.

Was bei dir natürlich passieren kann ist, dass du mehrere Anfragen eines Prozesses bekommst und diese können sogar parallel abgearbeitet werden und dann vielleicht noch auf mehreren CPUs/Cores dann wird das natürlich schneller gehen als bei mir.
Genau das ist meine Absicht. :-D In dem ich vom Kernel genau dort die Threads dynamisch erstellen lasse wo sie auch tatsächlich benötigt werden kann ich mit einem Minimum an Threads ein Maximum an Arbeit erledigen.

Seien wir mal ehrlich was wird denn dein OS die meiste Zeit machen, idlen oder? Es ist eigentlich normal das fast alle Threads/Prozesse blockieren und so gut wie keiner arbeitet.
So ist es. Und genau deshalb will ich dort nicht auch noch unnötig viele Threads zusätzlich schlafen lassen, das ergibt für mich keinen Sinn.


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 24. September 2010, 23:18
Zitat von: erik
Also Du möchtest für jeden Client in Deinem Service einen extra Port aufmachen und auch einen extra Thread starten, verstehe ich das richtig? Bei welcher Aktion des Clients wird ein neuer Port im Service geöffnet? Z.B. bei jedem open? Ich bin mir nicht ganz sicher aber ich kann mir gut Vorstellen das Deine Services vor lauter Threads und Ports geradezu überquellen. Wie viel Speicher kostet eigentlich so ein Port (Verwaltungsstrukturen usw.) und wie aufwendig sind die Syscalls zum erstellen bzw. löschen eines Ports? Meine Services sollen genau einen Port (bei mir nenne ich das Message-Target) haben oder auch 2 wenn synchrones RPC und asynchrones IPC angeboten werden soll. Das wird IMHO völlig reichen.
Ein Port für jedes Open wäre ja quatsch, weil solange wie ich kein A-I/O habe kann ein Thread sowieso nur eine Datei gleichzeitig lesen/schreiben.
Der Port wird nur erstellt wenn er auch wirklich benötigt wird, sprich wenn eine Anwendung keine Dateien öffnet dann wird auch kein Port erstellt oder wenn sie nur aus einem Thread besteht (was ja leider noch auf die meisten Anwendungen zutrifft) dann wird nur ein Port geöffnet.
Ich würde dir gerne sagen wieviel Speicher so eine Portstruktur bei mir braucht, da mir aber vorhin mein Laptop-Netzteil abgeraucht ist und ich jetzt an dem Laptop meiner Freundin sitze kann ich dir das leider nicht genau sagen, aber es ist auf jeden Fall weniger als bei nem Thread ;)

Zitat von: erik
Das würde bedeuten wenn ein Web-Server mit vielen Threads arbeitet um möglichst viele Verbindungen parallel zu betreiben würden in Deinem TCP-Service auch ne Menge an Ports und Threads existieren, selbst wenn die Daten nur so dahin tröpfeln, verstehe ich das richtig?
Ich weiß gar nicht wie sowas unter anderen OSs gemacht wird, aber der Web-Server ansich macht ja für jeden Clienten einen neuen Thread auf (wo sich mir gerade die Frage stellt wie das überhaupt funktioniert, ich erinnere mich da an einen Port (TCP-Port) pro Client, aber das kann nicht stimmen) und je nachdem über wieviele TCP-Ports das dann läuft, so viele Threads wären dann auch im Service.

Zitat von: erik
Erstmal besteht ein Thread aus mehr als nur seinem Stack, da gibt es noch Verwaltungsstrukturen im Kernel und Listen für den Scheduler und sicher noch einiges anderes. Zum anderen, wozu brauchst Du pro (User-Mode-)Thread einen eigenen Kernel-Stack? In meinem System wollte ich einen Kernel-Stack pro CPU, wird das bei x86 irgendwie anders gemanaged?
Mit der x86-Architektur hast du dich noch nicht so viel beschäftigt oder?

Jedes Mal wenn du einen Aufruf in den Kernel machst, brauchst du nen Kernel-Stack. Wie willst du es nun bewerkstelligen wenn ein IRQ deinen Scheduler aufruft (was auch alles noch über diesen Stack läuft) und ein anderer Thread zum Zug kommt? Du musst die Daten die auf dem Stack liegen ja behalten und da reicht dann auch nicht ein Stack pro CPU.

Zitat von: erik
Auf was für eine gleiche Datenstruktur sollten die Threads in einem Service gleichzeitig (und vor allem für die ganze Verarbeitungszeit) zugreifen?
Erwischt ;)

Da habe ich doch tatsächlich (in meinen Gedanken) die ganzen Verwaltungsdaten (was halt in seiner FILE struktur drinsteckt) mit in den Service gepackt. Aber spätestens beim HDD-Treiber machen die Popup-Threads dann wirklich keinen Sinn mehr. Denn was bringen dir viele Threads die von ein und der selben HDD lesen wollen (und wir lassen SATA job-queueing mal außen vor)? Die müssten dann ja blockieren, da die HDD gerade von einem anderen Thread benutzt wird. Wo sich mir dann auch die Frage stellt, willst du eigentlich einem Programm auch die Möglichkeit geben, das keine Popup-Threads erstellt werden, sondern das alles nur über einen Thread läuft (der nicht erstellt werden muss)?

Zitat von: erik
Ja, ich möchte da einen Pool haben wo die Stacks und Verwaltungsstrukturen erstmal auf Vorrat liegen. Der Unterschied ist das ich diesen Vorrat für jeden Service benutzen kann der gerade Aufträge bekommt und das nicht starr festgelegt ist. Auch in meinem Konzept wird es eine Menge Threads geben aber die Threads sind dort wo auch Arbeit verrichtet wird und gammeln nicht in irgendwelchen blockierenden Syscalls rum.
Ok, aber nach welchen Kriterium entscheidest du wieviele Threads in diesen Pool kommen?

Bei dir sehe ich dann, wie gesagt, das Problem das du entweder viel Zeit mit der Thread Erstellung verbringst (was passiert eigentlich wenn die Nachricht abgearbeitet ist bzw. muss der User den Thread selbst beenden und wo kommt er dann hin?) oder du halt viel zu viele Threads hast, die nichts machen und sinnlos im Pool rumliegen ;) Damit hättest du dann das selbe Problem wie ich.
Titel: Re:Shared Memory
Beitrag von: kevin am 24. September 2010, 23:31
Mit der x86-Architektur hast du dich noch nicht so viel beschäftigt oder?

Jedes Mal wenn du einen Aufruf in den Kernel machst, brauchst du nen Kernel-Stack. Wie willst du es nun bewerkstelligen wenn ein IRQ deinen Scheduler aufruft (was auch alles noch über diesen Stack läuft) und ein anderer Thread zum Zug kommt? Du musst die Daten die auf dem Stack liegen ja behalten und da reicht dann auch nicht ein Stack pro CPU.
Du könntest theoretisch auch nur einen Kernelstack pro CPU benutzen und den Zustand woanders hin wegkopieren.
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 24. September 2010, 23:53
Zitat von: taljeth
Du könntest theoretisch auch nur einen Kernelstack pro CPU benutzen und den Zustand woanders hin wegkopieren.
Dazu sag ich erstmal zero-copy ;) und was unterscheidet den Speicher (den du so oder so benötigst und nur der Kernel draufzugreifen darf) wo du die Daten hinkopierst von einem Stack?
Titel: Re:Shared Memory
Beitrag von: kevin am 24. September 2010, 23:59
Du könntest ihn knapper bemessen, weil es nicht gleichzeitig noch der Stack ist, auf dem der Kernel arbeiten und z.B. seine Syscalls ausführen muss.

Zero-copy ist ein Taskwechsel eh nicht, ob du die Register jetzt auf den Stack oder woanders hin sicherst. ;)
Titel: Re:Shared Memory
Beitrag von: Svenska am 25. September 2010, 02:50
Hallo,

Zitat von: erik
Das würde bedeuten wenn ein Web-Server mit vielen Threads arbeitet um möglichst viele Verbindungen parallel zu betreiben würden in Deinem TCP-Service auch ne Menge an Ports und Threads existieren, selbst wenn die Daten nur so dahin tröpfeln, verstehe ich das richtig?
Ich weiß gar nicht wie sowas unter anderen OSs gemacht wird, aber der Web-Server ansich macht ja für jeden Clienten einen neuen Thread auf (wo sich mir gerade die Frage stellt wie das überhaupt funktioniert, ich erinnere mich da an einen Port (TCP-Port) pro Client, aber das kann nicht stimmen) und je nachdem über wieviele TCP-Ports das dann läuft, so viele Threads wären dann auch im Service.
Da fallen mir vier Möglichkeiten ein:
(a) Bei einkommender Verbindung wird ein fork() mit dieser Verbindung gemacht, der neue Prozess kümmert sich. Einfach implementierbar, je nach OS recht gut skalierbar. Setzt schnelles fork() und guten Scheduler voraus.
(b) Bei einkommender Verbindung wird ein Thread erzeugt, der sich darum kümmert. Setzt eine vernünftige Threadunterstützung (Erzeugung, Scheduling) voraus. Hab ich keine Erfahrung.
(c) Es gibt eine Menge an rumliegenden Threads, bei einkommender Verbindung wird ein unbenutzter ausgesucht, der sich kümmert. Kommen mehr Verbindungen rein als Threads da sind, muss gewartet werden. Spart die Erzeugungszeit der Threads, wird oft unter Windows gemacht.
(d) AIO. In einer blockierenden Endlosschleife/Signalwarteschleife wird geschaut, auf welcher Verbindung irgendetwas geschehen ist und diese wird verarbeitet: poll(), epoll(), kqueue oder so. Skaliert je nach OS optimal und kann sehr schnell sein. Setzt aber eine High-Performance-AIO-API voraus. Kann man mit (c) mischen (z.B. ein Worker-Thread je CPU).

Wenn du einen Webserver auf Port 80 hast und der kriegt ne Anfrage, dann wird ein lokaler Port zugeteilt (z.B. 38855) und über den läuft die Kommunikation. Dein Webserver kann also keine 64k TCP-Verbindungen gleichzeitig halten, weil es soviele Ports dann nicht gibt. Korrigiert mich, wenn ich hier falsch liege.

Gruß
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 25. September 2010, 07:45
Zitat von: svenska
Wenn du einen Webserver auf Port 80 hast und der kriegt ne Anfrage, dann wird ein lokaler Port zugeteilt (z.B. 38855) und über den läuft die Kommunikation. Dein Webserver kann also keine 64k TCP-Verbindungen gleichzeitig halten, weil es soviele Ports dann nicht gibt. Korrigiert mich, wenn ich hier falsch liege.
Genauso habe ich das auch in Erinnerung, aber wie macht das dann ein Web-Server? Denn seien wir mal ehrlich, 64k gleichzeitige Zugriffe sind nicht wirklich viel für bestimmte Web-Seiten.

Zitat von: taljeth
Zero-copy ist ein Taskwechsel eh nicht, ob du die Register jetzt auf den Stack oder woanders hin sicherst.
Also ich weis ja nicht wie bei euch ein IRQ abläuft, aber sobald ein IRQ aufgerufen wurde (und das gilt auch für den Scheduler) sind alle Register schon auf dem Stack, also würde es durchaus etwas kosten diese nochmal, vom Stack, irgendwo hinzusichern! Ein Thread-Wechsel sollte nicht mehr als ein Austausch von "ESP" sein und bei einem Prozess-Wechsel wird dann nur noch zusätzlich "CR3" verändert.
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 25. September 2010, 10:02
Hallo,


weil solange wie ich kein A-I/O habe kann ein Thread sowieso nur eine Datei gleichzeitig lesen/schreiben.
Das bedeutet das wenn Du asynchrones I/O implementieren möchtest soll das über noch mehr Ports (und damit Threads) im Service gehen? Ich gebe zu das ich über asynchrones I/O auch noch nicht so viel nachgedacht habe aber diese Idee erscheint mir nicht sonderlich geschickt.

Der Port wird nur erstellt wenn er auch wirklich benötigt wird, sprich wenn eine Anwendung keine Dateien öffnet dann wird auch kein Port erstellt
Das bedeutet aber das wenn Du 8 parallele Compiler-Instanzen hast dann gibt es auch 8 Ports + Threads beim VFS-Service obwohl die Compiler die meiste Zeit damit verbringen die CPU zu beschäftigen (also kompilieren), File-I/O findet dort nur am Anfang (Quell-Code einlesen) und am Ende (Assembler-Code schreiben) statt.

Ich weiß gar nicht wie sowas unter anderen OSs gemacht wird, aber der Web-Server ansich macht ja für jeden Clienten einen neuen Thread auf (....) und je nachdem über wieviele TCP-Ports das dann läuft, so viele Threads wären dann auch im Service.
Ich denke bei einem Micro-Kernel-OS, wo der TCP-IP-Stack ein ganz normaler User-Mode-Service ist, ist das eine brauchbare Vorgehensweise.

Mit der x86-Architektur hast du dich noch nicht so viel beschäftigt oder?
Doch, sehr lange und intensiv sogar, aber ich hatte kurz vergessen das die x86-CPU die Register ja zwangsweise in den Speicher schreibt aber auch so bin ich davon ausgegangen das man das gesicherte Register-Set vom Stack in die Thread-Descriptor-Struktur kopiert und wenn man einen anderen Thread weiterlaufen lassen will dann wird sein Register-Set eben in den CPU-Lokalen-Kernel-Stack kopiert und ein IRET gemacht. Selbst bei x86-64 ist ein Register-Set nur 128 Bytes groß und dafür einen kompletten Stack zu reservieren finde ich eher ungeschickt, bei den paar Bytes fällt das kopieren nun wahrlich nicht ins Gewicht (das passt ja bequem in den L1-Cache so das der von Dir erhoffte Performance-Vorteil vermutlich weniger als 10 Takte pro Context-Switch beträgt). Im Tutorial wird doch wimre auch nur ein TSS pro CPU empfohlen, wo mir dann auffällt das Du dann dort immer einen anderen Kernel-Stack eintragen musst (also Speicherzugriffe) oder Du hast so viele TSS wie es Threads gibt und da dürften Dir dann doch ziemlich schnell die GDT-Einträge ausgehen (vor allem weil Du ja mit Threads seeeehr großzügig bist).

Denn was bringen dir viele Threads die von ein und der selben HDD lesen wollen (und wir lassen SATA job-queueing mal außen vor)?
Also gerade das SATA-Job-Queueing will ich ganz sicher nicht außen vor lassen. Aber etwas Recht hast Du schon, 2 Dateien von der selben HDD einzulesen geht eben nicht ganz gleichzeitig, aber es könnten ja auch 2 HDDs im Spiel sein oder ne Flash-Disk oder der HDD-Cache könnte vielleicht noch was haben oder oder ..... Ich denke es schadet nichts für 2 getrennte Vorgänge auch 2 Threads zu benutzen, im Worst-Case bin ich nicht schneller als Du und habe auch blockierte Threads rumgammeln aber im Best-Case kann ich eben sehr viel mehr rausholen.

Wo sich mir dann auch die Frage stellt, willst du eigentlich einem Programm auch die Möglichkeit geben, das keine Popup-Threads erstellt werden, sondern das alles nur über einen Thread läuft (der nicht erstellt werden muss)?
Ich möchte das ein Service bei der Erstellung seines Message-Targets bestimmen kann wie viele Threads maximal für dieses Message-Target erstellt werden dürfen, das bedeutet dann das wenn mehr Anfragen gleichzeitig kommen das dann gequeued werden muss. Dieses Maximum darf auch 1 betragen. Aber das ein bereits vorhandener Thread in dem Prozess benutzt werden soll ist definitiv nicht vorgesehen. Es wird weder aktives abholen von Messages geben (eben wegen den Problemen mit der unbekannten Message-Größe) und auch keine Unterbrechung eines Threads (in der Art der Unix-Signale, das hat mir zu viele Nachteile für die Signal-Handler bei der Benutzung von Ressourcen wie malloc usw.).

Ok, aber nach welchen Kriterium entscheidest du wieviele Threads in diesen Pool kommen?
Das hab ich noch nicht entschieden aber worum es mir geht ist das dieser Pool ja für alle Services zur Verfügung steht er also kleiner sein kann als die Menge Threads die Du benötigst (weil ja wohl kaum alle Services gleichzeitig stark belastet werden). Die Threads in einem Web-Server machen entweder TCP-Verkehr oder sie lesen Daten von der HDD oder machen eine Datenbankabfrage, aber nicht alles gleichzeitig (es könnte also passieren das für File-I/O, Datenbank und TCP immer wieder der selbe PopUp-Thread zum Einsatz kommt wogegen bei Dir dafür 3 Service-Threads benötigt werden obwohl maximal einer davon auch was zu tun bekommt).

Bei dir sehe ich dann, wie gesagt, das Problem das du entweder viel Zeit mit der Thread Erstellung verbringst
Deswegen den Pool, ich hoffe das ich die Thread-Erstellung so schnell machen kann das es kaum ins Gewicht fällt und außerdem hat ein neuer Thread ja keinen Kontext (also Register-Set) der aus dem Speicher gelesen werden muss (und auch die Register die vom Client-Thread gesichert werden müssen sind weniger weil der Syscall die selbe Aufrufkonvention benutzt wie ein normaler CALL und dort nur etwa ein Drittel der Register als Callee-Saved definiert sind, bei meinen 64 Registern wird das sicher spürbar sein).

(was passiert eigentlich wenn die Nachricht abgearbeitet ist bzw. muss der User den Thread selbst beenden und wo kommt er dann hin?)
Der PopUp-Thread muss bei synchronem IPC je eh das Ergebnis zurückgeben (mit einem speziellen Syscall) und wird dabei auch gleich wieder in den Pool zurückgelegt (also gekillt, wobei auch hier wieder der Kontext-Switch sehr klein ausfällt weil von dem PopUp-Thread nichts gesichert werden muss und vom Client-Thread nur ein Teil der Register geladen werden müssen, die Rückgabewerte usw. sind ja schon in Registern).

oder du halt viel zu viele Threads hast, die nichts machen und sinnlos im Pool rumliegen ;) Damit hättest du dann das selbe Problem wie ich.
Klar kann es sein das der Pool mal ziemlich voll wird aber dann muss der eben etwas geleert werden. Es kommt letztendlich auf eine gute Pool-Verwaltungsstrategie an aber wenn ich die finden sollte werde ich wohl weniger Speicher verbrauchen als Dein Konzept.


Da fallen mir vier Möglichkeiten ein ....
Variente a und b setzen beide einen guten Scheduler voraus und sind aus meiner Sicht daher ziemlich gleichwertig. Trotzdem denke ich dürfte Variante b schneller gehen weil es leichter ist einen neuen Thread zu erstellen als einen Prozess zu forken, mal abgesehen davon das bei einem fork das Paging-Directory geklont werden muss (zumindest teilweise was aber trotzdem Speicher kostet) und dann zur Laufzeit einige Paging-Exceptions anfallen in denen dann modifizierte Pages kopiert werden müssen. Für einen neuen Thread muss man einmalig einen Stack anlegen (neue Verwaltungsstrukturen usw. im Kernel fallen bei fork auch an) und das war es dann auch. Also kostet fork definitiv mehr Speicher und mehr CPU-Zeit.
Ein Gemisch aus Variante c und d klingt auch sehr interessant wobei dann wieder die Frage aufkommt: "Was kostet weniger ein Kontext-Switch oder ein State-Wechsel in der State-Maschine?". Nebst dessen das ein gutes Asynchrones I/O-Interface sicher keine leichte Übung ist und in Summe womöglich mehr Syscalls kostet.

Wenn du einen Webserver auf Port 80 hast und der kriegt ne Anfrage, dann wird ein lokaler Port zugeteilt (z.B. 38855) und über den läuft die Kommunikation. Dein Webserver kann also keine 64k TCP-Verbindungen gleichzeitig halten, weil es soviele Ports dann nicht gibt. Korrigiert mich, wenn ich hier falsch liege.
Bitte nicht übel nehmen aber das ist völliger Quatsch! Eine TCP-Verbindung wird über die 2 IP-Addressen und die 2 Port-Nummern (jeweils vom Client und vom Server) unterschieden und selbst wenn davon immer eine IP-Adresse und ein Port gleich sind (also die vom Server) so sind doch trotzdem mit IPv4 theoretisch bis zu 2^48 Verbindungen zu einem einzelnen Web-Server möglich (mit IPv6 entsprechend mehr).


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: kevin am 25. September 2010, 11:04
Also ich weis ja nicht wie bei euch ein IRQ abläuft, aber sobald ein IRQ aufgerufen wurde (und das gilt auch für den Scheduler) sind alle Register schon auf dem Stack, also würde es durchaus etwas kosten diese nochmal, vom Stack, irgendwo hinzusichern! Ein Thread-Wechsel sollte nicht mehr als ein Austausch von "ESP" sein und bei einem Prozess-Wechsel wird dann nur noch zusätzlich "CR3" verändert.
Ich weiß ja nicht, wo du deine x86-Fälschung erworben hast, die das so macht, aber mein x86 sichert von sich aus bei einem Interrupt oder Trap Gate nur ss, esp, eflags, cs und eip auf den Stack. Der Rest landet nur dann auf dem Stack, wenn du das so programmierst. Bei einem Task Gate geht sogar alles direkt in eine andere Struktur (nämlich das TSS).

Dass du es gewohnt bist, alles auf den Stack zu sichern, heißt ja nicht, dass es nicht anders ginge.
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 25. September 2010, 12:32
Zitat von: taljeth
Ich weiß ja nicht, wo du deine x86-Fälschung erworben hast, die das so macht, aber mein x86 sichert von sich aus bei einem Interrupt oder Trap Gate nur ss, esp, eflags, cs und eip auf den Stack. Der Rest landet nur dann auf dem Stack, wenn du das so programmierst. Bei einem Task Gate geht sogar alles direkt in eine andere Struktur (nämlich das TSS).
Naja, ich bin halt einfach davon ausgegangen das man alles auf dem Stack speichert, weil es das schnelleste und einfachste ist. Es dann nochmal umzukopieren wäre ja quatsch.

Zitat von: erik
Das bedeutet das wenn Du asynchrones I/O implementieren möchtest soll das über noch mehr Ports (und damit Threads) im Service gehen? Ich gebe zu das ich über asynchrones I/O auch noch nicht so viel nachgedacht habe aber diese Idee erscheint mir nicht sonderlich geschickt.
Ich wollte damit nur sagen, dass mein Konzept bei A-I/O nicht mehr funktioniert, aber diese Baustelle werde ich frühestens bearbeiten wenn alles andere läuft und bis dahin vergeht noch ne ganze Weile.

Zitat von: erik
Doch, sehr lange und intensiv sogar, aber ich hatte kurz vergessen das die x86-CPU die Register ja zwangsweise in den Speicher schreibt aber auch so bin ich davon ausgegangen das man das gesicherte Register-Set vom Stack in die Thread-Descriptor-Struktur kopiert und wenn man einen anderen Thread weiterlaufen lassen will dann wird sein Register-Set eben in den CPU-Lokalen-Kernel-Stack kopiert und ein IRET gemacht.
Naja, du vergisst, dass auf dem Stack aber nicht nur die Register liegen, da liegen auch noch Funktionsargumente, lokale Variablen usw., das müsstest du dann alles mit in den Thread-Descriptor speichern und das artet dann mal schnell darin aus, das du den gesamten Stack woanders hinkopierst, nur um zu erreichen das du nur einen Stack im Kernel verwendest. Das einzigste was du dadurch erreichen würdest wäre, dass du pro CPU einen Stack mehr hast als normalerweise nötig.

Ich bin jetzt mal ganz einfach davon ausgegangen, dass jeder Thread auch mal den Syscall macht und das der Kernel unterbrechbar ist.

Übrigens wiedersprichst du da deiner eigenen Philosophie des zero-copy (ich betrachte das Speichern auf dem Stack nicht als kopieren in dem Sinne, weil mind. 1 Register sowieso erstmal irgendwo zwischen gespeichert werden muss um den Rest dann irgendwo zu speichern).

Zitat von: erik
Selbst bei x86-64 ist ein Register-Set nur 128 Bytes groß und dafür einen kompletten Stack zu reservieren finde ich eher ungeschickt, bei den paar Bytes fällt das kopieren nun wahrlich nicht ins Gewicht (das passt ja bequem in den L1-Cache so das der von Dir erhoffte Performance-Vorteil vermutlich weniger als 10 Takte pro Context-Switch beträgt). Im Tutorial wird doch wimre auch nur ein TSS pro CPU empfohlen, wo mir dann auffällt das Du dann dort immer einen anderen Kernel-Stack eintragen musst (also Speicherzugriffe) oder Du hast so viele TSS wie es Threads gibt und da dürften Dir dann doch ziemlich schnell die GDT-Einträge ausgehen (vor allem weil Du ja mit Threads seeeehr großzügig bist).
Du hast, wie gesagt, das Szenario Syscall vergessen und das die Kernel-Funktionen ja auch den Stack verwenden. Wie kommst du auf 10 Takte bei 128byte? Zumal es mehr als nur die 16 Register sind, da wäre noch das RIP und das RFlags Register.
Und ja ich verwende nur ein TSS pro CPU, weil es halt nötig ist. Denn keiner von uns wird wohl Hardware-Taskswitching auf der x86-Architektur nutzen (wenn doch komm ich dem gleich mit der Portabilität ;) )!?

Zitat von: erik
Ich möchte das ein Service bei der Erstellung seines Message-Targets bestimmen kann wie viele Threads maximal für dieses Message-Target erstellt werden dürfen, das bedeutet dann das wenn mehr Anfragen gleichzeitig kommen das dann gequeued werden muss. Dieses Maximum darf auch 1 betragen. Aber das ein bereits vorhandener Thread in dem Prozess benutzt werden soll ist definitiv nicht vorgesehen. Es wird weder aktives abholen von Messages geben (eben wegen den Problemen mit der unbekannten Message-Größe) und auch keine Unterbrechung eines Threads (in der Art der Unix-Signale, das hat mir zu viele Nachteile für die Signal-Handler bei der Benutzung von Ressourcen wie malloc usw.).
Ok, wenn du das so machen willst, darfst du mir nie mehr damit kommen, das man nicht alzu sehr von gewohnten Weg abweichen sollte ;)

Ich sag mal ne normal GUI-App zu schreiben/portieren dürfte noch gehen, aber wenn es dann zu sachen kommen die normaler Weise nicht blockieren wenn keine Nachricht da ist, sondern weiter arbeiten, da stelle ich es mir dann schon schwierig vor (Bsp. Spiele, die warten ja auch nicht bis die Taste gedrückt wurde, sondern es wird geguckt, wurde eine gedrückt und dann wird der nächste Frame berechnet).

Aber ansich ist die Idee von daher gut, weil man damit zum Multithreading gezwungen wird, macht die Sache nicht einfacher, aber dann gibt es vielleicht irgendwann mal mehr Programme mit denen man Vorteile aus mehreren Kernen holen kann.

Zitat von: erik
Klar kann es sein das der Pool mal ziemlich voll wird aber dann muss der eben etwas geleert werden. Es kommt letztendlich auf eine gute Pool-Verwaltungsstrategie an aber wenn ich die finden sollte werde ich wohl weniger Speicher verbrauchen als Dein Konzept.
Das klingt für mich wie nach dem Kontept eines Garbage-Collectors. Wird von allen als das Allheilmittel angepriesen (weil sich der dumme Programmierer nicht mehr um die Speicherverwaltung kümmern muss). Jedes Mal wenn ich das Wort Garbage-Collector höre, dreht sich bei mir im Magen alles um.
Meine Erfahrung von Programmen die in Java oder python oder anderen Sprachen mit einem Garbage-Collector geschrieben sind, der wird immer in einem bestimmten Zeit-Interall aufgerufen, wo er halt aufräumt und in der Zeit geht die Performance meistens in den Keller.
Sowas hört sich in der Theorie immer toll an, aber in der Praxis hapert es fast immer :(

Zitat von: erik
Bitte nicht übel nehmen aber das ist völliger Quatsch! Eine TCP-Verbindung wird über die 2 IP-Addressen und die 2 Port-Nummern (jeweils vom Client und vom Server) unterschieden und selbst wenn davon immer eine IP-Adresse und ein Port gleich sind (also die vom Server) so sind doch trotzdem mit IPv4 theoretisch bis zu 2^48 Verbindungen zu einem einzelnen Web-Server möglich (mit IPv6 entsprechend mehr).
Das klingt doch schon ganz anders und ist auch viel logischer!
Titel: Re:Shared Memory
Beitrag von: kevin am 25. September 2010, 12:37
Zitat von: taljeth
Ich weiß ja nicht, wo du deine x86-Fälschung erworben hast, die das so macht, aber mein x86 sichert von sich aus bei einem Interrupt oder Trap Gate nur ss, esp, eflags, cs und eip auf den Stack. Der Rest landet nur dann auf dem Stack, wenn du das so programmierst. Bei einem Task Gate geht sogar alles direkt in eine andere Struktur (nämlich das TSS).
Naja, ich bin halt einfach davon ausgegangen das man alles auf dem Stack speichert, weil es das schnelleste und einfachste ist. Es dann nochmal umzukopieren wäre ja quatsch.
Ja, den Zustand irgendwohin zu kopieren, wo man ihn nicht benutzt, ist Quatsch. Auch wenn es noch so einfach ist. Aber das ist kein Argument - du kannst nicht einfach die eine Lösung zur Hälfte implementieren und dich dann beschweren, dass die andere Lösung damit nicht vernünftig zusammenspielt.
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 25. September 2010, 16:10
Hallo,


Ich wollte damit nur sagen, dass mein Konzept bei A-I/O nicht mehr funktioniert, aber diese Baustelle werde ich frühestens bearbeiten wenn alles andere läuft und bis dahin vergeht noch ne ganze Weile.
Ich sage es nur ungern aber falls Du dann später feststellen solltest das Dein Konzept für asynchrones I/O überhaupt nichts taugt wirst Du Dich ziemlich ärgern weil Du dann vor der Entscheidung stehst entweder ein zweites paralleles IPC-System zu implementieren oder alle Programme auf ein neues IPC-System umzustellen oder Dich mit dem Ärgernis irgendwie zu arrangieren oder es einfach ganz bleiben zu lassen. Ich hab es schon mal geschrieben und mache das gerne wieder: solche Arten von Entscheidungen sind ziemlich eklig.

Naja, du vergisst, dass auf dem Stack aber nicht nur die Register liegen, da liegen auch noch Funktionsargumente, lokale Variablen usw., das müsstest du dann alles mit in den Thread-Descriptor speichern und das artet dann mal schnell darin aus, das du den gesamten Stack woanders hinkopierst, nur um zu erreichen das du nur einen Stack im Kernel verwendest. Das einzigste was du dadurch erreichen würdest wäre, dass du pro CPU einen Stack mehr hast als normalerweise nötig.
Die Stack-Sachen des User-Mode-Thread liegen auf dem User-Mode-Stack und der wird natürlich mit einem Wechseln von SS:ESP komplett gewechselt. Die Sachen des Kernel-Mode-Stack liegen auf dem CPU-spezifischen-Kernel-Mode-Stack und sind nach dem IRET nicht mehr von Bedeutung. Von einem unterbrechbaren Kernel bin ich allerdings nicht ausgegangen, wenn Du sowas willst hast Du sicher noch ganz andere Probleme als nur die Zuordnung von Stacks. Wer hat den hier einen unterbrechbaren Kernel? Auf meiner Plattform gibt es keinen unterbrechbaren Kernel, wenn die CPU in einem System-Mode ist dann gehen keine weiteren Exceptions/Interrupts oder sonstwas (das würde zum Double-Error-Shutdown führen), aus dem System-Mode geht es nur raus wenn in den User-Mode zurück gewechselt wird.

Übrigens wiedersprichst du da deiner eigenen Philosophie des zero-copy (ich betrachte das Speichern auf dem Stack nicht als kopieren in dem Sinne, weil mind. 1 Register sowieso erstmal irgendwo zwischen gespeichert werden muss um den Rest dann irgendwo zu speichern).
Das mag auf x86 ja zutreffen aber auf viele andere CPUs trifft das nicht zu, z.B. ARM. Diese CPUs haben einen gewissen Teil der Register doppelt, einmal für den User-Mode und einmal für den System-Mode, und schalten beim Mode-Wechsel einfach um. Im System-Mode stehen einem gleich ein paar Register zur Verfügung die auch bereits sinnvolle Werte für den Kernel enthalten (z.B. einen Pointer zum Thread-Descriptor des gerade laufenden Threads) und das sichern der paar User-Mode-Register ist damit nicht viel mehr als ein einfacher Speicher-Zugriff. Schau Dir mal an wie ARM das macht, der einzigste Unterschied zu meiner CPU ist das ARM für jeden verschiedenen System-Mode ein extra Set an Schattenregistern hat so das auch innerhalb eines Syscall-Modes noch eine Page-Fault-Exception auftreten kann (ich will nur ein Schattenregister-Set implementieren und bin auch nicht der Meinung dass das ein Problem oder Nachteil ist).

Du hast, wie gesagt, das Szenario Syscall vergessen und das die Kernel-Funktionen ja auch den Stack verwenden.
Hä, Du schreibst wirr! Alles was vor dem Syscall im User-Mode passierte liegt im User-Mode-Thread und ist dort sicher. Alles was ab (einschließlich) dem Syscall passiert liegt auf dem Kernel-Mode-Stack und das sind in erster Linie die Register die die CPU automatisch dort hin legt und jene die Du dazu legst damit der Kernel selber erst mal ein paar Register zur Verfügung hat. Alles was im Syscall danach passiert ist doch für den User-Mode-Thread völlig ohne Bedeutung, solange seine paar Register wieder den richtigen Inhalt haben wenn dieser vom Scheduler wieder aktiviert wird. Auf meiner CPU wird es sogar so sein der der Kernel-Mode-Stack-Pointer weg ist sobald ein IRET gemacht wird, das bietet mir den Vorteil das ich nicht den ganzen Aufrufbaum wieder zurück gehen muss, ich kann also im Schduler (egal von wem er aufgerufen wurde) einfach die Register des neuen Threads laden und ein IRET machen und schon ist der Kernel-Mode-Stack verschwunden (die Daten im Speicher sind natürlich noch dort aber der Stack-Pointer ist weg, er wird nicht gesichert). Jedes mal wenn meine CPU in einen System-Mode wechselt hat sie einen jungfäulichen Kernel-Mode-Stack zur Verfügung.

Wie kommst du auf 10 Takte bei 128byte? Zumal es mehr als nur die 16 Register sind, da wäre noch das RIP und das RFlags Register.
Okay dann sind es eben 144 Bytes aber was glaubst Du wie lange das bisschen zu kopieren dauert wenn der Pfad in den L1-Cache 256 Bit breit ist? Auf heutigen x86-CPUs braucht ein "rep movsd" längst nicht mehr 1 Takt pro DWord wenn alles im L1-Cache ist (passendes Alignment usw. vorausgesetzt aber dem kann der Kernel-Programmierer ja nachhelfen). Nebst dessen das Du bei x86-64 ja nicht alle Register auf dem Stack in Sicherheit bringen musst um wenigstens etwas arbeiten zu können.

Ok, wenn du das so machen willst, darfst du mir nie mehr damit kommen, das man nicht alzu sehr von gewohnten Weg abweichen sollte ;)
Einverstanden. ;)
Wobei ich trotzdem der Meinung bin das mein Konzept (auf der libc-Ebene) weniger vom üblichen Weg abweicht als wenn read einen Pointer zurück gibt.

aber wenn es dann zu sachen kommen die normaler Weise nicht blockieren wenn keine Nachricht da ist, sondern weiter arbeiten, da stelle ich es mir dann schon schwierig vor (Bsp. Spiele, die warten ja auch nicht bis die Taste gedrückt wurde, sondern es wird geguckt, wurde eine gedrückt und dann wird der nächste Frame berechnet).
Hä, genau das ist doch der Vorteil meiner Methode, nichts muss blockiert oder unterbrochen werden nur weil eine Message empfangen wurde (es wird ja ein neuer Thread erstellt) oder eben keine Message kommt (es soll ja kein aktives Abholen geben).

Das klingt für mich wie nach dem Kontept eines Garbage-Collectors.
Was hat ein Speicher-Pool mit nem Garbage-Collector zu tun? Ich würde eher sagen das mein Pool einem Slab-Allocator etwas ähnelt. Und nur weil Du Das Prinzip des Garbage-Collectors nicht magst (oder möglicherweise gar nicht verstehst) heißt das nicht das es nicht funktioniert.


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 25. September 2010, 16:44
Zitat von: erik
Ich sage es nur ungern aber falls Du dann später feststellen solltest das Dein Konzept für asynchrones I/O überhaupt nichts taugt wirst Du Dich ziemlich ärgern weil Du dann vor der Entscheidung stehst entweder ein zweites paralleles IPC-System zu implementieren oder alle Programme auf ein neues IPC-System umzustellen oder Dich mit dem Ärgernis irgendwie zu arrangieren oder es einfach ganz bleiben zu lassen. Ich hab es schon mal geschrieben und mache das gerne wieder: solche Arten von Entscheidungen sind ziemlich eklig.
Das sehe ich noch nicht so. Denn ich könnte auch sagen, jede Nachricht wird von einem Thread abgeholt und dann wird ein neuer Thread erstellt der sie bearbeitet (worker Thread). Das ist dann deinen Popup-Threads sehr ähnlich nur das es keinen Pool gibt, den man aber auch schaffen könnte.
Also das Umstellen auf A-I/O sollte nicht das Problem darstellen.

Zitat von: erik
Von einem unterbrechbaren Kernel bin ich allerdings nicht ausgegangen, wenn Du sowas willst hast Du sicher noch ganz andere Probleme als nur die Zuordnung von Stacks. Wer hat den hier einen unterbrechbaren Kernel?
Welche anderen Probleme? Ich habe einen unterbrechbaren Kernel und jeder der nen Monolithischen Kernel schreibt, sollte bitte auch einen haben. Sicher ist das bei einem Mikrokernel nicht mehr so schlimm, aber da wären wir wieder bei dem Thema wie Mikro ein Kernel ist.

Zitat von: erik
Das mag auf x86 ja zutreffen aber auf viele andere CPUs trifft das nicht zu, z.B. ARM. Diese CPUs haben einen gewissen Teil der Register doppelt, einmal für den User-Mode und einmal für den System-Mode, und schalten beim Mode-Wechsel einfach um. Im System-Mode stehen einem gleich ein paar Register zur Verfügung die auch bereits sinnvolle Werte für den Kernel enthalten (z.B. einen Pointer zum Thread-Descriptor des gerade laufenden Threads) und das sichern der paar User-Mode-Register ist damit nicht viel mehr als ein einfacher Speicher-Zugriff.
Das ist z.B. ein Grund warum ich sage, das mein Kernel nicht portabel sein muss, sondern das ich dann für jede neue Architektur lieber einen neuen schreibe. Nur die meisten werden sich für den Anfang einfach auf x86 beschränken und ihn (Kernel) höchstens portabel halten.

Zitat von: erik
Alles was ab (einschließlich) dem Syscall passiert liegt auf dem Kernel-Mode-Stack und das sind in erster Linie die Register die die CPU automatisch dort hin legt und jene die Du dazu legst damit der Kernel selber erst mal ein paar Register zur Verfügung hat.
Naja, also zu erst hätte ich auf x86 gerne alle Register zur Verfügung (da sind so wenige und der Compiler will es so) und dann besteht der Kernel Code auch nur aus C-Code (oder welcher Sprache auch immer) und der benötigt einen Stack (den Kernel-Mode-Stack).
Da mein Kernel unterbrechbar ist, kann mitten in der Bearbeitung des Syscalls ein Interrupt kommen (kann auch ein Gerät sein) der den Scheduler aufruft, d.h. ich müsste jetzt den gesamten Stack in den Thread-Descriptor kopieren (weil ich die Daten die da stehen ja brauche um später den Thread fortsetzen zu können), aber warum sollte ich das tun, wenn die doch schon auf dem Stack liegen?

Was mir da gerade so auffällt ist wieso oft von Kernel-Threads und User-Threads gesprochen wird. Für mich war das immer das gleiche, aber es scheint wohl doch für viele einen Unterschied zu machen ob der Kernel im Kernel-Mode auch Threads haben kann (was für mich immer außer Frage stand).

Zitat von: erik
Wobei ich trotzdem der Meinung bin das mein Konzept (auf der libc-Ebene) weniger vom üblichen Weg abweicht als wenn read einen Pointer zurück gibt.
Irgendwie kommst du darüber nicht hinweg ;)

Was mir (und bestimmt auch uns was die Diskussion betrifft) weiterhelfen würde, wäre wenn jemand nen Mikrokernel kennt (der aber auch wirklich fast alles bis alles im UserSpace macht) wo der Source verfügbar ist. Damit man einfach mal gucken könnte wie andere sowas gelöst haben.

Zitat von: erik
Hä, genau das ist doch der Vorteil meiner Methode, nichts muss blockiert oder unterbrochen werden nur weil eine Message empfangen wurde (es wird ja ein neuer Thread erstellt) oder eben keine Message kommt (es soll ja kein aktives Abholen geben).
Das Problem sehe ich halt darin, das du also irgendetwas haben müsste wo du dann hinschreibst das eine Taste gedrückt wurde, wo dann der eigentliche worker Thread dann nachguckt (könnte z.B. ne Art Bitmap sein, wo du den KeyCode setzt oder resetest, was dann per cmpxchg passieren müsste/sollte).
Mir geht es nur darum, dass das auch nicht gerade der gewohnte Weg ist und man ganz schön umdenken muss, insbesondere in richtig Multithreading und Synchronisation.

Zitat von: erik
Was hat ein Speicher-Pool mit nem Garbage-Collector zu tun? Ich würde eher sagen das mein Pool einem Slab-Allocator etwas ähnelt. Und nur weil Du Das Prinzip des Garbage-Collectors nicht magst (oder möglicherweise gar nicht verstehst) heißt das nicht das es nicht funktioniert.
Ich habe an sich gegen den Garbage-Collector nichts, aber die Umsetzungen die ich bisher erlebt haben sprechen nicht gerade für ihn (das selbe Problem hat doch auch ein Mikrokernel).
Ich wollte damit nur sagen, das ein in der Theorie gutes Konzept erstmal in die Praxis vernünftig umgesetzt werden muss und genau daran hapert es meistens.
Titel: Re:Shared Memory
Beitrag von: Svenska am 25. September 2010, 17:09
Minix ist ein Mikrokernel, Sourcen sind frei verfügbar. Allerdings gibt es (zumindest bei Minix 2) kein Threading, Minix 3 hab ich mich nie befasst - setzt 32-Bit-Maschinen voraus.
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 25. September 2010, 17:59
Was ich nach einer kurzen Lesetour durch die Dokumentation sagen kann, ist das Minix lieber einmal zu viel kopiert als einmal zu wenig.
Und das der VFS-Service einen SharedMemory-Bereich an den HDD-Treiber schickt wo er die Daten reinschreiben soll oder drauß lesen soll ;)

Dann werde ich das wohl doch so machen müssen. Damit würde dann ja auch der Sinn eines "Speicher verschicken"-Syscalls wegfallen (ein Glück das ich momentan eh nicht Programmieren kann ;) ).

Edit::

Den Vorteil meiner Methode sehe ich darin, das weniger Syscalls nötig wären:

Bei mir: Speicher allokieren, Speicher versenden
Ansonsten: Speicher allokieren, SharedMem-Bereich erstellen, SharedMem Erlaubnis an anderen Prozess geben, SharedMem beim Empfänger mappen, SharedMem beim Emfpänger unmappen

Das wäre sogar eine ganz schöne Ersparnis!

Edit::

Eigentlich erstellt man doch einen SharedMem-Bereich eh nur dazu, das man den Speicher mit anderen Prozessen "teilen" kann, oder?
Dann wäre es aus performace Sicht nämlich besser nen Syscall zu machen, wo man eine Nachricht verschickt, ein SharedMem Bereich erstellt wird und die ID in die Msg (nicht sichtbarer Teil) gespeichert wird und beim Empfangen wird der SharedMem gleich gemappt und die Adresse wird mit der Msg übergeben.

Wie klingt diese Idee?
Titel: Re:Shared Memory
Beitrag von: Svenska am 25. September 2010, 19:27
Minix stammt aus einer Zeit, als man ein billiges Unix für 8086/80286er brauchte. Zero-Copy war da noch kein Thema. ;-)

Du vergisst die Messages vom VFS-Treiber zum HDD-Treiber ("lies mal block xy") und umgekehrt ("bin fertig"). Wobei ich nicht weiß, ob/wie ich da Shared Memory einsetzen würde. Aber ich hab da auch keinen anderen (guten) Ansatz.
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 25. September 2010, 19:54
Zitat von: svenska
Minix stammt aus einer Zeit, als man ein billiges Unix für 8086/80286er brauchte. Zero-Copy war da noch kein Thema.
Ich habe mir Minix3 angesehen.

Zitat von: svenska
Du vergisst die Messages vom VFS-Treiber zum HDD-Treiber ("lies mal block xy") und umgekehrt ("bin fertig"). Wobei ich nicht weiß, ob/wie ich da Shared Memory einsetzen würde. Aber ich hab da auch keinen anderen (guten) Ansatz.
Das habe ich weggelassen, weil die sich nicht vermeiden lassen, die anderen Syscalls ist im Endeffekt das einzige wo du etwas verbessern kannst indem du sie einsparst, aber die Nachricht an sich kannst du ja schlecht einsparen ;)

Ich habe mir das so vorgestellt, das du Pages allokiert hast wo die Daten aus Block xy reinsollen und eine Anfrage an den HDD-Treiber sendest das er Block xy in diese Pages schreiben soll. Die Pages werden als SharedMem mit der Nachricht/Anfrage an den HDD-Treiber übergeben.
Dieser läd Block xy in die Pages und sendet eine Antwort zurück das alles i.O. war.

Ich wollte den Syscall so gestallten, das du über ein Flag entscheiden kannst, ob der SharedMem versendet werden soll (er bleibt auch bei dir), ob er geunmappt werden soll (weil du alles gemacht hast und ihn nicht mehr benötigst) oder ob du ihn ganz versenden willst (er wird bei dir geunmappt).

Damit sollte ich dann alle Fälle abgedeckt haben und ich spare mir Syscalls. Im Endeffekt könnte man sogar sagen, dass ich damit ne Art dynamic-size Msgs habe ;) Nur halt auf ein vielfaches der Pagegröße beschränkt.
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 26. September 2010, 11:04
Hallo,


Denn ich könnte auch sagen, jede Nachricht wird von einem Thread abgeholt und dann wird ein neuer Thread erstellt der sie bearbeitet (worker Thread). Das ist dann deinen Popup-Threads sehr ähnlich
Ja, die Ähnlichkeit ist verblüffend, aber im Punkt Effizienz kommt das noch nicht an mein Konzept ran. Bei Dir wären pro Message 2 Syscalls zum Abholen nötig, GetMyNextMessage und CreateThread, bei mir sind es 0 Syscalls zum Abholen einer Message. Durch das aktive Abholen hast Du aber wieder das Problem der unbekannten Message-Größe, falls Du dynamische Größen benutzen möchtest. Das mit dem Pool ist nur ne kleine Beschleunigung, das Konzept funktioniert auch ohne und im ersten Anlauf werde ich das sicher auch ohne Pool programmieren.

Also das Umstellen auf A-I/O sollte nicht das Problem darstellen.
Was macht Dich da so sicher?

Ich habe einen unterbrechbaren Kernel und jeder der nen Monolithischen Kernel schreibt, sollte bitte auch einen haben.
Du programmierst an einen monolithischen Kernel? Das ist ja eine völlig neue Information! Warum diskutieren wir dann so intensiv über Dein IPC-Konzept? Bei einem monolithischen Kernel reichen ganz normale Calls um vom VFS zum HDD-Treiber zu kommen und die User-Mode-Programme nutzen klassische Syscalls für alles was sie brauchen.

Sicher ist das bei einem Mikrokernel nicht mehr so schlimm, aber da wären wir wieder bei dem Thema wie Mikro ein Kernel ist.
Klar ist schon deswegen ein Micro-Kernel leichter zu programmieren weil er eben nicht unterbrechbar sein muss. Wenn in dem Kernel keine der normalen Dienste (wie VFS, TCP/UDP/IP, Treiber, Geräte-Management usw.) drin sind dann entsteht auch keinerlei Nachteil wenn er nicht unterbrechbar ist.

Zitat von: erik
Das mag auf x86 ja zutreffen aber auf viele andere CPUs trifft das nicht zu, z.B. ARM. Diese CPUs haben einen gewissen Teil der Register doppelt, einmal für den User-Mode und einmal für den System-Mode, und schalten beim Mode-Wechsel einfach um. Im System-Mode stehen einem gleich ein paar Register zur Verfügung die auch bereits sinnvolle Werte für den Kernel enthalten (z.B. einen Pointer zum Thread-Descriptor des gerade laufenden Threads) und das sichern der paar User-Mode-Register ist damit nicht viel mehr als ein einfacher Speicher-Zugriff.
Das ist z.B. ein Grund warum ich sage, das mein Kernel nicht portabel sein muss, sondern das ich dann für jede neue Architektur lieber einen neuen schreibe.
Was? Wegen solcher Kleinigkeiten (und das sichern der Register ist wirklich nur ein winziges Detail) willst Du gleich einen neuen Kernel programmieren? Und das auch noch bei einem Monolithen?

Was mir da gerade so auffällt ist wieso oft von Kernel-Threads und User-Threads gesprochen wird. Für mich war das immer das gleiche, aber es scheint wohl doch für viele einen Unterschied zu machen ob der Kernel im Kernel-Mode auch Threads haben kann (was für mich immer außer Frage stand).
Bei Kernel-Threads stellt sich eben das Problem in welchen Speicher-Kontext sie sind, eigentlich müssten sie ja in allen Kontexten drin sein, so wie der ganze Kernel ja auch. Außerdem müssen diese Threads auch vom Scheduler berücksichtigt werden und der muss beim reinspringen in so einen Kernel-Thread ja keinen Kontext-Switch machen sondern nur den Stack umschalten.

Zitat von: erik
als wenn read einen Pointer zurück gibt.
Irgendwie kommst du darüber nicht hinweg ;)
Ja, da komme ich wirklich nicht so einfach drüber hinweg. Es ist eben so das diese Variante nicht nur vom üblichen Weg abweicht sondern sie ist auch ziemlich unpraktisch (vor allem für normale Programme).

Was mir (und bestimmt auch uns was die Diskussion betrifft) weiterhelfen würde, wäre wenn jemand nen Mikrokernel kennt (der aber auch wirklich fast alles bis alles im UserSpace macht) wo der Source verfügbar ist. Damit man einfach mal gucken könnte wie andere sowas gelöst haben.
Da würde ich mal QNX empfehlen, die rühmen sich u.a. damit das der Kernel niemals ein Eigenleben entwickelt sondern nur per Kommando (da gibt es nur Syscalls, Exceptions und HW-Interrupts) aktiv wird und so schnell wie möglich wieder fertig ist. Klar gibt es unter den Syscals einige die länger dauern, z.B. sowas wie AllocPages oder CreateProcess, aber es ist bei QNX recht gut dokumentiert welche Syscalls in deterministischer Zeit fertig sind und welche nicht. Außerdem soll er POSIX-konform sein.

Zitat von: erik
Hä, genau das ist doch der Vorteil meiner Methode, nichts muss blockiert oder unterbrochen werden nur weil eine Message empfangen wurde (es wird ja ein neuer Thread erstellt) oder eben keine Message kommt (es soll ja kein aktives Abholen geben).
Das Problem sehe ich halt darin, das du also irgendetwas haben müsste wo du dann hinschreibst das eine Taste gedrückt wurde,
Wozu? Der PopUp-Thread kann doch gleich die passende Arbeit selber erledigen, da besteht doch kein Grund zur Eile, schließlich dürfen pro Message-Target auch mehrere PopUp-Threads aktiv sein.

und man ganz schön umdenken muss, insbesondere in richtig Multithreading und Synchronisation.
In diese Richtung müssen die Programmierer von Heute doch eh umdenken, schließlich wird man zukünftige CPUs kaum noch mit nem Single-Thread-Programm ausreizen können.


Eigentlich erstellt man doch einen SharedMem-Bereich eh nur dazu, das man den Speicher mit anderen Prozessen "teilen" kann, oder?
Dann wäre es aus performace Sicht nämlich besser nen Syscall zu machen, wo man eine Nachricht verschickt, ein SharedMem Bereich erstellt wird und die ID in die Msg (nicht sichtbarer Teil) gespeichert wird und beim Empfangen wird der SharedMem gleich gemappt und die Adresse wird mit der Msg übergeben.

Wie klingt diese Idee?
Die klingt so wie meine Idee! Wenn das weiter so geht muss ich noch Lizenz-Gebühren verlangen. ;)
Schau Dir noch mal meine Syscalls an http://forum.lowlevel.eu/index.php?topic=2433 (http://forum.lowlevel.eu/index.php?topic=2433), das ist zwar nicht mehr ganz aktuell aber das Prinzip ist gut sichtbar.


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 26. September 2010, 11:52
Zitat von: erik
Was macht Dich da so sicher?
Weil ich noch keinerlei Erfahrung mit A-I/O habe  :wink:

Zitat von: erik
Du programmierst an einen monolithischen Kernel?
Um Gottes Willen, nein!!!!

Zitat von: erik
Klar ist schon deswegen ein Micro-Kernel leichter zu programmieren weil er eben nicht unterbrechbar sein muss. Wenn in dem Kernel keine der normalen Dienste (wie VFS, TCP/UDP/IP, Treiber, Geräte-Management usw.) drin sind dann entsteht auch keinerlei Nachteil wenn er nicht unterbrechbar ist.
Naja, der Kernel an sich ist leichter zu programmieren, aber der Rest des OS ist in vielen Sachen schwieriger (Huhn-Ei-Problem).
Wenn man, wie ich, die Speicherverwaltung im Kernel macht, dann sollte der Kernel schon unterbrechbar sein, zumal wo ist denn bitte das Problem damit? Wenn du nachher im UserMode dich eh mit Synchronisation befassen musst, kannst du das doch auch schon im Kernel machen, zumal ich finde das es dort einfacher ist.
Wenn ich jetzt mal von einem 4 CPU System ausgehe und der Prozess 4 Threads hat die alle zur gleichen Zeit laufen und alle dummerweise auch noch zur gleichen Zeit nen Syscall machen, dass sie mehr Speicher brauchen. Dann möchte ich (wie du das siehst weis ich ja ;) ) das mein Kernel unterbrechbar ist, weil ich nicht alle 4 Threads gleichzeitig in meinen Datenstrukturen rumspielen lassen und wenn dann der Kernel nicht unterbreachbar ist, dann kann das schon mal ne "Weile" dauern bis auch der letzte Thread seinen Speicher hat.

Zitat von: erik
Was? Wegen solcher Kleinigkeiten (und das sichern der Register ist wirklich nur ein winziges Detail) willst Du gleich einen neuen Kernel programmieren?
Das mit dem Monolithen habe ich mal weggelassen ;)

Es gibt bestimmt noch andere Unterschiede zw ARM und x86! Mir geht es darum, das man nicht immer alles versuchen sollte gleich zu machen, sondern jede Architektur sollte bestmöglichst ausgenutzt werden und gerade bei einem Mikrokernel sollte der Aufwand nicht so groß sein.
Ich meine mein PMM und VMM kann ich ja beibehalten, aber den Rest würde ich sehr großzügig neu schreiben wollen.

Zitat von: erik
Bei Kernel-Threads stellt sich eben das Problem in welchen Speicher-Kontext sie sind, eigentlich müssten sie ja in allen Kontexten drin sein, so wie der ganze Kernel ja auch. Außerdem müssen diese Threads auch vom Scheduler berücksichtigt werden und der muss beim reinspringen in so einen Kernel-Thread ja keinen Kontext-Switch machen sondern nur den Stack umschalten.
Naja, wenn ich einen reinen Kernel-Thread habe (der also auch nie in den UserMode geht) dann tausche ich bei mir auch nur den Stack aus. Ist es aber ein Thread der sich nur gerade im KernelMode befindet tausche ich auch noch CR3 aus.
Ich sehe da wie gesagt nicht so das Problem oder die Schwierigkeit.

Zitat von: erik
Da würde ich mal QNX empfehlen, die rühmen sich u.a. damit das der Kernel niemals ein Eigenleben entwickelt sondern nur per Kommando (da gibt es nur Syscalls, Exceptions und HW-Interrupts) aktiv wird und so schnell wie möglich wieder fertig ist. Klar gibt es unter den Syscals einige die länger dauern, z.B. sowas wie AllocPages oder CreateProcess, aber es ist bei QNX recht gut dokumentiert welche Syscalls in deterministischer Zeit fertig sind und welche nicht. Außerdem soll er POSIX-konform sein.
Der Source von QNX war ja mal verfügbar, aber ist er das immernoch wimre war da doch was, dass der nur noch für zahlende Kunden zu sehen ist?

Zitat von: erik
Wozu? Der PopUp-Thread kann doch gleich die passende Arbeit selber erledigen, da besteht doch kein Grund zur Eile, schließlich dürfen pro Message-Target auch mehrere PopUp-Threads aktiv sein.
Mit irgendwo hinschreiben meinte ich halt das was ich dann mit der Bitmap geschrieben habe, denn du kannst ja schlecht nen neuen Framen rendern, jedes Mal wenn ein Popup-Thread ne Nachricht bearbeitet, das wäre 1. bestimmt nicht einfach und 2. könnte es dann wohl ne ganze Weile dauern bis mal wieder ein neuer Frame berechnet wird, wenn der Nutzer nichts macht.

Zitat von: erik
Die klingt so wie meine Idee! Wenn das weiter so geht muss ich noch Lizenz-Gebühren verlangen.
Zum Glück ist es doch nicht das gleiche ;)

Was mir aber dann bei meinem Konzept wieder auffällt. Es ist egal ob der Client den Speicher bei der Anfrage mitsendet oder ob er ihn mit der Antwort erhält.
Im Code sollte das nicht viel anders aussehen. Man muss nicht mal großartig umdenken, deswegen frage ich mich halt wo das große Problem damit ist (das finde ich vielleicht dann wenn ich dann irgendwann mal Programme portiere oder schon bei meiner libc).
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 26. September 2010, 20:52
Hallo,


Zitat von: erik
Du programmierst an einen monolithischen Kernel?
Um Gottes Willen, nein!!!!
Da bin ich aber beruhigt, ich befürchtete schon Du bist den Verlockungen der dunklen Seite erlegen. ;)

aber der Rest des OS ist in vielen Sachen schwieriger (Huhn-Ei-Problem).
Also da sehe ich kaum Probleme (kommt aber sicher noch wenn ich meine Ideen in die Realität überführen muss), es erfordert nur einiges an fertiger Infrastruktur bis ein printf("Hello Wordl\n"); korrekt über IPC funktioniert als nur mit nem simplen Syscall.

Wenn man, wie ich, die Speicherverwaltung im Kernel macht, dann sollte der Kernel schon unterbrechbar sein, zumal wo ist denn bitte das Problem damit?
Wieso muss der Kernel deswegen unterbrechbar sein? Klar gibt es Kollisionen wenn mehrere Prozesse gleichzeitig Syscalls aufrufen die sich gegenseitig behindern könnten aber wenn man die Stellen wo serialisiert werden muss auf ein Minimum beschränkt (und das ist IMHO auch beim Speicher-Management möglich) dürfte das kein Problem werden.

Wenn du nachher im UserMode dich eh mit Synchronisation befassen musst, kannst du das doch auch schon im Kernel machen, zumal ich finde das es dort einfacher ist.
Abgesehen davon das die Synchronisation unabhängig von CPU-Modus immer die selben Konzepte benutzt haben Synchronisation im Kernel und im User-Mode nichts miteinander zu tun. Das sind zwar identische Mechanismen die sich aber nicht berühren.

Wenn ich jetzt mal von einem 4 CPU System ausgehe und der Prozess 4 Threads hat die alle zur gleichen Zeit laufen und alle dummerweise auch noch zur gleichen Zeit nen Syscall machen, dass sie mehr Speicher brauchen.
Das Beispiel finde ich doof, die Threads in einem Prozess sollten schon in dem einem malloc serialisiert werden, aber ich verstehe was Du meinst. Trotzdem bin ich nicht der Meinung das dies ein ernstes Problem darstellt, es soll auch bei mir möglich sein das ein Thread zwar einen normalen Syscall macht aber dann blockiert wird und der Scheduler einen anderen Prozess aufruft und der Thread erst später fortgesetzt wird wenn sein Wunsch erfüllt werden konnte (das Anfordern von Speicher wird aber wohl nicht in diese Kategorie fallen).

Es gibt bestimmt noch andere Unterschiede zw ARM und x86!
Da gibt es nichts was der Compiler nicht wegabstrahieren könnte. Alle CPUs führen im wesentlichen Rechenbefehle aus und greifen auf eindimensionalen Speicher zu. ;)
Das einzigste was in einem Kernel für jede CPU individuell sein muss sind die paar unvermeidlichen Assembler-Stückchen und ein paar Strukturen (für allerlei Descriptoren und die gesicherten Register).

Mir geht es darum, das man nicht immer alles versuchen sollte gleich zu machen, sondern jede Architektur sollte bestmöglichst ausgenutzt werden und gerade bei einem Mikrokernel sollte der Aufwand nicht so groß sein.
Ich meine mein PMM und VMM kann ich ja beibehalten, aber den Rest würde ich sehr großzügig neu schreiben wollen.
Ich behaupte mal der erzielbare Performancevorteil dadurch liegt noch unterhalb der Messbarkeitsgrenze, dafür wäre mir der Aufwand einfach viel zu groß.


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 26. September 2010, 21:09
Zitat von: erik
Also da sehe ich kaum Probleme (kommt aber sicher noch wenn ich meine Ideen in die Realität überführen muss), es erfordert nur einiges an fertiger Infrastruktur bis ein printf("Hello Wordl\n"); korrekt über IPC funktioniert als nur mit nem simplen Syscall.
Mir ging es noch nicht mal um so ein Bsp., sondern darum dein OS erstmal lauffähig zu bekommen. Ich meine bei einem monolithen wo auch noch alle Treiber in den Kernel reinkompiliert sind, brauchst du nur den Kernel laden und alles läuft (vereinfacht). Bei nem Mikrokernel, müssen Treiber und Server samt Kernel geladen werden und dann gibt es auch noch Abhängigkeiten bzw. Sonderfälle, weil wie "läd" man eine Datei ohne das der nötige Server dafür geladen wurde (bzw. wie lädst du die Library die der VFS-Service benötigt)?
Sowas in der Richtig, nicht solch einfache Sache wie IPC ;)

Zitat von: erik
Wieso muss der Kernel deswegen unterbrechbar sein?
Wieso denn nicht? Ich weiß noch immer nicht, was daran so schlimm sein soll (oder schwierig).

Zitat von: erik
Abgesehen davon das die Synchronisation unabhängig von CPU-Modus immer die selben Konzepte benutzt haben Synchronisation im Kernel und im User-Mode nichts miteinander zu tun.
Mir ging es darum, das sich eine Semaphore (als Bsp.) effektiver/effizienter im KernelMode implementieren lässt, da man dort auch die Ints ausschalten kann (ist einfach effizienter als den Scheduler aufzurufen bzw. von diesem unterbrochen zu werden).

Zitat von: erik
Da gibt es nichts was der Compiler nicht wegabstrahieren könnte. Alle CPUs führen im wesentlichen Rechenbefehle aus und greifen auf eindimensionalen Speicher zu. Wink
Das einzigste was in einem Kernel für jede CPU individuell sein muss sind die paar unvermeidlichen Assembler-Stückchen und ein paar Strukturen (für allerlei Descriptoren und die gesicherten Register).
Da wären in meinem Fall die Timer, die FPU Behandlung (besonders das lazy Laden/Speichern des FPU Contextes wie ich es auf x86 mache, dürfte es so nicht auf ARM geben), IRQ-Management, Paging und mir fallen bestimmt noch mehr Sachen ein, wenn ich mich mal mit ARM beschäftigen würde.

Ich finde es auch einfach sauberer und übersichtlicher einen neuen Kernel zu schreiben als alles wegzuabstrahieren (der Code der trotzdem in allen Kernel verwendet wird ist natürlich ne andere Sache/Problem).
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 27. September 2010, 12:05
Hallo,


sondern darum dein OS erstmal lauffähig zu bekommen. Ich meine bei einem monolithen wo auch noch alle Treiber in den Kernel reinkompiliert sind, brauchst du nur den Kernel laden und alles läuft (vereinfacht). Bei nem Mikrokernel, müssen Treiber und Server samt Kernel geladen werden und dann gibt es auch noch Abhängigkeiten bzw. Sonderfälle, weil wie "läd" man eine Datei ohne das der nötige Server dafür geladen wurde (bzw. wie lädst du die Library die der VFS-Service benötigt)?
Das ist schon ein kniffliges Problem aber das sollte auf dem PC ein guter Bootloader problemlos hinbekommen. Da hab ich es auf meiner Plattform schon deutlich schwieriger (ganz ohne BIOS o.ä.). Bei mir soll ein primitiver Initial-Boot-Loader einfach direkt den Code im Boot-Image des OS anspringen (der Boot-Flash wird direkt in den normalen Speicher-Adressraum eingeblendet). In diesem Boot-Image ist dann etwas Code, das Kernel-Image und 3 Executables (idle, exec und init) drin. Der Code initialisiert den RAM, entpackt den Kernel dort hinein und bereitet alle Datenstrukturen des Kernels vor, anschließend werden die 3 Executables ebenfalls in den RAM entpackt aber so das daraus 3 fertige Prozesse entstehen (bei idle werden sogar gleich mehrere Threads erstellt, so viele wie CPUs da sind) also mit allen zugehörigen Verwaltungsstrukturen im Kernel. Danach werden alle CPUs in Betrieb genommen und die springen alle gleichzeitig in den User-Mode in jeweils einen Thread von idle, bis hier hin wurde noch nicht ein Befehl des eigentlichen Kernel ausgeführt. Erst wenn die Zeitscheiben der idle-Threads abgelaufen sind kommt dann der Kernel an die Reihe, er ist zu diesem Zeitpunkt bereits voll einsatzfähig (das OS-Image macht quasi eine OS-Lebendgeburt) und der Bootvorgang ist aus seiner Sicht damit abgeschlossen. Erst der init-prozess führt dann den richtigen Boot-Vorgang durch, dazu soll init eine interne primitive ROM-Disk enthalten in der die Programme für PCI, VFS und der Driver-Loader (welcher seinerseits wieder ein paar Basis-Treiber wie SATA und ext2 enthält) drin sind. Erst der PCI-Prozess enumeriert das PCI-System und findet alle Devices, weist ihnen Ressourcen zu und holt vom Driver-Loader die zugehörigen Treiber (wobei sicher einige Devices erst mal leer ausgehen und der PCI-Prozess es später, wenn alle Dateisysteme gemountet sind und der Driver-Loader auch echten HDD-Zugriff benutzen kann, noch mal probiert). Bis init dann eine Shell startet ist einiges zu tun aber das sind aus Kernel-Sicht alles nur normale User-Mode-Aktivitäten, das eigentliche OS läuft also bereits bevor auch nur das erste PCI-Gerät gefunden ist. Der Kernel selber verwaltet nur RAM, CPUs und ein paar elementare Geräte wie den IRQ-Controller und den Timer. Dieser Vorgang ist sicher nicht ganz ohne aber ein Henne-Ei-Problem kann ich da nicht erkennen, das einzigste was etwas doof ist ist dass das OS-Image recht groß ausfällt weil ein Haufen Programme mit drin stecken müssen, aber ein Linux-Kernel-Image mit Init-RAM-Disk ist auch nicht besonders klein.

Ich weiß noch immer nicht, was daran so schlimm sein soll (oder schwierig).
[....]
Mir ging es darum, das sich eine Semaphore (als Bsp.) effektiver/effizienter im KernelMode implementieren lässt, da man dort auch die Ints ausschalten kann (ist einfach effizienter als den Scheduler aufzurufen bzw. von diesem unterbrochen zu werden).
Also erst mal muss man nicht immer gleich die IRQs abschalten, schon weil der IRQ-Handler in einem Micro-Kernel ja nichts weiter macht als eine Message an einen (unterbrechbaren) User-Mode-Treiber zu schicken. Und wenn doch mal ein Thread mitten in einer Critical-Section vom Scheduler unterbrochen werden sollte ist das bei User-Mode-Programmen nicht halb so kompliziert wie im Kernel. Wenn man den Kernel unterbrechbar macht dann muss man anfangen Dinge wie kmalloc dagegen abzusichern und das kann in ziemlich viel Arbeit ausarten. Frage ruhig mal taljeth wie viel Arbeit es war dafür zu sorgen das in einem tyndur-Programm auch ein Signal-Handler malloc u.ä. benutzen kann. Im User-Mode ist das alles noch mit reichlich Fleiß zu meistern aber im Kernel-Mode sind IMHO doch einige zusätzliche Schwierigkeiten zu erwarten. Aber ich will Dich nicht von Deinen Plänen abhalten, implementiere ruhig einen unterbrechbaren Kernel und erzähle uns hinterher wie einfach das war.

Wenn es Dir um das Abschalten der IRQs im User-Mode geht dann programmiere doch ein OS für eine CPU bei der das ohne Probleme möglich ist. Auf z.B. meiner CPU soll es möglich sein das ein User-Mode-Thread die IRQs für eine kurze/begrenzte Zeit abschalten kann (ohne Exception o.ä.) um zumindest kleine Code-Abschnitte, die atomar ausgeführt werden sollen, ohne externe Unterbrechungen durchführen zu können (selbst wenn die Zeitscheibe in diesem Abschnitt abläuft soll das ignoriert werden bis dieser Abschnitt fertig ist). Natürlich sollte der User-Mode-Code in so einem Abschnitt keine Exceptions verursachen oder Syscalls benutzen. Das wird mir zumindest ermöglichen viele kleine Critical-Sections sehr effizient und völlig ohne Hilfe des Kernels zu implementieren.


nicht solch einfache Sache wie IPC ;)
Seit wann ist IPC einfach? Bei den vielen Fragen und Unklarheiten die in diesem Forum dazu diskutiert werden (und das nicht nur von uns beiden) scheint das eine recht komplizierte Angelegenheit zu sein. ;)

Da wären in meinem Fall die Timer
Timer messen die Zeit und sind oft in der Lage zu bestimmten Zeitpunkten einen IRQ zu generieren. Wenn man das hinter einen vernünftigen/flexiblen Interface verbirgt dann gibt es da kein Problem.

die FPU Behandlung (besonders das lazy Laden/Speichern des FPU Contextes wie ich es auf x86 mache, dürfte es so nicht auf ARM geben)
Auch bei ARM ist die FPU ein Co-Prozessor aber ob es dort dieses lazy Sichern/Laden des FPU-Kontextes gibt weiß ich jetzt auch nicht. Aber trotzdem sehe ich hier kein Problem, man muss halt einen entsprechenden Exception-Handler implementieren den es auf anderen Plattformen einfach nicht gibt. Wie willst du das eigentlich für Kernel-Threads realisieren oder dürfen die keine FPU benutzen?

IRQ-Management
Was ist daran unterschiedlich? Alle IRQ-Controller bekommen IRQ-Signale und triggern einen zugehörigen IRQ-Handler an (wenn die CPU das gerade erlaubt), ob dabei der IRQ-Vector in einer IDT oder direkt im IRQ-Controller oder in SW gespeichert ist ist doch völlig unerheblich. Auch das ein EOI an den IRQ-Controller geschickt werden muss ist wimre überall gleich, der OS-Kernel muss einfach eine plattformspezifische EOI-Funktion anbieten die immer die gleiche Signatur haben kann.

Paging
Paging basiert auf allen mir bekannten CPUs auf Directorys (mit Page-Table-Walk in HW) oder der Page-Table-Walk muss gleich ganz in SW implementiert werden (was auch auf Directorys hinaus läuft). Die einzigste mir bekannte Ausnahme ist der Itanium und so sehr weicht der auch nicht von dem üblichen Weg ab.

und mir fallen bestimmt noch mehr Sachen ein, wenn ich mich mal mit ARM beschäftigen würde.
Nur zu, klar gibt es sehr viele Unterschiede aber das sind überwiegend Details. Die Grundkonzepte sind überall gleich, schon weil alle CPU-Hersteller (außer ich) wollen das die üblichen Betriebssysteme (Linux usw.) problemlos darauf laufen.

Ich finde es auch einfach sauberer und übersichtlicher einen neuen Kernel zu schreiben als alles wegzuabstrahieren (der Code der trotzdem in allen Kernel verwendet wird ist natürlich ne andere Sache/Problem).
Ja ist es nicht einfach nur eine Frage des Betrachtungsstandpunktes? Ob man nun von den Unterschieden ausgeht und die jeweils neu implementiert und dann das Gemeinsame hinzu fügt oder ob man vom Gemeinsamen ausgeht und jeweils die Unterschiede individuell hinzufügt kommt doch im Endeffekt auf das selbe Ergebnis hinaus. Die Frage ist wo Du die Grenze ziehst. Also in welchem prozentualem Verhältnis sollen der gemeinsame Code und der individuelle Code zueinander stehen?


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 27. September 2010, 12:58
Zitat von: erik
... das eigentliche OS läuft also bereits bevor auch nur das erste PCI-Gerät gefunden ist.
Naja, das ist jetzt Definitionssache, was du als OS bezeichnest, was du vielleicht meinst ist der Kernel und das da weniger Aufwand als bei einem monolithen ist, dürfte klar sein (macht ja auch weniger).
Ich meine aber wenn du einen monolithen mit einem mikro vergleichst, dann kann man sagen dass das OS erst läuft wenn alle Server und Treiber laufen und bis dahin ist es dann doch um einiges komplexer bei einem Mikrokernel.

Zitat von: erik
Also erst mal muss man nicht immer gleich die IRQs abschalten, schon weil der IRQ-Handler in einem Micro-Kernel ja nichts weiter macht als eine Message an einen (unterbrechbaren) User-Mode-Treiber zu schicken.
Sollte er  :roll:

Ich muss mir bei mir nochwas schönes einfallen lassen, da ich auch Hardware habe (Timer) die ihr Arbeit nicht im UserMode sondern im KernelMode verrichten, aber eigentlich wollte ich das die richtigen Treiber dann wirklich nur ne Nachricht bekommen bzw. aufgeweckt werden.

Zitat von: erik
Aber ich will Dich nicht von Deinen Plänen abhalten, implementiere ruhig einen unterbrechbaren Kernel und erzähle uns hinterher wie einfach das war.
Also es war wirklich nicht schwierig ;) (mein Kernel war von Anfang an so geplant und das war mit das erste was ich implementiert habe).

Zitat von: erik
Wenn man den Kernel unterbrechbar macht dann muss man anfangen Dinge wie kmalloc dagegen abzusichern und das kann in ziemlich viel Arbeit ausarten.
Kann sein das ich da anders ticke, aber die Arbeit etwas Multithreading tauglich zu machen ist im Kernel- und UserMode eigentlich gleich. Denn du musst halt einfach die Datenstrukturen wo ein gleichzeitiger Zugriff nicht möglich ist durch irgendetwas schützen und das geht halt wesentlich effizienter wenn man dann die Ints abschalten kann (aber das hatten wir schonmal diskutiert).

Zitat von: erik
Wenn es Dir um das Abschalten der IRQs im User-Mode geht
Nein! Man sollte nichtmal daran denken, das ein UserMode Programm die Möglichkeit hat die Ints abzuschalten, das wäre genauso "dämlich" wie cooperatives-Multitasking. Sicher kann das auch funktionieren, aber einem warum sollte man es einem Programm so leicht machen den PC unbenutzbar zu machen!?

Zitat von: erik
Timer messen die Zeit und sind oft in der Lage zu bestimmten Zeitpunkten einen IRQ zu generieren. Wenn man das hinter einen vernünftigen/flexiblen Interface verbirgt dann gibt es da kein Problem.
Da mein Kernel schon einige Interfaces hat (weil er aus Modulen besteht die vom Loader zusammengefügt werden), weis ich das ein Interface zwar für einen Programmieren schön ist, aber wenn ich daran denke, wie viele sinnlose Funktionen (z.B. "uint32t foo(void bar) { return 0; }") ich dadurch habe. Dann kann ich gerne darauf verzichten.

Zitat von: erik
Wie willst du das eigentlich für Kernel-Threads realisieren oder dürfen die keine FPU benutzen?
Also erstens wenn man Kernel-Threads hat, dann kannst du die genauso benutzen wie User-Threads (es funktioniert bei mir also, theoretisch) und zweitens ist eine Regel von mir keine FPU im Kernel ;)

Zitat von: erik
Nur zu, klar gibt es sehr viele Unterschiede aber das sind überwiegend Details. Die Grundkonzepte sind überall gleich, schon weil alle CPU-Hersteller (außer ich) wollen das die üblichen Betriebssysteme (Linux usw.) problemlos darauf laufen.
Und genau damit habe ich ein Problem. Wenn man immer darauf bedacht ist kompatibel zu bleiben wird nichts richtiges neues mehr Entwickelt.
Wo wir wieder bei dem Henne-Ei-Problem wären. Wir halten an den ganzen alten Sachen fest und das hindert uns daran was richtig neues zu machen.

Zitat von: erik
Ja ist es nicht einfach nur eine Frage des Betrachtungsstandpunktes? Ob man nun von den Unterschieden ausgeht und die jeweils neu implementiert und dann das Gemeinsame hinzu fügt oder ob man vom Gemeinsamen ausgeht und jeweils die Unterschiede individuell hinzufügt kommt doch im Endeffekt auf das selbe Ergebnis hinaus. Die Frage ist wo Du die Grenze ziehst. Also in welchem prozentualem Verhältnis sollen der gemeinsame Code und der individuelle Code zueinander stehen?
Richtig! Und ich schreibe lieber meinen Kernel neu ;)
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 27. September 2010, 15:09
Hallo,


Naja, das ist jetzt Definitionssache, was du als OS bezeichnest
Klar, ich meinte das auch nicht so ganz ernst, ich wollte auf die Tatsache hinaus das ein Micro-Kernel bereits zu 100% einsatzfähig sein muss (also IPC, Timing, Speicherverwaltung und Prozess/Thread-Management) bevor auch nur ein Treiber o.ä. geladen werden kann.

Ich meine aber wenn du einen monolithen mit einem mikro vergleichst, dann kann man sagen dass das OS erst läuft wenn alle Server und Treiber laufen und bis dahin ist es dann doch um einiges komplexer bei einem Mikrokernel.
Damit hast Du natürlich recht aber da das alles im User-Mode abläuft kann man da auch einiges tricksen wie z.B. den Boot-Vorgang von einem Script steuern lassen oder über alle verfügbaren CPUs verteilen.

Ich muss mir bei mir nochwas schönes einfallen lassen, da ich auch Hardware habe (Timer) die ihr Arbeit nicht im UserMode sondern im KernelMode verrichten, aber eigentlich wollte ich das die richtigen Treiber dann wirklich nur ne Nachricht bekommen bzw. aufgeweckt werden.
Die einzigste HW die mir da einfällt ist eben der Timer aber auch der wird bei einem Micro-Kernel nur dazu benutzt irgendwelchen User-Mode-Prozessen eine Message zu schicken bzw. Threads aufzuwecken, sollte also auch ziemlich schnell sein.

Also es war wirklich nicht schwierig ;) (mein Kernel war von Anfang an so geplant und das war mit das erste was ich implementiert habe).
Bist Du sicher? Bitte nicht falsch verstehen aber bist Du wirklich sicher das in Deinem Code keine potentiellen Dead-Locks usw. drin sind oder sind die bis her nur nicht aufgetreten? Auf wie vielen CPUs lief Dein Kernel schon? Was denkst Du eigentlich wie viel Performance dieser zusätzliche Code im Kernel kostet und wie viel Performance ein unterbrechbarer Kernel dafür wieder bringt? Ich weiß es nicht genau aber mein Bauch sagt mir das bei einem Micro-Kernel wohl eher der Verlust überwiegen dürfte. In einem Micro-Kernel gibt es IMHO keine Vorgänge die so lange brauchen das man sie unterbrechbar machen müsste oder für die der Kernel eigene Threads braucht.
Ich hoffe wirklich das Dein Kernel so funktioniert wie Du Dir das vorstellst aber ich weiß auch das es da einige sehr spezielle Stolperfallen gibt. Selbst in Linux gab es deswegen schon den einen oder anderen Bug.

Kann sein das ich da anders ticke, aber die Arbeit etwas Multithreading tauglich zu machen ist im Kernel- und UserMode eigentlich gleich.
Nein, da kann ich Dich beruhigen, diesbezüglich tickst Du ganz normal.

und das geht halt wesentlich effizienter wenn man dann die Ints abschalten kann
Sicher? Das würde ich aber doch bezweifeln. Es ist ein Trade-Off aus dem zusätzlichen Aufwand zum Abschalten und Reaktivieren der IRQs (was wohl in >99% aller Fälle unnötig ist da die CPU ja nicht ständig IRQs bekommt) und dem Verlust wenn ein Thread gerade auf einen anderen wartet weil dieser einen Lock hat aber unterbrochen wurde und den Lock daher nicht freigibt. Sicher wird dieser Trade-Off oft zugunsten der abschaltbaren IRQs aufgehen aber das muss nicht zwangsläufig so sein. Wenn man z.B. die IRQs dynamisch über viele CPUs verteilen kann so das die Wahrscheinlichkeit hoch ist das immer eine CPU Zeit hat oder wenn man Hardware benutzt die möglichst viele Jobs auf ein mal selbstständig erledigt und so nur wenige IRQs generiert kann es durchaus sein das dieser Trade-Off in die andere Richtung aufgeht.

Man sollte nichtmal daran denken, das ein UserMode Programm die Möglichkeit hat die Ints abzuschalten, das wäre genauso "dämlich" wie cooperatives-Multitasking. Sicher kann das auch funktionieren, aber einem warum sollte man es einem Programm so leicht machen den PC unbenutzbar zu machen!?
Wo siehst Du das Problem? Auf meiner CPU soll es möglich sein das ein User-Mode-Thread für eine sehr kurze Zeit, z.B. für die nächsten 32 Assemblerbefehle, das annehmen von IRQs blockieren kann. Wenn man dafür seiner aktuellen Restzeitscheibe immer 1ms als Fairness-Ausgleich abzieht dann kann es keine Gefahr geben das so ein Thread den Computer unbenutzbar macht. Zumal nach Ablauf dieser kurzen Zeitspanne die CPU ja prüfen kann ob IRQs in der Zeit angelaufen sind und diese dann verarbeiten, dadurch erhöht sich höchstens etwas die Latenz. Und da bei mir die IRQs dynamisch, anhand der Prioritäten des gerade ausgeführten Codes, über alle CPUs verteilt werden sollen dürfte es kaum messbar sein wenn ab und an mal eine CPU sich aus diesem Prozess kurz ausklingt, ein Syscall hat die selbe Wirkung weil bei mir ja der Kernel grundsätzlich nicht unterbrechbar ist.

Da mein Kernel schon einige Interfaces hat (weil er aus Modulen besteht die vom Loader zusammengefügt werden), weis ich das ein Interface zwar für einen Programmieren schön ist
Ja ist das denn kein wichtiger Grund? Was macht es schon ob Dein Loader 5 oder 50 Module lädt? Ein sauber programmierter und klar strukturierter Kernel in dem der Programmierer sich gut zurecht findet (der deswegen vielleicht weniger Fehler enthält) ist IMHO sehr viel mehr wert!

wie viele sinnlose Funktionen (z.B. "uint32t foo(void bar) { return 0; }") ich dadurch habe.
Ich weiß ja nicht wie Du Interfaces entwickelst aber solche Funktionen hatte ich noch nie (und wenn doch würde die ein guter Compiler wegoptimieren).

ist eine Regel von mir keine FPU im Kernel
Die armen Kernel-Threads, das empfinde ich schon als erhebliche Einschränkung.

Und genau damit habe ich ein Problem. Wenn man immer darauf bedacht ist kompatibel zu bleiben wird nichts richtiges neues mehr Entwickelt.
Wo wir wieder bei dem Henne-Ei-Problem wären. Wir halten an den ganzen alten Sachen fest und das hindert uns daran was richtig neues zu machen.
Dann kannst Du ja sehr gut nachvollziehen warum ich eine komplett neue Plattform entwickeln möchte. Ich will endlich mal mit ein paar alten Traditionen brechen und beweisen das es auch anders geht.
Wenn Du so denkst, warum programmierst Du dann Dein OS ausgerechnet für x86?

Richtig! Und ich schreibe lieber meinen Kernel neu ;)
Ich schreibe zwar definitiv kein portables OS aber wenn ich es tun würde würde ich mich schon bemühen meine Zeit etwas effizienter zu verwalten. Nimm es mir nicht über aber gleich den ganzen Kernel (und sei es nur ein Micro-Kernel) neu zu programmieren halte ich für reichlich ungeschickt, das geht mit einer guten Modularisierung auf Quell-Code-Ebene deutlich besser.


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 27. September 2010, 15:35
Zitat von: erik
Die einzigste HW die mir da einfällt ist eben der Timer aber auch der wird bei einem Micro-Kernel nur dazu benutzt irgendwelchen User-Mode-Prozessen eine Message zu schicken bzw. Threads aufzuwecken, sollte also auch ziemlich schnell sein.
Mir geht es gar nicht darum das es eventuell "länger" dauern könnte, sondern das ich gerne ein einheitliches Interface hätte (das perfekte Interface kann meiner Meinung nach nicht existieren bzw. wäre es mir zu langsam ;) ) was das betrifft.

Zitat von: erik
Bist Du sicher? Bitte nicht falsch verstehen aber bist Du wirklich sicher das in Deinem Code keine potentiellen Dead-Locks usw. drin sind oder sind die bis her nur nicht aufgetreten?
Sicher kann man nur so lange sein, bis einer auftritt ;) Ich hatte erst einen (daher auch der Thread, der war nämlich die Ursache) und der war ziemlich dämlich :(
Ich habe bei einem Read-Write-Lock, als Reader den Lock bekommen und habe ihn als Writer wieder freigegeben (bzw. hat der Code das versucht und hat mein OS festgefahren).

Zitat von: erik
... oder für die der Kernel eigene Threads braucht.
Ich habe z.B. pro CPU 1 Idle-Thread und das ist ein Kernel-Thread und dann kommt noch mein "Thread-Killer" dazu.

Zitat von: erik
Wo siehst Du das Problem?
Wie stellst du sicher dass das Programm die Ints nicht für immer deaktiviert? Das ist der Grund warum man sowas für gewöhnlich nicht zulässt.

Zitat von: erik
Ein sauber programmierter und klar strukturierter Kernel in dem der Programmierer sich gut zurecht findet (der deswegen vielleicht weniger Fehler enthält) ist IMHO sehr viel mehr wert!
Sorry, aber das klingt für mich nach Uni-gelabber ;) Du müsstest es doch besser wissen, das es in der Arbeitswelt sowas nicht gibt, da soll immer alles schneller als möglich gehen und das ist für mich auch ein Grund warum wir zwar immer schneller CPUs haben, aber irgendwie werden viele (bzw. bei nem normalen User fast alle) Programme nicht schneller.

Zitat von: erik
Ich weiß ja nicht wie Du Interfaces entwickelst aber solche Funktionen hatte ich noch nie (und wenn doch würde die ein guter Compiler wegoptimieren).
In dem Fall ging es darum, das die HW nicht dieses Feature unterstützt hat.

Zitat von: erik
Die armen Kernel-Threads, das empfinde ich schon als erhebliche Einschränkung.
Korregiere mich wenn ich falsch liege, aber ersten wozu brauchst du ne FPU im Kernel und welcher Kernel unterstützt das (wimre ist das auch bei Linux ne Regel, das ist aber nur ne wage Erinnerung ;) )?

Zitat von: erik
Wenn Du so denkst, warum programmierst Du dann Dein OS ausgerechnet für x86?
Fast alles bekannt und einfach zugänglich, es gibt genügend Bsp., ich kann es wunderbar einfach testen und ich komme billiger als billig an Hardware.
Wenn ich nicht so knapp bei Kasse wäre, hätte ich schon längst nen BeagleBoard!

Zitat von: erik
Ich schreibe zwar definitiv kein portables OS aber wenn ich es tun würde würde ich mich schon bemühen meine Zeit etwas effizienter zu verwalten. Nimm es mir nicht über aber gleich den ganzen Kernel (und sei es nur ein Micro-Kernel) neu zu programmieren halte ich für reichlich ungeschickt, das geht mit einer guten Modularisierung auf Quell-Code-Ebene deutlich besser.
Ich behaupte das es nicht wirklich lange dauern sollte, meinen Kernel auf ne andere Architektur zu portieren (das weis ich aber auch erst, wenn es denn mal passiert ist).
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 27. September 2010, 18:00
Hallo,


sondern das ich gerne ein einheitliches Interface hätte (das perfekte Interface kann meiner Meinung nach nicht existieren bzw. wäre es mir zu langsam ;) ) was das betrifft.
Du meinst das jeder Kernel ein Interface bekommt das individuell auf die entsprechende HW angepasst ist?

pro CPU 1 Idle-Thread und das ist ein Kernel-Thread
Bei mir ist idle ein User-Mode-Prozess. Da idle bei mir das Speicherdefragmentieren übernehmen soll hatte ich auch mal überlegt dafür doch Kernel-Mode-Threads zu implementieren aber ich denke es wird auch ohne gehen, der Kernel stellt ein paar spezielle Syscalls zur Verfügung die nur idle benutzen darf.

noch mein "Thread-Killer"
Der klingt aber gefährlich, welche Art von Programmen muss sich den vor dem in Acht nehmen?

Wie stellst du sicher dass das Programm die Ints nicht für immer deaktiviert?
Es soll einen speziellen IRQ-Disable-Befehl geben der die IRQs abschaltet, dieser IRQ-Disable-Befehl hat ein Immediate-Parameter das die Anzahl der Befehle angibt die dann geschützt werden sollen (über den gültigen Wertebereich hab ich noch nicht so genau nachgedacht aber vermutlich was in der Art 4...31, alles andere ist dann ein Illegal-OpCode). Wenn dieser IRQ-Disable-Befehl kommt rechnet die CPU die Strafzeit, z.B. pro Befehl 4 us, aus und verringert die Restzeitscheibe entsprechend, falls die übrige Restzeit <=0 ist gibt es sofort eine Yield-Exception und der IP wird auf diesen IRQ-Disable-Befehl gesetzt (so das wenn dann dieser Thread wieder vom Scheduler dran kommt ein neuer Versuch unternommen wird). Wenn der IRQ-Disable-Befehl erfolgreich verarbeitet wurde (wozu auch das flushen der Pipeline gehört) werden die nächsten Befehle geschützt ausgeführt und der lokale Zeitscheiben-Timer angehalten, wenn innerhalb der erlaubten X Befehle ein spezieller IRQ-React-Befehl (welcher auch die Pipeline flusht) kommt wird der Schutz wieder aufgehoben und die Zeitscheibe läuft wieder weiter. Wenn innerhalb der X Befehle kein solcher IRQ-React-Befehl kommt gibt es ne Exception (und der Prozess wird gekillt). Wenn einer der geschützten Befehle eine Exception verursacht (oder ein Syscall-Befehl dabei ist, was eigentlich nicht sein darf) dann geht die CPU in den Kernel-Mode und dieser Schutz ist vergessen, das heißt das wenn der Exception-Handler zurückkehrt werden die restlichen Befehle ohne diesen Schutz ausgeführt (das ist dann aber auch nicht mehr so schlimm weil der Thread, wohl im Besitz eines Lock, ja eh schon unterbrochen wurde), man tut also gut daran aufzupassen das dieser Code möglichst keine Exception verursacht (ist ja auch nicht so schwer). Mit dem IRQ-React-Befehl werden wieder die IRQs freigeschalten und die CPU prüft auch gleich ob was anliegt. Das die Schutzzeit in Befehlen und nicht in Zeit bemessen wird liegt daran das die Ausführungssgeschwindigkeit ja nicht konstant oder vorhersehbar ist.

Sorry, aber das klingt für mich nach Uni-gelabber ;) Du müsstest es doch besser wissen, das es in der Arbeitswelt sowas nicht gibt, da soll immer alles schneller als möglich gehen
Gerade weil ich im Berufsleben und auch privat schon mit Projekten sehr unterschiedlicher Quell-Code-Qualität zu tun hatte weiß ich ganz genau wie wichtig eine möglichst hohe Quell-Code-Qualität ist. Wobei ich auch sagen muss das die Projekte mit denen ich privat in Kontakt komme tendenziell eine höhere Quell-Code-Qualität haben, ist auch logisch weil ein Open-Source-Programmierer von seinen Mitstreitern anhand seiner Arbeit beurteilt wird und ein angestellter Programmierer wird von seinem Chef oft danach beurteilt wie gut er ihm in den Ar*** kriecht. Sauberer Quell-Code trägt erheblich zu einer fehlerarmen Software bei und lohnt sich daher immer (ich hatte auch schon Chefs die das genau so sahen und der Erfolg der Projekte hat ihnen Recht gegeben). Ich bin mir sicher dass das hier einige Leute bestätigen können.

In dem Fall ging es darum, das die HW nicht dieses Feature unterstützt hat.
Dann sollte der Compiler sich darum kümmern und die Funktion inlinen.

Korregiere mich wenn ich falsch liege, aber ersten wozu brauchst du ne FPU im Kernel und welcher Kernel unterstützt das (wimre ist das auch bei Linux ne Regel, das ist aber nur ne wage Erinnerung ;) )?
Also ich hab die FPU schon mal in einem Kernel benutzt, war aber auch ein ziemlich irrer Mechanismus für den ich die FPU brauchte. In Linux gibt es wimre Regeln in was für Code-Bereichen wie viel (von FPU/SSE/....) benutzt werden darf, aber komplett verboten ist das wimre nicht.

Fast alles bekannt und einfach zugänglich, es gibt genügend Bsp., ich kann es wunderbar einfach testen und ich komme billiger als billig an Hardware.
Das sind doch nur Ausreden und zum Großteil glatt falsch. ;)
ARM ist auch recht gut Dokumentiert (vor allem zu diesen voll integrierten SoC-Chips gibt es oft downloadbare Manuals mit über 1000 Seiten) und hinter dem genannten Board steht eine Community die sicher auch noch die restlichen Fragen beantworten kann. Für entsprechende SoCs auf MIPS- oder PowerPC-Basis sieht die Sache nicht anders aus.
Alle 3 Architekturen sind deutlich frischer, schlanker und vor allem konsistenter als es x86 ist.
(das sollte jetzt kein Flame gegen x86 sein, nur eine kurze Tatsachenbeschreibung)

Ich behaupte das es nicht wirklich lange dauern sollte, meinen Kernel auf ne andere Architektur zu portieren (das weis ich aber auch erst, wenn es denn mal passiert ist).
Da gibt es wirklich nur eine Methode das heraus zu finden. Mach das einfach mal und Du wirst sehen wie viel leichter das gehen kann wenn man ordentlich abstrahiert und strukturiert. ;)


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 27. September 2010, 18:13
Zitat von: erik
Du meinst das jeder Kernel ein Interface bekommt das individuell auf die entsprechende HW angepasst ist?
Selbstverständlich nicht, jeder Kernel muss die selben Syscalls zur Verfügung stellen (da frag ich mich gerade was meinst du eigentlich genau mit "jeder Kernel"?), aber ich wäre halt froh wenn man nur einmal abstrahieren müsste. Da ich aber verschiedene Module habe, müssen diese alles das selbe Interface implementieren damit keine "Symbol nicht gefunden" Fehler passieren können.

Zitat von: erik
Bei mir ist idle ein User-Mode-Prozess. Da idle bei mir das Speicherdefragmentieren übernehmen soll hatte ich auch mal überlegt dafür doch Kernel-Mode-Threads zu implementieren aber ich denke es wird auch ohne gehen, der Kernel stellt ein paar spezielle Syscalls zur Verfügung die nur idle benutzen darf.
Naja, ich unterscheide zw dem Idle-Thread (der wirklick nur "hlt" ausführt und im Kernel laufen muss) und anderen Idle-Threads, die immer dann laufen wenn nichts ansteht.

Zitat von: erik
Der klingt aber gefährlich, welche Art von Programmen muss sich den vor dem in Acht nehmen?
Jeder ;) Das ist mein Thread der Prozesse/Threads vollständig beendet (also deren ganze Ressourcen freigibt und sowas).

Zitat von: erik
Wenn dieser IRQ-Disable-Befehl kommt rechnet die CPU die Strafzeit, z.B. pro Befehl 4 us, aus und verringert die Restzeitscheibe entsprechend, falls die übrige Restzeit <=0 ist gibt es sofort eine Yield-Exception und der IP wird auf diesen IRQ-Disable-Befehl gesetzt (so das wenn dann dieser Thread wieder vom Scheduler dran kommt ein neuer Versuch unternommen wird).
Sicher das du das meinst was du geschrieben hast? Weil das hört sich danach an, das deine CPU deine Datenstrukturen kennt.

Ansonsten ist sowas natürlich zu begrüßen.

Zitat von: erik
Dann sollte der Compiler sich darum kümmern und die Funktion inlinen.
Das geht nicht, da diese Symbole ja erst beim Booten aufgelöst werden.

Zitat von: erik
ARM ist auch recht gut Dokumentiert (vor allem zu diesen voll integrierten SoC-Chips gibt es oft downloadbare Manuals mit über 1000 Seiten) und hinter dem genannten Board steht eine Community die sicher auch noch die restlichen Fragen beantworten kann.
Gut dann das Argument das ich ein Consumer-OS schreibe und nicht eins für nen Mikrokontroller. Das BeagleBoard kommt dem schon am nächsten, aber ich hab halt nicht einfach mal so 150€ rumliegen :(

Ich sags mal so, wenn ich einen rein finanziellen Grund hätte, würde ich mir irgendein Nischen-Produkt suchen und dafür ein OS schreiben, da das bestimmt mehr Erfolg hätte, aber so nehme ich die Arch die am weitverbreitesten ist (unter normalen PCs) ergo x86. Ob die Arch toll ist seih mal dahingestellt.
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 27. September 2010, 20:08
Hallo,


Selbstverständlich nicht, jeder Kernel muss die selben Syscalls zur Verfügung stellen (da frag ich mich gerade was meinst du eigentlich genau mit "jeder Kernel"?), aber ich wäre halt froh wenn man nur einmal abstrahieren müsste.
Mit jeder Kernel meinte ich jeden Kernel der auf eine neue HW-Plattform (wobei jetzt mal PC mit nur PIC und PC mit HPET schon zwei verschiedene Plattformen sind) kommt bekommt zu seinem Unterbau (also die fest eingebauten Treibern) ein angepasstes API was es ihm ermöglicht die spezielle HW auch so gut als möglich auszureizen. Das API zu den Applikationen (die Syscalls) bleibt natürlich immer gleich. Ich hoffe jetzt hab ich genau das getroffen was Du meinst.

ich unterscheide zw dem Idle-Thread (der wirklick nur "hlt" ausführt und im Kernel laufen muss) und anderen Idle-Threads, die immer dann laufen wenn nichts ansteht.
Jetzt bin ich aber neugierig, worin unterscheiden sich diese beiden Dinge? Wie entscheidet der Kernel oder jemand anderes darüber welche dieser Möglichkeiten gewählt wird? Gibt es von beiden Sorten so viele Threads wie es CPUs gibt? Warum muss der HLT im Kernel ausgeführt werden?

Das ist mein Thread der Prozesse/Threads vollständig beendet (also deren ganze Ressourcen freigibt und sowas).
Also eine Art Garbage-Collector, interessant das Du so eine Idee umsetzen willst. ;)

Sicher das du das meinst was du geschrieben hast? Weil das hört sich danach an, das deine CPU deine Datenstrukturen kennt.
Ja ich meine wirklich genau das was ich geschrieben hab. Welche Daten-Strukturen sollte meine CPU denn dafür kennen? Ich bin mir nicht bewusst irgendwelche Dinge vom Speicher genannt zu haben, meine CPU soll, mit Ausnahme der Descriptoren für Segmente und Pages, überhaupt nicht selbstständig auf den Speicher zugreifen. Der Zeitscheiben-Counter ist eh in HW implementiert (ein simpler Downcounter in den der Scheduler immer die Länge der Zeitscheibe des nächsten Thread reinlegt und der bei 0 aufhört zu zählen und eine Yield-Exception generiert also einen Kontext-Switch auslöst durch den dann der Scheduler aktiv wird) und kann daher auch dort manipuliert werden und Exceptions muss die CPU auch so auslösen können. Ein kleiner zusätzlicher Downcounter der die Befehle zählt und bei != 0 die IRQ-Annahme + Zeitscheiben-Counting sperrt ist IMHO keine so große Erweiterung mehr.

Ansonsten ist sowas natürlich zu begrüßen.
Genau deswegen will ich sowas ja implementieren, ich betrachte meine Plattform auch als Spielwiese um mal gänzlich neue Dinge auszuprobieren.

Gut dann das Argument das ich ein Consumer-OS schreibe und nicht eins für nen Mikrokontroller.
Weder ARM noch MIPS oder PowerPC sind nur Mikrocontroller! Die beiden letzten sind sogar in 64 Bit verfügbar und wurden schon in sehr großen Super-Computern eingesetzt! Was ist eigentlich an Mikrocontrollern so schlimm?


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 28. September 2010, 09:46
Zitat von: erik
Mit jeder Kernel meinte ich jeden Kernel der auf eine neue HW-Plattform (wobei jetzt mal PC mit nur PIC und PC mit HPET schon zwei verschiedene Plattformen sind) kommt bekommt zu seinem Unterbau (also die fest eingebauten Treibern) ein angepasstes API was es ihm ermöglicht die spezielle HW auch so gut als möglich auszureizen. Das API zu den Applikationen (die Syscalls) bleibt natürlich immer gleich. Ich hoffe jetzt hab ich genau das getroffen was Du meinst.
Das ist so ungefähr das was ich geplant hatte.

Zitat von: erik
Jetzt bin ich aber neugierig, worin unterscheiden sich diese beiden Dinge? Wie entscheidet der Kernel oder jemand anderes darüber welche dieser Möglichkeiten gewählt wird? Gibt es von beiden Sorten so viele Threads wie es CPUs gibt? Warum muss der HLT im Kernel ausgeführt werden?
Sie unterscheiden sich dahingehend, das so Programme immer nur dann ausgeführt werden wenn die CPU nichts zu tun hat, dann wird aber kein Strom gespart. Die Wahl ist ganz einfach, Idle-Threads (ohne "hlt") sind bei mir Priorität 0 und laufen, wie gesagt, nur wenn keine anderen Threads laufen und wenn gar keine Threads in der ready-Queue sind, läuft der richtige Idle-Thread der dann ein "hlt" ausführt.
Ich bin der Meinung das "hlt" nur im Ring0 ausgeführt werden darf, deswegen sind das Kernel-Threads.

Zitat von: erik
Also eine Art Garbage-Collector, interessant das Du so eine Idee umsetzen willst.
Nicht wirklich, denn nen Garbage-Collector rufst du ja nicht auf und dieser Thread ist aus der Not geboren, weil ich keine bessere Idee hatte.
So ist es halt einfacher, sowas wie Stack, Thread-Discriptor und solche Sachen freizugeben.
Der Thread wird per Semaphore aufgeweckt.

Zitat von: erik
Ja ich meine wirklich genau das was ich geschrieben hab. Welche Daten-Strukturen sollte meine CPU denn dafür kennen? Ich bin mir nicht bewusst irgendwelche Dinge vom Speicher genannt zu haben, meine CPU soll, mit Ausnahme der Descriptoren für Segmente und Pages, überhaupt nicht selbstständig auf den Speicher zugreifen. Der Zeitscheiben-Counter ist eh in HW implementiert (ein simpler Downcounter in den der Scheduler immer die Länge der Zeitscheibe des nächsten Thread reinlegt und der bei 0 aufhört zu zählen und eine Yield-Exception generiert also einen Kontext-Switch auslöst durch den dann der Scheduler aktiv wird) und kann daher auch dort manipuliert werden und Exceptions muss die CPU auch so auslösen können. Ein kleiner zusätzlicher Downcounter der die Befehle zählt und bei != 0 die IRQ-Annahme + Zeitscheiben-Counting sperrt ist IMHO keine so große Erweiterung mehr.
Also sowas wie nen APIC, denn was du beschreibst ist kein extra Timer-Chip, der muss in der CPU sitzen.

Zitat von: erik
Weder ARM noch MIPS oder PowerPC sind nur Mikrocontroller! Die beiden letzten sind sogar in 64 Bit verfügbar und wurden schon in sehr großen Super-Computern eingesetzt! Was ist eigentlich an Mikrocontrollern so schlimm?
Naja nen Mikrokontroller der genug Leistung und die Möglichkeit zur Eingabe und zur Bildschirmausgabe hat, ist einfach zu teuer. Ich weiß das es da genügend von gibt, aber das finanzielle spielt da halt auch ne Rolle und nur im Emulator testen wollte ich dann auch nicht. Das ist ein weiterer Grund ich habe schon Schwierigkeiten nen vernünftigen Emulator für ARM zu finden, der unter Windows läuft und nicht alzu schwer zu bedienen ist (da wäre nur Qemu aber selbst kompiliert bekomme ich es nicht und richtig zum Laufen hab ich den ARM Emulator auch noch nicht bekommen).
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 28. September 2010, 12:21
Hallo,


Sie unterscheiden sich dahingehend, das so Programme immer nur dann ausgeführt werden wenn die CPU nichts zu tun hat, dann wird aber kein Strom gespart.
Definiere mal Bitte "die CPU nichts zu tun hat". Das einzigste Kriterium ist doch das wenn es gar keine "runnable" Threads gibt und da es noch die Threads vom idle-Prozess gibt (die eigentlich niemals blockieren) wird diese Bedingung nie erfüllt.

Die Wahl ist ganz einfach, Idle-Threads (ohne "hlt") sind bei mir Priorität 0 und laufen, wie gesagt, nur wenn keine anderen Threads laufen und wenn gar keine Threads in der ready-Queue sind, läuft der richtige Idle-Thread der dann ein "hlt" ausführt.
Wie kommt es zur zweiten Situation? Warum wird nicht auch in der ersten Situation Energie gespart?
In meinem OS wollte ich das so machen das die idle-Threads immer eine Schleife mit einem HLT-Befehl ausführen also die CPU für ihre Zeitscheibe schlafen lassen (IRQs werden aber weiterhin angenommen). Wenn die Zeitscheibe abgelaufen ist versucht der Scheduler wieder andere Threads laufen zu lassen und wenn es keine anderen gibt dann eben wieder einen idle-Thread. Als Verbesserung könnte der idle-Thread die System-Auslastung erfragen und dann die CPU länger und tiefer schlafen lassen aber diesmal per Syscall so das der Kernel über das aufwecken bestimmen kann aber auch keine IRQs von der CPU akzeptiert werden. Ich könnte aber auch ein DoIdle-Syscall implementieren der vom idle-Thread immer in der Schleife aufgerufen wird und dessen Rückgabewert bestimmt ob der idle-Thread diesen Syscall in der nächsten Zeitscheibe noch mal aufrufen soll (weil es was zu tun gab) oder ob jetzt doch eher HLT dran ist (weil es nichts zu tun gab). In diesem Syscall könnte dann der Kernel entscheiden ob er die aktuelle CPU dazu benutzen möchte um etwas Speicher zu defragmentieren oder ob die CPU besser im Tiefschlaf aufgehoben ist oder ob sie lieber in Bereitschaft (HLT-Befehl im User-Mode) bleiben soll.

Ich bin der Meinung das "hlt" nur im Ring0 ausgeführt werden darf, deswegen sind das Kernel-Threads.
Da bin ich mir jetzt auch nicht sicher, was sagt den das Intel-Manual dazu?

Nicht wirklich, denn nen Garbage-Collector rufst du ja nicht auf und dieser Thread ist aus der Not geboren, weil ich keine bessere Idee hatte.
So ist es halt einfacher, sowas wie Stack, Thread-Discriptor und solche Sachen freizugeben.
Das heißt Du gibst den Speicher erst später frei weil es schwierig ist einen Thread-Descriptor wegzuräumen wenn man das im Kontext eben dieses Threads macht (weil der Thread sich selber gekillt hat).

Also sowas wie nen APIC, denn was du beschreibst ist kein extra Timer-Chip, der muss in der CPU sitzen.
Der Zeitscheiben-Timer ist ein simpler Down-Counter (der Scheduler legt die Zeit rein und der Counter zählt bis 0) und jede CPU hat natürlich einen eigenen. Dieser Counter ist für nichts anderes zu gebrauchen außer die Zeitscheibe eines User-Mode-Threads zu verwalten. Ich bemühe mich meinen CPUs genau die Fähigkeiten zu geben die ich auch in der Praxis benötige, nicht mehr und nicht weniger, für einen vollwertigen x86-Local-APIC hab ich einfach keine Verwendung und über den normalen HW-Timer (der im Chipsatz sitzt und einem HPET sehr ähnlich wird) kann ich keine IRQs an eine bestimme CPU schicken um eben einen bestimmten Thread am Ende seiner Zeitscheibe zu unterbrechen.


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 29. September 2010, 14:44
Zitat von: erik
Definiere mal Bitte "die CPU nichts zu tun hat". Das einzigste Kriterium ist doch das wenn es gar keine "runnable" Threads gibt und da es noch die Threads vom idle-Prozess gibt (die eigentlich niemals blockieren) wird diese Bedingung nie erfüllt.
Mein Idle-Threads tauchen nicht in der Liste auf, von daher kann das funktionieren.

Zitat von: erik
Wie kommt es zur zweiten Situation? Warum wird nicht auch in der ersten Situation Energie gespart?
Die zweite Situation hab ich dir ja oben erklärt. Naja, ich dachte mir halt das es auch Threads geben muss die nur Laufen wenn wirklich kein anderer die CPU braucht und dann muss ja auch nicht unbedingt Energie gespart werden.

Zitat von: erik
Da bin ich mir jetzt auch nicht sicher, was sagt den das Intel-Manual dazu?
Sagt Ring0.
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 29. September 2010, 17:56
Hallo,


Mein Idle-Threads tauchen nicht in der Liste auf
Das macht aber den Scheduler komplizierter als nötig, der muss dan ja normale User-Mode-Threads, User-Mode-Threads die nicht auf der Runnable-Liste stehen und Kernel-Threads (eventuell auch mit und ohne Runnable-Liste) unterstützen. Das wäre mir zu aufwendig, mein Scheduler soll genau eine Art von Threads kennen und die auch immer auf die selbe Art an die CPU lassen. Bei mir dient der idle-Prozess auch dazu das der Scheduler niemals auf eine komplett leere Runnable-Liste stößt (wie ich mehrere Teillisten für die unterschiedlichen Prioritäten verwalte weiß ich noch nicht genau).

Naja, ich dachte mir halt das es auch Threads geben muss die nur Laufen wenn wirklich kein anderer die CPU braucht und dann muss ja auch nicht unbedingt Energie gespart werden.
Schon klar, aber diese Threads (die nicht zum idle-Prozess gehören und echte Arbeit leisten) hätten dann eben nur die zweit niedrigste Priorität, bei mir soll es unmöglich sein das ein Thread auf normalem Weg auf die unterste idle-Priorität konfiguriert werden kann das wird nur beim Kernel-Boot gehen wenn der idle-Prozess vom Boot-Code gebaut wird. Wenn wirklich ein echter idle-Thread dran kommt dann kann auch immer Energie gespart werden.

Sagt Ring0.
Aha, dann würde ich den HLT-Befehl als Syscall anbieten den nur der idle-Prozess benutzen darf.


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 29. September 2010, 18:40
Zitat von: erik
Das macht aber den Scheduler komplizierter als nötig, der muss dan ja normale User-Mode-Threads, User-Mode-Threads die nicht auf der Runnable-Liste stehen und Kernel-Threads (eventuell auch mit und ohne Runnable-Liste) unterstützen. Das wäre mir zu aufwendig, mein Scheduler soll genau eine Art von Threads kennen und die auch immer auf die selbe Art an die CPU lassen. Bei mir dient der idle-Prozess auch dazu das der Scheduler niemals auf eine komplett leere Runnable-Liste stößt (wie ich mehrere Teillisten für die unterschiedlichen Prioritäten verwalte weiß ich noch nicht genau).
So kompliziert ist das gar nicht. Ich habe eine Datenstruktur wo Sachen drin stehen die pro CPU gelten und da ist auch ein Pointer zum entsprechenden Idle-Thread drin.
Ich rufe ne Funktion auf, die mir den Thread mit der höchsten Priorität aus der Ready-Queue holt, gibt die Funktion 0 zurück, wird der Idle-Thread ausgeführt.

Warum einen extra Prozess für das Idlen erstellen?

Zitat von: erik
Aha, dann würde ich den HLT-Befehl als Syscall anbieten den nur der idle-Prozess benutzen darf.
Da hätte ich dann aber ein Problem, wenn ich deine Ideen umsetzen würde. Denn mit deaktivierten Ints (zwecks nicht unterbrechbar) macht der "hlt" nicht viel Sinn.
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 30. September 2010, 13:06
Hallo,


Ich rufe ne Funktion auf, die mir den Thread mit der höchsten Priorität aus der Ready-Queue holt, gibt die Funktion 0 zurück, wird der Idle-Thread ausgeführt.
Dieses prüfen auf 0 ist ein Spezialfall der extra behandelt werden muss (macht den Code größer und tendenziell unübersichtlicher), nebst dessen das der Vergleich auf 0 extra CPU-Zeit kostet. Ich bemühe mich in meiner SW möglichst alles was grundlegend gleich ist auch gleich zu behandeln, das ist IMHO eine wichtige SW-Design-Regel. Sonderfälle kosten nur zusätzlichen Code und jeder zusätzliche Code kann wiederum Fehler enthalten, gerade bei einem Micro-Kernel sollte man bemüht sein die Code-Basis so klein wie möglich zu halten. Dazu kommt das sich in einfachen Code auch nur mit geringerer Wahrscheinlichkeit Fehler einschleichen können wie in komplexen Code.

Warum einen extra Prozess für das Idlen erstellen?
Um eben keinen Sonderfall zu haben. Da ich nur User-Mode-Threads habe müssen auch alle Threads zu einem Prozess gehören. Ich finde die minimale Platzverschwendung für die Prozess-Descriptor-Struktur ist harmloser als an mehreren Stellen im Kernel-Code extra Spezialfallbehandlungen zu implementieren.

Da hätte ich dann aber ein Problem, wenn ich deine Ideen umsetzen würde. Denn mit deaktivierten Ints (zwecks nicht unterbrechbar) macht der "hlt" nicht viel Sinn.
Tja, das ist eben eine Designschwäche von x86. ;)
Für diesen speziellen Syscall sollte man natürlich die IRQs freischalten, das würde auch nicht mit CPU-lokalen Kernel-Mode-Stacks kollidieren da die CPU ja für die gesamte Dauer von HLT im Besitzt bleibt.

Auf meiner CPU soll es 3 Arten solcher Befehle geben PAUSE, HLT und SLEEP. Der PAUSE-Befehl dient dazu die CPU kurz, etwa 10 bis 100 Takte, nichts tun zu lassen (das spart nur wenig Energie da ja alles aktiv bleibt) und dient dazu z.B. in einer Schleife auf einen Lock zu warten ohne das man gleich die ganze Zeitscheibe abgeben möchte, dieser Befehl funktioniert in allen CPU-Modi. Der HLT-Befehl darf nur im User-Mode benutzt werden und lässt die CPU tiefer schlafen aber noch auf externe IRQs reagieren (dabei wird diese CPU sogar bevorzugt weil sie mit HLT auch die niedrigste Priorität meldet), auch der Zeitscheibencounter läuft weiter und löst am Ende auch ne Yield-Exception aus so das ein HLT niemals die CPU ganz blockiert. Der SLEEP-Befehl darf nur im System-Mode benutzt werden und damit wird die CPU in einen sehr tiefen Schlaf versetzt, wo z.B. auch der Takt ganz abgeschaltet wird, um möglichst viel Energie zu sparen. Aufwecken funktioniert beim SLEEP aber nur wenn diese CPU von einer anderen CPU speziell angetriggert wird, der Kernel entscheidet also darüber welche CPUs sich wie lange im Tiefschlaf befinden sollen.


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: kevin am 30. September 2010, 13:21
Ich glaube, du hast bisher vergessen zu erwähnen, warum du Ring-0-Tasks für böse hältst. Das ist nämliche eine einfache Lösung für den Idle-Task, und die korrekte noch dazu. ;)

hlt im Userspace halte ich für Blödsinn. Wie soll denn der Userspace sagen können, ob es auf dieser CPU gerade nichts sinnvolles zu tun gibt? Ein Syscall "Hey Kernel, ich hab grad nichts zu tun" halte ich für sinnvoller. Und dann kann der Kernel entscheiden, ob man die CPU wirklich schlafen schicken sollte oder ob einfach erstmal eine anderer Task drankommt. Andere Tasks warten zu lassen, weil ein Thread meint, die CPU bis zum nächsten Timerinterrupt mit Nichtstun beschäftigen zu müssen, klingt jedenfalls nicht besonders hilfreich.
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 30. September 2010, 19:56
Hallo,


Ich glaube, du hast bisher vergessen zu erwähnen, warum du Ring-0-Tasks für böse hältst.
Stimmt, das habe ich noch nicht.
Ich halte System-Mode/Ring-0-Threads/Tasks nicht grundsätzlich für böse, ich betrachte es nur als Design-Ziel das mein Kernel keinerlei eigene Aktivitäten entfaltet. Außerdem würde ein unterbrechbarer System-Mode meine CPU komplizierter machen: ich habe nur ein einziges Set an Schattenregistern wogegen z.B. ARM für jeden der verschiedenen System-Modi ein eigenes Set hat aber dafür hab ich einfach zu viele Register und den x86-Weg das die CPU eigenständig in den Speicher/Stack schreibt möchte ich nicht gehen. Daraus ergibt sich das HLT ein User-Mode-Befehl ist und SLEEP ein System-Mode-Befehl. Und weil ich der Meinung bin keinen unterbrechbaren System-Mode zu brauchen (mit den sich daraus ergebenen Rahmenbedingungen komme ich IMHO sehr gut zurecht) erkläre ich das einfach zur Plattform-Eigenschaft. ;)

Das ist nämliche eine einfache Lösung für den Idle-Task, und die korrekte noch dazu.
Dass das eine einfache Lösung ist stimmt natürlich (zumindest für x86) aber warum das die korrekte Lösung ist musst Du mir mal erklären.

hlt im Userspace halte ich für Blödsinn. Wie soll denn der Userspace sagen können, ob es auf dieser CPU gerade nichts sinnvolles zu tun gibt? ....
Du hast wahrscheinlich nicht 5 Artikel davor geschaut (soll kein Vorwurf sein) :
Ich könnte aber auch ein DoIdle-Syscall implementieren der vom idle-Thread immer in der Schleife aufgerufen wird und dessen Rückgabewert bestimmt ob der idle-Thread diesen Syscall in der nächsten Zeitscheibe noch mal aufrufen soll (weil es was zu tun gab) oder ob jetzt doch eher HLT dran ist (weil es nichts zu tun gab). In diesem Syscall könnte dann der Kernel entscheiden ob er die aktuelle CPU dazu benutzen möchte um etwas Speicher zu defragmentieren oder ob die CPU besser im Tiefschlaf aufgehoben ist oder ob sie lieber in Bereitschaft (HLT-Befehl im User-Mode) bleiben soll.
Zusätzlich möchte ich ein Flag implementieren das den HLT-Befehl freischaltet und dieses wird der Kernel (bzw. der Boot-Code) nur für den idle-Prozess setzen.

Wie soll denn der Userspace sagen können, ob es auf dieser CPU gerade nichts sinnvolles zu tun gibt? ....
Das entscheidet nicht der User-Mode-Code selber sondern dessen idle-Priorität, die idle-Threads kommen eben nur dran wenn es nichts zu tun gibt.


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: kevin am 30. September 2010, 22:18
Naja, elegant ist imho was anderes, aber wenn es ein Workaround für Einschränkungen der Hardware ist, lass ich's dir mal durchgehen. ;)
Titel: Re:Shared Memory
Beitrag von: Svenska am 01. October 2010, 01:24
Das ist nämliche eine einfache Lösung für den Idle-Task, und die korrekte noch dazu.
Dass das eine einfache Lösung ist stimmt natürlich (zumindest für x86) aber warum das die korrekte Lösung ist musst Du mir mal erklären.
Auf x86 ist ein HLT ein Ring0-Befehl und kann daher nur von einem Ring0-Thread ausgeführt werden. ;-)
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 01. October 2010, 09:10
Hallo,


Naja, elegant ist imho was anderes, aber wenn es ein Workaround für Einschränkungen der Hardware ist, lass ich's dir mal durchgehen.
Was genau meinst Du damit? Ich finde meine Lösung schon recht elegant.


Auf x86 ist ein HLT ein Ring0-Befehl und kann daher nur von einem Ring0-Thread ausgeführt werden.
Das ist mir bewusst aber warum ist es korrekt das ein HLT-Befehl im Ring-0/System-Mode ausgeführt werden soll? Gerade weil doch der HLT-Befehl von externen Interrupts u.ä. unterbrochen werden können muss (im Gegensatz zu irgendwelchen Schlaf-Modi die es bei x86 ja auch gibt) ist es meiner Meinung nach nicht logisch das nur ein Kernel diesen Befehl ausführen können soll (natürlich sollte der Kernel die Ausführung dieses Befehls reglementieren können). Alle üblichen Wechsel in den Ring-0 (INT-Befehle, IRQs und Exceptions) deaktivieren auf x86 immer das IE-Flag womit zumindest suggeriert wird das der Ring-0 nicht per Default unterbrechbar ist. Es ist aus meiner persönlichen Sicht ein kleines x86-Design-Mysterium das der HLT-Befehl zwar in einem unterbrechbaren Umfeld sein muss (sonst käme die CPU da ja nicht mehr raus) aber nur in einem Umfeld benutzt werden kann das per Default nicht unterbrechbar ist.


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: kevin am 01. October 2010, 12:20
Naja, elegant ist imho was anderes, aber wenn es ein Workaround für Einschränkungen der Hardware ist, lass ich's dir mal durchgehen.
Was genau meinst Du damit? Ich finde meine Lösung schon recht elegant.
Du hast also einen Befehl, der nur im Userspace geht. Deswegen fragt der entsprechende Userspace-Prozess erstmal beim Kernel nach, ob er diesen Befehl benutzen soll. Damit das kein Userspaceprozess macht, der es nicht machen sollte, baust du ein Flag ein, mit dem der Kernel dem Userspaceprozess explizit erlauben muss, den Befehl auch auszuführen, nachdem er den Kernel gefragt hat, ob er es tun soll.

Ist das nicht ein bisschen von hinten durch die Brust ins Auge?

Zitat
Gerade weil doch der HLT-Befehl von externen Interrupts u.ä. unterbrochen werden können muss (im Gegensatz zu irgendwelchen Schlaf-Modi die es bei x86 ja auch gibt) ist es meiner Meinung nach nicht logisch das nur ein Kernel diesen Befehl ausführen können soll (natürlich sollte der Kernel die Ausführung dieses Befehls reglementieren können). Alle üblichen Wechsel in den Ring-0 (INT-Befehle, IRQs und Exceptions) deaktivieren auf x86 immer das IE-Flag womit zumindest suggeriert wird das der Ring-0 nicht per Default unterbrechbar ist. Es ist aus meiner persönlichen Sicht ein kleines x86-Design-Mysterium das der HLT-Befehl zwar in einem unterbrechbaren Umfeld sein muss (sonst käme die CPU da ja nicht mehr raus) aber nur in einem Umfeld benutzt werden kann das per Default nicht unterbrechbar ist.
Ich weiß nicht wo du diesen Default hernimmst, aber ich sehe in der Architektur keine besondere Bevorzugung von Interrupt Gates gegenüber Trap Gates (wobei der Unterschied ja bekanntlich der Wert von IF ist). Wenn du Interrupt Gates benutzt, aber eigentlich Trap Gates haben willst, bist du vollkommen selber schuld.

Ach, und ein iret nach Ring 0, also in einen Kernel-Task, ist so unüblich auch wieder nicht... Nicht jeder Kernel läuft komplett in Interrupthandlern ab.
Titel: Re:Shared Memory
Beitrag von: Svenska am 01. October 2010, 14:51
Nun, HLT ändert einen internen Zustand der CPU - wie ein CLI auch. Außerdem hält HLT die CPU solange an, bis ein externes, nicht vom Kernel ausgehendes(!) Ereignis auftritt.

Nur weil mein VLC in der Lage ist, die Videoframes schneller zu dekodieren als nötig, hat er noch lange nicht das Recht, meine CPU für unbekannt lange Zeit anzuhalten. Das soll mal schön der Kernel machen, wenn er der Meinung ist, dass wirklich nichts weiter auf dieser CPU ansteht.

Beachte: Der wichtige Punkt ist das unbekannt. Das kann auch unendlich sein (für Interrupts abgeschaltet, aus welchem Grund auch immer). Überhaupt sollte ein Userspace-Task die CPU nicht anhalten können, auch nicht für einen Zeitraum innerhalb der eigenen Zeitscheibe. Das ist genauso elegant wie busy waiting, nur energieeffizienter. ("Wenn ich schon nichts tun kann, soll es bitte auch kein anderer können.") Der Task sollte die Zeitscheibe lieber dem System zur Verfügung stellen, was dann irgendetwas damit anstellen kann, wie z.B. die CPU anhalten, wenn kein anderer möchte.

Für den Standardfall (d.h. größtenteils Leerlauf im System) spielt das keine Rolle, ob die CPU nun vom (einzigen) Programm oder vom Kernel angehalten wird, aber unter Last verschenkst du damit einiges an CPU-Zyklen.

Gruß,
Sebastian
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 01. October 2010, 15:17
Zitat von: erik
Alle üblichen Wechsel in den Ring-0 (INT-Befehle, IRQs und Exceptions) deaktivieren auf x86 immer das IE-Flag womit zumindest suggeriert wird das der Ring-0 nicht per Default unterbrechbar ist. Es ist aus meiner persönlichen Sicht ein kleines x86-Design-Mysterium das der HLT-Befehl zwar in einem unterbrechbaren Umfeld sein muss (sonst käme die CPU da ja nicht mehr raus) aber nur in einem Umfeld benutzt werden kann das per Default nicht unterbrechbar ist.
Das ist, wie dir taljeth ja schon gesagt hat, nicht korrekt.

Was mir noch aufgefallen ist, es dürfte verdammt schwierig werden andere OS auf deine Platform zu portieren, wenn dein System-Mode Grundsätzlich nicht unterbrechbar ist.

Ansonsten seh ich immernoch kein Problem damit das "hlt" nur im Ring0 ausgeführt werden kann, genauso wenig damit, das meine Idle-Threads auch Kernel-Threads und keine kompletten Prozesse sind.
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 01. October 2010, 15:21
Hallo,


Ist das nicht ein bisschen von hinten durch die Brust ins Auge?
Ein bisschen hast Du schon recht aber ich habe eben die Plattform-Eigenschaft das der System-Mode grundsätzlich nicht unterbrechbar ist (damit ich die CPU möglichst einfach halten kann) und von daher muss der HLT-Befehl zwangsläufig in den User-Mode. Mag sein das dies eine Stelle ist wo mir mein Konzept eine winzige Unannehmlichkeit aufzwingt aber als so schlimm empfinde ich das an dieser Stelle gar nicht, der DoIdle-Syscall macht ja schließlich noch andere Sachen (den hätte ich sowieso). Ein unterbrechbarer System-Mode würde mir auf jeden Fall deutlich mehr Unannehmlichkeiten aufzwingen (komplexeres Register-File in der CPU und einen komplexeren Scheduler wenn er verschiedene Arten von Threads können soll usw.) und ich kann da einfach keinen Vorteil erkennen. Bis jetzt hab ich noch kein Killer-Argument für Kernel-Mode-Threads bei Micro-Kerneln gesehen.

Zu den Freischalt-Flags muss ich sagen das ich eh ein Controll-Register (in jedem CPU-Kern) dafür benötige, viele Arten von Befehlen müssen erst Freigeschalten werden (aus recht unterschiedlichen Gründen) und von daher macht dort ein Bit mehr auch nichts mehr aus. Außerdem werden diese Bits bereits im Decoder verarbeitet und kosten so im eigentlichen CPU-Kern keine zusätzliche Logik, nicht freigeschaltete Befehle werden vom Deocer einfach als Illegal-OpCode decodiert.

Ich weiß nicht wo du diesen Default hernimmst, aber ich sehe in der Architektur keine besondere Bevorzugung von Interrupt Gates gegenüber Trap Gates (wobei der Unterschied ja bekanntlich der Wert von IF ist). Wenn du Interrupt Gates benutzt, aber eigentlich Trap Gates haben willst, bist du vollkommen selber schuld.
Ich schätze die Zeit hat dann doch bereits ein paar Lücken in meine x86-Kenntnisse gefressen.
Normalerweise ist es aber schon besser wenn beim Einsprung in den Kernel erst mal die IRQs aus sind und der Kernel dann die IRQs wieder freischaltet wenn es sicher ist (anders habe ich das auch noch nie gesehen). Wenn wir an den Real-Mode denken dann ist das IE-Flag auch per Default gelöscht wenn in einen Interrupt/IRQ/Exception-Handler gesprungen wird. Wie sieht das eigentlich im Long-Mode oder bei den SYSCALL/SYSENTER-Befehlen aus?

Ach, und ein iret nach Ring 0, also in einen Kernel-Task, ist so unüblich auch wieder nicht... Nicht jeder Kernel läuft komplett in Interrupthandlern ab.
Das ist mir vollkommen klar, ich weiß das es viele Kernel dieser Art gibt. Für mein OS möchte ich das aber nicht, ich sehe bei einem Micro-Kernel dafür einfach keine Notwendigkeit. Und wie gesagt, ich möchte meine CPU so einfach wie möglich halten und da ist ein IRET in den System-Mode nicht gerade förderlich.

Wimre ist doch der tyndur-Kernel auch nicht unterbrechbar, oder?


HLT ändert einen internen Zustand der CPU - wie ein CLI auch.
Welchen Zustand ändert HLT? Und wo ist da die Gemeinsamkeit mit CLI?

Außerdem hält HLT die CPU solange an, bis ein externes, nicht vom Kernel ausgehendes(!) Ereignis auftritt.
Ganz recht, aber wenn das im User-Mode gemacht wird dann kommt dieses Ereignis spätestens mit dem Ablaufen der Zeitscheibe.

Nur weil mein VLC in der Lage ist, die Videoframes schneller zu dekodieren als nötig, hat er noch lange nicht das Recht, meine CPU für unbekannt lange Zeit anzuhalten. Das soll mal schön der Kernel machen, wenn er der Meinung ist, dass wirklich nichts weiter auf dieser CPU ansteht.
Ich bin völlig Deiner Meinung, hab ich das irgendwo mal anders dargelegt?

Beachte: Der wichtige Punkt ist das unbekannt. Das kann auch unendlich sein (für Interrupts abgeschaltet, aus welchem Grund auch immer).
Das kann bei mir deswegen nicht unendlich sein weil es auf meiner CPU keinen Weg gibt die IRQs im User-Mode dauerhaft abzuschalten (selbst der Kernel kann da nichts drehen). Ein User-Mode-Thread ist spätestens mit Ablauf seiner Zeitscheibe die CPU wieder los!

Überhaupt sollte ein Userspace-Task die CPU nicht anhalten können, auch nicht für einen Zeitraum innerhalb der eigenen Zeitscheibe. Das ist genauso elegant wie busy waiting, nur energieeffizienter.
Deswegen muss das ja auch freigeschaltet werden damit nur die Threads des idle-Prozess das dürfen.

Für den Standardfall (d.h. größtenteils Leerlauf im System) spielt das keine Rolle, ob die CPU nun vom (einzigen) Programm oder vom Kernel angehalten wird, aber unter Last verschenkst du damit einiges an CPU-Zyklen.
Wo bitte werden da CPU-Zyklen verschenkt? Der HLT-Befehl ist doch nicht ineffizienter nur weil er im User-Mode arbeitet. Außerdem werden die Threads des idle-Prozess unter Last gar nicht erst aufgerufen (wegen deren niedrigerer Priorität.)


Was mir noch aufgefallen ist, es dürfte verdammt schwierig werden andere OS auf deine Platform zu portieren, wenn dein System-Mode Grundsätzlich nicht unterbrechbar ist.
Das ist auch nicht mein Ziel, ich betrachte Plattform und OS als eine Einheit.

Ansonsten seh ich immernoch kein Problem damit das "hlt" nur im Ring0 ausgeführt werden kann, genauso wenig damit, das meine Idle-Threads auch Kernel-Threads und keine kompletten Prozesse sind.
Ich sehe eben 2 verschiedene Arten von Threads und um diese auf die CPU zu bringen benötigst Du 2 verschiedene Mechanismen (auch wenn die Unterschiede nur marginal sind).


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: Svenska am 01. October 2010, 15:30
Hallo,

HLT ändert einen internen Zustand der CPU - wie ein CLI auch.
Welchen Zustand ändert HLT? Und wo ist da die Gemeinsamkeit mit CLI?
Welchen, ist egal. CLI sorgt dafür, dass keine Interrupts mehr gemeldet werden und HLT sorgt dafür, dass die CPU nichts mehr tut, bis ein Interrupt auftritt. Das sind zwei Zustände, die dem System obliegen sollten, nicht irgendwelchen Threads.

Außerdem hält HLT die CPU solange an, bis ein externes, nicht vom Kernel ausgehendes(!) Ereignis auftritt.
Ganz recht, aber wenn das im User-Mode gemacht wird dann kommt dieses Ereignis spätestens mit dem Ablaufen der Zeitscheibe.
Eleganter ist es, wenn für diese Zeit die CPU nicht angehalten wird, sondern ein anderer Thread noch die Möglichkeit kriegt, zu arbeiten.

Überhaupt sollte ein Userspace-Task die CPU nicht anhalten können, auch nicht für einen Zeitraum innerhalb der eigenen Zeitscheibe. Das ist genauso elegant wie busy waiting, nur energieeffizienter.
Deswegen muss das ja auch freigeschaltet werden damit nur die Threads des idle-Prozess das dürfen.
Das entspricht aber deinem SLEEP - ein Prozess kann sagen "hey, ich muss grad mal 10-100 Takte warten" und hält dann die CPU an. Sofern die Endzeit noch in seine Zeitscheibe fällt.

Für den Standardfall (d.h. größtenteils Leerlauf im System) spielt das keine Rolle, ob die CPU nun vom (einzigen) Programm oder vom Kernel angehalten wird, aber unter Last verschenkst du damit einiges an CPU-Zyklen.
Wo bitte werden da CPU-Zyklen verschenkt? Der HLT-Befehl ist doch nicht ineffizienter nur weil er im User-Mode arbeitet. Außerdem werden die Threads des idle-Prozess unter Last gar nicht erst aufgerufen (wegen deren niedrigerer Priorität.)
Nein, wenn ein Userspaceprogramm die CPU anhalten kann, so wird damit potentiell die Möglichkeit verschenkt, den Rest der Zeitscheibe einem bedürftigem Thread zukommen zu lassen.

Bei dir gibst du dem Idle-Thread damit einen Teil der Ring0-Rechte über ein Flag. Userspace darf auch bei dir kein HLT machen.

So, jetzt ist wieder der Punkt, ab dem man Haare spalten kann. ;-)

Gruß,
Sebastian
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 01. October 2010, 15:40
Zitat von: erik
Ich sehe eben 2 verschiedene Arten von Threads und um diese auf die CPU zu bringen benötigst Du 2 verschiedene Mechanismen (auch wenn die Unterschiede nur marginal sind).
Jetzt sind wir zwar wirklich beim Haare spalten, aber egal.

Der für mich einzige wirkliche Unterschied zw. Kernel- und User-Threads liegt in der Erstellung, wenn man den Stack aufbauen muss, ansonsten ist es auch meinem Scheduler egal (und er weis es nicht mal direkt) was das für ein Thread ist.

Zitat von: erik
Bis jetzt hab ich noch kein Killer-Argument für Kernel-Mode-Threads bei Micro-Kerneln gesehen.
Wie willst du Threads/Prozesse beenden und ihre Ressourcen freigeben?

Zitat von: erik
Deswegen muss das ja auch freigeschaltet werden damit nur die Threads des idle-Prozess das dürfen.
Darauf möchte ich jetzt mit etwas antworten, was du geschrieben hast:
Zitat von: erik
... als an mehreren Stellen im Kernel-Code extra Spezialfallbehandlungen zu implementieren.
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 01. October 2010, 15:58
Hallo,


Eleganter ist es, wenn für diese Zeit die CPU nicht angehalten wird, sondern ein anderer Thread noch die Möglichkeit kriegt, zu arbeiten.
Ich hab doch jetzt nun mehrmals geschrieben das die idle-Threads nur dann dran kommen wenn wirklich niemand sonst will, das wird über die Prioritäten gemanaged.

Das entspricht aber deinem SLEEP - ein Prozess kann sagen "hey, ich muss grad mal 10-100 Takte warten" und hält dann die CPU an. Sofern die Endzeit noch in seine Zeitscheibe fällt.
Du meinst wohl PAUSE (nicht SLEEP, der macht noch ganz andere Sachen). Aber auch PAUSE hat nicht so viel gemeinsam mit HLT. HLT wartet bis zum nächsten Ereignis und PAUSE nur für ein paar Takte.

Nein, wenn ein Userspaceprogramm die CPU anhalten kann, so wird damit potentiell die Möglichkeit verschenkt, den Rest der Zeitscheibe einem bedürftigem Thread zukommen zu lassen.
Darf ich Dich noch mal an die Prioritäten erinnern? Die Threads des idle-Prozess kommen nur dran wenn es wirklichen gar keinen bedürftigen Thread gibt!

Bei dir gibst du dem Idle-Thread damit einen Teil der Ring0-Rechte über ein Flag. Userspace darf auch bei dir kein HLT machen.
Genau so ist es, das ist eben der Preis den ich zahlen muss damit ich keinen unterbrechbaren System-Mode brauche aber ein Bit in irgendeinem Controll-Register in der CPU kostet nun wirklich nicht viel.

So, jetzt ist wieder der Punkt, ab dem man Haare spalten kann. ;-)
Ich liebe es Haare zu spalten! :-D



Der für mich einzige wirkliche Unterschied zw. Kernel- und User-Threads liegt in der Erstellung, wenn man den Stack aufbauen muss, ansonsten ist es auch meinem Scheduler egal (und er weis es nicht mal direkt) was das für ein Thread ist.
Es bleibt eine Sonderbehandlung, egal ob im Scheduler oder in CreateThread().

Wie willst du Threads/Prozesse beenden und ihre Ressourcen freigeben?
Mit einem kfree(), ich verstehe nicht worauf Deine Frage abziehlt.

Zitat von: erik
Deswegen muss das ja auch freigeschaltet werden damit nur die Threads des idle-Prozess das dürfen.
Darauf möchte ich jetzt mit etwas antworten, was du geschrieben hast:
Zitat von: erik
... als an mehreren Stellen im Kernel-Code extra Spezialfallbehandlungen zu implementieren.
Dann antworte ich auch mit etwas das ich selber geschrieben hab:
Zusätzlich möchte ich ein Flag implementieren das den HLT-Befehl freischaltet und dieses wird der Kernel (bzw. der Boot-Code) nur für den idle-Prozess setzen.
Ich meine die 3 Worte zwischen den Klammern. Der idle-Prozess wird bei mir ja nicht zur Laufzeit per CreateProcess() gebaut (hab ich auch in dieser Diskussion erklärt) sondern ist schon da wenn der Kernel zum Leben erwacht.


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 01. October 2010, 17:42
Zitat von: erik
Mit einem kfree(), ich verstehe nicht worauf Deine Frage abziehlt.
Naja, ich finde/fand es immer nicht wirklich einfach diese ganzen Ressourcen von dem Thread freigeben zu lassen der ja noch läuft (wie gibt man denn Stack und PageTables/Dir frei wenn der Thread noch läuft) und das wäre halt ein guter Grund für mind. einen Kernel-Thread.

Zitat von: erik
Ich meine die 3 Worte zwischen den Klammern. Der idle-Prozess wird bei mir ja nicht zur Laufzeit per CreateProcess() gebaut (hab ich auch in dieser Diskussion erklärt) sondern ist schon da wenn der Kernel zum Leben erwacht.
Ändert ja nichts daran, das du dafür ne Extra-Behandlung, wo auch immer, benötigst und du ja geschrieben hast, dass das nicht so toll ist zwecks Fehler.
Der Punkt ist einfach, der Idle-Thread ist halt was "besonderes" und wo ich den nun mit extra Code besonders behandle ist doch egal (ob Scheduler oder Boot-Code).
Titel: Re:Shared Memory
Beitrag von: kevin am 01. October 2010, 17:42
Ist das nicht ein bisschen von hinten durch die Brust ins Auge?
Ein bisschen hast Du schon recht aber ich habe eben die Plattform-Eigenschaft [...]
Wie gesagt, ich lass es dir als einen Workaround für die Einschränkungen der Hardware durchgehen. Aber erzähl mir nichts von elegant. Auf x86 gibt es jedenfalls keinen Grund, das so zu machen, weil es da diese Einschränkungen der Hardware nicht gibt.

Zitat
Normalerweise ist es aber schon besser wenn beim Einsprung in den Kernel erst mal die IRQs aus sind und der Kernel dann die IRQs wieder freischaltet wenn es sicher ist (anders habe ich das auch noch nie gesehen).
Wenn du dann mal was neues sehen willst, empfehle ich zur Lektüre trap_init() in /usr/src/linux/arch/x86/kernel/traps_32.c - lies und staune. ;)

Zitat
Wenn wir an den Real-Mode denken dann ist das IE-Flag auch per Default gelöscht wenn in einen Interrupt/IRQ/Exception-Handler gesprungen wird. Wie sieht das eigentlich im Long-Mode oder bei den SYSCALL/SYSENTER-Befehlen aus?
Das mit dem Real Mode ist schon richtig. Da bleibt aber auch nicht viel anderes übrig, wenn man nur eine Art von Interrupthandlern zur Verfügung stellt.

Laut Intel-Manual schaltet sysenter IF aus, während syscall eine vom OS definierten Maske für eflags benutzt.

Zitat
Wimre ist doch der tyndur-Kernel auch nicht unterbrechbar, oder?
Er wird nicht preempted, ja. Aber kooperativ machen wir es an bestimmten Stellen.

Wie ist das eigentlich dann bei dir, wenn im Kernel eine Exception passiert? Ist die dann grundsätzlich nicht behebbar? Ansonsten müsstest du ja quasi nach einem Interrupt in einen Ring-0-Task zurückkehren und hättest damit schon alles wesentliche, was auch einen unterbrechbaren Kernel erlaubt, oder?
Titel: Re:Shared Memory
Beitrag von: kevin am 01. October 2010, 17:46
Zitat von: erik
Mit einem kfree(), ich verstehe nicht worauf Deine Frage abziehlt.
Naja, ich finde/fand es immer nicht wirklich einfach diese ganzen Ressourcen von dem Thread freigeben zu lassen der ja noch läuft (wie gibt man denn Stack und PageTables/Dir frei wenn der Thread noch läuft) und das wäre halt ein guter Grund für mind. einen Kernel-Thread.
Naja, dafür würde ich nicht groß mit einem Thread anfangen. Das freizugebende Zeug in irgendeine Queue stecken und beim nächstbesten Interrupt (dann in anderem Kontext) kann man freigeben.
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 01. October 2010, 18:09
Hallo,


Naja, ich finde/fand es immer nicht wirklich einfach diese ganzen Ressourcen von dem Thread freigeben zu lassen der ja noch läuft (wie gibt man denn Stack und PageTables/Dir frei wenn der Thread noch läuft) und das wäre halt ein guter Grund für mind. einen Kernel-Thread.
Dafür könnte ich ja meinen DoIdle-Syscall benutzen. Aber ich glaube nicht dass das nötig ist, ich sehe einfach kein Problem darin das ein Thread seine eigenen Ressourcen wegräumt.

der Idle-Thread ist halt was "besonderes" und wo ich den nun mit extra Code besonders behandle ist doch egal (ob Scheduler oder Boot-Code).
Den Boot-Code muss ich eh extra schreiben und der kostet zur OS-Laufzeit auch keinen einzigen Takt extra.


Wie gesagt, ich lass es dir als einen Workaround für die Einschränkungen der Hardware durchgehen. Aber erzähl mir nichts von elegant. Auf x86 gibt es jedenfalls keinen Grund, das so zu machen, weil es da diese Einschränkungen der Hardware nicht gibt.
Ja Okay, besonders elegant ist der HLT im User-Mode nicht aber ich sehe darin keine Einschränkung oder irgendwelche Nachteile (bist Du da anderer Meinung?) sondern einfach nur eine kleine Abweichung vom üblichen Weg.

Wenn du dann mal was neues sehen willst, ....
Offensichtlich ist meine Kenntnis von dieser Welt nicht absolut vollständig. ;)

Laut Intel-Manual schaltet sysenter IF aus, während syscall eine vom OS definierten Maske für eflags benutzt.
Aha, sagen wir es gibt eine leichte Präferenz dafür mit ausgeschalteten IRQs in einen System-Mode zu wechseln, sieh Dir dazu auch mal andere CPUs an bei den meisten dürfte das der Normalfall sein. Es ist eben in den meisten (wenn auch nicht allen) Situationen von Vorteil wenn die IRQs erst mal aus sind und der Kernel dann selber entscheiden darf. Für meine Plattform (da zähle ich das OS mal mit) sehe ich einfach nur keine Notwendigkeit für einen unterbrechbaren System-Mode also wird es keine Möglichkeit geben IRQs im System-Mode anzuschalten.

Zitat
Wimre ist doch der tyndur-Kernel auch nicht unterbrechbar, oder?
Er wird nicht preempted, ja. Aber kooperativ machen wir es an bestimmten Stellen.
:-D

Wie ist das eigentlich dann bei dir, wenn im Kernel eine Exception passiert? Ist die dann grundsätzlich nicht behebbar?
Ja, dann soll die CPU in einen Double-Fault-Shut-Down-Mode gehen und eine Nachricht an einen dafür zuständigen Controller im Chipsatz schicken. Dieser Controller löst dann einen normalen IRQ (mit hoher Priorität) aus welcher von irgend einer der anderen CPUs verarbeitet wird, der IRQ-Handler muss dann sehen was los war und diese Infos ins System-HW-Log schreiben um danach eine Kernel-Panic auszulösen. Exceptions im Kernel sind bei mir schwere Fehler und bedeuten das im Kernel-Code Fehler drin sind. Da ich keine Kernel-Mode-Threads o.ä. hab sehe ich auch darin keine Einschränkung oder irgendwelche Nachteile.


mir ist da noch was eingefallen:

es dürfte verdammt schwierig werden andere OS auf deine Platform zu portieren, wenn dein System-Mode Grundsätzlich nicht unterbrechbar ist.
Schon allein die Segmentierung wird das Portieren anderer OSe sehr zuverlässig verhindern. Ich will eben mal ganz andere Wege gehen was Speichermanagement und solche Dinge anbelangt. Normale Programme hingegen möchte ich schon gerne Portieren und da sehe ich auch keine unüberwindbaren Probleme sobald ich es schaffe einen vernünftigen Compiler und gescheite Librarys anzubieten.

das meine Idle-Threads auch Kernel-Threads und keine kompletten Prozesse sind.
Das bedeutet aber das mindestens CreateThread() und KillThread() doppelt oder zumindest mit verschiedenen Ausführungspfaden da sein müssen.


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: kevin am 01. October 2010, 18:24
Dafür könnte ich ja meinen DoIdle-Syscall benutzen. Aber ich glaube nicht dass das nötig ist, ich sehe einfach kein Problem darin das ein Thread seine eigenen Ressourcen wegräumt.
Naja, den Stack, auf dem man grad läuft, sollte man vielleicht nicht sofort freigeben (vorausgesetzt man benutzt einen Kernelstack pro Thread, ansonsten stellt sich das Problem natürlich nicht).

Zitat
Ja, dann soll die CPU in einen Double-Fault-Shut-Down-Mode gehen und eine Nachricht an einen dafür zuständigen Controller im Chipsatz schicken. Dieser Controller löst dann einen normalen IRQ (mit hoher Priorität) aus welcher von irgend einer der anderen CPUs verarbeitet wird, der IRQ-Handler muss dann sehen was los war und diese Infos ins System-HW-Log schreiben um danach eine Kernel-Panic auszulösen. Exceptions im Kernel sind bei mir schwere Fehler und bedeuten das im Kernel-Code Fehler drin sind. Da ich keine Kernel-Mode-Threads o.ä. hab sehe ich auch darin keine Einschränkung oder irgendwelche Nachteile.
Ja, solange du dein OS als untrennbaren Bestandteil der Plattform siehst, lässt sich das alles rechtfertigen. Es ist halt alles recht starr, so dass du die Hardware gleich mitändern musst, wenn dir das OS nicht mehr gefällt.

Zitat
Schon allein die Segmentierung wird das Portieren anderer OSe sehr zuverlässig verhindern. Ich will eben mal ganz andere Wege gehen was Speichermanagement und solche Dinge anbelangt.
Och, beim i386 hat man es auch sehr erfolgreich geschafft, die Segmentierung zu ignorieren. Warum sollte das bei deiner Architektur nicht klappen?

Deine anderen Entscheidungen, die praktisch nur einen Mikrokernel zulassen, blockieren das ganze wohl effektiver.
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 01. October 2010, 19:11
Zitat von: erik
Das bedeutet aber das mindestens CreateThread() und KillThread() doppelt oder zumindest mit verschiedenen Ausführungspfaden da sein müssen.
Nope, nur CreateThread() (und selbst das könnte ich verhindern, denn der Unterschied ist wirklich minimal bzw. werde ich das bald über ein Flag machen => läuft auf eine if-Schleife hinaus mit einmal 5 Zeilen und einmal 3 Zeilen Code) und KillThread interessiert es wieder gar nicht was für ein Thread das ist, da man eh auf ungültige Werte prüfen sollte.

Zitat von: erik
Dafür könnte ich ja meinen DoIdle-Syscall benutzen. Aber ich glaube nicht dass das nötig ist, ich sehe einfach kein Problem darin das ein Thread seine eigenen Ressourcen wegräumt.
Da brauchst du dann aber ein wirklich gutes kfree(). Denn wenn auf allen CPUs zur gleichen Zeit Threads beendet werden und diese ihre Ressourcen (je nach dem wie viele das sind, könnte das auch mal dauern) freigeben, müsstest du ja irgendwo einen Lock (mind.) haben und da sind wir dann wieder da was man als lange empfindet. Denn in der Zeit kann ja nichts anderes gemacht werden, da ja dein Kernel nicht unterbrechbar ist.
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 01. October 2010, 19:55
Hallo,


Naja, den Stack, auf dem man grad läuft, sollte man vielleicht nicht sofort freigeben (vorausgesetzt man benutzt einen Kernelstack pro Thread, ansonsten stellt sich das Problem natürlich nicht).
Das ist einer der Gründe warum ich im System-Mode einen CPU-verwalteten Stack haben möchte.

Ja, solange du dein OS als untrennbaren Bestandteil der Plattform siehst, lässt sich das alles rechtfertigen. Es ist halt alles recht starr, so dass du die Hardware gleich mitändern musst, wenn dir das OS nicht mehr gefällt.
Es geht mir weniger ums rechtfertigen, es geht mir darum das ich im VHDL-Code für die CPU nichts implementieren möchte was ich im Assembler/C-Code für das OS nicht auch benutze. Mir ist klar das dadurch meine Plattform nicht sonderlich flexibel sein wird aber schlussendlich brauche ich auch nur einen Weg um mein Ziel zu erreichen. Solange dieser Weg ein guter Weg ist bin ich damit zufrieden. dieser Weg wird mir wohl sehr schnelles IPC und durchgehendes Zero-Copy ermöglichen so das ich pro CPU-Takt sicher eine recht hohe Performance erreichen werde. Sollte ich tatsächlich konzeptionelle Probleme im OS oder in der Plattform finden bedeutet das natürlich das mit hoher Wahrscheinlichkeit das andere auch geändert werden muss.

Och, beim i386 hat man es auch sehr erfolgreich geschafft, die Segmentierung zu ignorieren. Warum sollte das bei deiner Architektur nicht klappen?
Dann wirst Du auch auf das Paging verzichten müssen (zumindest zum Speicherschutz), auf meiner Plattform gibt es nur ein Paging-Directory auf das alle CPUs zugreifen und zum wechseln muss man vielleicht sogar das Paging kurz abschalten (macht sich wahrscheinlich im VHDL-Code leichter wenn man nicht in ein aktives System eingreifen muss). Meine Segmente wird man wohl auch nicht so ohne weiteres auf Flat-Memory-Simulation umstellen können, nebst dessen das im System-Mode erstmal direkt mit den physischen Adressen gearbeitet wird (außer für den System-Mode-Stack der liegt in einem CPU-spezifischen Segment). Es ist sicher nicht gänzlich unmöglich auf meiner Plattform ein OS ohne Segmentierung laufen zu lassen aber dafür ist meine Plattform einfach nicht konzipiert und dementsprechend umständlich wird dieses Unterfangen werden. Es ist sicher auch extrem schwierig mein OS auf ner anderen Plattform laufen zu lassen (selbst i386 könnte schwierig werden), es wird einfach zu anhängig von den speziellen Features meiner Plattform sein.
Du nimmst ja auch keinen Schraubendreher um einen Nagel in die Wand zu klopfen obwohl man das damit schon irgendwie hinbekommen kann. Oder doch? ;)

Deine anderen Entscheidungen, die praktisch nur einen Mikrokernel zulassen, blockieren das ganze wohl effektiver.
Ich habe mich vor etwa 2 Jahren ganz klar für Micro-Kernel entschieden und genau darauf wird meine Plattform optimiert. Meine Plattform soll nicht viel können aber das was sie kann soll sie überragend gut machen.


Zitat von: erik
Das bedeutet aber das mindestens CreateThread() und KillThread() doppelt oder zumindest mit verschiedenen Ausführungspfaden da sein müssen.
Nope, nur CreateThread() (und selbst das könnte ich verhindern, denn der Unterschied ist wirklich minimal bzw. werde ich das bald über ein Flag machen => läuft auf eine if-Schleife hinaus mit einmal 5 Zeilen und einmal 3 Zeilen Code) und KillThread interessiert es wieder gar nicht was für ein Thread das ist, da man eh auf ungültige Werte prüfen sollte.
Sicher? Für einen User-Mode-Thread musst Du auch einen User-Mode-Stack anlegen und wieder löschen, für einen Kernel-Mode-Thread nicht. Auch muss ein User-Mode-Thread zu einem Prozess gehören im Gegensatz zu einem Kernel-Mode-Thread. Ich denke diese kleinen Fallunterscheidungen werden noch an einigen anderen Stellen erforderlich sein.

Da brauchst du dann aber ein wirklich gutes kfree(). Denn wenn auf allen CPUs zur gleichen Zeit Threads beendet werden und diese ihre Ressourcen (je nach dem wie viele das sind, könnte das auch mal dauern) freigeben, müsstest du ja irgendwo einen Lock (mind.) haben und da sind wir dann wieder da was man als lange empfindet. Denn in der Zeit kann ja nichts anderes gemacht werden, da ja dein Kernel nicht unterbrechbar ist.
Der kritische Abschnitt in dem kfree wird wohl auf wenigen dutzend Assemblerbefehlen mit vielleicht 6 Speicherzugriffen hinauslaufen, klar wäre es unschön wenn da wirklich 8 CPUs zur exakt gleichen Zeit durch wollen aber wie wahrscheinlich ist das? Es geht darum das auf mehreren CPUs mit einem zeitlichen Abstand von deutlich unter einer µs gleichzeitig Speicher freigeräumt werden muss, das passiert nicht gerade nennenswert häufig. Mit einem geschickten (hierarchischen) Aufbau der Kernel-Heap-Verwaltung muss es vielleicht noch nicht mal zwangsläufig zu einer Kollision kommen.


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: Svenska am 01. October 2010, 20:02
Es gibt keine if-Schleifen! If ist eine Verzweigung!

Eventuell lässt sich beim Thread-Killen ein Zeiger auf eine "Datenbank" zurückgeben, in der alle Ressourcen dieses Threads aufgeführt werden. Dann braucht man nicht ewig lange Listen traversieren. Kostet dafür etwas Speicher.

Was mich an den Idle-Threads irgendwie interessiert ist, warum man nicht einfach den Kernel selbst dafür missbrauchen kann. Im Endeffekt läuft das ja ungefähr so ab:

-alles Benötigte laden (Bootloader)
-Datenstrukturen vorbereiten
-diverse Tasks/Threads erzeugen (z.B. init)
-Ende: kann man da nicht einfach ein >> while(true) asm("HLT"); << hinsetzen?

Wenn der Scheduler nichts mehr zu verteilen hat, so führt der Kernel halt sich selbst aus - und das ist dann diese Idle-Endlosschleife. Damit wäre der Idle-Thread auch kein eigener Thread mehr und man muss CreateProcess() / CreateThread() nicht anpassen.

Ich behaupte, ich habe einen Denkfehler da drin. Welcher ist es nur?

Gruß,
Sebastian
Titel: Re:Shared Memory
Beitrag von: kevin am 01. October 2010, 20:48
Dann hast du halt einen Kernelthread, der in einer etwas exotischen Weise erstellt wurde. Kann man natürlich machen, ich seh nur nicht den Vorteil...
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 01. October 2010, 20:58
Hallo,


Ich behaupte, ich habe einen Denkfehler da drin. Welcher ist es nur?
Du hast vergessen das es SMP gibt?


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: kevin am 01. October 2010, 21:21
Ein Task, der while(1) asm("hlt"); macht könnte sogar auf mehreren CPUs gleichzeitig laufen, ohne dass was böses passiert. ;)
Titel: Re:Shared Memory
Beitrag von: Svenska am 02. October 2010, 05:39
Eben.
Natürlich läuft der Kernel auf jeder CPU, die er verwaltet (wobei es natürlich Gründe gibt, Kernel auf einer CPU und Userspace auf den restlichen CPUs zu verteilen - insbesondere Hartechtzeitsysteme), also führt er diese Schleife auch auf jeder CPU aus.
Also eher kein Denkfehler, nur ein 80er-Jahre-Ansatz...
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 02. October 2010, 08:55
@svenska

Das ist genau das was ich in meinem Kernel mache. Da sind wir uns als mal so richtig einig ;)

Zitat von: erik
Sicher? Für einen User-Mode-Thread musst Du auch einen User-Mode-Stack anlegen und wieder löschen, für einen Kernel-Mode-Thread nicht. Auch muss ein User-Mode-Thread zu einem Prozess gehören im Gegensatz zu einem Kernel-Mode-Thread. Ich denke diese kleinen Fallunterscheidungen werden noch an einigen anderen Stellen erforderlich sein.
Was das wieder freigeben betrifft, da ich eh auf "0" überprüfe bevor ich freigebe, ist es egal ob ein Stack vorhanden ist oder nicht.
Auch ein KernelThread kann bei mir zu einem Prozess gehören (wird sogar bei jeder Prozesserstellung genutzt).
Zumal auch der Kernel ein Prozess ist (um viele Sachen einfacher zu machen hat bei mir der Kernel auch die selben Prozessstruturen wie alle anderen auch und ist der Prozess mit der ID 1 oder 0 da bin ich mir grad nicht sicher).

@erik

Da du ja nen MikroKernel haben willst und deine Msgs über Segmente erstellt werden, solltest du eventuell überlegen nur nen SlabAllocator zu benutzen. Denn im Endeffekt allokierst du ja nur Objekte bekannter Größe und da ist der dann schneller (und an sich auch Speicher schonender).
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 02. October 2010, 09:27
Hallo,


Ein Task, der while(1) asm("hlt"); macht könnte sogar auf mehreren CPUs gleichzeitig laufen, ohne dass was böses passiert.
Der Code ja, aber Du brauchst für jede CPU einen individuellen Stack. Aus meiner persönlichen Sicht wird ein Thread über seinen Stack definiert (Call-Chain und die lokalen Variablen in den Stack-Frames sind die wesentlichen Merkmale eines Thread-Kontextes) und nicht über den Code den er gerade ausführt (der Code ist nur das Werkzeug das diesen Kontext bearbeitet).


Was das wieder freigeben betrifft, da ich eh auf "0" überprüfe bevor ich freigebe, ist es egal ob ein Stack vorhanden ist oder nicht.
Also ich will auch auf "ungültig" Prüfen (nur eben einen Selector) aber wenn dort nichts gültiges ist dann ist das ein fataler Fehler (Thread ohne Stack geht nicht). Du opferst da IMHO etwas Fehlererkennung. Wie ist das bei CreateThread(), da musst Du doch schon genau wissen wie viele Stacks erstellt werden müssen?

Zumal auch der Kernel ein Prozess ist (um viele Sachen einfacher zu machen hat bei mir der Kernel auch die selben Prozessstruturen wie alle anderen auch und ist der Prozess mit der ID 1 oder 0 da bin ich mir grad nicht sicher).
Das ist vernünftig. Du versuchst also auch Sonderfälle zu vermeiden. ;)

Da du ja nen MikroKernel haben willst und deine Msgs über Segmente erstellt werden, solltest du eventuell überlegen nur nen SlabAllocator zu benutzen. Denn im Endeffekt allokierst du ja nur Objekte bekannter Größe und da ist der dann schneller (und an sich auch Speicher schonender).
Auf was willst Du hinaus? Für meine Messages wird kein neuer Speicher alloziert sondern über bereits vorhandenen Speicher (in irgendeinem bereits bestehenden Segment) ein neues Segment gelegt das nur Zugriffe auf genau den von der Message darin belegten Teilabschnitt zulässt (aus Byte genau). Das Original-Segment (beim Client) bekommt also ein Alias-Segment in der LDT eines anderen Prozesses (dem Service), es wird quasi beerbt (wenn auch nur ein bestimmter Abschnitt daraus). Wenn eine Applikation aber z.B. ein komplette Datei mit etlichen MB einlesen möchte und dafür extra ein Segment mit passender Größe erstellt dann ist das erbende Alias-Segment beim Service natürlich genau so groß (also zeigt auf komplett den selben linearen Speicherbereich) wie das Original-Segment. Für einen richtigen Slab-Allocator sehe ich da nirgends eine Verwendungsmöglichkeit oder meinst Du etwas anderes in meinem OS?


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 02. October 2010, 09:38
Zitat von: erik
Wie ist das bei CreateThread(), da musst Du doch schon genau wissen wie viele Stacks erstellt werden müssen?
Jap, richtig. Ich habe im Moment noch 2 Funktionen (die auch noch fast genauso aussehen) und will die mal in eine vereinen.

Zitat von: erik
Für einen richtigen Slab-Allocator sehe ich da nirgends eine Verwendungsmöglichkeit oder meinst Du etwas anderes in meinem OS?
Naja, alles wofür du ein kmalloc/kfree einsetzen würdest.

Ich habe kein kmalloc/kfree in meinem Kernel, da wird alles über den SlabAllocator gemacht, weil ich ja nur Objekte bekannter Größer allokieren möchte und das halt mit dem SlabAllocator wesentlich schneller geht.
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 02. October 2010, 09:54
Hallo,


Naja, alles wofür du ein kmalloc/kfree einsetzen würdest.
Das sind in erster Linie die Strukturen für Prozesse, Threads, Message-Targets und Events, dazu kommen noch kleine Elemente für verkette Listen. Auf dem eigentlichen Kernel-Heap liegen also Objekte mit maximal 5 verschiedenen Größen. Alle Segmente (für die User-Mode-Prozesse) werden über die normale Speicherverwaltung für den linearen Speicher gehen. Wie ich die Kernel-Heap-Verwaltung genau aufbauen möchte weiß ich noch nicht aber ich denke ein generischer Slab-Allocator ist dafür etwas zu umfangreich. Darüber mache ich mir genauere Gedanken wenn ich meinen Kernel auch tatsächlich Assemblieren und Ausführen kann.


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: kevin am 02. October 2010, 13:23
Ein Task, der while(1) asm("hlt"); macht könnte sogar auf mehreren CPUs gleichzeitig laufen, ohne dass was böses passiert.
Der Code ja, aber Du brauchst für jede CPU einen individuellen Stack. Aus meiner persönlichen Sicht wird ein Thread über seinen Stack definiert (Call-Chain und die lokalen Variablen in den Stack-Frames sind die wesentlichen Merkmale eines Thread-Kontextes) und nicht über den Code den er gerade ausführt (der Code ist nur das Werkzeug das diesen Kontext bearbeitet).
while(1) asm("hlt") interessiert sich für den Stack herzlich wenig. Du kannst denselben Code also mit demselben Stack auf mehreren CPUs ausführen und es passiert immer noch nichts böses.
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 02. October 2010, 15:50
Hallo,


while(1) asm("hlt") interessiert sich für den Stack herzlich wenig. Du kannst denselben Code also mit demselben Stack auf mehreren CPUs ausführen und es passiert immer noch nichts böses.
Sicher?

Jetzt muss ich mal meine letzten x86-Kenntnisse zusammenkratzen:
Gehen wir davon aus (ich hab gleich mal den Compiler laufen lassen):label_loop:
    hlt
    jmp   label_loop
und das soll in Ring 0 laufen (wegen dem HLT). Wenn jetzt ein IRQ kommt dann muss doch der aktuelle EIP (und noch einiges mehr) auf den Stack geschrieben werden und da wir bereits im Ring 0 sind kann die CPU nicht einen anderen Stack aus dem TSS nehmen (in den Gates in der IDT sind doch nur CS:EIP enthalten). Oder irre ich da? Wenn also mehrere CPUs auf IRQs reagieren müssen (ich weiß gar nicht ob das bei x86 überhaupt geht aber gehen wir mal davon aus es ginge, zumindest mit allen APICs sollte es gehen) dann würden IMHO die EIPs usw. alle auf dem selben Stack landen. Der kleine Code selber interessiert sich wirklich nicht für den Stack aber der IRQ, der ja nötig ist um HLT zu beenden, interessiert sich schon für den Stack.
Ich hoffe mal ich hab mich jetzt nicht zu sehr blamiert. :roll:


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 02. October 2010, 15:56
Nope, alles soweit korrekt.

Und genau deswegen (weil man ja eh alles braucht was man auch für Kernel-Threads braucht), sollten doch Kernel-Threads auf x86 nicht so schwer sein.
Titel: Re:Shared Memory
Beitrag von: kevin am 02. October 2010, 17:11
und das soll in Ring 0 laufen (wegen dem HLT). Wenn jetzt ein IRQ kommt dann muss doch der aktuelle EIP (und noch einiges mehr) auf den Stack geschrieben werden und da wir bereits im Ring 0 sind kann die CPU nicht einen anderen Stack aus dem TSS nehmen (in den Gates in der IDT sind doch nur CS:EIP enthalten). Oder irre ich da? Wenn also mehrere CPUs auf IRQs reagieren müssen (ich weiß gar nicht ob das bei x86 überhaupt geht aber gehen wir mal davon aus es ginge, zumindest mit allen APICs sollte es gehen) dann würden IMHO die EIPs usw. alle auf dem selben Stack landen. Der kleine Code selber interessiert sich wirklich nicht für den Stack aber der IRQ, der ja nötig ist um HLT zu beenden, interessiert sich schon für den Stack.
Hm, ja, ich habe nicht berücksichtigt, dass wir in Ring 0 sein müssen und daher keinen Stackwechsel kriegen. Ich könnte jetzt versuchen, mich mit Task Gates rauszureden, aber das ist natürlich nicht praktikabel, auch wenn es theoretisch wohl ginge. Ich gebe mich hier geschlagen. ;)
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 02. October 2010, 19:35
Hallo,


Ich könnte jetzt versuchen, mich mit Task Gates rauszureden, aber das ist natürlich nicht praktikabel, auch wenn es theoretisch wohl ginge.
Das könnte schon funktionieren aber ich glaube nicht das sowas schon mal jemand ernsthaft gemacht hat.
Ich denke wir sind heute auf ein faires Unentschieden gekommen. ;)


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 02. October 2010, 20:02
Zitat von: erik
Das könnte schon funktionieren aber ich glaube nicht das sowas schon mal jemand ernsthaft gemacht hat.
So blöd es klingt, doch ;)

Es wird dir nämlich empfohlen für bestimmte Exceptions nen TaskGate zu nehmen, weil dann ein neuer Stack geladen wird.

Wird unter OS-X nicht was ähnliches gemacht, wimre habe ich mal was gelesen das dort der Kernel gar nicht in jedem Prozess eingeblendet ist, sondern das nur ein "Stub" existiert der dann das PageDir wechselt, in den Kernel rein.
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 14. October 2010, 20:18
Ich habe mal wieder ein wenig an meinem VMM gearbeitet und SharedMemory macht mir noch immer sorgen :(

Ich bin jetzt soweit das ich für jede physikalische Page die gemappt ist einen refCount führe und in welchen Prozess sie gemappt ist (kann sogar mehrmals der selbe sein).

Ich überlege halt gerade ob es überhaupt Sinn macht zu wissen ob eine Page als SharedRead oder SharedWrite gemappt ist? Denn die einzige Info die ich mal irgendwann bräuchte wäre ob eine Page COW ist, damit ich dann entsprechend reagieren kann.
Ich wüsste halt nicht wozu ich wissen müsste das eine Page irgendwie geshared wird (also mehrmals gemappt ist) wenn ich eh nen refCount habe. Oder fällt euch ein Grund ein? (Es geht darum, das ich nen Flag mit in den PTE packe, ob eine Page SharedRead oder -Write ist)

Noch eine Frage zu COW. Ansich ist das ja ne klare Sache, man markiert eine Page in allen Prozessen, bis auf dem ersten Prozess, als COW und wenn dann geschrieben werden soll, dann wird ne neue Page allokiert und der Inhalt wird kopiert. Soweit so klar. Was ist aber wenn der erste Prozess beendet wurde und die Page nur noch in einem anderen Prozess gemappt ist, das Flag für COW steht da ja weiterhin drin, dann würde die Page kopiert werden obwohl das gar nicht nötig ist!?

Meine Lösung wäre halt, dass jedes Mal bevor eine neue Page allokiert und kopiert wird, nachgeguckt wird ob dies auch wirklich noch nötig ist.

Wie habt ihr das gelöst?
Titel: Re:Shared Memory
Beitrag von: Jidder am 14. October 2010, 20:36
Noch eine Frage zu COW. Ansich ist das ja ne klare Sache, man markiert eine Page in allen Prozessen, bis auf dem ersten Prozess, als COW und wenn dann geschrieben werden soll, dann wird ne neue Page allokiert und der Inhalt wird kopiert.
So hätte ich das nicht gemacht. Ich würde die Seite in allen Prozessen als COW markieren. Dann tritt dein Problem nicht auf. Keine Sonderbehandlung für den ersten Prozess.
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 14. October 2010, 20:38
Hallo,


ein RefCount müsste IMHO reichen, wenn der >1 ist dann ist klar das die Page in mehreren Prozessen gemappt ist.

COW-Pages müssen in allen Prozessen als RO gemappt sein, wenn das bei einem davon nicht so wäre dann würden die anderen Prozesse dessen Änderungen sehen und das darf ja nicht sein. Für eine COW-Page braucht man auch einen RafCount und wenn der >1 ist dann muss eine Kopie (für den Prozess der gerade versucht zu schreiben) erstellt werden und anschließend wird der RefCount dekrementiert. Wenn der RefCount bei 1 angekommen ist kann die Page auch für den letzten verbleibenden Prozess als RW markiert werden (spart immerhin eine Exception). Wenn ein Prozess gekillt wird ohne das er die COW-Page beschrieben hat dann muss der RefCount natürlich auch dekrementiert werden und wenn einer der Prozesse forkt dann wird inkrementiert.


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 14. October 2010, 20:51
Zitat von: erik
So hätte ich das nicht gemacht. Ich würde die Seite in allen Prozessen als COW markieren. Dann tritt dein Problem nicht auf. Keine Sonderbehandlung für den ersten Prozess.
Hast recht, ich hatte mir sorgen gemacht, wenn ich das im ersten Prozess auch mache, dass ich dann wieder das Problem habe, das ich eine Page leake, aber das ist ja nicht der Fall, weil ja alle anderen Prozesse die ursprüngliche Page haben und die darf ja nicht verändert werden.

Jetzt bin ich gerade dabei meine Henne-Ei-Problem zu lösen, das ich ohne einen funktionierenden VMM keine Pages mappen kann, weil dann auch immer die Struktur allokiert werden muss, für den refCount und den Prozess in welche die Page gemappt ist.

Eine Frage hätte ich noch. Ich will die Prozesse in denen eine Page gemappt ist speicher, um später auch eine Page die in mehreren Prozessen gemappt ist, auslagern zu können. Macht man sowas überhaupt, weil ich es mir auch schwer vorstelle überhaupt rauszubekommen, das die Page eigentlich in allen Prozessen lange nicht genutzt wurde?
Titel: Re:Shared Memory
Beitrag von: Svenska am 14. October 2010, 21:13
Ich würde ja behaupten, dass heutzutage genug RAM in einem Rechner verbaut ist, dass man sich darum nicht besonders kümmern muss. Wenn der RAM vollläuft wird der Plattencache entsorgt (unter der Annahme, dass der über alle Grenzen wachsen darf), reicht das nicht, werden einfach gemappte Pages entsorgt, reicht das auch nicht, werden mehrfach gemappte Pages entsorgt, reicht das nicht, kommt der OOM-Killer.

Meine Vermutung ist, dass die Zahl der mehrfach gemappten Pages verschwindend klein ist gegenüber der Menge an Pages, die jedes Programm für sich hat. Sind letztere alle ausgelagert, hast du ohnehin andere Probleme als noch eine effiziente Auslagerung nachzulegen...

Oder du ignorierst den Fakt komplett und lagerst einfach "Pages" aus und speicherst pro Page, ob sie alt/ungenutzt ist oder nicht. Wobei ich nicht weiß, wie sich das in deinem Mikrokernel implementieren ließe.

Gruß
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 14. October 2010, 21:41
Zitat von: svenska
Meine Vermutung ist, dass die Zahl der mehrfach gemappten Pages verschwindend klein ist gegenüber der Menge an Pages, die jedes Programm für sich hat.
Unter der Annahme das der Speicher den ein Programm erst zur Laufzeit allokiert größer ist als das Image der Executeable.
Denn bei mir ist jedes Executeable (wenn die physikalischen Sektionen 4KB aligned sind) gemappt (so ist es effizienter wenn man das gleiche Executeable mehrmals ausführt).

Zitat von: svenska
Oder du ignorierst den Fakt komplett und lagerst einfach "Pages" aus und speicherst pro Page, ob sie alt/ungenutzt ist oder nicht. Wobei ich nicht weiß, wie sich das in deinem Mikrokernel implementieren ließe.
Das verstehe ich jetzt nicht. Denn du kannst den Fakt das eine Page gemappt ist, nicht einfach ignorieren. Ich meine stell dir mal vor, du lagerst eine solche Page aus und dort kommen jetzt andere Daten rein, dann sind diese Daten jetzt in allen Prozessen die diese Page auch gemappt hatten und wenn das dann noch Code war, wird es interessant ;)

Zum Thema wie man Paging in einem MikroKernel macht (der vorallem den VFS-Service nicht im Kernel hat) sollte es fast genauso funktionieren wie bei allen anderen auch (sag ich mal einfach so, da ich noch keine Ahnung davon habe ;) ). Nur das du halt den Inhalt der Pages erst an den VFS-Service schickst und dieser den dann in die Swap-Datei schreibt.
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 15. October 2010, 22:02
Das mit dem refCount für jede Page ist zwar ganz nett, aber zeigt leider, wieder einmal, große Design Probleme meines VMMs.

Ich überlege im Moment ob ich wirklich wissen muss in welchen Prozessen eine Page gemappt ist oder ob es nicht einfach reicht nen refCount zu haben?!

Problem ist mal wieder eine Henne-Ei-Geschichte. Jedes Mal wenn eine Page gemappt wird, wird halt eine Funktion aufgerufen, die den refCount erhöht und den laufenden Prozess in die Liste der Prozesse, die die Page gemappt haben, reinpackt. Soweit so gut, aber um den Prozess in die Liste zu packen oder um eine neue Page in meine Tabelle zu packen muss Speicher allokiert werden. Jetzt kann natürlich das Problem auftreten dass das in einer endlos Kette endet. Denn jedes mal wenn Speicher gebraucht wird, kann es ja sein, das eine neue Page gemappt werden muss damit ich neuen Speicher bekomme, aber wenn jetzt die neue Page gemappt werden soll, brauche ich ja wieder Speicher und da wird wieder festgestellt das ne neue Page benötigt wird und die muss wieder gemappt werden ... ne klassische Endlosschleife halt.

Mir ist leider noch kein Weg eingefallen (außer halt keine Liste mit Prozessen, sondern nur nen RefCount zu haben) das Problem zu lösen (bisher tritt es nur auf mehreren CPUs auf, nicht aber auf einer).

Habt ihr auch solche Henne-Ei-Probleme in eurem VMM und wenn ja, wie habt ihr sie gelöst?
Titel: Re:Shared Memory
Beitrag von: Svenska am 17. October 2010, 23:15
Naja, den Fakt, dass eine Page "shared" ist, kannst du im Swapping komplett ignorieren.

Du speicherst halt pro Page irgendwelche Informationen zur Nutzung, um deinen welche-page-auslagern-Algorithmus betreiben zu können und ignorierst den Zustand des Sharings komplett. Wird eine solche, von mehreren Programmen genutzte, Page ausgelagert, bricht natürlich die Systemperformance ein - unter der Voraussetzung, dass du aber genug RAM hast, spielt das keine Rolle.

Den Rest deines Problems kann ich nicht so recht nachvollziehen, wahrscheinlich auch, da ich weder das Zusammenspiel deiner Verwaltung zueinander noch das Paging im x86-speziellen kenne, daher kann ich dazu nichts sagen. Ich bezog mich also nur auf identische, in mehreren Prozessen gemappte, Pages.
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 18. October 2010, 10:14
Zitat von: svenska
Naja, den Fakt, dass eine Page "shared" ist, kannst du im Swapping komplett ignorieren.

Du speicherst halt pro Page irgendwelche Informationen zur Nutzung, um deinen welche-page-auslagern-Algorithmus betreiben zu können und ignorierst den Zustand des Sharings komplett. Wird eine solche, von mehreren Programmen genutzte, Page ausgelagert, bricht natürlich die Systemperformance ein - unter der Voraussetzung, dass du aber genug RAM hast, spielt das keine Rolle.
Naja, ignorieren würde ich das nicht unbedingt. Ich würde sogar sagen, das wenn eine Page bei mehr als 2 Prozessen gemappt ist, dann lohnt sich das Auslagern schon gar nicht mehr.

Bei mir ist es halt so das du von einem Prozess nicht in den anderen reingucken kannst (auch nicht vom Kernel aus und erst recht nicht auf die PageTables) und von daher kannst du ja nicht wissen ob die Page die du gerade in dem Prozess ausgewählt hast, in dem anderen Prozess auch nicht benötigt wird. Da müsstest du also erst mal das PD von dem anderen Prozess mappen, dann die PageTable um dann nachzusehen ob die Page eventuell ausgelagert werden kann.
Das ganze temporäre mappen in den KernelSpace dauert wahrscheinlich auch ein wenig und ich denke mal da lohnt es sich dann nicht mehr die Page noch auszulagern. Bei mehr als zwei Prozessen wird das noch aufwendiger und wenn man dann noch SMP dazunimmt kann man sich solche SharedPages wahrscheinlich ganz sparen.

Zitat von: svenska
Den Rest deines Problems kann ich nicht so recht nachvollziehen, wahrscheinlich auch, da ich weder das Zusammenspiel deiner Verwaltung zueinander noch das Paging im x86-speziellen kenne, daher kann ich dazu nichts sagen. Ich bezog mich also nur auf identische, in mehreren Prozessen gemappte, Pages.
Naja, die Deadlock lässt sich leider aufgrund meines Designs nicht vermeiden und das eigentliche Problem habe ich inzwischen auch gefunden (habe ja nen neuen Thread aufgemacht).
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 18. October 2010, 11:50
Hallo,


die Deadlock lässt sich leider aufgrund meines Designs nicht vermeiden
Dann ist Dein Design fehlerhaft!


SCNR
Erik
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 18. October 2010, 12:11
Zitat von: erik
Dann ist Dein Design fehlerhaft!
Naja, so schlimm ist es auch nicht. Nur habe ich bei meinem Design halt viele Henne-Ei-Probleme und die zu lösen ist nicht immer schön.

Mein Lieblingsbeispiel ist das Allokieren von Speicher. Während des Vorgangs passiert es das der SlabAllocator aufgerufen wird damit du Speicher für eine Verwaltungsstruktur bekommst. Dieser wiederrum könnte den Allocator aufrufen, weil er mehr Speicher benötigt.

Dass das so nicht funktionieren kann ist klar, aber solche ähnlichen Probleme müssten eigentlich bei anderen auch auftreten, wenn man denn nicht alles statisch macht.

Das "lustige" an dem Bsp. ist, das ich also nicht mehr den SlabAllocator aufrufe, sondern nen speziellen Allocator, der nur Speicher aus nem vorher festgelegten Bereich holt (ansonsten funktioniert er genauso wie ein SlabAllocator) und genau dieser Allocator macht mir jetzt gerade die Probleme mit meiner vmmMap() ;)
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 18. October 2010, 15:28
Hallo,


Nur habe ich bei meinem Design halt viele Henne-Ei-Probleme
Oh ja, sehr viele. Irgendetwas scheinst Du anders zu machen.

Mein Lieblingsbeispiel ist das Allokieren von Speicher. Während des Vorgangs passiert es das der SlabAllocator aufgerufen wird damit du Speicher für eine Verwaltungsstruktur bekommst. Dieser wiederrum könnte den Allocator aufrufen, weil er mehr Speicher benötigt.
Dazu ist es erforderlich das die Verwaltungsstrukturen immer mit etwas Überschuss alloziert werden. Wenn Du welche brauchst dann musst Du dem Allocator mitteilen ob es momentan sicher ist das er selber die Speicherverwaltung bemüht oder eben nicht. Der Überschuss ermöglicht es diesem Allocator auch einige Aufrufe durchzustehen ohne das er selber externe Hilfe bekommen kann und wenn es sicher ist dann muss er eben den Überschuss wieder großzügig auffüllen.
Eine andere Lösung wäre die Verwaltungsstrukturen im voraus zu allozieren (schließlich weiß man ja das man welche benötigt) bevor der Heap in einen unsicheren Zustand gebracht wird und dann könnten sich die verschiedenen Allocatoren gegenseitig beliebig verschachtelt aufrufen.
Und es gibt sicher noch ne Menge anderer Wege dieses Problem zu lösen.


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 18. October 2010, 16:13
Zitat von: erik
Irgendetwas scheinst Du anders zu machen.
Ja definitiv  :-D

Zitat von: erik
Eine andere Lösung wäre die Verwaltungsstrukturen im voraus zu allozieren (schließlich weiß man ja das man welche benötigt) bevor der Heap in einen unsicheren Zustand gebracht wird und dann könnten sich die verschiedenen Allocatoren gegenseitig beliebig verschachtelt aufrufen.
Das war meine erste Lösung die leider die Anzahl der CPUs nicht in betracht gezogen hatte und daher ab einer bestimmten Anzahl auch nicht mehr funktioniert hat.

Ich versuche schon die ganze Zeit meine jetzige Lösung so zu gestalten, das ich den Lock irgendwie ändern könnte.
Allerdings bin ich so langsam an einem Punkt angekommen wo ich nicht mehr glaube dass das möglich ist :( Habe nämlich gerade festgestellt, das mein SlabAllocator Memory leakt und um das Leak zu schließen, müsste ich die Ints ausmachen und meine vmmAlloc() aufrufen welche ja die vmmMap() aufruft und das auch irgendwann zu einem Problem wird :(
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 18. October 2010, 16:33
Hallo,


also hast Du doch ein Design-Problem.


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 18. October 2010, 16:51
@erik

Siehe anderer Thread. Ich habe eventuell eine Möglichkeit gefunden mein Design, mal wieder, zu fixen ;)
Titel: Re:Shared Memory
Beitrag von: Svenska am 19. October 2010, 13:04
Zitat von: svenska
Naja, den Fakt, dass eine Page "shared" ist, kannst du im Swapping komplett ignorieren.

Du speicherst halt pro Page irgendwelche Informationen zur Nutzung, um deinen welche-page-auslagern-Algorithmus betreiben zu können und ignorierst den Zustand des Sharings komplett. Wird eine solche, von mehreren Programmen genutzte, Page ausgelagert, bricht natürlich die Systemperformance ein - unter der Voraussetzung, dass du aber genug RAM hast, spielt das keine Rolle.
Naja, ignorieren würde ich das nicht unbedingt. Ich würde sogar sagen, das wenn eine Page bei mehr als 2 Prozessen gemappt ist, dann lohnt sich das Auslagern schon gar nicht mehr.
Wenn du zwingend Speicher benötigst und nur noch shared Pages da sind, dann musst du die zwingend mit auslagern oder OOM-Exceptions werfen. Sonst kann dir dann jeder das System lahmlegen, indem er einfach den RAM mit shared Pages vollmüllt.

Bei mir ist es halt so das du von einem Prozess nicht in den anderen reingucken kannst (auch nicht vom Kernel aus und erst recht nicht auf die PageTables) und von daher kannst du ja nicht wissen ob die Page die du gerade in dem Prozess ausgewählt hast, in dem anderen Prozess auch nicht benötigt wird. Da müsstest du also erst mal das PD von dem anderen Prozess mappen, dann die PageTable um dann nachzusehen ob die Page eventuell ausgelagert werden kann.
Wie gesagt, das ist nicht nötig. Du solltest als Maßstab für die Auslagerfähigkeit einer Page den Nutzungsgrad annehmen und nicht den Zustand - eine Page, die in hundert Prozessen gemappt, aber trotzdem ungenutzt ist, kannst du problemlos auslagern.

Gruß
Titel: Re:Shared Memory
Beitrag von: FlashBurn am 19. October 2010, 13:45
Zitat von: svenska
Wie gesagt, das ist nicht nötig. Du solltest als Maßstab für die Auslagerfähigkeit einer Page den Nutzungsgrad annehmen und nicht den Zustand - eine Page, die in hundert Prozessen gemappt, aber trotzdem ungenutzt ist, kannst du problemlos auslagern.
Ok, wie definierst du Nutzungsgrad und wie würdest du den rausbekommen?

Problem ist hier redet ein Blinder (ich) vom Sehen. Denn ich weiß theoretisch wie das mit dem Auslagern so ungefähr funktioniert, wie das aber implementiert wird da habe ich nicht wirklich eine Ahnung. Aber dafür wird meiner Meinung nach das Dirty-Flag benutzt und das müsstest du dann in allen Prozessen prüfen wo die Page gemappt ist und das finde ich zu aufwendig.

Zitat von: svenska
Sonst kann dir dann jeder das System lahmlegen, indem er einfach den RAM mit shared Pages vollmüllt.
So einfach wird es nicht, dafür würdest du dann schon 2 Programme benötigen, weil so wie ich jetzt mein SharedMemory geplant habe, kann der nur erstellt werden in dem du Speicher verschickst und dann brauchst du noch nen Prozess der den dann auch nicht mehr freigibt.
Titel: Re:Shared Memory
Beitrag von: Svenska am 19. October 2010, 15:22
Hm, ich komm derzeit nicht an mein Minix2-Buch ran, da ich wegen Praktikum wieder zuhause wohne... da standen ein paar Ideen drin.

Ein Beispiel: Du brauchst über jede irgendwo gemappte Page systemweit eine Statistik, die dir angibt, wie oft in einem bestimmten Zeitraum (z.B. zwischen 2 Scheduleraufrufen) eine Page benutzt wurde. Das kann ein Bit oder ein Zähler pro Page sein. Wichtig ist, dass diese Statistik extern gehalten und regelmäßig genullt wird. Brauchst du Speicher, sortierst du diese Liste aufsteigend und lagerst die vorne stehenden Pages der Reihe nach aus, bis genug Speicher frei ist.

Der Zählerstand ist somit der Nutzungsgrad. Allerdings kannst du für jeden Algorithmus auch Fälle konstruieren, bei denen er versagt. In meinem Fall genau dann, wenn dein Prozess alle 500ms seine Daten braucht, der wird dann bei RAM-Knappheit ständig ausgelagert und wieder eingelesen.
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 19. October 2010, 17:48
Hallo,


Du brauchst über jede irgendwo gemappte Page systemweit eine Statistik, die dir angibt, wie oft in einem bestimmten Zeitraum (z.B. zwischen 2 Scheduleraufrufen) eine Page benutzt wurde. Das kann ein Bit oder ein Zähler pro Page sein. Wichtig ist, dass diese Statistik extern gehalten und regelmäßig genullt wird. Brauchst du Speicher, sortierst du diese Liste aufsteigend und lagerst die vorne stehenden Pages der Reihe nach aus, bis genug Speicher frei ist.
Hm, interessante Idee, genau sowas möchte ich für mein Paging in HW implementieren. Da ich nur ein einziges Paging-Directory (natürlich mit vielen wild verstreuten Paging-Tables) habe (ach ich liebe Segmentierung) kann ich dafür einfach in HW einen Mechanismus drüber laufen lassen, mit einem bestimmten Zeitraster, der in jedem gültigen Eintrag 2 Zähler dekrementiert (für Lese- und Schreibzugriffe getrennt) und die CPU tut bei jedem Zugriff den entsprechenden Zähler auf 0xFF setzen. Nur sortiert ist das natürlich nicht so das ich zum auslagern immer erst nach den kleinsten Zählern suchen muss, dafür muss ich mir noch was einfallen lassen.


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: Svenska am 19. October 2010, 21:23
Wie wäre es, das Sortieren erst im Schadensfall ("jetzt muss ausgelagert werden") durchzuführen? Du kannst ja einen Sicherheitspuffer für z.B. den QuickSort-Rekursionsstack bereithalten. Unter der Voraussetzung, das Swapping eh nicht oft nötig ist und wenn, dann richtig, lohnt es nicht, bereits vorher zu sortieren.
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 20. October 2010, 09:51
Hallo,


Wie wäre es, das Sortieren erst im Schadensfall ("jetzt muss ausgelagert werden") durchzuführen?
Das sowieso, mir ging es aber eigentlich darum dass das Sortieren CPU-Zeit kostet. Das könnte man so lösen das der automatische HW-Page-Table-Walker (der nur aktiviert wird wenn der Speicher langsam knapp wird, schließlich kostet er auf jeden Fall etwas Speicherbandbreite) einfach immer die Page-Einträge deren Zähler unter einer dynamischen Schwelle liegen in eine extra Liste einträgt. Dann hätte man zu jedem Zeitpunkt zumindest einige gute Kandidaten (falls diese Pages in der Zwischenzeit nicht wieder von einer CPU angefasst wurden). Ich muss da auf jeden Fall mal ein paar Notizen meiner Ideen-Liste hinzufügen.


Grüße
Erik
Titel: Re:Shared Memory
Beitrag von: Svenska am 20. October 2010, 09:52
Das sowieso, mir ging es aber eigentlich darum dass das Sortieren CPU-Zeit kostet.
Wenn du auslagerst, verlierst du Milliarden(!) CPU-Zyklen beim Zugriff auf den Auslagerungsspeicher. Die paar tausend Takte für das Sortieren der Liste fallen da nicht wirklich ins Gewicht.
Titel: Re:Shared Memory
Beitrag von: erik.vikinger am 20. October 2010, 10:32
Hallo,


verlierst du Milliarden(!) CPU-Zyklen
Da meine CPUs wohl höchstens in den zweistelligen MHZ-Bereich kommen sind es wohl nur Millionen CPU-Zyklen. ;)

Die paar tausend Takte für das Sortieren der Liste fallen da nicht wirklich ins Gewicht.
Aber wenn ich das vermeiden kann in dem die Sortierung in HW abläuft hab ich trotzdem noch was gewonnen, wenn auch nur wenig (zumindest spare ich den Speicher für den Sortier-Code).

Da fällt mir ein das diese Info kaum aktuell ist da ich ja bei den meisten Segmenten ohne Paging auskomme. Wenn der Speicher anfängt knapp zu werden muss ich erst mal bei möglichst vielen Segmenten überhaupt das Paging aktivieren und für diese Bereiche des linearen Adressraums auch (1:1-)Page-Tables erstellen damit es überhaupt sinnvolle Infos über die Speicherbenutzung geben kann.
Das wird auf jeden Fall ein größerer Eintrag in meiner Ideen-Liste. ;)


Grüße
Erik