Autor Thema: Flexibles und einfaches Prozess erzeugen  (Gelesen 27397 mal)

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #20 am: 16. October 2011, 13:38 »
Zitat von: taljeth
FlashBurn, eine Frage, die sich mir bei deinem ursprünglichen Modell stellt, ist, ob man in deinem OS auf Sachen wie Block und Character Devices mit den normalen Dateisystemoperationen zugreifen können soll. Habe ich also irgendwie sowas wie ein /dev/hda? Wenn ja, dann hast du schon Treiber vorgesehen, die außerhalb des VFS liegen und an die Requests einfach durchgereicht werden müssen. Warum nicht also konsistent das gleiche mit Dateisystemen machen?
Ansich würde ich schon sowas wie /dev/ haben wollen, nur habe ich noch keine Vorstellung wie das Interface dann aussehen soll und wie das ganze dann läuft. Ich dachte da irgendwie daran, das man Devices öffnen, schließen und lesen kann. In der "Datei" wären dann genau 4bytes gespeichert und das wäre der IPC-Port des Treibers.
Problem ist halt, ich habe sowas noch nie programmiert (Hardwarezugriff über solchen "Dateien").

Ansonsten hast du mir doch jetzt einen sehr guten Grund geliefert die Dateisysteme doch als eigenständige Prozesse zu implementieren.

Nur wie setzt man dann sowas (besser wo) um wie gleichzeitiger (unabhängiger) Lese-Zugriff und ein anderes Programm schreibt in diese Datei. Ich möchte etwas implementieren so dass ich bei Änderungen eine Benachrichtigung bekomme. Nur wird das im VFS oder im Dateisystem implementiert?

Zitat von: taljeth
Die Metadaten in eine Pipe und die Nutzdaten eventuell über einen anderen Mechanismus wie Shared Memory klingt für mich eigentlich sehr sinnvoll (und deswegen ist das ja auch die Richtung, in die wir mit LIOv2 gehen).
Klingt ähnlich wie mein IPC System, meine Nachrichten bestehen aus 32byte "Metadaten" und Shared Memory als Nutzdaten. Allerdings kopiert man dadurch natürlich viel wenn nicht direkt das IPC, sondern z.B. die libc genutzt wird.

So wir werden schonwieder ganz schön OT, für eine Diskussion zwecks VFS oder IPC sollte man vllt nen neuen Thread aufmachen ;)

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #21 am: 16. October 2011, 14:52 »
In einer Device-Datei unter Unix stehen nur Major- und Minor-Nummern, also auch nur 2-4 Bytes. Allerdings sind das dort nicht die "Daten" der Datei, sondern spezielle Inode-Verweise. Es sind ja trotzdem Dateien. Die Größe könnte die Größe des Block-Devices (oder null) sein, der Inhalt ist genau das, was das Blockdevice rauswirft.

Da aber jeder Zugriff auf diese Datei durchs VFS hindurch muss, kann eben das VFS auch einfach ein paar Standard-Requests direkt an den Blockdevice-Treiber weiterreichen. Wenn der einen bestimmten Request nicht implementiert, geht der Fehler durch das VFS hindurch zum aufrufenden Programm.

Der Fall "fünf Prozesse lesen, während gleichzeitig ein Prozess in die Datei schreibt" muss nur soweit behandelt werden, dass die Daten konsistent sind, also entweder die kompletten neuen oder die kompletten alten Daten. Alles weitere ist egal.

Änderungen im Dateisystem gehen ebenfalls immer durch das VFS, also gehört die entsprechende Implementation auch dorthin. (Außerdem möchtest du nicht, dass sich verschiedene Dateisysteme unterschiedlich verhalten.) Das Dateisystem sollte aber in der Lage sein, so ein Ereignis zu triggern; bei Netzwerk-Dateisystemen könnte das nützlich sein.

Gruß,
Svenska

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #22 am: 16. October 2011, 22:56 »
Hallo,


ist kein Plug-In geladen ist auch kein Element in der Liste. Die Listeneinträge werden erst erstellt wenn ein Plug-In geladen ist
Aha, da ist dann aber doppelte Indirektion weil der Code im Programm ja nicht direkt das Plug-In aufruft sondern dies immer über einen Vermittlungsmechanismus tut, sehe ich das richtig?

.... und kann durch einen Funktionsaufruf geholt werden.
Und der Loader findet diese Funktion anhand eines fest definierten Label-Namens?

Ich kann dir gerade nicht ganz folgen was du genau meinst ....
Ist okay, das trifft Dich beim Flat-Memory eh nicht weil Du ja für jeden Prozess ein eigenes PD hast und daher immer weißt wem der Speicher gehört den Du auslagern möchtest (das der Page-Ersetzungsalgorithmus keine Pages vorschlägt die gar nicht ausgelagert werden dürfen hab ich mal stillschweigend vorausgesetzt). Dieses Problem trifft nur mich weil ich für das gesamte System nur ein einziges PD habe und daher schon wissen muss wem der Speicher gehört und in welchem Segment er enthalten ist damit ich dann auch im richtigen Segment-Descriptor das Paging anschalten kann, sonst würde der Prozess auf Speicher zugreifen können der schon längst für was anderes benutzt wird.

Was dynamisches Laden zur Laufzeit betrifft: http://en.wikipedia.org/wiki/Dynamic_loading
Interessant aber erklärt auch nicht wie dieses dynamische Linken hinter den Kulissen genau funktioniert.
Dafür habe ich mal wieder etwas (für mich) neues über C gelernt, nämlich das man void-Pointer nicht offiziell in Funktions-Pointer umwandeln kann. ;)


Willst du also das ganze Netzwerkzeug in den VFS-Server einbauen?
Das VFS direkt per TCP/UDP von Außen erreichbar zu machen fällt IMHO in die Kategorie "grob fahrlässig". ;)

Was du in der Regel für IPC brauchst sind einige wenige Metadaten und je nach Request möglicherweise noch ein großer Haufen Nutzdaten.
Von den Daten her ist das richtig, trotzdem sollte nicht vergessen werden das die meisten IPC-Anwendungen eher RPC sind also auch immer eine zugehörige Antwort benötigen, deswegen würde ich jetzt mal vermuten das einfache Dateioperationen für effizientes IPC nicht die erste Wahl sind, als Antwort beim RPC wird man oft mehr benötigen als die Rückgabe-Werte von read()/write() leisten können.

Die Metadaten in eine Pipe und die Nutzdaten eventuell über einen anderen Mechanismus wie Shared Memory klingt für mich eigentlich sehr sinnvoll (und deswegen ist das ja auch die Richtung, in die wir mit LIOv2 gehen).
Solange ich nicht weiß wie der Shared Memory ins IPC eingebunden ist (ob integraler Bestandteil ober über zusätzliche Syscalls) kann ich dazu eigentlich nix sagen, von daher warte ich einfach mal ab wie das Ergebnis werden wird. Auch wäre es interessant zu wissen ob derart erhaltener Speicher auch per IPC wieder weitergereicht werden kann, damit das Zero-Copy auch über mehrere Ebenen hinweg funktioniert, und was mit den Rändern der Pages passieren soll, die keine erwünschten Nutzdaten enthalten. Oder soll Ausrichten Pflicht sein?


Ansich würde ich schon sowas wie /dev/ haben wollen ....
Dem will ich mich anschließen, ich hatte mir das so vorgestellt das bei einem open(/dev/hdb6) vom VFS an die libc eine spezielle Antwort kommt in der dann das entsprechende Device-Interface eindeutig benannt ist (zusammen mit der Device-Art) und die libc für read/write/... direkt den Device-Treiber benutzt, alternativ kann die Antwort bei open natürlich auch "Access denied" sein. Beim close meldet die libc wieder ans VFS das es jetzt dieses Device geschlossen hat so das Devices die nur exklusiv benutzt werden können (Block-Devices gehören IMHO in diese Kategorie) dann auch wieder für andere zur Verfügung stehen. Das einzigste Problem das ich sehe ist das die Zugangsdaten zum Device-Treiber ja noch immer in dem Programm vorhanden sein können und demzuvollge auch benutzt werden können, von daher wäre es nicht schlecht wenn das VFS bei jedem open auf ein Device dem Treiber ein weiteres Set an Zugangsdaten mitteilt die dann auch das Programm bekommt und diese Zugangsdaten beim close vom VFS beim Device-Treiber wieder gelöscht werden. Sicher is Sicher.

Nur wie setzt man dann sowas (besser wo) um wie gleichzeitiger (unabhängiger) Lese-Zugriff und ein anderes Programm schreibt in diese Datei.
Kleines Beispiel: Programm A schreibt in eine Datei in 4 * 100 Byte Häppchen neue Daten und Programm B ließt die Datei aber ein einem Stück (also 400 Bytes) ein dann müssen die Schreibzugriffe von Programm A in der getätigten Reihenfolge für Programm B sichtbar werden. Wenn also Programm A die 4 Häppchen in aufsteigender Reihenfolge geschrieben hat dann ist es zwar möglich das Programm B in den Häppchen 1 und 2 schon neue Daten findet und in den Häppchen 3 und 4 noch alte Daten aber es darf niemals passieren das Programm B in den Häppchen 1 und 3 die neuen Daten sieht und in den Häppchen 2 und 4 noch alte Daten. Äquivalent auch wenn Programm A alles mit einem mal schreibt und Programm B in 4 unabhängigen Häppchen liest. Das muss Dein VFS zusichern können, solange das erfüllt ist kannst Du frei Cachen wie Du willst.

Der Benchritigungs-Mechanismus gehört auch meiner Meinung nach ganz klar ins VFS, dort müssen alle wirksamen/sichtbaren Änderungen durch. Wobei das mit Devices die am VFS vorbei gehen wieder schwierig wird aber dort könnte das VFS ja auch die Einrichtung einer Benachrichtigungsfunktion verweigern, ich vermute mal dass das dort keiner vermissen wird.


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: 17. October 2011, 09:49 »
Von den Daten her ist das richtig, trotzdem sollte nicht vergessen werden das die meisten IPC-Anwendungen eher RPC sind also auch immer eine zugehörige Antwort benötigen, deswegen würde ich jetzt mal vermuten das einfache Dateioperationen für effizientes IPC nicht die erste Wahl sind, als Antwort beim RPC wird man oft mehr benötigen als die Rückgabe-Werte von read()/write() leisten können.
Also erstmal: Wenn man es richtig macht, sollte die Antwort auf die RPCs oft asynchron verarbeitet werden. Zum Beispiel könnte eine Implementierung von cp während des Schreibens auf das Ziel schonmal weitermachen und das nächste Stück aus der Quelle lesen. Es gibt keinen guten Grund, warum es warten und nichts tun sollte.

Du hast natürlich insofern recht als die meiste Software nicht so geschrieben ist. Die Frage ist, ob Software, der es nicht wert ist, darauf zu achten, wirklich perfekte Performance braucht. Ist halt ein Trade-off von Einfachheit gegen Perfomance.

Aber wenn du es wirklich darauf anlegst und sagst, dass erst ein read() und danach ein write() zwei Syscalls und damit viel zu langsam sind (wofür ich ehrlichgesagt keine Grundlage sehe), dann könnte man theoretisch auch einen read_and_write()-Syscall bauen, der halt beides auf einmal macht und damit hast du dein synchrones RPC wieder. Man dürfte natürlich immer nur einen Request gleichzeitig auf dieser Pipe laufen haben, aber das ist ja bei synchronem RPC sowieso so. Ich sehe nur keinen rechten Vorteil darin, es so zu machen.

