Autor Thema: erik´s IPC Idee ;)  (Gelesen 19075 mal)

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #20 am: 25. January 2011, 21:50 »
Zitat von: erik
Dann mach doch über 2 oder 3 weitere Syscalls klassisches SharedMem. Warum versuchst Du in die IPC-Syscalls so viele verschiedene Funktionalitäten rein zu packen?
Die eigentliche Idee dahinter war, das ich nicht (am Bsp. SharedMem versenden) so wenig wie möglich Syscalls machen will (nicht die die vorhanden sind, sondern die die gemacht werden müssen damit etwas bestimmt erledigt werden kann).
Im Normalfall wird SharedMem benötigt um Daten per IPC zu versenden. Deswegen wollte/will ich es da gleich mit drin haben und dann wollte ich noch die Anzahl der Syscalls beschränken. Denn den SharedMem musst du so oder so versenden damit dann ein anderer Prozess Zugriff drauf hat. Also kann ich mir einen Syscall um SharedMem zu erstellen auf jeden Fall sparen. Nur einen um SharedMem wieder zu unmappen wäre vllt angebracht.

Zitat von: erik
Im Endeffekt wird es darauf hinauslaufen das Du dann in der OS-Library versuchst für jeden üblichen Verwendungszweck ein eigenes kleines API zu bauen wo die Funktionen dann nichts weiter machen als die richtigen Flags für ihre spezielle Verwendung der Alles-in-Eins Syscalls anzuhängen.
Diese API wäre ja nur damit der Programmierer ein einfacheres Leben hat. Problem ist es halt eine gewisse Balance zu finden, auf der einen Seite will ich das so wenig wie nötig in den Kernel gewechselt werden muss und auf der anderen Seite will ich die Anzahl der Syscalls begrenzen (warum weiß ich gerade auch nicht  :roll: ).

Zitat von: erik
In Deiner Idee müsste der VFS-Service eine Page allozieren und diese in der RPC-Antwort dem fstat-Stub übermitteln welcher dann die Infos dort raus kopiert und die Page wieder frei gibt, das sind 2 zusätzliche Syscalls (für allozieren und freigeben der Page) und einmal Kopieren als zusätzlicher Aufwand ohne das Du da etwas gewinnst (die eigentliche Nutzarbeit des Service, das befüllen der Struktur, ist dagegen kaum relevant). Genau deswegen bin ich der Meinung das mein IPC-Konzept um viele Größenordnungen schneller ist.
Also erstmal werde ich an dein IPC System eh nicht rankommen können, weil du ja Segmente nutzt. Das nächste ist, dass du die Richtung verwechselt hast ;) Der Client schickt eine Page mit und der Service kopiert dort die Daten rein. Für solche Sachen kann man auch einfach pro Thread provisorisch schonmal einfach eine Page vorallokieren und die dann immer für sowas nutzen.

Ich habe einfach das Problem das ich mit den dynamic-Size Nachrichten nicht richtig umzugehen wüsste. Kopieren wird in den meisten Fällen dort nicht ausbleiben (erste und letzte Page) und sind die Daten kleiner als eine Page muss ohnehin kopiert werden und dann bekomme ich da ein Problem das ich diese Sachen (Daten < eine Page) ja im Kernel Zwischenspeichern muss und da müsste ich auch erstmal nen Allocator für schreiben und der müsste dann auch noch sehr gut mit parallelen Zugriffen klarkommen. Von daher halte ich meine Idee für effizienter in der Geschwindigkeit, aber dafür ineffizienter was den Speicherverbrauch betrifft.

Ich habe mir nochmal Gedanken wegen synchron und asynchron und sowas gemacht. Also ich will auf jeden Fall Popup-Threads für synchrone=RPC Sachen nehmen und da hat man dann die Wahl wieviele Threads man gleichzeitig zulässt. Sicher bin ich mir jetzt nicht ob ich die Queuetiefe auch noch beschränken sollte (das ist bei meinem momentanen Code noch möglich). Was meinst du dazu?

Was die asynchronen Sachen betrifft, will ich dort die Möglichkeit des aktiven Abholens behalten. Es programmiert sich einfach am einfachsten und wenn man (besser ich ;) ) besser mit den Popup-Threads umgehen kann, finde ich ja vllt einen Weg wie ich das dann auch noch damit hinbekomme. Aber ich denke halt immernoch an GUI-Programmierung und ich sag mal bei einem einfachen Programm würde das mit den Popup-Threads wahrscheinlich noch einfach umzusetzen sein. Wenn ich dann aber mal wieder an ein Spiel denke stelle ich es mir blöd vor alles erstmal nochmal in eine Queue zu packen nur damit es dann ein anderer Thread da wieder rausholen kann, wenn er denn soweit ist (obwohl das vllt auch gar nicht so verkehrt wäre, weil der dann nicht in den Kernel müsste?).
Auch ein Media-Player (besonders Video) wäre vllt auch ganz günstig wenn der Dekoder in einem extra Thread läuft und die GUI in einem anderen.

Es zeigt sich halt das man schlecht Sachen designen kann mit denen man noch keine Erfahrung hat.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #21 am: 25. January 2011, 22:01 »
Timeouts sind dafür da, dass Anwendungen (d.h. die Dinge, die in der Regel von Nicht-System-Programmierern programmiert werden!) sich nicht mit Multithreading oder asynchronen Dingen herumplagen müssen, wenn sie es nicht wollen.

Sicherlich ist es für bestimmte Anwendungen sinnvoll, einen eigenen Thread für die Verwaltung von RPC oder Signalen aufzumachen, aber einfache, lineare Anwendungen brauchen auch eine Möglichkeit, aus einer potentiellen Endlosschleife wieder rauszukommen.

Der Wert des Timeouts ist in der Regel anwendungsabhängig. Für einen DNS-Resolver sind das vielleicht 1-2 Sekunden, ehe er auf einen zweiten Server umschwenkt. Und wenn ich ein simples Netcat baue und da gerne mit synchronen APIs arbeite (und weiß, dass die Masse der Daten nach innen geht), dann kann ich einfach etwas in der Art tun:

while(1) {
  result = read_data(network_id, timeout=50ms, &buf);
  if(result) { send_to_screen(buf, length=result); }
  result = read_data(keyboard_id, timeout=50ms, &buf);
  if(result) { send_to_network(buf, length=result); }
}

Das ist sicherlich keine optimale Lösung, aber eine, die recht häufig vorkommt. Außerdem sind Syscalls mit Ablaufzeit immer eine Möglichkeit, einfaches Timeout-Handling zu haben - ohne sich mit Timer-Signalen und Signal-Handling rumschlagen zu müssen.

Ich habe mir nochmal Gedanken wegen synchron und asynchron und sowas gemacht. Also ich will auf jeden Fall Popup-Threads für synchrone=RPC Sachen nehmen und da hat man dann die Wahl wieviele Threads man gleichzeitig zulässt.
Synchron heißt, Client schickt Frage an Server und kriegt direkt eine Antwort (die Frage blockiert, bis die Antwort vorliegt). Da sind Popup-Thread definitiv der falsche Weg, das ist asynchron.

Was die asynchronen Sachen betrifft, will ich dort die Möglichkeit des aktiven Abholens behalten.
Aktives Abholen ist sinnvoll, aber nicht überall - und nicht unbedingt der Königsweg, den man erzwingen muss. Zumal das eher synchron ist, also hast du die Begriffe verwechselt.

Also eher für asynchrone Vorgänge einen Popup-Thread erzeugen, synchrone Vorgänge blockieren. Und es gibt eine Funktion, die auf einen Popup-Thread (also auf asynchrones IPC) aktiv wartet.

Wenn ich dann aber mal wieder an ein Spiel denke stelle ich es mir blöd vor alles erstmal nochmal in eine Queue zu packen nur damit es dann ein anderer Thread da wieder rausholen kann, wenn er denn soweit ist (obwohl das vllt auch gar nicht so verkehrt wäre, weil der dann nicht in den Kernel müsste?).
Ob nun der Kernel drei Dutzend Popup-Threads erzeugt, die alle mangels CPU-Zeit nicht an die Reihe kommen (und die auch in irgendeine bestimmte Reihenfolge gebracht werden müssen), oder ob der Kernel die Events einfach queuet und dann der Hauptschleife schenkt, ist doch egal.

Bei komplexen Spielen mit vielen Threads hast du eh spezielle Threads für Input-Handling, Netzwerk-Handling und so weiter, die sich nur für bestimmte Ereignisse interessieren und von der Hauptschleife unabhängig sind.

Auch ein Media-Player (besonders Video) wäre vllt auch ganz günstig wenn der Dekoder in einem extra Thread läuft und die GUI in einem anderen.
Ja, sicher ist es günstig. Aber willst du es durch dein API-Design erzwingen?

