Autor Thema: Shared Memory  (Gelesen 36210 mal)

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #20 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.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #21 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.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #22 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 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
Reality is that which, when you stop believing in it, doesn't go away.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #23 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...
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #24 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?

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #25 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
Reality is that which, when you stop believing in it, doesn't go away.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #26 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.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #27 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).

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #28 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
Reality is that which, when you stop believing in it, doesn't go away.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #29 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.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #30 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
« Letzte Änderung: 24. September 2010, 02:45 von Svenska »

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #31 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.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #32 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.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #33 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?

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #34 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.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #35 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
Reality is that which, when you stop believing in it, doesn't go away.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #36 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.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #37 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
Reality is that which, when you stop believing in it, doesn't go away.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #38 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.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #39 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.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

 

Einloggen