Zitat
Solange ich nicht weiß wie der Shared Memory ins IPC eingebunden ist (ob integraler Bestandteil ober über zusätzliche Syscalls) kann ich dazu eigentlich nix sagen, von daher warte ich einfach mal ab wie das Ergebnis werden wird. Auch wäre es interessant zu wissen ob derart erhaltener Speicher auch per IPC wieder weitergereicht werden kann, damit das Zero-Copy auch über mehrere Ebenen hinweg funktioniert, und was mit den Rändern der Pages passieren soll, die keine erwünschten Nutzdaten enthalten. Oder soll Ausrichten Pflicht sein?
Also dass Zero Copy möglich ist, sollte Grundvoraussetzung für einen vernünftigen Mechanismus sein. Darin sehe ich aber auch keine größeren Designprobleme.

Im Moment haben wir für SHM eigene Syscalls und der Speicher ist immer alignt (man fordert halt eine bestimmte Größe an und kriegt dann frische Pages dafür). Ich könnte mir aber auch andere, ins VFS integrierte Methoden vorstellen, zum Beispiel eine gemmapte Datei in shm:/ oder so.

Eine interessante Frage ist auch, ob man bei jedem Request neuen SHM nimmt oder ob der nicht einmal beim open() der Pipe angelegt und wiederverwendet werden kann. Je nachdem, wie man diese Frage beantwortet, muss man darauf achten, ob das Einrichten/Öffnen von SHM optimiert werden muss oder nicht.

Zitat
Dem will ich mich anschließen, ich hatte mir das so vorgestellt das bei einem open(/dev/hdb6) vom VFS an die libc eine spezielle Antwort kommt in der dann das entsprechende Device-Interface eindeutig benannt ist (zusammen mit der Device-Art) und die libc für read/write/... direkt den Device-Treiber benutzt
Damit verliert FlashBurn dann seinen gemeinsamen Dateizeiger nach dem fork(), den wollte er ja im VFS implementieren.

Zitat
Kleines Beispiel: Programm A schreibt in eine Datei in 4 * 100 Byte Häppchen neue Daten und Programm B ließt die Datei aber ein einem Stück (also 400 Bytes) ein dann müssen die Schreibzugriffe von Programm A in der getätigten Reihenfolge für Programm B sichtbar werden.
Da kommen wir in ein interessantes Gebiet. :)

Was man muss oder nicht muss hängt natürlich in erster Linie von der API ab, die man implementieren will. Ich halte die Vorgabe, die du machst, grundsätzlich für sinnvoll, aber theoretisch könnte man verlangen, dass A flushen muss, wenn es eine Reihenfolge garantieren will.

Das macht an dieser Stelle wahrscheinlich wenig Sinn, weil man es vermutlich fast automatisch so macht, dass das sowieso passt. Aber das gleiche Problem hast du ja auch wieder mit Plattenzugriffen: Garantierst du, dass diese vier Writes auch in der richtigen Reihenfolge auf der Platte ankommen? Wie spielen Dateisystem-Metadaten mit rein? Was muss wann aktualisiert werden, und was geht automatisch und was muss das Programm explizit anfordern?
« Letzte Änderung: 17. October 2011, 09:51 von taljeth »
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: 17. October 2011, 10:58 »
Zitat von: erik
Aha, da ist dann aber doppelte Indirektion weil der Code im Programm ja nicht direkt das Plug-In aufruft sondern dies immer über einen Vermittlungsmechanismus tut, sehe ich das richtig?
Ich weiß nicht was du mit doppelter Indirektion meinst, aber es ist nix anderes als Irgendwo Funktionspointer zu haben, zumal die Performance in dem Fall keine Rolle spielt.

Zitat von: erik
Und der Loader findet diese Funktion anhand eines fest definierten Label-Namens?
Jap, du suchst z.B. per dlsym("initFS") nach einem Symbol und kannst die Funktion dann aufrufen. Im Endeffekt implementiert jedes Modul mind. die nötigen API Symbole (init, deinit usw).

Zitat von: erik
Interessant aber erklärt auch nicht wie dieses dynamische Linken hinter den Kulissen genau funktioniert.
Wo genau hapert es bei dir? Ich meine du lädst halt das Modul, was nen shared-object ist und löst alles externen Symbole auf. Dazu brauchst du entweder weitere Libs, aber auf jeden Fall brauchst du die Symbole von dem Programm was das Modul geladen hat.
Die Symbole die du aus deinem Programm heraus dann im Modul nutzen willst, holst du dir halt über dlsym().

Zitat von: erik
Das einzigste Problem das ich sehe ist das die Zugangsdaten zum Device-Treiber ja noch immer in dem Programm vorhanden sein können und demzuvollge auch benutzt werden können, von daher wäre es nicht schlecht wenn das VFS bei jedem open auf ein Device dem Treiber ein weiteres Set an Zugangsdaten mitteilt die dann auch das Programm bekommt und diese Zugangsdaten beim close vom VFS beim Device-Treiber wieder gelöscht werden. Sicher is Sicher.
Das will ich direkt mit Hilfe des IPCs lösen. Bei meinen Ports kannst du die Lese- und Schreibrechte grob angeben, heißt lesen global, ein Thread oder ein Task und schreiben auch global, ein Thread oder ein Task.
Für die Treiber bräuchte man dann 2 Ports, einen für das VFS (wo auch niemand anders reinschreiben darf) und einen für den Nutzer. Wenn der Nutzer eine Gerätedatei öffnet, sagt das VFS dem Treiber wer das war und der Treiber ändert entsprechend die Schreibrechte für den anderen Port. Genauso wenn der Nutzer die Gerätedatei wieder schließt, dann bekommt der Treiber ne Nachricht und ändert die Rechte entsprechend.

Zitat von: erik
Der Benchritigungs-Mechanismus gehört auch meiner Meinung nach ganz klar ins VFS, dort müssen alle wirksamen/sichtbaren Änderungen durch.
D.h. aber das wirklich alle Sachen auch durchs VFS müssen und dass man z.B. die libc nicht direkt mit dem Treiber/Dateisystem kommunizieren lassen kann.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #25 am: 17. October 2011, 18:37 »
Hallo,


Also erstmal: Wenn man es richtig macht, sollte die Antwort auf die RPCs oft asynchron verarbeitet werden.
Ja, dem muss ich unbedingt zustimmen. Das bedeutet aber das man eigentlich 3 Arten von IPC benötigt: einmal asynchrones IPC wo der Sender einfach eine Message an den Empfänger schickt und sofort weiter arbeitet ohne zu blockieren und der Empfänger diese Message irgendwann (möglichst zeitnah) empfängt und verarbeitet es aber keine zugehörige Antwort gibt (das ist z.B. für GUI-Events oder für IRQ-Messages passend). Dann das synchrone blockierende IPC (also simples RPC) wo der Caller solange blockiert bis der Service den Request fertig bearbeitet hat und die zugehörige Antwort abschickt (die dann der Caller gleich quasi als Rückgabewert des IPC-Calls bekommt). Und dann noch das synchrone nicht-blockierende IPC (also erweitertes RPC) wo der Caller nicht blockiert aber dafür eine Callback-Funktion benennt die dann vom Kernel (mit der zugehörigen Antwort) aufgerufen wird wenn der Service den Request fertig bearbeitet hat. Die zwei synchronen Varianten sind für den Service gleichwertig (der muss ja nicht mal unbedingt wissen ob der Caller in der Zeit was anderes erledigt oder nicht, wenngleich ich diese Info der Höflichkeit halber dann doch mitgeben würde).

Es gibt keinen guten Grund, warum es warten und nichts tun sollte.
Keinerlei Widerspruch meinerseits.

Also dass Zero Copy möglich ist, sollte Grundvoraussetzung für einen vernünftigen Mechanismus sein.
Das sehe ich auf jeden Fall ganz genau so. Aber ob das auch wirklich funktioniert wenn der IPC-Mechanismus dafür eigenen (extra allozierten) Speicher nutzen will und nicht direkt mit dem User-Buffer (der z.B. bei read() mitgegeben wird) arbeitet möchte ich dann doch bezweifeln. Bei mir soll es auf jeden Fall möglich sein das z.B. beim write()-Aufruft für eine TCP-Connection der Ethernet-Controller auf der Netzwerkkarte direkt die Daten aus dem User-Buffer der eigentlichen Anwendung holt. Wenn das VFS nicht cachen würde würde das bei Dateien und dem AHCI-Controller ebenso klappen, auch wenn die Dateien z.B. vom NFS-Client kommen soll der Ethernet-Controller direkt auf den Cache-Speicher vom VFS zugreifen. Dieses komplett durchgängige Zero-Copy ist IMHO für optimale Performance essentiell wichtig.

Damit verliert FlashBurn dann seinen gemeinsamen Dateizeiger nach dem fork(), den wollte er ja im VFS implementieren.
Hm, stimmt auffallend, da wird sich FlashBurn wohl oder übel einen Kompromiss einfallen lassen müssen.

Was man muss oder nicht muss hängt natürlich in erster Linie von der API ab, die man implementieren will.
Um das zu beurteilen weiß ich nicht genug darüber was POSIX oder auch die WinAPI an Kohärenz zusichern. Wie ich auch schon mal beim Thema mit dem gemeinsamen File-Pointer erklärt habe denke ich das es nur extrem wenige Szenarien gibt in denen tatsächlich mehrere Programme gleichzeitig auf einer Datei rumarbeiten. Das z.B. mehrere gcc-Instanzen gleichzeitig die selbe H-Datei einlesen ist sicher nichts ungewöhnliches aber das echt ein Programm eine Datei ändert und ein anderes diese Datei gleichzeitig ließt halte ich für ziemlich Praxisfern. Trotzdem muss das VFS hier natürlich irgendwelche Zusicherungen machen. Meine Vorgabe war wohl auch eher eine Art Diskussionsgrundlage und keine endgültige Festlegung, so oder so ähnlich würde ich persönlich das versuchen umzusetzen, einfach weil ich das so für sinnvoll erachte, ob das auch in der Praxis nötig oder überhaupt nützlich ist muss ich aber auch erst mal nachlesen.

Den Datei-System-Treiber oder gar den Block-Device-Treiber würde ich da aber eher nicht mit einbeziehen wollen (das Dateisystem wird schon eigene Ansprüche haben um z.B. immer einen einigermaßen konsistenten Zustand auf der HDD zu erreichen), ich denke hier ist das VFS alleine gefordert, alles was immer über das VFS geht kann das VFS schließlich auch alleine konsistent halten.


So langsam sollten wir dieses ganze IPC-Zeugs wirklich in einen eigenen Thread verlagern.


Jap, du suchst z.B. per dlsym("initFS") nach einem Symbol und kannst die Funktion dann aufrufen.
Okay, ich glaube so langsam hab ich das kapiert, Danke.

D.h. aber das wirklich alle Sachen auch durchs VFS müssen und dass man z.B. die libc nicht direkt mit dem Treiber/Dateisystem kommunizieren lassen kann.
Also direkt mit dem Dateisystem-Treiber würde ich eh keine beliebige Applikation kommunizieren lassen, was soll das auch bringen, echte Dateizugriffe müssen IMHO immer durch das VFS. Bei den Block-/Character-Devices sehe ich das anders aber gerade die sollten IMHO ja eh auf eigene Weise geshared werden bzw. die Block-Devices immer nur exklusiv geöffnet werden dürfen so das in beiden Fällen ein Benachrichtigungsmechanismus eh nur von geringem Nutzen oder sehr proprietär ist. Ich persönlich hätte zumindest kein Problem wenn das VFS beim Versuch Benachrichtigungen für Devices einzurichten immer "Service not available" antwortet.


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: 17. October 2011, 20:27 »
Das bedeutet aber das man eigentlich 3 Arten von IPC benötigt: [...]
Dazu will ich nur mal festhalten, dass wir offenbar eine unterschiedliche Terminologie benutzen, bevor das noch zu Schwierigkeiten führt. Bei synchron sind wir uns einig, aber was du "synchron nicht-blockierend" nennst, ist für mich asynchron (welche zwei Sachen sollten denn da synchron sein?). Das dritte ist bei mir einfach eine Funktion ohne Rückgabewert. ;)