Gruß,
Svenska

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #22 am: 25. January 2011, 22:14 »
Zitat von: svenska
Synchron heißt, Client schickt Frage an Server und kriegt direkt eine Antwort (die Frage blockiert, bis die Antwort vorliegt). Da sind Popup-Thread definitiv der falsche Weg, das ist asynchron.
Nope ;)

Der Client macht eine RPC-Anfrage an einen Service und blockiert. Beim Service wird ein Thread erzeugt (Popup-Thread) der die Anfrage entgegen nimmt, bearbeitet und die Antwort sendet.

Zitat von: svenska
Aktives Abholen ist sinnvoll, aber nicht überall - und nicht unbedingt der Königsweg, den man erzwingen muss. Zumal das eher synchron ist, also hast du die Begriffe verwechselt.
Also ich will das nicht erzwingen, sondern ich will mir die Möglichkeit offen halten und nicht die Popup-Threads erzwingen. Ich denke eher du hast die Begriffe verwechselt ;)

Beim Empfangen würde ich es synchron nennen, wenn der Empfänger solange blockiert bis eine Nachricht da ist und asynchron wenn er Polling macht (also jedes Mal gleich wieder zurückkehrt wenn keine Nachricht da ist). Beides will ich ermöglichen, aber halt über das aktive Abholen (was ja eigentlich logisch ist).

Zitat von: svenska
Ja, sicher ist es günstig. Aber willst du es durch dein API-Design erzwingen?
Nein, wie oben schon geschrieben will ich gar nichts erzwingen, aber ich will auch nicht das man sich verrenken muss um bestimmte Dinge zu machen. Deswegen will ich beide Wege anbieten (aktives Abholen und Popup-Threads).

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #23 am: 25. January 2011, 23:52 »
Ich betrachte alles aus der Sicht der Anwendung, die am Ende RPC machen möchte. Wie das intern implementiert ist und wie der Server strukturiert ist, um darauf zu reagieren, ist an der Stelle egal. Von daher ist die Bezeichnung "synchron im Server" nicht unbedingt zielführend. Und weder "Empfangen" noch "Senden" können damit bezeichnet werden, weil die Begriffe "synchron"/"asynchron" gerade das Zusammenspiel zwischen Senden und Empfangen bezeichnen.

Entweder du hast ein synchrones RPC-Spielchen, wo die Anwendung eine Funktion aufruft, solange blockiert, bis die Antwort vorliegt und sie direkt hat, oder du hast asynchrones RPC, wo die Anwendung eine Funktion aufruft, die sofort zurückkehrt und die Antwort per Signal* zugestellt wird (oder nur der Hinweis, dass jetzt etwas angekommen ist und die Anwendung muss sich das Ereignis selbst abholen).

Sinnvoll finde ich an der Stelle eine Funktion "waitfor_event", die blockiert, bis ein asynchrones Ereignis auftritt, aber das Signal* ist meiner Meinung nach notwendig, um vollkommen asynchrone Anwendungen zu ermöglichen.

*Das Signal kann dein Popup-Thread sein.

Im Server selbst kannst du einen Popup-Threads erzeugen, wenn eine RPC-Anfrage eingeht, aber das halte ich nicht für zielführend. Ich weiß, du stehst auf viele Threads, aber wenn jede RPC-Anfrage einen Thread erzeugt, kannst du den Server ganz schnell DoSen.
Um das zu verhindern, erzeugt dein Service einfach einen Thread für jede CPU (oder weniger), die in einer Endlosschleife jeweils auf ein Ereignis warten und es dann abarbeiten. Das heißt, es gibt maximal CPUZAHL gleichzeitige Ereignisse je Server, alle weiteren werden gequeued. Das erspart dem Scheduler viel Arbeit, spart bei vielen RPC-Anfragen eine Menge Speicher und macht die Services einfacher.

Gruß,
Svenska

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #24 am: 26. January 2011, 07:41 »
Zitat von: svenska
Entweder du hast ein synchrones RPC-Spielchen, wo die Anwendung eine Funktion aufruft, solange blockiert, bis die Antwort vorliegt und sie direkt hat
Genauso läuft es auf der Client Seite ab.

Zitat von: svenska
Ich weiß, du stehst auf viele Threads, aber wenn jede RPC-Anfrage einen Thread erzeugt, kannst du den Server ganz schnell DoSen.
Um das zu verhindern, erzeugt dein Service einfach einen Thread für jede CPU (oder weniger), die in einer Endlosschleife jeweils auf ein Ereignis warten und es dann abarbeiten. Das heißt, es gibt maximal CPUZAHL gleichzeitige Ereignisse je Server, alle weiteren werden gequeued. Das erspart dem Scheduler viel Arbeit, spart bei vielen RPC-Anfragen eine Menge Speicher und macht die Services einfacher.
Und ich weiß du magst Multithreading nicht so ;) Ich bin davon seit BeOS überzeugt. Das Problem mit einem Thread pro CPU ist das, wie du schon richtig in einem anderen Thread festgestellt hast, oft ein Warten von I/O Ereignisse stattfindet und da wäre es wesentlich besser wenn man so viele Threads wie halt gehen hat und nicht so viele wie CPUs dasind. Denn das würde nicht den maximalen Durchsatz bringen.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #25 am: 26. January 2011, 11:17 »
Hallo,


Die eigentliche Idee dahinter war, das ich nicht (am Bsp. SharedMem versenden) so wenig wie möglich Syscalls machen will (nicht die die vorhanden sind, sondern die die gemacht werden müssen damit etwas bestimmt erledigt werden kann).
Ich verstehe was Du meinst aber normalerweise sind richtiges Shared-Memory und IPC (das nur temporäres Shared-Memory benutzt um eben Parameter und Rückgabewerte zu übertragen) zwei völlig verschiedene Dinge. Das IPC überhaupt Shared-Memory benutzt liegt einfach daran das Kopieren zu langsam/umständlich ist aber eben nicht daran das Shared-Memory konzeptioneller Bestandteil von IPC wäre. Richtiges Shared-Memory wird normalerweise für längere Zeit eingerichtet so das mehrere Prozesse gemeinsam/parallel an einem Datenbestand arbeiten können und das wiederum hat nichts mit IPC zu tun. Das der Kernel intern für das ummappen der Pages auf die selben internen Funktionen zu greift ist klar, es steckt ja auch der selbe Paging-Mechanismus dahinter, aber gerade deswegen bin ich der Meinung das ein paar zusätzliche dedizierte Shared-Memory-Syscalls Deinen Kernel kaum vergrößern werden.

Im Normalfall wird SharedMem benötigt um Daten per IPC zu versenden.
Nein, wir verwenden Shared-Memory für IPC um das teure Kopieren zu umgehen. Wenn das Kopieren nichts kosten würde würde keiner von uns auf die Idee kommen Shared-Memory für die Übermittlung der Parameter/Rückgabewerte bei IPC einzusetzen.

Das nächste ist, dass du die Richtung verwechselt hast ;) Der Client schickt eine Page mit und der Service kopiert dort die Daten rein.
Ich habe die Richtung nicht gewechselt, so war das von Anfang an in meinem Konzept vorgesehen, eben weil es sich genau so für synchrones IPC (also RPC mit Anfrage und zugehöriger Antwort) am besten anbietet.

Für solche Sachen kann man auch einfach pro Thread provisorisch schonmal einfach eine Page vorallokieren und die dann immer für sowas nutzen.
Wer soll schon mal Pages provisorisch vorallozieren? Der Client oder der Service? Nur weil Du etwas im voraus machst reduziert sich dadurch nicht der Gesamtaufwand. Wenn ein Client permanent RPC ausführt (z.B. ls) dann gibt es kein provisorisch sondern dieses vorgelagerte Allozieren ist einfach Bestandteil des vollständigen RPC-Vorgangs. Wenn ein Service vor jedem Abholen einer Anfrage erst mal eine Page allozieren muss (eventuell bräuchte der Service für die nächste Anfrage aber auch 1000 Pages aber er kann ja nicht in die Zukunft schauen) dann ist das einfach ein Teil-Vorgang der immer etwas Zeit kostet und bei permanenten ununterbrochenen Anfragen (wie mehrere parallel laufende ls sie für den VFS-Service generieren würden) eben doch wieder den Durchsatz drosselt.

Ich habe einfach das Problem das ich mit den dynamic-Size Nachrichten nicht richtig umzugehen wüsste.
Deswegen ja nicht aktives Abholen sondern zustellen lassen. Der Postbote bringt das Paket immer in einem passenden Karton (wurde vom Absender passend verpackt) wogegen der Abholer ja nie weiß was er denn nun als nächstes bekommt. Und auch für die Rückgabewerte beim RPC ist es besser wenn der Client schon einen leeren Karton mitliefert denn er weiß ja normalerweise wie viele Daten er will (und bei den meisten libc-Funktionen wird auch bei Lese-Funktionen einen Pointer für die gewünschte Datenablage mitgegeben). Klar gibt es bei den Lese-Funktionen ein paar Ausnahmen die nicht im voraus wissen wie groß die Antwort denn werden wird aber das sind eben Ausnahmen und dafür gibt es dann auch in der API bereits passende Auswege.