Inhaltlich will ich dir nicht widersprechen, aber ich bin mir nicht sicher, ob die Unterscheidung besonders relevant ist. Ich kann z.B. mit der Pipe alle drei Varianten ohne weiteres abbilden.

Zitat
Aber ob das [Zero Copy] auch wirklich funktioniert wenn der IPC-Mechanismus dafür eigenen (extra allozierten) Speicher nutzen will und nicht direkt mit dem User-Buffer (der z.B. bei read() mitgegeben wird) arbeitet möchte ich dann doch bezweifeln. Bei mir soll es auf jeden Fall möglich sein das z.B. beim write()-Aufruft für eine TCP-Connection der Ethernet-Controller auf der Netzwerkkarte direkt die Daten aus dem User-Buffer der eigentlichen Anwendung holt.
An dieser Stelle bist du natürlich mit deinen bytegenauen Segmenten fein raus. Das gemeine Fußvolk hat nur Page-Granularität und damit geht das nicht ganz so uneingeschränkt. Ich sehe darin allerdings auch kein Problem: Wenn read()/write() kopieren, dann tun sie es eben. Das ist immer noch nur eine einzige Kopie und wird in den meisten Fällen nicht weh tun. Wenn ich sage, dass es die Möglichkeit geben muss, heißt das für mich, dass es auch akzeptabel ist, wenn das Programm dafür ein bisschen Aufwand treiben und meinetwegen ausgerichtete Puffer benutzen muss (mit O_DIRECT unter Linux muss man das ja auch, zum Beispiel).

Zitat
Was man muss oder nicht muss hängt natürlich in erster Linie von der API ab, die man implementieren will.
Um das zu beurteilen weiß ich nicht genug darüber was POSIX oder auch die WinAPI an Kohärenz zusichern.
Wieso musst du das dazu wissen? Du hast doch sicher eine eigene Meinung, was sinnvoll ist?

Um nur mal ein paar Varianten auf einem POSIX-System aufzuzählen: Normalerweise ist alles, was man mit read()/write() macht, sofort für andere Programme sichtbar, aber nicht zwingend auf der Platte. Mit fsync() zwingt man es auf die Platte, fdatasync() schreibt die Daten auf die Platte, aber aktualisiert die Metadaten des Dateisystems nicht. Mit O_SYNC oder O_DSYNC kann man automatisch das eine oder andere nach jedem write() machen lassen. Wenn man unter Linux O_DIRECT benutzt, wird der Pagecache umgangen, aber die Daten könnten noch in einem flüchtigen Schreibcache der Platte (oder ähnliches) hängen. Und dann gibt es noch die stdio.h-Funktionen, die in der Regel erstmal puffern und gar nichts ans OS weitergeben, so dass die Änderungen erst nach einem vollen Block bzw. einem Zeilenumbruch oder einem expliziten fflush() für andere Programme sichtbar sind.

Was ich sagen will: Es gibt da annähernd unendlich viele Möglichkeiten und man sollte sich schon Gedanken drüber machen, was man eigentlich haben will.

Zitat
Das z.B. mehrere gcc-Instanzen gleichzeitig die selbe H-Datei einlesen ist sicher nichts ungewöhnliches aber das echt ein Programm eine Datei ändert und ein anderes diese Datei gleichzeitig ließt halte ich für ziemlich Praxisfern.
Aber nicht unbedingt praxisfern, weil es nicht nützlich wäre, sondern vor allem deswegen, weil es nicht leicht ist, das korrekt zu implementieren. Da kommt man dann schnell in die Nähe von Clusterdateisystemen oder ähnlichem...

Zitat
Den Datei-System-Treiber oder gar den Block-Device-Treiber würde ich da aber eher nicht mit einbeziehen wollen (das Dateisystem wird schon eigene Ansprüche haben um z.B. immer einen einigermaßen konsistenten Zustand auf der HDD zu erreichen), ich denke hier ist das VFS alleine gefordert, alles was immer über das VFS geht kann das VFS schließlich auch alleine konsistent halten.
Das VFS weiß nicht, was nötig ist, um Anwendungsdaten konsistent zu halten, wenn die Anwendung es ihm nicht sagt. Du kannst natürlich immer deine Entsprechung von O_SYNC benutzen, dann ist es ganz bestimmt immer konsistent, aber dann ist auch deine Performance konsistent niedrig...

Und der Dateisystemtreiber muss dem Blocktreiber auch mitteilen können, was für eine Art von Konsistenz er gerade braucht.

Zitat
So langsam sollten wir dieses ganze IPC-Zeugs wirklich in einen eigenen Thread verlagern.
Tu dir keinen Zwang an. ;)
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 #27 am: 18. October 2011, 08:41 »
Hallo,


Dazu will ich nur mal festhalten, dass wir offenbar eine unterschiedliche Terminologie benutzen
Ja, das war mir schon während des Schreibens bewusst, mir ist nur keine bessere Benahmsung eingefallen (Du weißt ja dass das nicht gerade meine Stärke ist), "Funktion ohne Rückgabewert" klingt jedenfalls irgendwie doof gegenüber '"synchron" und "asynchron". Mach doch da Bitte mal einen geeigneten Vorschlag.

Die Unterscheidung zwischen diesen 3 Varianten halte ich für durchaus relevant da es ja Unterschiede bei der Dauer des Memory-Sharings gibt (zumindest wenn man das, so wie ich, auch tatsächlich über Memory-Sharing machen will). Gerade bei der ganz simplen Variante ("Funktion ohne Rückgabewert") ist es für den Caller durchaus relevant wann er den so gesharten Speicher wieder für andere Dinge nutzen kann. Bei mir hab ich vorgesehen das der Call in diesem Fall das Kopieren erzwingen kann (was ja gerade für kleine Events oder auch IRQ-Messages auch absolut kein Problem ist, 20 oder 30 Bytes zu kopieren dürfte meistens schneller sein als ein neues Mapping anzulegen).

Ich kann z.B. mit der Pipe alle drei Varianten ohne weiteres abbilden.
Das man das alles jeweils auch mit anderen Mitteln abbilden kann ist zwar richtig aber ob dabei dann auch noch eine anständige Performance rauskommt muss doch arg bezweifelt werden. Ich bin schon der Meinung das bei einem Mechanismus, der bei einem Micro-Kernel-OS von so zentraler Bedeutung ist wie eben IPC, nicht nur das "Ob" sondern auch das "Wie" zählt. Dass das flachspeichernde Fußvolk hier ein paar zusätzliche Nachteile zu tragen hat ringt mir aber nur sehr begrenzt Mitleid ab. ;)

(mit O_DIRECT unter Linux muss man das ja auch, zum Beispiel).
Aha, das wusste ich noch gar nicht, ich sollte mich damit wirklich mal etwas näher beschäftigen, schon um einfach mal ein Gefühl dafür zu bekommen wo ich gegenüber der Konkurenz eigentlich stehen könnte.

Wieso musst du das dazu wissen?
Es ist IMHO schon wichtig zu wissen was die anderen so bieten um auch abschätzen zu können was die typischen Programme so verlangen könnten. Man will ja schließlich kein unperformantes Over-Engineering oder untaugliches Under-Engineering abliefern.

Du hast doch sicher eine eigene Meinung, was sinnvoll ist?
Das ich zu fast allem eine eigene Meinung habe ist doch hier schon mehr als einmal unangenehm aufgefallen.

Um nur mal ein paar Varianten auf einem POSIX-System aufzuzählen: ....
Danke, das war doch mal sehr informativ.
Das alles was auch tatsächlich ins VFS geht sofort für andere sichtbar ist spricht dafür dass das VFS unter POXIS recht streng mit der Kohärenz ist (also vermutlich in der Nähe meines Vorschlages von vorgestern Abend liegt). Die libc wird intern sicher immer nur einzelne Blöcke Cachen (vermutlich eine Pagegröße o.ä.) um damit vor allem kleine fread()/fwrite()-Häppchen möglichst effizient ans OS übergeben zu können, das ist wichtig und sollte von einer guten libc auch angemessen unterstützt werden aber für die große Kohärenz im Gesamtsystem ist das IMHO nur von untergeordneter Bedeutung. Das die HDD die per SATA angelieferten Daten nicht sofort auf die Magnetscheiben wegschreibt ist ein bekanntes Problem, trotzdem ist die HDD von Außen betrachtet (wenn wir mal von plötzlichem Stromausfall u.ä. absehen) immer Konsistent (wenn ein Sektor gelesen wird der nur im HDD-Cache neu ist aber noch nicht auf den Magnetscheiben gespeichert wurde dann werden die aktuellen Daten aus dem Cache geliefert). Diese Vorgehensweise trifft in dem Stapel Applikation/VFS/Dateisystem/Block-Device auf jede Schicht zu. Wenn in einem Programm per fwrite() ein paar Bytes geschrieben wurden dann wird fread beim anschließenden Lesen der selben Bytes auch immer die aktuellen (gerade geschriebenen) Daten liefern egal ob diese Bytes bereits ans VFS durchgereicht wurden oder nicht. Bei einer SATA-HDD mit NCQ kann man aber bei jedem Commando mitteilen welches andere Commando (von den noch ausstehenden) abgeschlossen sein muss bevor dieses ausgeführt wird, damit kann z.B. erreicht werden das die Nutzdaten bereits geschrieben sind bevor die Metadaten aktualisiert werden (eine Methode die die besseren Dateisystem-Treiber im Linux-Kernel bestimmt nutzen, insbesondere jene die Journaling können), unbeteiligte andere Commandos kann die HDD dazu in jeder beliebigen Reihenfolge ausführen (wobei diese untereinander auch wieder Abhängigkeiten haben können).

Zitat
Das z.B. mehrere gcc-Instanzen gleichzeitig die selbe H-Datei einlesen ist sicher nichts ungewöhnliches aber das echt ein Programm eine Datei ändert und ein anderes diese Datei gleichzeitig ließt halte ich für ziemlich Praxisfern.
Aber nicht unbedingt praxisfern, weil es nicht nützlich wäre, sondern vor allem deswegen, weil es nicht leicht ist, das korrekt zu implementieren.
Doch, ich denke auch weil es tatsächlich nicht nützlich ist. Wenn Du da anderer Meinung bist nenne doch mal ein nützliches Beispiel wo eine Datei von einem Programm wahlfrei modifiziert wird während ein anderes Programm diese Datei einließt, mir fällt da absolut nichts ein. Das einzigste was wenigstens grob in die Nähe kommt sind Log-Dateien aber die werden nicht wahlfrei beschrieben sondern es wird (für gewöhnlich) ausschließlich angehängt und andere Programme sehen die neuen (angehängten) Log-Meldungen bereits oder eben noch nicht. In meinem VFS möchte ich es so machen das eine Datei die zum beliebigen Beschreiben geöffnet wurde nicht mehr einfach so von anderen Programmen geöffnet werden kann, bei Dateien die als "Append-Only" geöffnet wurden (also typischerweise Log-Dateien) ist das natürlich anders.

Zitat
So langsam sollten wir dieses ganze IPC-Zeugs wirklich in einen eigenen Thread verlagern.
Tu dir keinen Zwang an. ;)
Ich kann leider keine Admin-Werkzeuge in meiner Forumansicht sehen so das es mir leider nicht möglich ist die letzten Posts dieses Threads in einen neuen Thread zu verschieben. ;)


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 #28 am: 18. October 2011, 10:16 »
Ja, das war mir schon während des Schreibens bewusst, mir ist nur keine bessere Benahmsung eingefallen (Du weißt ja dass das nicht gerade meine Stärke ist), "Funktion ohne Rückgabewert" klingt jedenfalls irgendwie doof gegenüber '"synchron" und "asynchron". Mach doch da Bitte mal einen geeigneten Vorschlag.
Wie gesagt, ich glaube nicht, dass es einen entscheidenden Unterschied macht, deswegen brauche ich dafür auch selten ein Wort. Ich könnte dir höchstens eine pascalige Unterscheidung zwischen Funktion und Prozedur anbieten...

Zitat
Die Unterscheidung zwischen diesen 3 Varianten halte ich für durchaus relevant da es ja Unterschiede bei der Dauer des Memory-Sharings gibt
Davon bin ich nicht ganz überzeugt. Ja, es gibt Unterschiede in der Dauer des Memory Sharing. Die haben aber nicht zwingend was mit der Art des RPC zu tun, sondern damit, was der RPC mit dem übergebenen Speicher machen will. Wer sagt denn, dass der Empfänger den Speicher nicht weiterbenutzen will, nachdem er eine Antwort geschickt hat? (Eine Antwort in der Art "Ich hab den Puffer bekommen, der Inhalt sieht gültig aus, danke, ich kann jetzt allein damit weiterarbeiten")

Oder umgekehrt könnte es passieren, dass der Speicher eigentlich schon wieder unbenutzt ist, während noch andere Operationen durchgeführt werden müssen, bevor man eine Antwort schicken kann.

Von der formalen Art der Funktion auf ihr Verhalten zu schließen finde ich fragwürdig.

Zitat
Bei mir hab ich vorgesehen das der Call in diesem Fall das Kopieren erzwingen kann (was ja gerade für kleine Events oder auch IRQ-Messages auch absolut kein Problem ist, 20 oder 30 Bytes zu kopieren dürfte meistens schneller sein als ein neues Mapping anzulegen).
Ja. Kleine Sachen in die Pipe, größere in den SHM. ;)

Zitat
Das man das alles jeweils auch mit anderen Mitteln abbilden kann ist zwar richtig aber ob dabei dann auch noch eine anständige Performance rauskommt muss doch arg bezweifelt werden. Ich bin schon der Meinung das bei einem Mechanismus, der bei einem Micro-Kernel-OS von so zentraler Bedeutung ist wie eben IPC, nicht nur das "Ob" sondern auch das "Wie" zählt.
Dann erklär doch mal, wo ich bei meinem Ansatz die ganze Performance verliere. Oder welche der drei Arten ich nicht vernünftig, sondern nur irgendwie abbilden kann.

Zitat
Das alles was auch tatsächlich ins VFS geht sofort für andere sichtbar ist spricht dafür dass das VFS unter POXIS recht streng mit der Kohärenz ist (also vermutlich in der Nähe meines Vorschlages von vorgestern Abend liegt).
Ja, ich denke auch, dass deine Vorstellung relativ gut darauf passt. (An dieser Stelle muss man vielleicht noch erwähnen, dass unter Linux nicht alles POSIX-kompatibel ist, NFS bietet diese Kohärenz nicht unbedingt)

Zitat
Die libc wird intern sicher immer nur einzelne Blöcke Cachen (vermutlich eine Pagegröße o.ä.) um damit vor allem kleine fread()/fwrite()-Häppchen möglichst effizient ans OS übergeben zu können, das ist wichtig und sollte von einer guten libc auch angemessen unterstützt werden
Jede standardkonforme libc muss das unterstützen. Und mit setvbuf() kannst du den Puffer selber so einrichten, wie du es für richtig hältst.

Zitat
(wenn wir mal von plötzlichem Stromausfall u.ä. absehen)
Und genau das ist falsch!

Stromausfälle, Abstürze und so weiter sind kein theoretisches, sondern ein ganz reales Problem. Wenn nach einem Stromausfall die Datenbank oder sogar das ganze Dateisystem inkonsistent wird und mir womöglich komplett um die Ohren fliegt, dann bleibt mir beim besten Willen nichts anderes übrig als mal wieder das K-Attribut an den zuständigen Code zu verteilen.

Dateisysteme in ganz besonderem Maß, aber auch normale Anwendungen müssen sicherstellen, dass die Daten auf der Platte immer soweit konsistent sind, dass man bei einem Absturz keine Korruption, sondern maximal Datenverlust bekommt. Wenn sie das nicht tun, sind sie buggy.

Zitat
Doch, ich denke auch weil es tatsächlich nicht nützlich ist. Wenn Du da anderer Meinung bist nenne doch mal ein nützliches Beispiel wo eine Datei von einem Programm wahlfrei modifiziert wird während ein anderes Programm diese Datei einließt, mir fällt da absolut nichts ein.
Backup eines Snapshots im Image einer laufenden VM. Sonstige Zugriffe des Host auf das Image. Clusterdateisystem auf einem Image, das von zwei VMs geteilt wird.

Ich weiß, ein bisschen einseitig, aber das ist halt mein Arbeitsbereich. Andere können sicher andere Beispiele liefern. ;)

Zitat
Ich kann leider keine Admin-Werkzeuge in meiner Forumansicht sehen so das es mir leider nicht möglich ist die letzten Posts dieses Threads in einen neuen Thread zu verschieben. ;)
Hm, da wüsste ich sowieso nicht, wo anpacken, das war ja mal wieder ein fließender Übergang... Ich dachte einfach einen neuen Thread erstellen, wo wir weitermachen.
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 #29 am: 18. October 2011, 13:10 »
Hallo,


Ich könnte dir höchstens eine pascalige Unterscheidung zwischen Funktion und Prozedur anbieten...
Hm, klingt irgendwie interessant, aber ich benutze bereits 'proc' als Abkürzung für Process so das mir da jetzt nichts geeignetes für Procedure einfällt. Ich hatte da noch an simple ('smpl') gedacht.

Die haben aber nicht zwingend was mit der Art des RPC zu tun, sondern damit, was der RPC mit dem übergebenen Speicher machen will. Wer sagt denn, dass der Empfänger den Speicher nicht weiterbenutzen will, nachdem er eine Antwort geschickt hat? (Eine Antwort in der Art "Ich hab den Puffer bekommen, der Inhalt sieht gültig aus, danke, ich kann jetzt allein damit weiterarbeiten")
Das hängt wohl auch davon ab ob das Sharing integraler Bestandteil des IPC ist (so wie bei mir) oder ob das Sharing außen herum gemanaged wird und das IPC nur dem Austausch von SHM-IDs + Requests/Responses dient (so wie ihr das wohl machen wollt und FlashBurn wohl auch, falls ich Euch da missverstanden habe so Bitte ich um konkrete Aufklärung). Bei ersterem muss das IPC-System natürlich klar angeben wann und wie der Speicher geshared wird damit sich die Applikation auch darauf verlassen kann und damit wird es dann eben erforderlich dass das IPC hier diese 3 Szenarien explizit anbietet (wenn man nicht mit einem Weg einen anderen Weg emulieren möchte), dafür komme ich mit einem Minimum an Modus-Wechseln und Syscalls aus (für die simple Variante und die blockierende synchrone Variante sind nur 4 Modus-Wechsel und für die nichtblockierende asynchrone Variante sind nur 6 Modus-Wechsel erforderlich, um Missverständnissen vorzubeugen: ein Modus-Wechsel ist jeweils zwischen User-Mode und Kernel-Mode oder anders herum). Vor allem hab ich bei meinen Segmenten das Problem das ich Speicher nicht so ohne weiteres von einem Prozess zum nächsten Migrieren kann (also das er im ersten Prozess komplett verschwindet und im zweiten Prozess dauerhaft enthalten bleibt), von daher ist bei mir das Sharing immer zeitlich limitiert (also an den IPC-Vorgang gebunden), ich denke für die überwiegende Mehrheit der IPC/RPC-Vorgänge ist das völlig in Ordnung. Für Dein Beispiel mit "ich mach dann mal alleine weiter" würde ich das so lösen das der eigentlich IPC-Vorgang so lange dauert bis der Service wirklich fertig ist (irgendwann muss er ja den PopUp-Thread wieder beenden), aber dieser Zwischenbericht über einen zusätzlichen simplen IPC-Call an den eigentlichen Client übermittelt wird, der Client muss dann in seinem Request per async/nonblocking IPC eben ein zusätzliches Message-Target/Port-Nummer/sonstwas mitgeben wo der Service diese Zwischenberichte unkompliziert abliefern kann. Der aus meiner Sicht wesentliche Vorteil meiner Methode ist der das bei mir immer noch der Postbote kommt und ein passend verpacktes Packet abliefert und niemand zur Post hin muss um dort etwas abzuholen und dabei auch noch die Schwierigkeit hat immer eine passende Verpackung dabei zu haben.

Oder umgekehrt könnte es passieren, dass der Speicher eigentlich schon wieder unbenutzt ist, während noch andere Operationen durchgeführt werden müssen, bevor man eine Antwort schicken kann.
Dazu könnte der Service aber auch einen weiteren Workerthread erstellen und den IPC-Vorgang sofort beenden.

Von der formalen Art der Funktion auf ihr Verhalten zu schließen finde ich fragwürdig.
Naja, ich betrachte das lieber anders herum, ich schaue mir die wesentlichen Verhaltensmuster an und biete dafür passende Unterstützung. Solange ich den selteneren Verhaltensmustern (die ich nicht direkt unterstütze) nicht unnötig viele bzw. teure Steine in den Weg lege ist das aus meiner Sicht auch Okay. Bis jetzt hast Du zumindest noch kein Szenario genannt das ich nicht relativ performant und unkompliziert unterstützen kann.

Dann erklär doch mal, wo ich bei meinem Ansatz die ganze Performance verliere. Oder welche der drei Arten ich nicht vernünftig, sondern nur irgendwie abbilden kann.
Also ich denke das einer der wesentlichen Performanceverluste dadurch kommt das Du nicht direkt mit den User-Buffern der Applikation arbeitest sondern für das IPC extra SHM erstellst so das Du oft (wenn auch sicher nicht immer) kopieren musst und dann vermute ich mal das für das SHM oft (wenn auch sicher nicht immer) zusätzliche Syscalls erforderlich sind. Alternativ könntest Du wegen den Syscalls natürlich einen IPC-Mega-Syscall anbieten der alles optional unterstützt (ob das dann wieder dem KISS-Pinzip dient sei jetzt mal egal).

NFS bietet diese Kohärenz nicht unbedingt
Davon bin ich wiederum nicht so wirklich überzeugt, ich denke das hängt davon ab wo man die vereinheitlichende Linie zieht. Wenn man nur das betrachtet was der NFS-Client beim NFS-Server wirklich abliefert bzw. frisch holt dann dürfte das wohl schon Kohärent sein, aber da der NFS-Client intern sicher etwas aufwendigeren Cache betreibt als z.B. die libc dürfte das etwas stärker von den Anwendungen, die auf den verschiedenen Client-Computern laufen, entkoppelt sein. Das setvbuf() aus der libc gibt auch immer nur einen einzelnen Buffer vor der immer nur einen einzelnen zusammenhängenden Abschnitt der Datei puffern kann, das ist also ein Cache mit nur einer einzigen Cache-Line, die kann zwar recht groß sein und muss wohl auch nicht unbedingt eine Cache-Line-Boundary einhalten aber es bleibt eine einzelne Cache-Line die auch immer komplett geleert werden muss bevor neue Daten rein können. Mit einer einzelnen Cache-Line ist die Kohärenz eigentlich immer gewahrt. Innerhalb des VFS, oder auch eines NFC-Clients, kommen wohl für gewöhnlich deutlich komplexere Cache-Mechanismen zum Einsatz.