Kopieren wird in den meisten Fällen dort nicht ausbleiben (erste und letzte Page) und sind die Daten kleiner als eine Page muss ohnehin kopiert werden
Okay, das ist ein Nachteil des Pagings aber das trifft IMHO auch nur die oberste Ebene, wenn eine Applikation den VFS-Service ancallt das 5 Bytes aus einer Datei gelesen werden soll dann wirst Du dort für den VFS ne neue Page nehmen und kopieren aber wenn der VFS diese Page dann an den Dateisystemtreiber weiterreicht dann ist das nicht mehr nötig (ebenso wenn der Dateisystemtreiber diese Page an den SATA-Treiber weiterreicht), einmal Kopieren ist genug.

ja im Kernel Zwischenspeichern muss
Also im Kernel zwischenspeichern solltest Du nicht müssen und wenn doch würde es bei einem nicht unterbrechbaren Kernel reichen jeder CPU einen festen Buffer zuzuordnen (mehr als 2 Pages müssen da ja nicht rein).

Sicher bin ich mir jetzt nicht ob ich die Queuetiefe auch noch beschränken sollte (das ist bei meinem momentanen Code noch möglich). Was meinst du dazu?
Die Queue muss ja nicht die Daten enthalten (die können ja noch beim Client-Thread liegen, ummappen/rauskopieren sollte erst gemacht werden wenn der IPC-Vorgang auch wirklich gestartet werden kann) sondern nur eine Thread-ID o.ä. und daher bin ich der Meinung das man die Queue nicht extra begrenzen muss (vorausgesetzt Du hast ein gutes kmalloc im Kernel).

Was die asynchronen Sachen betrifft, will ich dort die Möglichkeit des aktiven Abholens behalten.
Auch hier sehe ich darin keinen Sinn, muss aber auch sagen das ich mit Spielen und Mediaplayern noch keine Erfahrung hab. Ich vergleiche asynchrone IPC-Messages gerne mit Signalen und die werden ja auch nicht abgeholt.


Synchron heißt, Client schickt Frage an Server und kriegt direkt eine Antwort (die Frage blockiert, bis die Antwort vorliegt). Da sind Popup-Thread definitiv der falsche Weg, das ist asynchron.
Wie kommst du denn auf die blöde Idee das PopUp-Threads für synchrones IPC keinen Sinn ergeben? Ich finde gerade bei synchronem IPC machen die PopUp-Thread sehr viel Sinn weil so der Service in der Lange ist jede Anfrage sofort zu bearbeiten und das nicht davon abhängt ob der Service gerade selber auf eine Anfrage wartet. Und im Client braucht es sowas gar nicht, der blockiert ja bis die Antwort kommt so das der blockierte Thread genau dann aufgeweckt werden kann wenn die Antwort da ist.

Ob nun der Kernel drei Dutzend Popup-Threads erzeugt, die alle mangels CPU-Zeit nicht an die Reihe kommen (und die auch in irgendeine bestimmte Reihenfolge gebracht werden müssen), oder ob der Kernel die Events einfach queuet und dann der Hauptschleife schenkt, ist doch egal.
Ich denke das die meisten Sachen nicht CPU-Limitiert sind sondern selber wieder auf (externe) Ereignisse warten müssen, in sofern wäre es mir persönlich lieber wenn ein paar kB mehr RAM verbraucht werden (für die vielen PopUp-Thread-Stacks) anstatt das z.B. der IP-Stack auf 4 gleichzeitig aktive TCP-Streams begrenzt wird nur weil der PC nur 4 CPUs hat.


Im Server selbst kannst du einen Popup-Threads erzeugen, wenn eine RPC-Anfrage eingeht, aber das halte ich nicht für zielführend. Ich weiß, du stehst auf viele Threads, aber wenn jede RPC-Anfrage einen Thread erzeugt, kannst du den Server ganz schnell DoSen.
Wenn ein Schadprogramm erst mal auf dem System läuft und beliebige Syscalls/RPCs machen kann dann ist eh schon vieles zu spät. Wichtig ist dann nur noch das es sich nicht noch zusätzliche Rechte holen kann damit z.B. der Schaden auf den User begrenzt bleibt in dessen Kontext das Schadprogramm läuft. Auch wäre es gut wenn root-Prozesse gewisse Vorrechte hätten damit root wenigstens die Möglichkeit hat dieses Schadprogramm zu killen. Aber viel mehr kann ein OS in der Situation nicht mehr leisten und auch keines der üblichen OSe tut das.

Um das zu verhindern, erzeugt dein Service einfach einen Thread für jede CPU (oder weniger), die in einer Endlosschleife jeweils auf ein Ereignis warten und es dann abarbeiten. Das heißt, es gibt maximal CPUZAHL gleichzeitige Ereignisse je Server, alle weiteren werden gequeued. Das erspart dem Scheduler viel Arbeit, spart bei vielen RPC-Anfragen eine Menge Speicher und macht die Services einfacher.
Und das drosselt bei eher I/O-Lastigen Services erheblich den Durchsatz weil nur wenige Anfragen parallel bearbeitet werden können. Außerdem belasten die vielen blockierten (weil gerade auf I/O wartenden) PopUp-Threads nicht den Scheduler (und wenn doch dann ist Dein Scheduler Mist). Und ob durch künstliche Begrenzung der Anzahl der PopUp-Threads die Services wirklich einfacher werden wage ich zu bezweifeln, sobald Du mehr als einen PopUp-Thread gleichzeitig zu lässt muss der Code passend ausgelegt sein (mit Locks/Semaphoren/usw. an allen relevanten Stellen). Ich meinem Konzept möchte ich es der Services schon ermöglichen die Anzahl paralleler PopUp-Threads zu begrenzen aber da bin ich mir auch noch nicht sicher welcher konkrete Wert da den angebracht wäre (es ist mir erst mal wichtig das der Kernel überhaupt eine Begrenzung anbietet). Ob für den VFS-Service 256 PopUp-Threads schon zu viele oder doch noch zu wenige sind hängt sicher auch von der CPU/RAM-Ausstattung des betreffenden Computers ab.


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 #26 am: 26. January 2011, 11:44 »
Also nochmal zum SharedMem. Mir geht es darum das du nach deiner Variante einen Syscall machst um einen SharedMem-Bereich zu erstellen und dann eventuell noch einen zweiten um einem anderen Prozess die Rechte zu geben diesen nutzen zu dürfen und dann auf jeden Fall noch einen dritten (oder zweiten) um dem anderen Prozess die ID (oder soetwas in der Art) zukommen zu lassen. Der Empfänger hat dann die ID und muss auch noch mal nen Syscall machen um den SharedMem zu mappen.

Das ist doch alles viel zu viel Aufwand. Da benutze ich doch lieber das was ich schon habe und erledige das alles in einem Syscall.

Zitat von: erik
Wenn das Kopieren nichts kosten würde würde keiner von uns auf die Idee kommen Shared-Memory für die Übermittlung der Parameter/Rückgabewerte bei IPC einzusetzen.
Selbst dann würdest du es wahrscheinlich auf einer 32bit Architektur nicht unbedingt machen wollen, weil das Problem bleibt immernoch die Daten von einem Adressraum in einen anderen zu bekommen und da der Kernel das einzige ist was alle Adressräume gemeinsam haben wirst du die Daten dort zwischen speichern und ohne SharedMem (wo du ja nur ne Liste mit den Pages speichern musst) könnte dir da schnell der virtuelle Speicher ausgehen.

Zitat von: erik
Nur weil Du etwas im voraus machst reduziert sich dadurch nicht der Gesamtaufwand.
Von daher und wie du ja später schreibst (weil man nicht immer weiß wieviele Daten es nun werden), wäre es halt nicht schlecht wenn nicht der Client das Behältnis mitschickt, sondern er bekommt es.

Irgendwo muss so oder so Speicher allokiert werden. Eine andere Möglichkeit wäre noch (ich spezialisiere meinen Syscall noch weiter ;) ) dem Syscall die Anzahl der Pages die benötigt werden zu sagen und im Kernel wird dann ein Bereich im Adressraum genommen und mit Pages hinterlegt und als SharedMem mitgesendet (so spart man sich wieder einen Syscall ;) ).
Was wäre mit der Idee?

Die funktioniert natürlich nur, wenn du Daten haben möchtest, wenn du welche senden willst, muss der Client weiterhin den Speicher selbst allokieren. Aber auch das lässt sich leicht lösen. Man muss ja einen Pointer und eine Länge mitgeben (damit der Kernel weiß wo und wieviele Pages als SharedMem mitgesendet werden sollen) und wenn man als Pointer "0" angibt, heißt das für den Kernel das er den Speicher allokieren soll und wenn der Pointer != "0" ist, dann ist der Speicher schon vorhanden.

Ich denke mal das wäre dir wider zu umständlich/Fehler anfällig oder?

Zitat von: erik
Deswegen ja nicht aktives Abholen sondern zustellen lassen. Der Postbote bringt das Paket immer in einem passenden Karton (wurde vom Absender passend verpackt) wogegen der Abholer ja nie weiß was er denn nun als nächstes bekommt. Und auch für die Rückgabewerte beim RPC ist es besser wenn der Client schon einen leeren Karton mitliefert denn er weiß ja normalerweise wie viele Daten er will (und bei den meisten libc-Funktionen wird auch bei Lese-Funktionen einen Pointer für die gewünschte Datenablage mitgegeben).
Seien wir mal ehrlich, das einzige was aktives Abholen von deinen Popup-Threads unterscheidet ist der Zeitpunkt wann es geschieht und wo. Denn den Rest kann ich auch analog machen, sprich auch beim aktiven Abholen kann ich dem Empfänger einfach nen Pointer übergeben wo die Nachricht dann ist (so muss der Empfänger nicht mehr wissen wie groß die Nachricht ist), aber ich habe den Vorteil das ich weiß wann die Nachricht kommt, nämlich dann wenn ich sie abholen möchte und nicht irgendwann. Das ist im Endeffekt wie ein Signal, da muss ich soviele Dinge beachten damit ich Signale nutzen kann.
Am Bsp. deiner Popup-Threads müsste ich auf jeden Fall viel Code haben der Threadsafe ist, was z.B. beim aktiven Abholen nicht der Fall wäre.

Zitat von: erik
Okay, das ist ein Nachteil des Pagings aber das trifft IMHO auch nur die oberste Ebene, wenn eine Applikation den VFS-Service ancallt das 5 Bytes aus einer Datei gelesen werden soll dann wirst Du dort für den VFS ne neue Page nehmen und kopieren aber wenn der VFS diese Page dann an den Dateisystemtreiber weiterreicht dann ist das nicht mehr nötig (ebenso wenn der Dateisystemtreiber diese Page an den SATA-Treiber weiterreicht), einmal Kopieren ist genug.
Das Problem ist, dass ich alle Nachrichten < einer Page im Kernel zwischen speichern würde und wenn ich mir nicht gerade nen sehr effizienten Allocator schreibe oder portiere, müsste ich dafür dann auch jedes Mal eine Page allokieren und das im Kernel, wo der virtuelle Speicher ja begrenzt ist. Ich sehe es halt als effizienter an, wenn man nur Pages und fixed-Size Objekte durch die Gegend schiebt als wenn erstnoch ein Allocator einen Bereich suchen muss der dann passt.

Edit::

Was ich bei meinem SharedMem Bsp. noch vergessen habe, du brauchst auch nochmal mind. einen Syscall um herauszufinden welche Prozess hinter einem Port steckt. Denn die Prozess ID brauchst du um dem Prozess zu erlauben einen bestimmten SharedMem Bereich nutzen zu dürfen und mit anderen Prozessen wirst du nur über Ports kommunizieren.
« Letzte Änderung: 26. January 2011, 12:31 von FlashBurn »

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #27 am: 26. January 2011, 16:32 »
Hallo,


Also nochmal zum SharedMem. Mir geht es darum das du nach deiner Variante einen Syscall machst um einen SharedMem-Bereich zu erstellen und dann ....
Das ist doch alles viel zu viel Aufwand. Da benutze ich doch lieber das was ich schon habe und erledige das alles in einem Syscall.
Ich denke das ich mit einem Syscall pro Teilnehmer auskomme, zuzüglich der Übermittlung der Zugangsdaten, aber das ist gar nicht der Punkt. Richtiges Shared-Memory wird für längerfristige Sachen benutzt und da spielen 2 oder 3 Syscalls zum Erstellen absolut keine Rolle. Das Problem was ich sehe ist das Du den Shared-Memory für eigenes IPC Zweckentfremden möchtest (wahrscheinlich weil Du Dir davon eine Beschleunigung erhoffst). Dein derzeitiges IPC-Konzept bereitet Dir offenbar Kopfzerbrechen wegen dem Übertragen der Parameter/Rückgabewerte und da versuchst Du das mit erweiterten Shared-Memory-Funktionalitäten zu kompensieren. Möglicherweise gelangst du damit auch ganz gut zum Ziel aber ein vernünftiges Design kommt da IMHO nicht bei raus. Auch diese fürchterlich überladenen Alles-in-Einem-Syscalls empfinde ich als extrem ungeschickt. Schon allein die vielen Verzweigungen kosten Dich bei jedem Aufruf dieser Syscalls etwas an Performance.

weil das Problem bleibt immernoch die Daten von einem Adressraum in einen anderen zu bekommen und da der Kernel das einzige ist was alle Adressräume gemeinsam haben wirst du die Daten dort zwischen speichern
Nein, dafür mappt man die Quelle temporär in den Ziel-Adressraum (oder anders herum) und kopiert nur einmal. Was bleibt ist das IPC/RPC konzeptionell nichts mit Shared-Memory zu tun haben sondern das die gegebene Computer-Architektur Mapping (ob nun Pages oder Segmente ist ja egal) eben als geeignetes Werkzeug anbietet um damit IPC/RPC hochperformant zu unterstützen.

Von daher und wie du ja später schreibst (weil man nicht immer weiß wieviele Daten es nun werden), wäre es halt nicht schlecht wenn nicht der Client das Behältnis mitschickt, sondern er bekommt es.
Hä, wie kommst Du denn jetzt plötzlich da drauf? Der Client weiß doch wie viele Daten er lesen/empfangen oder schreiben/senden möchte und der Service weiß das erst nachdem er die Anfrage empfangen hat insofern ist es IMHO absolut logisch das der Client die Behälter bei der Anfrage bereits alle mitschickt (nebst dessen das sich das so auch mit der Semantik der libc-Funktionen deckt).

Irgendwo muss so oder so Speicher allokiert werden.
Nein, der Client hat den Speicher bereits, siehe fread da muss der Applications-Code ja auch schon den Speicher haben wo dann die Daten hin sollen. Gerade deswegen empfinde ich mein IPC-Konzept ja als so schnell weil da eigentlich nie extra Speicher alloziert werden muss sondern es wird immer der Speicher benutzt der schon da ist (was dann noch den zusätzlichen Performance-Vorteil des Zero-Copy mit sich bringt).

das einzige was aktives Abholen von deinen Popup-Threads unterscheidet ist der Zeitpunkt wann es geschieht und wo.
Richtig! Während bei mir der Kernel dann einen PopUp-Thread erzeugt (und diesen sofort auf der CPU die gerade noch der Client-Thread belegt hat los laufen lässt) wenn die Anfrage gestellt wird muss bei Dir der Kernel eventuell warten (bzw. unterbrechen und was anderes tun) bis der Service bereit ist die Anfrage entgegen zu nehmen. Ich habe also in vielen Fällen eine deutlich kürzere Latenz.

aber ich habe den Vorteil das ich weiß wann die Nachricht kommt
Wo ist da ein Vorteil? Was hat der VFS-Service davon wenn er weiß wann die Anfragen der vielen Clients rein kommen? Wichtig ist doch das die Anfragen möglichst zügig bearbeitet werden, zumindest meiner persönlichen Meinung nach.

Das ist im Endeffekt wie ein Signal, da muss ich soviele Dinge beachten damit ich Signale nutzen kann.
Hä, was ist denn an Signalen bitte so kompliziert? Aber die Analogie zwischen einem Signal und dem Eintreffen einer RPC-Anfrage stimmt ganz gut.

Am Bsp. deiner Popup-Threads müsste ich auf jeden Fall viel Code haben der Threadsafe ist, was z.B. beim aktiven Abholen nicht der Fall wäre.
Wenn Du Deinem Service erlauben möchtest mehr als eine Anfrage parallel zu bearbeiten (also z.B. mit mehreren Threads aktiv abholst) benötigst Du das doch eh alles. Wo ist da das Problem? Bei meinen PopUp-Threads kann ich das auch auf 1 begrenzen wenn ein Service eh keinen Vorteil daraus zieht wenn er mehrere Anfragen parallel bearbeiten kann (z.B. Dein Diskettentreiber).

Das Problem ist, dass ich alle Nachrichten < einer Page im Kernel zwischen speichern würde
Du musst gar nichts im Kernel zwischen Speichern sondern kopierst immer direkt von der Quelle zum Ziel.