Zitat
(wenn wir mal von plötzlichem Stromausfall u.ä. absehen)
Und genau das ist falsch!
Natürlich gibt es in der Realität Stromausfall und andere Katastrophen die das Dateisystem auf der HDD bedrohen, ich wollte das auch nur für meine Betrachtungen bezüglich der Kohärenz die das VFS den Anwendungen bietet mal außen vor lassen, das ist eine Sache die der Dateisystem-Treiber über den Block-Device-Treiber direkt mit der HDD regelt (z.B. mit dem von mir angesprochenen SATA-Mechanismus der voneinander anhängigen SATA-Kommandos). Ein System das hierbei nicht zumindest grundlegende Vorkehrungen trifft würde sich Deines beliebten K-Attributes auch mehr als nur würdig erweisen.

Backup eines Snapshots im Image einer laufenden VM. Sonstige Zugriffe des Host auf das Image. Clusterdateisystem auf einem Image, das von zwei VMs geteilt wird.
Das mit den Snap-Shots ist natürlich ein wichtiger Punkt (den muss ich mir gleich mal notieren) aber das ist eigentlich auch eine Aufgabe die das VFS erledigen können sollte, alle Änderungen an einer Datei die vor dem Snap-Shot-Kommando im VFS ankommen sind noch enthalten und alles was danach kommt eben nicht mehr. Hier könnte man für die Anwendungen noch eine höfliche Benachrichtigung generieren damit diese auch alle ihre Änderungen in einem konsistenten Zustand in den Snap-Shot bekommen.

Andere können sicher andere Beispiele liefern. ;)
Das wäre sehr schön, den Snap-Shots sind nicht unbedingt das was ich meinte.

Hm, da wüsste ich sowieso nicht, wo anpacken, das war ja mal wieder ein fließender Übergang... Ich dachte einfach einen neuen Thread erstellen, wo wir weitermachen.
Tja, da müsste mal einer den ersten Schritt tun, aber eigentlich driften wir auch schon die ganze Zeit zwischen zwei Themen hin und her. Ich bin fast der Meinung das wir unser bisheriges Konversationsschema beibehalten können. Wozu überhaupt Threads in diesem Forum? ;)


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 #30 am: 18. October 2011, 14:45 »
Hm, klingt irgendwie interessant, aber ich benutze bereits 'proc' als Abkürzung für Process so das mir da jetzt nichts geeignetes für Procedure einfällt. Ich hatte da noch an simple ('smpl') gedacht.
sync/async/detached?

Zitat
Das hängt wohl auch davon ab ob das Sharing integraler Bestandteil des IPC ist (so wie bei mir) oder ob das Sharing außen herum gemanaged wird und das IPC nur dem Austausch von SHM-IDs + Requests/Responses dient (so wie ihr das wohl machen wollt und FlashBurn wohl auch, falls ich Euch da missverstanden habe so Bitte ich um konkrete Aufklärung).
Umgekehrt wird ein Schuh draus: Wenn das IPC-Design es unmöglich macht, dass ein Prozess Speicher weiterbenutzen kann nachdem er die Antwort geschickt hat, dann muss man sich überlegen, ob Memory Sharing fest daran zu koppeln wirklich eine gute Idee ist.

Der Workaround, den du beschreibst, hinterlässt bei mir den Eindruck, dass das nicht wirklich mit deinem Design zusammenpasst, sondern du dir mit irgendwelchen Zwischennachrichten behilfst, die eigentlich nicht vorgesehen sind und dein einfaches System komplizierter machen (gibt es dann einen zusätzlichen Typ "RPC mit zwei synchronen und einer asynchronen Zwischennachricht und einer asynchronen Endnachricht"?) Dasselbe gilt für den Workerthread, den man irgendwo in der Mitte des Handlers starten soll, wenn man den IPC-Speicher nicht mehr braucht.

Oder mit deiner eigenen Aussage von oben: Man kann das zwar alles irgendwie abbilden, aber ob es dann so toll ist, ist die andere Frage.

Zitat
Vor allem hab ich bei meinen Segmenten das Problem das ich Speicher nicht so ohne weiteres von einem Prozess zum nächsten Migrieren kann (also das er im ersten Prozess komplett verschwindet und im zweiten Prozess dauerhaft enthalten bleibt), von daher ist bei mir das Sharing immer zeitlich limitiert (also an den IPC-Vorgang gebunden), ich denke für die überwiegende Mehrheit der IPC/RPC-Vorgänge ist das völlig in Ordnung.
Schön ist es nicht und kann manche Fälle auf Senderseite etwas umständlicher machen, weil er seine Speicherfreigabe verzögern muss, bis der RPC fertig ist. Interessant könnte der Fall werden, wenn der Empfänger das alleinige Schreibrecht auf den erhaltenen Puffer haben will (und das will er in aller Regel nachdem er den Puffer geprüft hat, sonst kriegst du oft genug ein sicherheitskritisches Race). Dazu müsste er in deinem Konzept wahrscheinlich kopieren.

Zitat
Der aus meiner Sicht wesentliche Vorteil meiner Methode ist der das bei mir immer noch der Postbote kommt und ein passend verpacktes Packet abliefert und niemand zur Post hin muss um dort etwas abzuholen und dabei auch noch die Schwierigkeit hat immer eine passende Verpackung dabei zu haben.
Naja, damit kommen wir direkt wieder zur Diskussion von Popup-Threads. Ich halte es nach wie vor übertrieben, von allen Programmen zu verlangen, dass sie threadsafe sind.

Zitat
Also ich denke das einer der wesentlichen Performanceverluste dadurch kommt das Du nicht direkt mit den User-Buffern der Applikation arbeitest sondern für das IPC extra SHM erstellst so das Du oft (wenn auch sicher nicht immer) kopieren musst und dann vermute ich mal das für das SHM oft (wenn auch sicher nicht immer) zusätzliche Syscalls erforderlich sind. Alternativ könntest Du wegen den Syscalls natürlich einen IPC-Mega-Syscall anbieten der alles optional unterstützt (ob das dann wieder dem KISS-Pinzip dient sei jetzt mal egal).
Naja, ein IPC-Mega-Syscall ist ja genau das was du machst. Der ist wohl unabhängig davon, ob am Ende Pipes oder Popup-Threads stehen.

Was die Puffer angeht, führt bei einem x86-OS mit flachem Speichermodell nichts dran vorbei, das ist keine IPC-Designentscheidung.

Zitat
NFS bietet diese Kohärenz nicht unbedingt
Davon bin ich wiederum nicht so wirklich überzeugt, ich denke das hängt davon ab wo man die vereinheitlichende Linie zieht. Wenn man nur das betrachtet was der NFS-Client beim NFS-Server wirklich abliefert bzw. frisch holt dann dürfte das wohl schon Kohärent sein, aber da der NFS-Client intern sicher etwas aufwendigeren Cache betreibt als z.B. die libc dürfte das etwas stärker von den Anwendungen, die auf den verschiedenen Client-Computern laufen, entkoppelt sein.
Änderungen an einer Datei auf NFS müssen erst dann auf dem Server sichtbar sein, wenn die Datei geschlossen wird.

Zitat
Natürlich gibt es in der Realität Stromausfall und andere Katastrophen die das Dateisystem auf der HDD bedrohen, ich wollte das auch nur für meine Betrachtungen bezüglich der Kohärenz die das VFS den Anwendungen bietet mal außen vor lassen, das ist eine Sache die der Dateisystem-Treiber über den Block-Device-Treiber direkt mit der HDD regelt (z.B. mit dem von mir angesprochenen SATA-Mechanismus der voneinander anhängigen SATA-Kommandos). Ein System das hierbei nicht zumindest grundlegende Vorkehrungen trifft würde sich Deines beliebten K-Attributes auch mehr als nur würdig erweisen.
Es ist aber eben gerade keine Sache, die das Dateisystem mit dem Blocktreiber allein ausmachen kann. Wenn du nur Dateisystem-Metadaten konsistent halten willst, dann reicht das. Aber auch Anwendungen haben für ihre Dateien Anforderungen in Sachen Konsistenz, und das heißt, dass die Anwendungen dem VFS sagen müssen können, was Sache ist. Nur dann kann das VFS dem Dateisystem erklären, wo die Abhängigkeiten sind, so dass das dem Blocktreiber wiederum erklären kann, wo es besser mal ein FLUSH runterschickt. Wenn irgendwo zwischen Anwendungscode und Platte eine Schicht nicht ordentlich mitspielt, wird es schwierig.

Es ist also schon die Frage, welches Interface das VFS dafür zur Verfügung stellen sollte. Nur einen Holzhammer fsync(fd); oder irgendwelche feineren Operationen?

Zitat
Das mit den Snap-Shots ist natürlich ein wichtiger Punkt (den muss ich mir gleich mal notieren) aber das ist eigentlich auch eine Aufgabe die das VFS erledigen können sollte, alle Änderungen an einer Datei die vor dem Snap-Shot-Kommando im VFS ankommen sind noch enthalten und alles was danach kommt eben nicht mehr. Hier könnte man für die Anwendungen noch eine höfliche Benachrichtigung generieren damit diese auch alle ihre Änderungen in einem konsistenten Zustand in den Snap-Shot bekommen.
Hm, ich würde sagen, dass dazu das Dateisystem mitspielen sollte. Ansonsten musst du ja alles im Speicher machen.

Der Fall mit zwei VMs und einem Clusterdateisystem gefällt dir nicht, wenn du noch was anderes als Snapshots willst?
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #31 am: 18. October 2011, 18:03 »
Zitat von: erik
und FlashBurn wohl auch, falls ich Euch da missverstanden habe so Bitte ich um konkrete Aufklärung
Bei mir gibt es SHM nur in Verbindung mit IPC. Ich mache es daher mehr oder weniger so wie du. Du kannst beim Senden einer Nachricht angeben, dass du nen gewussen Speicherbereich mitsenden willst (was dann halt SHM wird). Ich möchte den Syscall sogar so allgemein machen, dass du nur durch ein Flag auch den Speicher versenden (Speicher wird auf Senderseite ungemappt) kannst.

Einen Nachteil den ich darin sehe ist, dass man für SHM also immer nen Port braucht (der die Nachricht/den SHM bekommt), aber das ist nicht wirklich schlimm. Denn wie bekommt man die SHM-ID in einen anderen Prozess, wenn nicht über IPC, sprich der Port muss eh vorhanden sein.

Zitat von: taljeth
Naja, damit kommen wir direkt wieder zur Diskussion von Popup-Threads. Ich halte es nach wie vor übertrieben, von allen Programmen zu verlangen, dass sie threadsafe sind.
Das trifft nur Programme die Nachrichten auch empfangen können (und eine Antwort ist bei mir nicht eine Nachricht zu empfangen) und bei denen sehe ich nicht so das Problem.

Was für Programme und Probleme schweben dir denn dort genau vor?

Zitat von: taljeth
Was die Puffer angeht, führt bei einem x86-OS mit flachem Speichermodell nichts dran vorbei, das ist keine IPC-Designentscheidung.
Das stimmt so nicht. Es wäre möglich, indem einfach alle Puffer an 4kb ausgerichtet wären oder man es mit der Sicherheit nicht so genau nimmt, nicht schön, aber möglich.

Vorallem sollte man auch immer das Verhältnis betrachten, von was für Puffern sprechen wir hier? Alles was größer als 4kb ist, kann man doch auch ruhig an 4kb ausrichten und alles andere müsste halt kopiert werden, aber da ja niemand hier mit solch "alter" Hardware wie ich plant, sollte das auch kein Problem darstellen.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #32 am: 18. October 2011, 19:27 »
Hallo,