du brauchst auch nochmal mind. einen Syscall um herauszufinden welche Prozess hinter einem Port steckt.
So einen Syscall wird es bei mir gar nicht geben. Dem Client geht die Prozess-ID des Service nichts an.

Denn die Prozess ID brauchst du um dem Prozess zu erlauben einen bestimmten SharedMem Bereich nutzen zu dürfen
Nein, das geht über Zugriffs-Keys die beim Erstellen des Shared-Memorys angelegt werden und die ich nur den Prozessen gebe die ich dazu einladen möchte beim Sharen mit zu machen. Sieht Dir mal die PSOX-Funktionen für Shared-Memory an, ungefähr das ist es auch was die Programme typischerweise erwarten (unter Windows funktioniert das wimre sehr ähnlich).


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 #28 am: 26. January 2011, 16:59 »
Zitat von: erik
Auch diese fürchterlich überladenen Alles-in-Einem-Syscalls empfinde ich als extrem ungeschickt. Schon allein die vielen Verzweigungen kosten Dich bei jedem Aufruf dieser Syscalls etwas an Performance.
Da bin ich halt anderer Meinung bzw. will ich ja die Möglichkeit haben mehr als nur die 32byte Daten als Nachricht zu versenden (bei mir dann halt immer vielfaches der Pagegröße) und das ist im Endeffekt ja SharedMem.
Warum sollte ich dann nicht schon das vorhandene nutzen? Die Performance die durch die Überprüfung der Flags verloren geht dürfte wesentlich geringer sein, als die die verloren gehen würde, wenn ich dafür mehrere Syscalls aufrufen müsste.

Zitat von: erik
Nein, der Client hat den Speicher bereits, siehe fread da muss der Applications-Code ja auch schon den Speicher haben wo dann die Daten hin sollen.
Und wo kommt dieser Speicher her ;) Der muss doch erst allokiert werden und das erfordert irgendwann auch mal nen Syscall. Wo du recht hast ist, dass ich im Clienten wahrscheinlich nochmal allokieren müsste (damit ich 4KB alignment habe) und auch kopieren (aus der Page in den User-Buffer).
Aber soweit wie ich mir die libc angeguckt habe, ist das ganze fread/fwrite eh gepuffert und wenn ich da dann nen 4KB Buffer nehme sehe ich da kein Problem.

Zitat von: erik
Ich habe also in vielen Fällen eine deutlich kürzere Latenz.
Jein ;) Ich denke mal was hier untergegangen ist, das ich beim aktiven Abholen dann davon ausgehe dass das Senden asynchron war (ich rede also nur von irgendwelchen Events und sowas) und dass das nichts mit dem Service zutun hat, sondern es geht mir dabei um den Clienten, der die Nachrichten abholt. Die Latenz ist dabei egal, weil ob die Nachricht nun im UserSpace in eine Queue kommt oder im KernelSpace hat mit der Latenz erstmal nichts zu tun.

Ich kann dann aber als Programmierer entscheiden wann ich die Nachricht haben möchte und bearbeite sie dann und hole auch die nächste Nachricht erst wenn ich wieder soweit bin und kann mir, wenn es sein muss den ganzen Multithread-Kram sparen (sollte das Portieren erleichtern). Sobald ich Popup-Threads für den Empfang von irgendwelchen Events nutze, muss das gesamte Programm Threadsafe sein und wenn ich eh ein Programm habe wo die GUI vom restlichen Programm getrennt ist (also in einem anderen Thread) werde ich bestimmt auch einen Popup-Thread nutzen, aber alle anderen Programme sollen halt auch die Möglichkeit haben es anders zu lösen.

Zitat von: erik
Hä, was ist denn an Signalen bitte so kompliziert?
Du hattest doch mal selber gebracht das es eher ungünstig ist, wenn man ein Signal bekommt und der Thread war gerade mitten in einem malloc() und das Signal will jetzt ebenfalls malloc() benutzen. Im Endeffekt läuft es darauf hinaus dass das gesamte Programm Threadsafe sein muss und seien wir mal ehrlich Linux bzw die ganzen "alten" Programme hat damit noch so seine Probleme.

Zitat von: erik
Du musst gar nichts im Kernel zwischen Speichern sondern kopierst immer direkt von der Quelle zum Ziel.
Und wie bekomme ich die Daten aus dem einem Adressraum in den anderen, wenn ich nicht einfach nur eine Page ummappen kann (bei Daten < einer Page)? Ich muss doch die Daten aus dem einem Adressraum in eine temporäre Page kopieren um diese dann beim Empfänger reinzumappen und um dann beim Beenden des RPC wieder die Daten aus der temporären Page in den Clienten zu kopieren (was alles im Endeffekt das selbe ist, was ich mit meinem IPC System machen will, nur das mehr im UserSpace stattfindet).

Zitat von: erik
Nein, das geht über Zugriffs-Keys die beim Erstellen des Shared-Memorys angelegt werden und die ich nur den Prozessen gebe die ich dazu einladen möchte beim Sharen mit zu machen. Sieht Dir mal die PSOX-Funktionen für Shared-Memory an, ungefähr das ist es auch was die Programme typischerweise erwarten (unter Windows funktioniert das wimre sehr ähnlich).
Theoretisch wäre es also möglich per Zufall genau den richtigen Key zu erraten und man kann einfach SharedMem benutzen für den man gar nicht gedacht war? Das finde ich aber sehr zweifelhaft. Ich dachte eigentlich immer das läuft alles über Dateien und die Diskriptoren und das die dann an einen anderen Prozess weitergegeben werden (wofür man wieder die ID des Prozesses bräuchte).

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #29 am: 27. January 2011, 12:45 »
Hallo,


Da bin ich halt anderer Meinung bzw. will ich ja die Möglichkeit haben mehr als nur die 32byte Daten als Nachricht zu versenden (bei mir dann halt immer vielfaches der Pagegröße) und das ist im Endeffekt ja SharedMem.
Nein!  IPC != Shared-Memory
Beides basiert zwar auf dem Konzept des Ummappen von Speicher (weil beides mit der gegebenen CPU-Architektur eben so am besten zu lösen ist) aber es sind 2 verschiedene Mechanismen. Den einen (Shared-Memory) benötigst Du um auf einem Datenbestand von mehreren Prozessen aus gleichzeitig zu zugreifen und den anderen (IPC/RPC) um von einem Prozess aus eine Funktion in einem anderen Prozess aufzurufen. Das sich für die Realisierung beider Mechanismen Mapping (egal ob Pages oder Segmente) anbietet liegt eben in der Natur der CPU. Du darfst gerne beide Mechanismen in einen Syscall zusammen murkseln aber ich persönlich rate davon dringend ab.

Warum sollte ich dann nicht schon das vorhandene nutzen? Die Performance die durch die Überprüfung der Flags verloren geht dürfte wesentlich geringer sein, als die die verloren gehen würde, wenn ich dafür mehrere Syscalls aufrufen müsste.
Für was benötigt man mehrere Syscalls? Entweder Du möchtest RPC machen (dann braucht der Client einen Syscall um seine Anfrage los zu schicken und um die Antwort zu erhalten und der Service braucht auch nur einen Syscall um die Antwort zurück zu schicken) oder Du möchtest richtiges Shared-Memory machen und dann wirst Du pro Teilnehmer auch einen Syscall benötigen zuzüglich dem Übertragen der Zugangsdaten (oder wie auch immer Du das realisieren möchtest). Aber wenn Du Shared-Memory von IPC abhängig machst dann bedeutet dass das nie zwei Prozesse Speicher teilen können ohne das zumindest einer davon einen IPC-Port einrichten muss und das finde ich doof.

Zitat von: erik
Nein, der Client hat den Speicher bereits, siehe fread da muss der Applications-Code ja auch schon den Speicher haben wo dann die Daten hin sollen.
Und wo kommt dieser Speicher her ;) Der muss doch erst allokiert werden und das erfordert irgendwann auch mal nen Syscall.
Das könnte aber auch ein statisches Array sein oder auf dem Stack liegen, nebst dessen das nicht jeder Aufruf von malloc auch zwangsläufig einen Syscall benötigt (falls Du ne anständige libc hast)

Ich denke mal was hier untergegangen ist, das ich beim aktiven Abholen dann davon ausgehe dass das Senden asynchron war (ich rede also nur von irgendwelchen Events und sowas) und dass das nichts mit dem Service zutun hat, sondern es geht mir dabei um den Clienten, der die Nachrichten abholt. Die Latenz ist dabei egal, weil ob die Nachricht nun im UserSpace in eine Queue kommt oder im KernelSpace hat mit der Latenz erstmal nichts zu tun.
Also langsam weiß ich echt nicht mehr wovon Du eigentlich so schreibst! Wenn wir über RPC (also synchrones IPC mit Anfrage und zugehöriger Antwort) schreiben dann gibt es da in meinem Konzept nur in so fern aktives Abholen weil der Client ja so lange blockiert bis seine Antwort vom Service eintrifft, der Client benutzt quasi einen send+recv-Syscall der eben so lange blockiert bis die zugehörige Antwort kommt (alle anderen Messages die sonst noch durchs System geistern können diesen wartenden Client nicht erreichen und er ist auch nicht adressierbar sondern wartet nur genau auf seine Antwort). Auch sonst kann dieser wartende Client-Thread nicht von außen beeinflusst werden (man kann ihn auch nicht einfach aufwecken o.ä.).

es geht mir dabei um den Clienten, der die Nachrichten abholt
Also Clienten (im Zusammenhang von RPC, bei asynchronem IPC gibt es ja nur Sender und Empfänger) holen ihre Antwort nie selber aktiv ab, das erledigt der RPC-send+recv-Syscall automagisch mit.

Ich kann dann aber als Programmierer entscheiden wann ich die Nachricht haben möchte und bearbeite sie dann und hole auch die nächste Nachricht erst wenn ich wieder soweit bin und kann mir, wenn es sein muss den ganzen Multithread-Kram sparen (sollte das Portieren erleichtern).
Also ich als Programmierer möchte das alle meine Services alle Anfragen so schnell als möglich bearbeiten. Außerdem wüsste ich gar nicht wie ich es umsetzen sollte das meine Services zur Laufzeit entscheiden wann sie eine Anfrage erhalten möchten und wann nicht. In den Services werde ich eh alles Thread-Save machen müssen schließlich fällt mir kein Service ein bei dem ich alle Anfragen nur rein seriell verarbeiten möchte (außer vielleicht der für den Diskettencontroller aber sowas hab ich nicht). Also sehe ich keinen Vorteil darin mir bei Services das Multithreading ersparen zu können, ich will es doch eh haben damit ich auf vernünftige Performance komme. Die Clients könnten trotzdem Singlethreaded sein, der RPC-send+recv-Syscall macht da keine Einschränkungen.
Meine libc wird es auch nur ein einer Thread-Save-Version geben, bei Locks die sicher frei sind dürfte der Performanceverlust nur ziemlich minimal sein.

Du hattest doch mal selber gebracht das es eher ungünstig ist, wenn man ein Signal bekommt und der Thread war gerade mitten in einem malloc() und das Signal will jetzt ebenfalls malloc() benutzen.
Das hängt davon ab wie Signale implementiert sind. Wenn ein Signal die Ausführung eines Threads wirklich unterbricht, so wie z.B. tyndur das macht, dann muss malloc nicht nur Thread-Save sein sondern auch temporär das Zustellen von Signalen unterbinden (kostet jeweils 2 Syscalls, ausschalten+einschalten). Wenn Signale aber einen neuen Thread erzeugen (ich möchte PopUp-Threads auch für asynchrones IPC benutzen) dann reicht normales Thread-Save völlig aus.

seien wir mal ehrlich Linux bzw die ganzen "alten" Programme hat damit noch so seine Probleme.
Dem werde ich sicher nicht widersprechen aber solche "alten" Programme muss man ja nicht unbedingt portieren. Und mit Linux sollen doch unser beider OSe nicht viel gemein haben.

Zitat von: erik
Du musst gar nichts im Kernel zwischen Speichern sondern kopierst immer direkt von der Quelle zum Ziel.
Und wie bekomme ich die Daten aus dem einem Adressraum in den anderen
Der Kernel macht für den kurzen Zeitraum des Kopierens beide Adressräume gleichzeitig zugänglich, zumindest jeweils den Teil der benötigt wird (also wo die Daten liegen bzw. hin sollen). Sobald das Kopieren fertig ist werden die beiden Adressräume wieder sauber getrennt und da das Kopieren ja im Kernel statt findet gibt es da auch keine Sicherheitslecks.

Theoretisch wäre es also möglich per Zufall genau den richtigen Key zu erraten und man kann einfach SharedMem benutzen für den man gar nicht gedacht war? Das finde ich aber sehr zweifelhaft.
Man wird auf jeden Fall noch eine gültige Shared-Memory-ID brauchen und ansonsten kannst Du ja den Key 128 Bit groß machen so das raten wirklich keinen Sinn mehr ergibt.

Ich dachte eigentlich immer das läuft alles über Dateien und die Diskriptoren und das die dann an einen anderen Prozess weitergegeben werden (wofür man wieder die ID des Prozesses bräuchte).
Das kann man auch so machen, ich hab gestern einfach nur mal kurz nach Beispiel-Code gesucht und eben die Variante mit dem Key gefunden und das war in irgendwelchen Linux-Man-Pages drin.


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 #30 am: 27. January 2011, 13:21 »
Also wenn ich jetzt von asynchronen IPC rede und von einem Clienten dann geht es nicht um RPC! Ich bin da wieder bei meinem lieblings Bsp. GUI-Anwendung. Diese GUI-Anwendung bekommt eine Nachricht, in der Nachricht steht dass das Fenster vergrößert wurde und neugezeichnet werden muss. Der Service sendet die Nachricht asynchron, weil warum sollte der Service darauf warten das die Nachricht auch bearbeitet wurde?
Der Client wartet entweder auf eine Nachricht oder er macht gerade etwas, die Nachricht kommt in die Queue des Ports und wenn er Client fertig ist macht er seinen Syscall um die Nachricht abzuholen.
Der Client kann also jedes Mal die Nachricht ganz einfach verarbeiten, bei meinem Bsp., wird er also seinen Fensterinhalt neu zeichnen und erst wenn er fertig ist, guckt er nach ob eine neue Nachricht vorhanden ist.

Würde ich das jetzt über Popup-Threads machen, würde ich einfach die Queue, für Nachrichten, in den UserSpace packen und wahrscheinlich mit einer Semaphore arbeiten. So dass der Popup-Thread die Semaphore inkrementiert wenn er die Nachricht in die Queue gepackt hat und damit den eventuell wartenden Worker-Thread startet (was wieder einen Syscall kostet) und der Worker-Thread dekrementiert die Semaphore und wenn noch mind. eine Nachricht vorhanden ist bearbeitet er die nächste.

So könnte ich das Problem welches mit dem Multi-Threading kommen würde auch noch umgehen, aber ich würde die Popup-Threads nutzen.

Was ich momentan noch gar nicht sehe ist, dass mehrere Nachrichten gleichzeitig=parallel bearbeitet werden. Kann sein das ich das irgendwann auch hinbekomme (gedanklich), aber im Moment einfach noch nicht.

So ich hoffe jetzt du weißt was ich mit aktiven Abholen und asynchronen IPC meine!

Dann nochmal zum SharedMem  :-D

Ich sehe da wie gesagt keinen Unterschied, bei beiden Sachen (Nachricht und "richtiger" SharedMem) wird der Speicher in beiden Prozessen gemappt sein und theoretisch können in dem Moment beide darauf zugreifen (theoretisch deswegen, weil ja ein anderer Thread des Clienten auf die dumme Idee kommen könnte, in dem Speicher der als Nachricht verschickt wurde, herumzufrickeln). Der einzige Unterschied wäre, dass der SharedMem beim RPC zeitlich für das Antworten begrenzt ist und ansonsten solange bleibt bis ihn der Empfänger unmappt.

Was den IPC Port betrifft, den man dann zum Übertragen von SharedMem bräuchte, wie willst du denn dem anderen Prozess den SharedMem zukommen lassen und wie willst du rausfinden welchem Prozess du den Speicher zukommen lassen willst?
Was mir im Moment fehlt, wäre mal ein konkretes Bsp. wo SharedMem verwendet wird.

Zitat von: erik
Der Kernel macht für den kurzen Zeitraum des Kopierens beide Adressräume gleichzeitig zugänglich
Das geht bei mir halt nicht, ich kann von einem Prozess aus nicht auf den Adressraum eines anderen Prozess zugreifen (weil die Daten welche Bereiche frei und welche nicht frei sind, werden im Kernel immer an der selben Stelle gespeichert und sind Prozessbezogen).

Zitat von: erik
Man wird auf jeden Fall noch eine gültige Shared-Memory-ID brauchen und ansonsten kannst Du ja den Key 128 Bit groß machen so das raten wirklich keinen Sinn mehr ergibt.
Wieso so umständlich?! Zumal du den immer noch durch einen dummen Zufall bekommen könntest. Bei mir ist das Unmöglich! Wenn ein Prozess einen Speicher-Bereich an einen anderen sendet, gibt es für alle anderen Prozesse keine Möglichkeit da ran zukommen, nicht mal per Zufall (und es braucht wie gesagt auch noch weniger Syscalls).