sync/async/detached?
Okay, gekauft, Zahlung erfolgt später. ;)
Ich nutze diese Terminologie dann gleich mal.

Umgekehrt wird ein Schuh draus: Wenn das IPC-Design es unmöglich macht, dass ein Prozess Speicher weiterbenutzen kann nachdem er die Antwort geschickt hat, dann muss man sich überlegen, ob Memory Sharing fest daran zu koppeln wirklich eine gute Idee ist.
Also da alle wichtigen Dinge (also read()/write(), bis einschließlich dem asynchronen File-I/O) den User-Buffer nur so lange ausleihen bis die Aktion abgeschlossen ist sehe ich darin erst einmal keine Beschränkung, solltest Du oder jemand anders ein praxisrelevantes Szenario kennen in dem das anders ist dann würde ich das sehr gerne lesen. Ansonsten steht auf meinem System ja nichts im Wege auch dediziertes Memory-Sharing mit extra erstellten Segmenten zu machen so das dann per IPC nur noch die Kommandos ausgetauscht werden müssen, es ist bei meinem IPC-Konzept nicht Pflicht das Memory-Sharing immer in vollem Umfang zu nutzen. Da mein IPC-Konzept aber eben komplett durchgängiges Zero-Copy (und zwar wirklich vom Hardware-Gerät bis zur eigentlichen Applikation) ermöglicht, und Du hast ja selber geschrieben dass das wichtig ist, kann ich durchaus damit leben das mein IPC-Konzept nur die normalen Dinge ohne Verrenkungen ermöglich und für die exotischen Dinge eben das Shared-Memory extra gemanaged werden muss (so wie bei Euch immer). Das heißt für mich das ich in über 99% aller Fälle mit maximaler Performance arbeiten kann und beim Rest eben auf das (niedrige) Niveau der Konkurrenz zurückfalle. Das gezielte abkoppeln des geteilten Speichers durch den Callee habe ich nur für detached IPC vorgesehen, wobei auch gerade da es sich lohnt wenn der Caller angibt das der Kernel in jedem Fall kopieren soll (egal wie viele Daten es sind) damit der Caller eben wirklich unabhängig bleibt.

Der Workaround, den du beschreibst, hinterlässt bei mir den Eindruck, dass das nicht wirklich mit deinem Design zusammenpasst, ...
Ja, da hast Du recht. Deine beiden Szenarien, mit dem allein Weiterarbeiten und dem Nacharbeiten, sind so ohne weiteres in meinem IPC-Konzept nicht vorgesehen, da beides nicht der Regelfall ist (oder siehst Du das anders?) habe ich damit auch absolut kein Problem. Wenn man das normale IPC trotzdem dafür nutzen möchte muss man eben einen Workaround nutzen oder man entscheidet sich besser dafür das Memory-Sharing und das Übermitteln der Kommandos/Antworten zu trennen (ersteres über dedizierte Memory-Sharing-Funktionen des OS-Kernels und zweites z.B. per detached IPC).

Interessant könnte der Fall werden, wenn der Empfänger das alleinige Schreibrecht auf den erhaltenen Puffer haben will (und das will er in aller Regel nachdem er den Puffer geprüft hat, sonst kriegst du oft genug ein sicherheitskritisches Race). Dazu müsste er in deinem Konzept wahrscheinlich kopieren.
Ich verstehe nicht ganz was Du meinst. In meinem Konzept bleibt der geteilte Speicher beim IPC-Caller immer so erhalten wie er ist und der IPC-Callee bekommt entweder Leserechte oder Schreibrechte (aber nie beides gleichzeitig) auf die bis zu 4 übergebenen Speicherbereiche (davon maximal 2 Lesen für Kommando und write-Nutzdaten und maximal 2 Schreiben für Antwort und read-Nutzdaten). Klar kann es damit passieren das der Caller z.B. im Kommando rumfuscht obwohl der IPC-Call noch nicht beendet ist (bei Multithreading im Caller ja durchaus möglich) aber wenn der Callee dann das Kommando nicht korrekt verarbeitet und Blödsinn zurück schickt ist das ein Problem des Callers. Das einzigste was natürlich nicht passieren darf ist das damit z.B. ein Service gestört wird oder gar abstürzt, da die kleinen Kommandos eh meistens vom Kernel kopiert werden (bei Speicherbereichen unterhalb einer gewissen Größe soll der Kernel grundsätzlich kopieren und nicht Sharen, das wird für jeden der bis zu 4 Speicherbereiche individuell entschieden) und der Callee auch prüfen kann ob wirklich kopiert wurde und falls nicht das notfalls selber nachholen kann sehe ich da keine große Gefahr. Auch wenn das bei Eurem Shared-Memory wohl eher nur die Nutzdaten betrifft so ist diese Gefahr ja grundsätzlich ebenfalls vorhanden.

Naja, damit kommen wir direkt wieder zur Diskussion von Popup-Threads. Ich halte es nach wie vor übertrieben, von allen Programmen zu verlangen, dass sie threadsafe sind.
Da hast Du grundsätzlich recht, deswegen kommt bei mir ein reiner IPC-Caller der nur synchrounus und detached IPC nutzt auch mit einem einzelnen Thread aus, das dürfte die meisten einfachen Programme (die kein asynchrones File-I/O u.ä. nutzen) betreffen, ich würde sogar soweit gehen zu behaupten das die meisten Programme die auf tyndur laufen auch auf meinem System als simple singlethreaded Applikationen laufen müssten. Erst komplexere Programme, die z.B. für asynchrones File-I/O eben auch asynchrones IPC nutzen, oder richtige Services müssen bei mir zwangsläufig multithreadingtauglich sein. Meine libc möchte ich grundsätzlich nur multithreadingtauglich anbieten.

Naja, ein IPC-Mega-Syscall ist ja genau das was du machst. Der ist wohl unabhängig davon, ob am Ende Pipes oder Popup-Threads stehen.
Hä, nein, einen Mega-IPC-Sycall hab ich nicht, ich habe in meinem aktuellen Konzept 19 Syscalls für IPC vorgesehen. Es gibt auch für sync/async/detached jeweils einen eigenen Call-Syscall. Natürlich werden die intern für viele Dinge die selben Unterfunktionen nutzen (z.B. für das Vererben/Kopieren des Speichers oder für das Erstellen des PopUp-Threads), weswegen ich auch nicht davon ausgehe das diese doch recht große Anzahl an Syscalls nur fürs IPC meinen Kernel unangemessen komplex werden lässt, aber nach außen sind das unabhängige Syscalls.

Was die Puffer angeht, führt bei einem x86-OS mit flachem Speichermodell nichts dran vorbei, das ist keine IPC-Designentscheidung.
Ist mir bekannt.

.... Wenn irgendwo zwischen Anwendungscode und Platte eine Schicht nicht ordentlich mitspielt, wird es schwierig.
Natürlich, mit all dem hast Du uneingeschränkt recht. Es ging mir doch erst mal nur um die Kohärenz die die Anwendungen auf jeden Fall sehen müssen, das es da noch mehr gibt ist mir auch klar.

Es ist also schon die Frage, welches Interface das VFS dafür zur Verfügung stellen sollte. Nur einen Holzhammer fsync(fd); oder irgendwelche feineren Operationen?
Wenn ich Dich vorhin richtig verstanden hab dann bietet doch eine konforme POSIX-Implementierung schon so einige Mitelchen an, ich denke das ist zumindest eine gute Orientierung was sinnvoll ist und was auch von existierenden Programmen so alles benutzt wird.

Hm, ich würde sagen, dass dazu das Dateisystem mitspielen sollte. Ansonsten musst du ja alles im Speicher machen.
Ja, ein berechtigter Einwand. Ich denke mal das btrfs und ZFS sowas von Hause aus bieten, NTFS wimre auch (zumindest sein kurzer Zeit) und ob da auch die ext?-Familie was zu bieten hat weiß ich gar nicht.

Der Fall mit zwei VMs und einem Clusterdateisystem gefällt dir nicht, wenn du noch was anderes als Snapshots willst?
Ah zwei VMs (das hatte ich heute Mittag offensichtlich überlesen), das macht die Sache schon wesentlich interessanter. Ja da müsste man wirklich überlegen welche Konsistenzen das VFS zusichern muss und welche Mechanismen die VMs nutzen wollen um das zu gewährleisten. Ich schätze mal da hat POSIX auch was hübsches anzubieten.
Trotzdem muss ich ganz ehrlich sagen das ich noch gerne ein etwas alltäglicheres Beispiel lesen würde. Clusterdateisysteme und mehrere VMs sind IMHO nicht so das typische Anwendungsszenario für ein Hobby-OS.


Ich möchte den Syscall sogar so allgemein machen, dass du nur durch ein Flag auch den Speicher versenden (Speicher wird auf Senderseite ungemappt) kannst.
Da ich eher versuchen möchte möglichst immer direkt den eh vorhandenen User-Buffer für das IPC zu nutzen (wegen dem lieben Zero-Copy) ist das für mich sowieso kein interessantes Ziel, nebst dessen das ich aus meinen Segmenten nicht einfach Speicher mitten raus klauen kann.

Denn wie bekommt man die SHM-ID in einen anderen Prozess, wenn nicht über IPC, sprich der Port muss eh vorhanden sein.
Also da kann ich mir so einiges vorstellen: z.B. ein Programm könnte den SHM erstellen und dann weitere Kind-Prozesse starten und diesen die nötigen Zugriffsinformationen gleich als Command-Line-Parameter o.ä. mitgeben.

aber da ja niemand hier mit solch "alter" Hardware wie ich plant, sollte das auch kein Problem darstellen.
Der Nutzwert von Zero-Copy ist IMHO unabhängig vom Alter des Systems, Kopieren kostet immer unnütz CPU-Zeit.


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 #33 am: 18. October 2011, 19:53 »
Zitat von: erik
Also da kann ich mir so einiges vorstellen: z.B. ein Programm könnte den SHM erstellen und dann weitere Kind-Prozesse starten und diesen die nötigen Zugriffsinformationen gleich als Command-Line-Parameter o.ä. mitgeben.
Das stimmt wohl, irgendwie vergesse ich immer die Command-Line-Parameter (auch bei meinen Überlegungen zwecks Prozesserstellung und sowas). Hast du dafür mal nen richtiges (in Form einer Anwendung, real existent) Bsp?
Michwürde halt interessieren, ob das eher selten ist oder ob sogar ein Service mit reinspielt und ob es sich lohnt das ganze noch anders umzusetzen.

Zitat von: erik
Der Nutzwert von Zero-Copy ist IMHO unabhängig vom Alter des Systems, Kopieren kostet immer unnütz CPU-Zeit.
Ist erstmal richtig, aber wenn man sich dann die Zeit anguckt, die gespart werden kann, kann man den ganzen Spaß auch vernachläßigen (vorallem wenn man daran denkt, das die meisten Rechner die meiste Zeit eh nix tun).

Zumal man auch den Overhead durchs Mappen nicht vergessen darf, ich denke mal bis zu einer gewissen Menge an Speicher ist kopieren sogar schneller als Mappen. Allerding bleibt es auch so, das die Zeit die absolut dafür gebraucht wird, bei vielen Sachen inzwischen zu gering ist als das sie auffallen würde.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #34 am: 18. October 2011, 20:52 »
Ein Beispiel wäre übrigens eine Datenbank... vierzig lesende Threads und vier schreibender Threads, wobei die interne Datenbank-Kohärenz in jedem Fall gewahrt bleiben muss. Schon muss das VFS fein gestückeltes Locking und eine garantierte Abarbeitungsreihenfolge bieten, die vollständig von der Anwendung gesteuert werden kann.