Was die Anzahl der Syscalls betrifft, das möchte inzwischen nicht mehr so schlimm sein, aber da ich immernoch für Pentium Systeme programmiere (und da nutzt man nen Interrupt und das kostet richtig) lege ich Wert darauf das man nur so wenig wie nötig machen muss.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #31 am: 27. January 2011, 15:12 »
Hallo,


Also wenn ich jetzt von asynchronen IPC rede
Das es Dir um asynchrones IPC ging war im Deinem letzten Post nicht ersichtlich!

von einem Clienten dann geht es nicht um RPC!
Bei asynchronem IPC gibt es keinen Client! Da gibt es nur Sender und Empfänger. Mit ganz viel Fantasie könnte man den Empfänger noch als Service betrachten weil er ja einen öffentlich erreichbaren Port hat (es gibt bestimmt auch richtige Services die man nur asynchron antriggern braucht) aber der Begriff Client ist da definitiv fehl am Platz.

So ich hoffe jetzt du weißt was ich mit aktiven Abholen und asynchronen IPC meine!
Jetzt ergeben für mich ein paar Deiner vergangenen Beiträge etwas mehr Sinn aber warum Du unbedingt aktiv abholen möchtest leuchtet mir trotzdem nicht ein. Ist multithreading-taugliche SW denn wirklich so furchtbar das man das unbedingt vermeiden muss?

Dann nochmal zum SharedMem  :-D
Ich sehe da wie gesagt keinen Unterschied, bei beiden Sachen (Nachricht und "richtiger" SharedMem) wird der Speicher in beiden Prozessen gemappt sein
Naja, richtiges Shared-Memory macht man für einen erstmal unbestimmt langen Zeitraum weil man ja Daten sharen möchte und das Mapping beim RPC ist nur so lange wie eben der RPC dauert und auch nur Mittel zu Zweck (weil man könnte ja auch immer kopieren und da bräuchte man gar kein Mapping für RPC).
Aber lass Dich nicht von mir aufhalten, mach das so wie Du es für richtig hältst. Meine Meinung kennst Du jetzt und darum ging es Dir doch, oder?
Wenn Du Shared-Memory zwangsweise per IPC machst, wie möchtest Du dann mehr als 2 Prozesse teil nehmen lassen?

Was den IPC Port betrifft, den man dann zum Übertragen von SharedMem bräuchte, wie willst du denn dem anderen Prozess den SharedMem zukommen lassen und wie willst du rausfinden welchem Prozess du den Speicher zukommen lassen willst?
Ich könnte auch einfach die Shared-Memory-ID zusammen mit dem ReadOnly-Key in globalen Umgebungsvariablen o.ä. veröffentlichen um so zu ermöglichen das beliebige Prozesse auf meine allgemein nützlichen Daten zugreifen können (keine Ahnung ob es für so ein Szenario eine ernsthafte Anwendung gibt).

Was mir im Moment fehlt, wäre mal ein konkretes Bsp. wo SharedMem verwendet wird.
Ich hab zwar ein paar Beispiele gefunden wie man Shared-Memory benutzt aber leider keine Beispiele wofür man Shared-Memory ernsthaft braucht.

Zitat von: erik
Der Kernel macht für den kurzen Zeitraum des Kopierens beide Adressräume gleichzeitig zugänglich
Das geht bei mir halt nicht
Das ist doof! Sogar der L4 kann das.

gibt es für alle anderen Prozesse keine Möglichkeit da ran zukommen, nicht mal per Zufall (und es braucht wie gesagt auch noch weniger Syscalls).
Wenn man wirklich richtigen Shared-Memory machen möchte dann ist IMHO die Anzahl der Syscalls zum Einrichten nicht so das Kriterium, ich denke wenn man sowas macht dann nicht bloß kurz um mal schnell ne Hand voll Bytes zu kopieren sondern weil man wirklich mit mehreren Prozessen an einem Datensatz ernsthaft (länger) arbeiten möchte.


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 #32 am: 27. January 2011, 17:14 »
Ist multithreading-taugliche SW denn wirklich so furchtbar das man das unbedingt vermeiden muss?
Manchmal ist es halt einfacher ohne Multithreading und Multithreading wird bestimmt auch nicht überall mehr Performance bringen oder anders gesagt es wird manchmal mehr Probleme schaffen als es lösen wird.

Wenn Du Shared-Memory zwangsweise per IPC machst, wie möchtest Du dann mehr als 2 Prozesse teil nehmen lassen?
Ganz einfach, ich sende den Speicher an alle Prozesse die teilnehmen dürfen.

Was zur Sicherheit noch. Geht es wenn Prozess B von Prozess A Speicher bekommen hat, den dann weiter an Prozess C zu geben ohne dass das Prozess A will?

Zitat von: erik
Der Kernel macht für den kurzen Zeitraum des Kopierens beide Adressräume gleichzeitig zugänglich
Das geht bei mir halt nicht
Das ist doof! Sogar der L4 kann das.
Bei mir sind die Prozesse halt so richtig von einander getrennt (unter anderem um virtuellen Speicher zu sparen. Ich würde wahrscheinlich sogar sehr gut mit nur 512MB KernelSpace auskommen.

Wenn man wirklich richtigen Shared-Memory machen möchte dann ist IMHO die Anzahl der Syscalls zum Einrichten nicht so das Kriterium, ich denke wenn man sowas macht dann nicht bloß kurz um mal schnell ne Hand voll Bytes zu kopieren sondern weil man wirklich mit mehreren Prozessen an einem Datensatz ernsthaft (länger) arbeiten möchte.
Naja, so ähnlich könnte man mit einem langsamen IPC System argumentieren (z.B. bei meinem ;) ). Wenn es so viele Daten sind das die Übertragung lange dauert. Dann sollte das nicht so ins Gewicht fallen, weil die Bearbeitung der Daten viel länger dauern wird. Bestes Bsp. ist die Festplatte, da das Laden so lange dauert, fällt ein langsamerer "Datenverkehr" zum Clienten auch nicht mehr auf.

erik.vikinger

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


.... anders gesagt es wird manchmal mehr Probleme schaffen als es lösen wird.
Solche Situationen gibt es ganz bestimmt aber das trifft wohl kaum auf Services zu die einen hohen Durchsatz und geringe Latenz aufweisen sollen. Bei einfachen Client-Programmen wird man sicher gerne auf Multithreading verzichten aber bei einem High-Performance-Service wird man eher alle Tricks aus dem Ärmel ziehen die nur irgendwie gehen. Im Rahmen der Personality Deines OS wirst Du sicher kaum irgendwelche Dinge portieren sondern die lieber selber und für Dein OS optimal passend implementieren, bei normalen User-Space-Programmen wirst Du sicher eher aufs Portieren zurückgreifen anstatt für alles mögliche das Rad neu zu erfinden. Deswegen bin ich ja der Meinung das man zumindest auf libc-Ebene eine gute Kompatibilität erreichen sollte.

Wenn Du Shared-Memory zwangsweise per IPC machst, wie möchtest Du dann mehr als 2 Prozesse teil nehmen lassen?
Ganz einfach, ich sende den Speicher an alle Prozesse die teilnehmen dürfen.
Und wenn die den Speicher gar nicht wollen? So könnte ja schnell ein DoS-Angriff auf den virtuellen Adressraum von anderen Prozessen möglich sein. Ich bin schon der Meinung das alle teilnehmenden Prozesse sich den Shared-Memory immer aktiv holen sollten.

Was zur Sicherheit noch. Geht es wenn Prozess B von Prozess A Speicher bekommen hat, den dann weiter an Prozess C zu geben ohne dass das Prozess A will?
Das hängt sehr von der zu Grunde liegenden Shared-Memory-Implementierung ab. Was ich da beschrieben hab ist ja nur ein Beispiel und Shared-Memory kann man sicher auf sehr viele verschiedene Arten implementieren. Ich persönlich hab noch nie etwas mit richtigem Shared-Memory programmiert und werde dafür wohl auch kaum gleich in meinen ersten Kernel alles nötige einbauen. Wenn ich irgendwann mal ein Programm portieren möchte das Shared-Memory benötigt kann ich immer noch schauen wie dieses Programm das erwartet und was es sonst noch so für Möglichkeiten gibt und mir dann ein passendes Syscall-API ausdenken das meinen persönlichen Wünschen entspricht und natürlich mit den Möglichkeiten meines Kernels und meiner Plattform harmoniert. Ich könnte mir z.B. vorstellen das ich dafür grundsätzlich ein eigenes Segment erstelle das sich dann auch vergrößern/verkleinern lässt und das für alle Teilnehmer synchron. Vielleicht entschließe ich mich auch dazu da eine etwas komplexere Rechteverwaltung ein zu bauen damit der Initiator des Shared-Memory genau bestimmen kann wer was darf. Aber egal wie das auch werden wird es wird mit Sicherheit völlig unabhängig vom IPC-System in den Kernel integriert (zumindest auf Syscall-Ebene, das ich intern im Kernel für das Mappen der Segmente sicher auf die gleichen Funktionen zurückgreife ist wohl logisch).

Bei mir sind die Prozesse halt so richtig von einander getrennt
Tja, das ist halt doof. So musst Du wohl immer gleich doppelt Kopieren wenn Du doch mal Kopieren musst.


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 #34 am: 27. January 2011, 19:47 »
Zitat von: erik
Bei einfachen Client-Programmen wird man sicher gerne auf Multithreading verzichten aber bei einem High-Performance-Service wird man eher alle Tricks aus dem Ärmel ziehen die nur irgendwie gehen.
Mein reden ;) Das ich den Service mit Hilfe von Multithreading schneller machen will sollte ja inzwischen klar sein, aber ich möchte halt die User-Programme nicht unnötig kompliziert machen (und des wegen das ganze aktive Abholen).

Zitat von: erik
Und wenn die den Speicher gar nicht wollen? So könnte ja schnell ein DoS-Angriff auf den virtuellen Adressraum von anderen Prozessen möglich sein.
Das ist eine gute Frage, aber genauso kann man einen DoS-Angriff per RPC auf einen Service durchführen!

Zitat von: erik
Tja, das ist halt doof. So musst Du wohl immer gleich doppelt Kopieren wenn Du doch mal Kopieren musst.
Naja, das perfekte Design wird es wohl nicht geben, es gibt immer etwas was man anders machen könnte, aber das wird dann auch wieder Nachteile haben.
Und bisher brauche ich es halt nicht wirklich.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #35 am: 27. January 2011, 20:23 »
Hallo,


aber ich möchte halt die User-Programme nicht unnötig kompliziert machen (und des wegen das ganze aktive Abholen).
Aber für RPC benötigt der Client doch gar keine Threads oder andere komplizierte Sachen, genauso wie der Sender bei asynchronen Messages, und abgeholt werden (egal ob aktiv oder per PopUp-Thread) muss da auch nichts. Erst wenn das Programm selber einen Port aufmacht um einen Service anzubieten oder um asynchrone Messages zu empfangen wird es komplizierter, aber das benötigen einfache Programme ja gar nicht.


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: 27. January 2011, 20:31 »
Es gibt durchaus einfache GUI-Programme und die sind nicht Multithreaded und es würde auch keinen Sinn machen sie Multithreaded zu machen. Trotzdem werden diese Programme einen Port haben weil sie Events empfangen wollen/müssen.

Deswegen meinte ich ja auch am Anfang das ich Port nicht mit Service gleichsetze auch ein normales Programm kann einen Port haben ohne das es einen Service anbietet.

Edit::

So langsam wird mir klar warum noch niemand so ein IPC System wie du es vorhast implementiert hat. Ich meine das mit den DoS Attacken ist nicht ohne und ein wirkliches Problem (zumindest auf Architekturen die keine Segmente habe). Umgehen kann man das nur in dem der Client eben nicht das Gefäß mitbringt (was ja das eigentliche Problem ist) oder man muss es so machen das der Popup-Thread einen Wert (nämlich die Größe des Gefäßes) mitbekommt und der Popzp-Thread dann entscheiden kann ob er die Nachricht annehmen will oder nicht.

Ein anderes Problem ist auch was passiert wenn das Gefäß zu groß ist und nicht mehr in den virtuellen Adressraum passt?
« Letzte Änderung: 27. January 2011, 21:08 von FlashBurn »

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #37 am: 27. January 2011, 21:15 »
Hallo,


Es gibt durchaus einfache GUI-Programme ....
Wenn diese Programme wirklich so einfach sind dann wird es wohl kaum auf Performance ankommen. Bastle doch eine Library die den Port und den PopUp-Thread intern managed und die Messages in eine User-Mode-Queue stellt (sowas hattest Du doch vor ein paar Posts schon mal beschrieben) und schon ist alles komplizierte wieder gut versteckt. Ob der Mode-Wechsel nun vor dem reinpacken in die Queue (mit PopUp-Thread) oder nach dem rausholen aus der Queue (mit Aktiv-Abhol-Syscall) steckt ist doch nun wirklich egal, letzteres erspart Dir zumindest die entsprechende Speicherverwaltung im Kernel.


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: 27. January 2011, 21:21 »
Zitat von: erik
Wenn diese Programme wirklich so einfach sind dann wird es wohl kaum auf Performance ankommen. Bastle doch eine Library die den Port und den PopUp-Thread intern managed und die Messages in eine User-Mode-Queue stellt (sowas hattest Du doch vor ein paar Posts schon mal beschrieben) und schon ist alles komplizierte wieder gut versteckt. Ob der Mode-Wechsel nun vor dem reinpacken in die Queue (mit PopUp-Thread) oder nach dem rausholen aus der Queue (mit Aktiv-Abhol-Syscall) steckt ist doch nun wirklich egal, letzteres erspart Dir zumindest die entsprechende Speicherverwaltung im Kernel.
Naja, die Performance ist da wirklich nicht so wichtig, aber die Library muss auch erstmal geschrieben werden ;) Das eigentliche Problem wird dann sein wie man die Library so hinbekommt das man einerseits solch einfachen Programme unterstützt andererseits aber auch Programme die kein Problem mit dem Multithreading haben und das ohne das der Programmierer alles selbst machen muss.

Wäre auch schön wenn du auf die Sache mit der DoS-Attacke eingehen würdest!

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #39 am: 28. January 2011, 07:41 »
Hallo,


Naja, die Performance ist da wirklich nicht so wichtig, aber die Library muss auch erstmal geschrieben werden ;)
Na also ne simple FIFO-Queue mit nem einfachen aber wirkungsvollen Lock sollte doch jemanden der ein OS baut nicht überfordern. ;)
Das kannst Du dann auch für andere Dinge außer nur GUI einsetzen wo es darauf ankommt externe Signale/Messages zu empfangen aber das Programm ein simples Singlethreading-Konzept einhalten soll und das dann lieber aktiv abholt. Ich denke das ist in wenigen Tagen komplett programmiert.

Das eigentliche Problem wird dann sein wie man die Library so hinbekommt das man einerseits solch einfachen Programme unterstützt andererseits aber auch Programme die kein Problem mit dem Multithreading haben und das ohne das der Programmierer alles selbst machen muss.
Also für den besseren/komplexeren Programme würde ich ne andere Library schreiben die eher den Fähigkeiten Deines GUI-Systems entspricht, ich denke diese 2 extremen Gegensätze sollte man nicht versuchen in eine einzige Library zu packen.

Wäre auch schön wenn du auf die Sache mit der DoS-Attacke eingehen würdest!
Also rein aus Speichersicht ist bei mir so ein DoS-Angriff auf den Service gar nicht direkt möglich da der Verbrauch der PopUp-Threads (also Stack- und TLS-Segment) ja logisch dem Client-Prozess zugeordnet wird und der Service ja nicht nur einen virtuellen Adressraum hat der voll werden könnte, nur das was der Code den der PopUp-Thread ausführt an Speicher anfordert (z.B. per malloc) muss sich der Service auch selber anschreiben lassen aber es ist ja auch sein Code der das verursacht. Ob das auch über mehrere Ebenen hinweg funktioniert muss ich dann mal sehen. Wie ich das konkret umsetze weiß ich allerdings noch überhaupt nicht, so weit bin ich auch noch gar nicht.
Bei der CPU-Belastung möchte ich das ähnlich machen. Wenn ich irgendwann einmal einen besseren Scheduler habe der Gruppen unterstützt dann sollen die PopUp-Threads immer den ursprünglichen Client-Prozess logisch zugeordnet werden so das sie z.B. der PopUp-Thread im SATA-Treiber seine CPU-Zeit immer noch von dem Prozess holen muss der ursprünglich das fread an den VFS-Service geschickt hat.
Mit dieser Vorgehensweise dürfte sich der böse Schadprozess nur selber schaden aber nicht das gesamte System komplett lahm legen können. Auf der anderen Seite muss man auch ganz ehrlich sagen das wenn ein Schadprozess erst mal richtig läuft das dann unter jedem normalen OS ein ernstes Problem darstellt. Die eigentliche Verteidigungslinie ist damit bereits durchbrochen und es geht hier nur noch darum das wenigstens root noch die Chance bekommt den Prozess zu killen ohne das ganze System herunterfahren zu müssen oder gar direkt den Stecker ziehen zu müssen nur weil er es nicht mal mehr bis an eine Konsole schafft. Wenn dieser Schadprozess aber auch noch an root-Rechte kommt und sich damit z.B. eine extrem hohe Priorität geben kann dann ist eh alles zu spät.


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

 

Einloggen