An IPC halte ich synchron (Sender wartet auf Antwort) und asynchron (Sender wartet nicht auf Antwort) für ausreichend. Shared Memory ist erst ab einer gewissen Nutzdatenmenge sinnvoll und gilt bei synchronem IPC bis zum Erhalt der Antwort, bei asynchronem IPC bis unendlich (wenn der vorletzte Prozess den Speicherbereich freigibt, ist es kein SHM mehr).

Am Ende spielt die interne IPC-Implementation - abgesehen von der Performance - eh keine Rolle, weil die Anwendungen davon nichts mitkriegen. Die libc muss das IPC nur möglichst effizient nutzen können.

Die Command Line als Träger wichtiger Informationen zu missbrauchen halte ich für unelegant.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #35 am: 18. October 2011, 21:50 »
Das trifft nur Programme die Nachrichten auch empfangen können (und eine Antwort ist bei mir nicht eine Nachricht zu empfangen) und bei denen sehe ich nicht so das Problem.
Wenn eine Antwort was anderes ist, wie funktionieren denn bei dir asynchrone Antworten?

Zitat
Das stimmt so nicht. Es wäre möglich, indem einfach alle Puffer an 4kb ausgerichtet wären oder man es mit der Sicherheit nicht so genau nimmt, nicht schön, aber möglich.
Also mit der Sicherheit nicht genau nehmen ist keine Option, Korrektheit steht immer noch an erster Stelle. Ich sehe kein größeres Problem darin, Puffer einfach richtig auszurichten, aber Erik scheint das nicht zu gefallen.

sync/async/detached?
Okay, gekauft, Zahlung erfolgt später. ;)
Ich nutze diese Terminologie dann gleich mal.
Ansonsten hätte ich auch noch uni-/bidirektional im Angebot. Ist vielleicht einfacher verständlich, wenn man den Begriff in diesem Zusammenhang nicht kennt.

Zitat
Also da alle wichtigen Dinge (also read()/write(), bis einschließlich dem asynchronen File-I/O) den User-Buffer nur so lange ausleihen bis die Aktion abgeschlossen ist sehe ich darin erst einmal keine Beschränkung, solltest Du oder jemand anders ein praxisrelevantes Szenario kennen in dem das anders ist dann würde ich das sehr gerne lesen.
Wie funktioniert das eigentlich bei detached/unidirektionalen IPCs? Gibt es da doch eine Antwort, nur dass sie eben keine zusätzliche Information außer "bin fertig, kannst den Speicher freigeben" enthält?

Interessant könnte der Fall werden, wenn der Empfänger das alleinige Schreibrecht auf den erhaltenen Puffer haben will (und das will er in aller Regel nachdem er den Puffer geprüft hat, sonst kriegst du oft genug ein sicherheitskritisches Race). Dazu müsste er in deinem Konzept wahrscheinlich kopieren.
Ich verstehe nicht ganz was Du meinst. In meinem Konzept bleibt der geteilte Speicher beim IPC-Caller immer so erhalten wie er ist und der IPC-Callee bekommt entweder Leserechte oder Schreibrechte (aber nie beides gleichzeitig) auf die bis zu 4 übergebenen Speicherbereiche (davon maximal 2 Lesen für Kommando und write-Nutzdaten und maximal 2 Schreiben für Antwort und read-Nutzdaten). Klar kann es damit passieren das der Caller z.B. im Kommando rumfuscht obwohl der IPC-Call noch nicht beendet ist (bei Multithreading im Caller ja durchaus möglich) aber wenn der Callee dann das Kommando nicht korrekt verarbeitet und Blödsinn zurück schickt ist das ein Problem des Callers.[/quote]
Und wenn der Callee dann abstürzt, weil er erst Datensatz A validiert hat und anschließend auf Datensatz B arbeitet, dann ist das ein Bug, der durch das IPC-Design begünstigt worden ist. Ich glaube, es wäre ein nützliches Feature, wenn beim Übergeben des Speichers der Caller (ggf. vorübergehend) die Schreibrechte auf den Speicherbereich verlieren würde.

Zitat
Hä, nein, einen Mega-IPC-Sycall hab ich nicht, ich habe in meinem aktuellen Konzept 19 Syscalls für IPC vorgesehen. Es gibt auch für sync/async/detached jeweils einen eigenen Call-Syscall. Natürlich werden die intern für viele Dinge die selben Unterfunktionen nutzen (z.B. für das Vererben/Kopieren des Speichers oder für das Erstellen des PopUp-Threads), weswegen ich auch nicht davon ausgehe das diese doch recht große Anzahl an Syscalls nur fürs IPC meinen Kernel unangemessen komplex werden lässt, aber nach außen sind das unabhängige Syscalls.
Was ich damit meinte, ist, dass du einen Syscall hast, der gleichzeitig einen Haufen Speicherverwaltung macht, wenn ich das richtig verstanden habe. Also nicht erst den Speicher zuweisen und dann gesondert die RPC-Anfrage absetzen und dann gesondern die Antwort holen, sondern alles in einem.

Zitat
Es ist also schon die Frage, welches Interface das VFS dafür zur Verfügung stellen sollte. Nur einen Holzhammer fsync(fd); oder irgendwelche feineren Operationen?
Wenn ich Dich vorhin richtig verstanden hab dann bietet doch eine konforme POSIX-Implementierung schon so einige Mitelchen an, ich denke das ist zumindest eine gute Orientierung was sinnvoll ist und was auch von existierenden Programmen so alles benutzt wird.
Ich habe schon die Erfahrung gemacht, dass das, was vorhanden ist, zwar genug unterschiedliche Varianten hat um zu verwirren, aber dass es schon noch einige Wünsche offen lässt. Zum Beispiel ein fsync nicht für die ganze Datei, sondern nur für einen Bereich ist nicht vorgesehen. Abhängigkeiten zwischen Requests definieren wollen vermutlich die wenigsten Anwendungen, aber auch das wäre denkbar und könnte von Anwendungen, bei denen es sich lohnt, auch sinnvoll eingesetzt werden.

Zitat
Ja, ein berechtigter Einwand. Ich denke mal das btrfs und ZFS sowas von Hause aus bieten, NTFS wimre auch (zumindest sein kurzer Zeit) und ob da auch die ext?-Familie was zu bieten hat weiß ich gar nicht.
Nein, ext4 kann kein COW.

Ein Beispiel wäre übrigens eine Datenbank... vierzig lesende Threads und vier schreibender Threads, wobei die interne Datenbank-Kohärenz in jedem Fall gewahrt bleiben muss. Schon muss das VFS fein gestückeltes Locking und eine garantierte Abarbeitungsreihenfolge bieten, die vollständig von der Anwendung gesteuert werden kann.
Stimmt, Multithreading ist ein guter Einwand. Damit kriegt man solche Szenarien fast schon automatisch.
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 #36 am: 18. October 2011, 22:50 »
Hallo,


ein richtig gutes Beispiel für allgemeinen Shared-Memory, inklusive der Verteilung der Zugangsdaten (was z.B. auch über stdin beim Kindprozess passieren kann falls die Commad-Line-Parameter nicht sicher genug sind), kenne ich leider auch nicht, von den akademischen Beispielen zum Grundverständnis mal abgesehen.

Ist erstmal richtig, aber wenn man sich dann die Zeit anguckt, die gespart werden kann, kann man den ganzen Spaß auch vernachläßigen
Klar kann eine moderne CPU 1000 mal schneller kopieren als eine von vor 20 Jahren aber sie kann eben auch 1000 mal mehr sinnvolle Arbeit in der selben Zeit verrichten! Das Arbeitsäquivalent für die Zeit in der eine CPU X Bytes kopiert dürfte sich in den letzten 20 Jahren nur wenig verändert haben, genau weiß ich das aber nicht, so das man davon ausgehen kann das Kopieren in Software immer etwa gleich teuer ist (relativ unabhängig von der absoluten Performance der CPU). Meiner persönlichen Meinung nach ist Kopieren einer der wesentlichen CPU-Verschwender in modernen Software-Architekturen und genau deswegen versuche ich so energisch dies zu vermeiden.

Natürlich ist auch das Erstellen eines Mappings nicht kostenlos, bei meinen Segmenten könnte eventuell das Erstellen eines neuen Segments sogar ein bisschen mehr kosten als zwei oder drei Pages bei Flat-Memory, so das man auf jeden Fall mal ausmessen sollte wie viele Bytes man in der selben Zeit kopieren könnte um zu wissen aber welcher Menge an Nutzdaten sich das Mapping überhaupt lohnt.


Ein Beispiel wäre übrigens eine Datenbank... vierzig lesende Threads und vier schreibender Threads, wobei die interne Datenbank-Kohärenz in jedem Fall gewahrt bleiben muss. Schon muss das VFS fein gestückeltes Locking und eine garantierte Abarbeitungsreihenfolge bieten, die vollständig von der Anwendung gesteuert werden kann.
Okay, das ist auch ein gutes Beispiel (wenn auch sicher ebenfalls nicht das typische Anwendungsszenario für ein Hobby-OS). Das VFS muss also auf jeden Fall anständige Kohärenz und auch ein zugehöriges API bieten.

An IPC halte ich synchron (Sender wartet auf Antwort) und asynchron (Sender wartet nicht auf Antwort) für ausreichend. Shared Memory ist erst ab einer gewissen Nutzdatenmenge sinnvoll und gilt bei synchronem IPC bis zum Erhalt der Antwort, bei asynchronem IPC bis unendlich (wenn der vorletzte Prozess den Speicherbereich freigibt, ist es kein SHM mehr).
Das finde ich reicht nicht aus, bei Deinem asynchronem IPC wird ja effektiv der Speicher dem Caller weggenommen: er weiß einfach nicht ab wann er diesen Speicher wieder benutzen kann also bleibt ihm nichts anderes übrig als ihn frei zu geben. Das mag für kleine Messages/Signale und ähnlichen Kleinkram (wo dann wohl eh meistens nur so wenige Bytes im Spiel sind das sowieso gleich kopiert werden kann) reichen aber für z.B. asynchrones File-I/O, wo der User-Buffer nach dem Transfer weiter benutzt werden soll/muss ist das nicht ausreichend.

Am Ende spielt die interne IPC-Implementation - abgesehen von der Performance - eh keine Rolle, weil die Anwendungen davon nichts mitkriegen.
Einverstanden.

Die libc muss das IPC nur möglichst effizient nutzen können.
Und genau hier sind wir beim entscheidenden Knackpunkt. Als die libc entworfen wurde waren monolithische OS-Kernel das Mass der Dinge und bei einem Monolithen kann man einfach einen Pointer auf den User-Buffer mitgeben und fertig (der Kernel kommt an den User-Buffer ja problemlos ran). Von daher bin ich der Meinung wenn man effizientes IPC für existierende Programme haben will dann darf man genau dieses Paradigma nicht aus den Augen verlieren.


aber Erik scheint das nicht zu gefallen.
Ganz recht, das gefällt mir absolut nicht. Ich möchte nicht das mir die libc oder irgendeine andere Library vorschreibt was ich für Speicher benutzen soll, ich möchte auch einfach mal ein paar Bytes in einen Puffer holen können der als lokale Variable auf dem Stack liegt und ich möchte auch nicht dem malloc sagen müssen das ich nur ausgerichteten Speicher haben will (vor allem dann nicht wenn mein Speicherbedarf selber auch kein exaktes Vielfache einer Pagegröße ist). In meiner Welt gibt es nur eine Art von Speicher und der geht für alles, fertig. Ich will auch bei DMA nicht zwischen "hohem" und "normalen" Speicher unterscheiden so das bei mir ein HW-Gerät immer den gesamten physischen Adressraum unterstützen muss wenn es DMA nutzen will, und das soll es tun weil ich ja Zero-Copy will.

Ansonsten hätte ich auch noch uni-/bidirektional im Angebot. Ist vielleicht einfacher verständlich, wenn man den Begriff in diesem Zusammenhang nicht kennt.
Hm, ne das gefällt mir nicht so sehr wie Dein letzter Vorschlag.

Wie funktioniert das eigentlich bei detached/unidirektionalen IPCs? Gibt es da doch eine Antwort, nur dass sie eben keine zusätzliche Information außer "bin fertig, kannst den Speicher freigeben" enthält?
Nein, bei dem detached IPC gibt es keine Antwort, auch der Rückgabewert des Call-Syscalls kann nicht durch den Callee beeinflusst werden (der sagt nur ob das IPC grundsätzlich geklappt hat oder nicht). Alles was ich vorgesehen hab ist ein Parameter für diesen Call-Syscall das sagt ob der Caller solange blockiert werden möchte bis der Callee den Speicher freigegeben hat (bei recht großen Datenmengen wo das Kopieren unangenehm wäre könnte das eventuell nützlich sein). Der Callee kann den Speicher entweder manuell freigeben oder der Speicher wird automatisch am Ende vom PopUp-Thread freigegeben. Da ich aber davon ausgehe das detached IPC üblicherweise nur mit kleinen Datenmengen benutzt wird (wo der Kernel eh kopiert) bzw. der Caller zur Sicherheit das Kopieren immer explizit anfordert dürfte das wohl keine echte Relevanz haben.

Ich glaube, es wäre ein nützliches Feature, wenn beim Übergeben des Speichers der Caller (ggf. vorübergehend) die Schreibrechte auf den Speicherbereich verlieren würde.
Da gebe ich Dir zwar uneingeschränkt recht aber das kann ich mit Segmenten nicht bieten, eben weil ich ja möchte das ich jeden beliebigen Speicher für IPC nutzen kann. Ich denke das man im Service mit etwas Umsicht die Gefahr für ernste Fehler und Abstürze auch erheblich reduzieren kann, nebst dessen das ja gerade die Kommandos wohl eh meistens kopiert werden (wegen geringer Größe) und wenn nicht der Service das auch selber erledigen kann. Habt Ihr den vor in tyndur entsprechende Mechanismen vor zusehen?

Was ich damit meinte, ist, dass du einen Syscall hast, der gleichzeitig einen Haufen Speicherverwaltung macht, wenn ich das richtig verstanden habe.
Ja. Der Call-Syscall erstellt Alias-Segmente (eine Funktionalität die ich eventuell gar nicht einzeln anbiete, außer für Debugger fällt mir auch keine sinnvolle Anwendung dafür ein) oder kopiert, erstellt einen neuen Thread im Ziel-Prozess und blockiert dann noch eventuell den Caller-Thread oder hängt ihn an die runnable-Liste des Schedulers an (damit erst mal der neue PopUp-Thread an die CPU kann). Zusammen mit dem eigentlichen IPC-Kram und allen Sicherheitsprüfungen ist das schon so einiges, aber nichts davon ist wirklich teuer so das ich davon ausgehe trotzdem eine hohe Performance erreichen zu können. Der Ende-Syscall der vom PopUp-Thread aufgerufen wird löscht die Alias-Segmente und gibt die Antwort entweder dem blockierten Caller oder einer asynchronen Callback-Funktion (was auch immer das ist auf jeden Fall als nächstes auf der aktuellen CPU dran, hier wird also der Scheduler gar nicht benötigt).

Das Problem mit der Kohärenz im VFS eines Hobby-OS ist sicher auch das man erst mal überlegen muss was man von diesem ganzen Zeugs wirklich alles echt implementieren muss damit zumindest die gewünschten Anwendungen sauber laufen. Ich schätze das wird man selbst mit intensiven Code-Studium nicht immer vollständig heraus bekommen weil sich manche Dinge wohl erst indirekt ergeben.

Multithreading ist ein guter Einwand. Damit kriegt man solche Szenarien fast schon automatisch.
Wobei dann wieder die Frage aufkommt ob die alle über das selbe Datei-Handle gehen oder die Datei mehrfach geöffnet wird. Wird letzteres in der libc eventuell abgefangen? Falls nein, was passiert wenn man das doch tut? (aber den Datei-Pointer getrennt lässt)


Grüße
Erik
« Letzte Änderung: 18. October 2011, 22:53 von erik.vikinger »
Reality is that which, when you stop believing in it, doesn't go away.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #37 am: 19. October 2011, 09:14 »
Zitat von: taljeth
Wenn eine Antwort was anderes ist, wie funktionieren denn bei dir asynchrone Antworten?
Man muss bei mir zw. RPC (wozu auf der Sender-Seite kein Port notwendig ist) und asynchronem IPC unterscheiden (hier ist auf der Sender und Empfänger-Seite ein Port notwendig).

Aber ihr habt mir mit dem Callback schon neue Ideen geliefert. Ich überlege, ob man darüber nicht Signale (in Form eines Signal-Servers) anbieten könnte. Allerdings hieße das, das man dann immer "Nachrichten" an diesem Callback bekommen könnte, sprich da wäre kein Anfrage-Antwort-Prinzip.

Könnte denn mal jemand ein Bsp. aus der Praxis bringen, wo asynchrones IPC genutzt wird, weil mir fällt so nur asynch-I/O ein, aber da wartet man doch auch nicht auf eine Antwort (das habe ich sowieso nicht ganz verstanden wie das genau funktioniert)?

Zitat von: erik
Das Arbeitsäquivalent für die Zeit in der eine CPU X Bytes kopiert dürfte sich in den letzten 20 Jahren nur wenig verändert haben, genau weiß ich das aber nicht, so das man davon ausgehen kann das Kopieren in Software immer etwa gleich teuer ist (relativ unabhängig von der absoluten Performance der CPU).
Das ist halt Situationsabhängig. Der Flaschenhals bleibt im Endeffekt der Speicherbus. Desto schneller die CPUs werden, desto mehr Takte müssen sie auch warten bis der Speicher dann "verfügbar" ist. Das kann negativ für das Kopieren sein, aber auch fürs Mappen.

Zitat von: erik
Natürlich ist auch das Erstellen eines Mappings nicht kostenlos, bei meinen Segmenten könnte eventuell das Erstellen eines neuen Segments sogar ein bisschen mehr kosten als zwei oder drei Pages bei Flat-Memory, so das man auf jeden Fall mal ausmessen sollte wie viele Bytes man in der selben Zeit kopieren könnte um zu wissen aber welcher Menge an Nutzdaten sich das Mapping überhaupt lohnt.
Man könnte davon ausgehen, das die Daten schon im Cache sind und von daher, sollte das Kopieren von 2 oder 3 Pages wirklich kein Problem darstellen (vorallem nicht bei den heutigen Caches).
Zumal ich nicht von mehreren MBs rede, sondern von wenigen KBs, das sollte sich wirklich vernachlässigen lassen.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #38 am: 19. October 2011, 09:53 »
Könnte denn mal jemand ein Bsp. aus der Praxis bringen, wo asynchrones IPC genutzt wird, weil mir fällt so nur asynch-I/O ein, aber da wartet man doch auch nicht auf eine Antwort (das habe ich sowieso nicht ganz verstanden wie das genau funktioniert)?
Naja, überall da, wo man noch was anderes zu tun hat und das Ergebnis nicht zwingend sofort braucht, um was sinnvolles zu tun. Und natürlich, wo einem der Durchsatz wichtig genug ist, dass man nicht einfach aus Bequemlichkeit wartet. Asynchron programmieren ist immer schwieriger als synchron.

AIO für Dateien ist wahrscheinlich einer der wichtigstens Anwendungsfälle, wobei das sicher auch damit zusammenhängt, wie stark man "alles ist eine Datei" macht. Und natürlich will man bei AIO eine Antwort haben, die einem wenigstens sagt, ob der Request erfolgreich war oder was für ein Fehler aufgetreten ist.
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 #39 am: 19. October 2011, 13:25 »
Hallo,


asynchronem IPC unterscheiden (hier ist auf der Sender und Empfänger-Seite ein Port notwendig).
Immer? Auch wenn es sich nur um eine unidirektionale Kommunikation handelt? Falls ja empfände ich das als etwas übertrieben.

Ich überlege, ob man darüber nicht Signale (in Form eines Signal-Servers) anbieten könnte. Allerdings hieße das, das man dann immer "Nachrichten" an diesem Callback bekommen könnte, sprich da wäre kein Anfrage-Antwort-Prinzip.
Dann scheint Dein Call-Back in etwa das zu sein was ich als detached IPC bezeichne. Bei mir gibt es Call-Back nur beim asynchronen IPC um anzuzeigen das der IPC-Vorgang abgeschlossen ist und um das Ergebnis zu übermitteln, beim synchronen IPC würde ja der Caller solange blockieren bis der IPC-Vorgang abgeschlossen ist und beim asynchronen IPC tut er das nicht also ist am Ende ein simpler Call-Back erforderlich.

(das habe ich sowieso nicht ganz verstanden wie das genau funktioniert)
Asynchrones File-I/O (was auch Character-Devices und Stream-Sockets usw. mit einschließt) funktioniert in etwas so das der aio_read()/aio_write()-Aufruft nicht so lange bis alles erledigt ist intern blockiert sondern das der Caller (fast) sofort aus der aio_read()/aio_write()-Funktion zurückkommt und weiterarbeiten kann, intern (im VFS) wird dann dieser Job eigenständig abgearbeitet (und, zumindest bei einem Monolithen, auch direkt mit dem bei aio_read()/aio_write() übergebenen User-Buffer gearbeitet) so das dann am Ende oder bei einem Fehler der zum Abbruch führt (z.B. EOF) dann ein Call-Back o.ä. an die Applikation durchgeführt werden kann der das Ergebnis signalisiert (ab hier kann die Applikation auch die Buffer die bei aio_read()/aio_write() mitgegeben wurden wieder normal nutzen, eventuelles Memory-Sharing muss hier also beendet werden).

Man könnte davon ausgehen, das die Daten schon im Cache sind und von daher, sollte das Kopieren von 2 oder 3 Pages wirklich kein Problem darstellen (vorallem nicht bei den heutigen Caches).
Es können aber auch die Daten für die nächste sinnvolle Arbeit bereits im Cache liegen so das diese von der CPU auch sehr effizient abgearbeitet werden könnte. Cache ist bei dieser Betrachtung völlig irrelevant, wichtig ist das in der Zeit wo die CPU händisch kopiert eben keine sinnvolle Arbeit von dieser CPU verrichtet werden kann und das die Kosten fürs kopieren wohl immer so einigermaßen konstant sind (in Relation zur Arbeitsmenge die die CPU anstatt dem Kopieren erledigen könnte).

Zumal ich nicht von mehreren MBs rede, sondern von wenigen KBs, das sollte sich wirklich vernachlässigen lassen.
Auch die Datenmenge ist ersteinmal irrelevant, hier ist eben nur zu beachten dass das Anlegen eines Mappings eben auch gewisse Grundkosten hat die von der Anzahl der Pages unabhängig sind, wenn die nicht wären würde sich Mapping (fast) immer lohnen (außer wenn die Daten so wenig sind das nicht mal eine einzige Page voll wird).


Grüße
Erik
« Letzte Änderung: 19. October 2011, 13:27 von erik.vikinger »
Reality is that which, when you stop believing in it, doesn't go away.

 

Einloggen