Autor Thema: IPC Methoden  (Gelesen 7798 mal)

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« am: 27. August 2010, 15:25 »
Ich starte dann mal eine Diskussion zum Thema der verschiedenen IPC Methoden und wie diese implementiert werden können.

Mir fallen als grobe Methoden erstmal synchrones und asynchrones Message-Handling, Pipes, Signal-Handler und Shared-Memory ein.

Fangen wir mit dem synchronen Message-Handling an.

Darunter ist zu verstehen, das der Sender solange blockiert bis der Empfänger bereit ist die Nachricht entgegen zu nehmen.

Vorteil ist, das man als Programmierer daran gewöhnt ist und es die meisten Aufgaben erleichtert. Auch könnte man so einige schöne Optimierungen (siehe z.B. L4-Mikrokernel) reinpacken.
Als weiterer Vorteil wird oft genannt das man die Nachrichten nicht hin und her kopieren muss, das stimmt so aber nicht. Denn man muss die Daten ja aus einem Thread in den nächsten kopieren.
Was aber wirklich als Vorteil genannt werden kann, ist dass die Nachrichten nicht extra im Kernel zwischen gespeichert werden müssen.

Als Nachteil sehe ich das damit nicht alles praktikabel gelöst werden kann. Man stelle sich z.B. einen Server vor der nur eine Statusnachricht an viele Empfänger verschicken möchte. Dort wäre es aus performance Gründen blöd, wenn jedes Mal gewartet wird bis der Empfänger bereit ist die Nachricht abzuholen auch kann das ein interaktives System vereiteln.

Als nächstes also das asynchrone Message-Handling.

Vorteil ist halt das man eine Nachricht senden kann und gleich weiter arbeiten kann, was halt (wie oben geschrieben) vorallem aus Server sicht und für Statusnachrichten gut ist.

Als Nachteil wird oft genannt das die Nachrichten im Kernel zwischengespeichert werden.

Ein Thema was für beide (synchron/asynchron) zutrifft, ist ob man sich für feste oder dynamisch Lange Nachrichten entscheidet.

Nimmt man Nachrichten mit einer fixen Länge, erleichtert es natürlich die Arbeit des Kernelprogrammierers. Allerdings muss man dann bei langen Nachrichten entweder eine andere Art der Übertragung nutzen oder mehrere Nachrichten (komplizierter) senden.

Bei Nachrichten mit dynamischer Länge sehe ich das Problem das man sich als Kernelprogrammierer ganz schön verrenken muss oder sich das Problem der Fragmentierung in den Kernel holt damit das klappt.

Zu Signal-Handlern kann ich gar nichts sagen, da ich sowas noch nie verwendet habe und was ich davon weiß auch nicht sonderlich begeistert bin ;)

Pipe´s kann man gut als Mittel verwendet wenn es darum geht Daten dynamischer Länger zu übermitteln, man das aber nicht über Nachrichten machen kann/will.
Wie man die Pipe´s implemenitert ob im Kernel- oder zum großteil im UserSpace ist eine Performance- und Sicherheitsfrage.

SharedMem ist sicherlich die schnellste Möglichkeit Daten aus einem Prozess in einen anderen zu bekommen. Hier ist halt das Problem das man sich das Kommunikationsmodell selbst einfallen lassen muss (als App-Programmieren) und nur hoffen kann das der andere sich daran hält.

Ich denke am einfachsten wäre es, wenn jemand einfach mal ein "Problem" in den Raum stellt. Daran kann man dann am besten diskutieren welche Variante die beste ist.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 27. August 2010, 22:24 »
Also was das Nachrichten senden/empfangen an sich betrifft, bin ich ein Anhänger von asynchron mit der Möglichkeit des Wartens auf eine Nachricht von einem bestimmten Sender. Das ganze noch gepaar mit Nachrichten fixer Länge.

Asynchron weil es halt gerade bei einem Mikrokernel viele Sachen der Server performancemäßig verbessert und die fixen Längen der Nachrichten, weil ich so schneller an Speicher, zur Zwischenspeicherung im Kernel, komme und mir eine immer zunehmendere Fragmentierung ersparen möchte.

Alles was dann nicht mehr praktikabel über Nachrichten gemacht werden kann, soll über meine UserSpace Pipe´s passieren (wie die genau arbeiten ändert sich gerade). Darunter würde z.B. eine Anforderung an den Storage-Server fallen, wo man den Dateinamen+Pfad übergibt.

Ich weiß allerdings noch nicht ob es besser ist, die Anforderung (sprich ob FILE_OPEN, FILE_CLOSE usw.) per Nachricht und nur der Dateiname+Pfad per Pipe gesendet werden soll oder ob ich alles per Pipe mache.
« Letzte Änderung: 28. August 2010, 14:11 von FlashBurn »

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 28. August 2010, 14:19 »
Ich habe gerade das Prinzip aus folgendem Artikel übernommen (pipe-light):
http://www.ddj.com/showArticle.jhtml?articleID=189401457

Erstmal der Code:
static const int cachelineInWords= 32 / sizeof(uint32t);

struct pipeLightKernel_t {
uint32t id;
uint32t numPages;
struct list_t *pages;
struct sem_t reader;
struct sem_t writer;
};

struct pipeLight_t {
uint32t id;
uint32t size;
uint32t * volatile data;
};

static uint32t pipeLightNextNull(uint32t *ptrData, uint32t max);

void pipeLightReaderWait(struct pipeLightKernel_t *pipe) {
semRelease(&pipe->writer);
semAcquire(&pipe->reader);
}

void pipeLightReaderFlush(struct pipeLightKernel_t *pipe) {

}

void pipeLightWriterWait(struct pipeLightKernel_t *pipe) {
semRelease(&pipe->reader);
semAcquire(&pipe->writer);
}

void pipeLightWriterFlush(struct pipeLightKernel_t *pipe) {

}

void pipeLightReader(struct pipeLight_t *pipe, void *buffer, uint32t count) {
uint32t i= 0, *ptrData= (uint32t *)buffer, pos= 0, nextNull= 0;

while((nextNull= pipe->data[i]) == 0)
pipeLightReaderWait(pipe->id);

i++; pos++;

while(true) {
if(!count)
break;

uint32t data;
//if there is no data, go into the kernel and wait for the writer to wake us up
while((data= pipe->data[i]) == 0)
pipeLightReaderWait(pipe->id);

if((i + 1) % cachelineInWords == 0) {
uint32t j;

for(j= 0; j < cachelineInWords; j++)
pipe->data[i - j]= 0;
}

if(pos == nextNull) {
nextNull= data;
data= 0;
}
if(count >= 4) {
*ptrData= data;
count-= 4;
ptrData++;
} else {
memcpy(ptrData,&data,count);
count= 0;
}

pos++;
i= (i + 1) % pipe->size;
}

if(i % cachelineInWords != 0) {
uint32t j;

for(j= 0; j < i % cachelineInWords; j++)
pipe->data[i - j]= 0;
}

pipeLightReaderFlush(pipe->id);
}

void pipeLightWriter(struct pipeLight_t *pipe, void *buffer, uint32t count) {
uint32t i= 0; *ptrData= (uint32t *)buffer, pos= 0, nextNull= 0;

while(true) {
if(!count)
break;

uint32t data= 0;
//if there is no free mem to write to, go into the kernel and wait for the reader to wake us up
while(pipe->data[i] != 0)
pipeLightWriterWait(pipe->id);

if(pos == nextNull) {
nextNull= pipeLightNextNull(ptrData++,(count / 4) + 1) + pos + 1;
data= nextNull
} else {
if(count >= 4)
data= *ptrData;
count-= 4;
ptrData++;
} else {
memcpy(&data,ptrData,count);
count= 0;
}
}

pipe->data[i]= data;

pos++;
i= (i + 1) % pipe->size;
}

pipeLightWriterFlush(pipe->id);
}

uint32t pipeLightNextNull(uint32t *ptrData, uint32t max) {
uint32t x;

for(x= 0; x < max; x++) {
if(ptrData[x] == 0)
break;
}

return x;
}

Mein Problem sind die beiden "Flush"-Funktionen. Genauer ist das Problem zu vermeiden, das der Reader in den Kernel geht um auf Daten zu warten, bevor er das aber fertigstellen kann, kommt der Scheduler dazwischen und der Writer kann sein Flush ausführen (Problem: der Reader hat sich noch nicht in die Semaphore gepackt).
Nächste Situation wäre, wenn der Writer einen Flush ausführt und im Kernel unterbrochen wird und der Reader alle Daten fertig gelesen hat und auch seinen Flush fertig ausführt und dann kommt der Writer zurück und will (eventuell) einen nicht vorhandenen Reader aufwecken.

Ich hoffe ihr versteht was ich meine und könnt mir helfen wie man das lösen könnte.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 28. August 2010, 16:26 »
Hallo,


Ich denke am einfachsten wäre es, wenn jemand einfach mal ein "Problem" in den Raum stellt. Daran kann man dann am besten diskutieren welche Variante die beste ist.
Als Testlauf würde ich vorschlagen das ein Programm eine Datei komplett in den Speicher lesen möchte, also OPEN, GET_SIZE, READ und CLOSE. Die Datei soll dabei 300MBytes groß sein. Schwierigkeiten sind dabei der unbekannt lange Dateiname (aus Sicht des VFS unbekannt) und die große Datenmenge beim Lesen. Das Programm macht intern (nach dem GET_SIZE) einfach ein malloc mit der Dateigröße und dann ein einzelnes fread um die gesamte Datei auf einen Schlag einzulesen. Beschrieben werden sollte wie das IPC-System diese Wünsche an das VFS übermittelt und wie die Funktionen (fopen/fstat/fread/fclose) in der libc ungefähr arbeiten (also die IPC benutzen), alles was unter dem VFS steckt interessiert hier mal nicht.
Wenn das Szenario Okay ist stelle ich gerne meine Lösung vor.

Mir fallen als grobe Methoden erstmal synchrones und asynchrones Message-Handling, Pipes, Signal-Handler und Shared-Memory ein.
Bezüglich Deiner Definition von synchronem Message-Handling fehlt dann noch (als Steigerung) richtiges Message-basiertes RPC, also wo der Client nicht nur wartet bis der Service seine Anfrage bearbeitet sondern solange wartet bis er auch seine Antwort vom Service bekommt. Als weiteres Kriterium kommt dann noch ob das übermitteln der Antwort wieder ein eigenständiger IPC-Vorgang ist oder ob das alles Teil eines komplexeren RPC-Vorgangs ist.

Als weiterer Vorteil wird oft genannt das man die Nachrichten nicht hin und her kopieren muss, das stimmt so aber nicht. Denn man muss die Daten ja aus einem Thread in den nächsten kopieren.
Wieso sollte das nicht ohne kopieren gehen? Ich hab doch nebenan genau erläutert wie es auch komplett ohne kopieren geht.

Ein Thema was für beide (synchron/asynchron) zutrifft, ist ob man sich für feste oder dynamisch Lange Nachrichten entscheidet.
Aber das ist ein lösbares Problem.

Zu Signal-Handlern kann ich gar nichts sagen, da ich sowas noch nie verwendet habe und was ich davon weiß auch nicht sonderlich begeistert bin ;)
Ich persönlich bin auch der Meinung dass das klassische Signal-Handler-Konzept, von Unix, für richtiges IPC eher ungeeignet ist. Wie das ausgeht sieht man ja bei tyndur. Sorry an die tyndur-Macher aber von dieser IPC-Methode ist IMHO wirklich nicht viel zu halten.

Pipe´s kann man gut als Mittel verwendet wenn es darum geht Daten dynamischer Länger zu übermitteln
Aber bei Byte-orientierten Pipes muss man gut aufpassen das Sender und Empfänger nie uneins werden wo Nachrichten anfangen und aufhören, man benötigt also ein gutes (komplexes) Kommunikations-Protokoll.


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 #4 am: 28. August 2010, 16:56 »
Zitat von: erik
Bezüglich Deiner Definition von synchronem Message-Handling fehlt dann noch (als Steigerung) richtiges Message-basiertes RPC, also wo der Client nicht nur wartet bis der Service seine Anfrage bearbeitet sondern solange wartet bis er auch seine Antwort vom Service bekommt.
Ähm, das habe ich bei mir mit dem Syscall "send+recv" und nem Flag das man auf die Antwort des Empfängers warten will.

Zitat von: erik
Wieso sollte das nicht ohne kopieren gehen? Ich hab doch nebenan genau erläutert wie es auch komplett ohne kopieren geht.
Um das Thema auch für weniger wissende interessant zu machen, würde ich sagen das wir deine Hardware-Architektur mal außen vor lassen ;) Die Mehrheit der heute vorhandenen Architekturen kennt keine richtigen Segmente mehr.
Und wenn du keine Segmente hast, dann musst du ja in irgendeiner Form kopieren. Einzige Möglichkeit ohne kopieren auszukommen, wäre auf einer Architektur ohne MMU (auch das dürfte für uns nicht so interessant sein).

Zitat von: erik
Aber bei Byte-orientierten Pipes muss man gut aufpassen das Sender und Empfänger nie uneins werden wo Nachrichten anfangen und aufhören, man benötigt also ein gutes (komplexes) Kommunikations-Protokoll.
Bitte genauer erklären was du meinst, da ich dir nicht genau folgen kann, am besten an einem Bsp.

Zu dem Bsp. möchte ich mal noch meinen, das es ziemlich schlechter Programmierstil wäre eine 300MB Datei per fread in einem Ritt zu laden. Zumal du dir ja ein schönes Bsp ausgesucht hast, was deine Architektur ja bevorzugt (da es keinen Unterschied für dich macht ob du 3MB oder 300MB per Segment einblendest) ;)

Also ich sage jetzt mal das ich das alles über Pipe´s löse (da bin ich mir aber noch nicht 100% sicher). Bei der Initialisierung der libc (oder dem 1. benutzen von Dateifunktionen) macht man sich mit dem Storage-Server 2 Pipe´s aus, eine zum Senden und eine zum Empfangen.

Dann "stellst" du eine Nachricht zusammen, die halt aus dem MsgCode für OPEN besteht und dem String für den Dateinamen.
Als Rückgabewerte bekommst du dann den ErrorCode (0 falls alles in Ordnung war) sowie den Dateihandle (ne einfache Nummer),
Für GET_SIZE dann wieder das gleiche Spiel, MsgCode halt für GET_SIZE und noch den Dateihandle.
Als Rückgabewerte halt wieder ErrorCode und die Size.
READ wird dann schon interessanter. Man stellt halt wieder eine Nachricht zusammen, MsgCode von READ und dann noch den Dateihandle und halt wieviel man von wo an lesen möchte. (An dieser stelle möchte ich mal anmerken, das es sinnvoller wäre von fread hier zu entscheiden, die Datei irgendwie per SharedMem zu lesen, bzw. einzublenden)
Die Daten kommen dann halt per Pipe und müssen in den von malloc allokierten Bereich kopiert werden. Wenn alles optimal läuft (auf einem SMP System) dann kann das sogar halbwegs gleichzeitit ablaufen (sprich der Storage-Server schreibt in die Pipe und auf der anderen CPU kann fread gleichzeitig aus der Pipe lesen).

Da ich aber ja gerade versuche ne neue Pipe (siehe früherer Post) zu implementieren, würde ich die "kleinen" Sachen (wie MsgCode, Dateihandle, Errorcode) per Msg verschicken und nur sowas wie Dateinamen und die Daten die zu lesen sind per Pipe.
« Letzte Änderung: 28. August 2010, 17:11 von FlashBurn »

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 28. August 2010, 17:00 »
Als Testlauf würde ich vorschlagen das ein Programm eine Datei komplett in den Speicher lesen möchte, also OPEN, GET_SIZE, READ und CLOSE. Die Datei soll dabei 300MBytes groß sein. Schwierigkeiten sind dabei der unbekannt lange Dateiname (aus Sicht des VFS unbekannt) und die große Datenmenge beim Lesen. Das Programm macht intern (nach dem GET_SIZE) einfach ein malloc mit der Dateigröße und dann ein einzelnes fread um die gesamte Datei auf einen Schlag einzulesen. Beschrieben werden sollte wie das IPC-System diese Wünsche an das VFS übermittelt und wie die Funktionen (fopen/fstat/fread/fclose) in der libc ungefähr arbeiten (also die IPC benutzen), alles was unter dem VFS steckt interessiert hier mal nicht.
Wenn das Szenario Okay ist stelle ich gerne meine Lösung vor.
Klingt okay, außer dass ich den ganzen Stack anschauen würde. Das heißt, die Kommunikation mit dem Dateisystem und dem Blockgerät auch noch mit reinnehmen. Dann hat man nämlich auch noch eine Situation drin, in der ein Treiber Daten weiterreichen muss (bzw. umgangen werden muss, wenn man das nicht will).

Etwas üblicher als der 300-MB-Request wären viele kleine Requests - und gleichzeitig sind die kleinen Requests auch gemeiner, weil sie den Kommunikationsoverhead deutlicher sichtbar machen. Aber vom Prinzip her ist es natürlich dasselbe, insofern keine Einwände gegen das Szenario an dieser Stelle.

Zitat
Ich persönlich bin auch der Meinung dass das klassische Signal-Handler-Konzept, von Unix, für richtiges IPC eher ungeeignet ist. Wie das ausgeht sieht man ja bei tyndur.
Man erhält unter den Betriebssystemen der Lowlevel-Community das fortgeschrittenste? ;)

tyndur ist jetzt nicht unbedingt instabiler als andere Hobbysysteme, die ich so gesehen habe. Das Konzept hat seine Probleme, aber ich hätte tyndur jetzt eher nicht als Beispiel zitiert, dass es in die Katastrophe führen muss.
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 #6 am: 28. August 2010, 21:35 »
Hallo,


außer dass ich den ganzen Stack anschauen würde. Das heißt, die Kommunikation mit dem Dateisystem und dem Blockgerät auch noch mit reinnehmen. Dann hat man nämlich auch noch eine Situation drin, in der ein Treiber Daten weiterreichen muss (bzw. umgangen werden muss, wenn man das nicht will).
Das mit dem weiterreichen der Daten ist in einem Micro-Kernel-OS natürlich ein gewichtiges Argument, weil es wohl nur wenige Dinge gibt die sich mit nur einer einzigen Ebene erledigen lassen, das würde ich in das Szenario gerne mit aufnehmen. Man könnte noch einen Datei-System-Treiber und einen SATA/IDE-Treiber dazu nehmen, das wäre ein gutes Szenario. Caching sollten wir aber der Einfachheit halber erstmal außen vor lassen.

Etwas üblicher als der 300-MB-Request wären viele kleine Requests - und gleichzeitig sind die kleinen Requests auch gemeiner, weil sie den Kommunikationsoverhead deutlicher sichtbar machen. Aber vom Prinzip her ist es natürlich dasselbe, insofern keine Einwände gegen das Szenario an dieser Stelle.
Die Anzahl der Aktionen ist mir eigentlich egal, ich denke man sieht auch bei einem einzelnen Vorgang wo z.B. mehr Kontext-Wechsel nötig sind, wenn das bei einem Konzept aus irgendeinem Grund nicht linear nach oben skaliert dann muss das natürlich extra erklärt werden. Die große Datenmenge hab ich absichtlich gewählt damit man zeigen muss wie gut die Daten von einem Prozess zum nächsten kommen (hier wäre auch wieder das Durchreichen über mehrere Ebenen interessant). Ich denke mal 300MBytes sind noch nicht so viel das meine Segmente da den extremen Vorteil bringen (wir können aber auch gerne auf 128MBytes runter gehen), mit 4MBytes-Pages kann man das noch mit vertretbaren Aufwand von einem Flat-Memory-Prozess in den nächsten mappen, günstiges Alignment können wir mal annehmen auch wenn man drauf hinweisen sollte das ungünstiges Alignment Performance kostet.

Man erhält unter den Betriebssystemen der Lowlevel-Community das fortgeschrittenste? ;)
Hä, was meinst Du den mit diesem Satz?

tyndur ist jetzt nicht unbedingt instabiler als andere Hobbysysteme, die ich so gesehen habe. Das Konzept hat seine Probleme, aber ich hätte tyndur jetzt eher nicht als Beispiel zitiert, dass es in die Katastrophe führen muss.
Ich wollte in keinster Weise tyndur als instabil oder gar untauglich abstempeln sondern nur sagen das tyndur ein ungünstiges (aber trotzdem funktionierendes) IPC-Konzept verwendet.


Zitat von: erik
Bezüglich Deiner Definition von synchronem Message-Handling fehlt dann noch (als Steigerung) richtiges Message-basiertes RPC, also wo der Client nicht nur wartet bis der Service seine Anfrage bearbeitet sondern solange wartet bis er auch seine Antwort vom Service bekommt.
Ähm, das habe ich bei mir mit dem Syscall "send+recv" und nem Flag das man auf die Antwort des Empfängers warten will.
Das ist ein wichtiges Detail das durchaus mit erwähnt werden muss.

Und wenn du keine Segmente hast, dann musst du ja in irgendeiner Form kopieren.
Nein, man kann auch in MMU-basierten Falt-Memory-Systemen einfach mal eine größere Menge an Daten von einem Prozess in einen anderen mappen. Man sucht sich dazu einfach in dem Ziel-Prozess einen passenden freien Block im virtuellen Adressraum und mappt dort die selben Pages des physischen Speichers rein wie in dem Quell-Prozess. Wenn man eine größere Menge an Pages verarbeiten muss ist das zwar langsamer als mit meinen Segmenten aber trotzdem nicht unmöglich, und so groß dürfte der Performance-Vorteil meiner Segmente nun auch wieder nicht sein, schon weil ich dafür einen komplexeren Speicher-Manager brauche (ich gehe davon aus das ich bei sehr kleinen Datenmengen, z.B. unter 4kBytes, immer kopieren werde weil das Kopieren dann doch schneller ist als das erstellen eines erbenden Segments).

Zitat von: erik
Aber bei Byte-orientierten Pipes muss man gut aufpassen das Sender und Empfänger nie uneins werden wo Nachrichten anfangen und aufhören, man benötigt also ein gutes (komplexes) Kommunikations-Protokoll.
Bitte genauer erklären was du meinst, da ich dir nicht genau folgen kann, am besten an einem Bsp.
Naja, man muss eben auf beiden Seiten aufpassen das man keine Message fehlerhaft dekodiert ansonsten verliert man die Synchronisation. Wenn man da z.B. ein fehlertolerantes Protokoll benutzen möchte bringt das eben wieder etwas zusätzliche Komplexität ins Spiel.

Zu dem Bsp. möchte ich mal noch meinen, das es ziemlich schlechter Programmierstil wäre eine 300MB Datei per fread in einem Ritt zu laden.
Wieso, was ist daran schlechter Programmierstiel dem OS klar zu sagen das ich viele Daten lesen will anstatt dem OS etliche male zu sagen (lügen) das ich nur wenige Daten lesen will? Mal vom zusätzlichen Kommunikationsoverhead vieler Anfragen abgesehen.

Zumal du dir ja ein schönes Bsp ausgesucht hast, was deine Architektur ja bevorzugt (da es keinen Unterschied für dich macht ob du 3MB oder 300MB per Segment einblendest) ;)
Gönne mir doch den kleinen Vorsprung. ;)

(da bin ich mir aber noch nicht 100% sicher)
Du darfst auch gerne mehrere Szenarien vorstellen, vielleicht können wir Dir helfen die jeweiligen Vorteile und Nachteile besser gegenüber zu stellen.

Bei der Initialisierung der libc (oder dem 1. benutzen von Dateifunktionen) macht man sich mit dem Storage-Server 2 Pipe´s aus, eine zum Senden und eine zum Empfangen.
Benötigt man dieses Pipe-Paar ein mal pro Prozess oder ein mal pro Thread? Kann man über ein Pipe-Paar verschiedene Dinge parallel/verschachtelt erledigen?

Dann "stellst" du eine Nachricht zusammen, die halt aus dem MsgCode für OPEN besteht und dem String für den Dateinamen. ...
Okay, hab ich verstanden. Das bedeutet das Du in jedem Prozess je einen Thread benötigs um die Kommunikation aufrecht zu erhalten auch wenn mal gerade nichts kommuniziert wird. Wenn sehr viele Programme auf Deinem OS laufen die alle mal auf Dateien zugegriffen haben würde das auch bedeuten das in Deinem "Storage-Server" eine ziemlich große Anzahl an Threads nutzlos schläft und Speicher verbraucht.

An dieser stelle möchte ich mal anmerken, das es sinnvoller wäre von fread hier zu entscheiden, die Datei irgendwie per SharedMem zu lesen, bzw. einzublenden
Wenn Du Dir davon ein Vorteil versprichst dann nur zu. Erkläre dann eben wie Du Dir einen Lesevorgang für eine ordentlich große Datenmenge vorstellst so das die Daten in dem an fread übergebenen Buffer ankommen. Wir könnten jetzt auch ein anderes Szenario nehmen wo es um Memory-Mapped-Datei-Zugriff geht aber das hätte ja nicht mehr viel mit IPC zu tun, schließlich ist IPC unser aktuelles Thema.

Wenn alles optimal läuft (auf einem SMP System) dann kann das sogar halbwegs gleichzeitit ablaufen (sprich der Storage-Server schreibt in die Pipe und auf der anderen CPU kann fread gleichzeitig aus der Pipe lesen).
Das heißt Du möchtest für einen Vorgang gleich 2 CPUs belegen?


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 #7 am: 28. August 2010, 22:33 »
Zitat von: erik
Nein, man kann auch in MMU-basierten Falt-Memory-Systemen einfach mal eine größere Menge an Daten von einem Prozess in einen anderen mappen. Man sucht sich dazu einfach in dem Ziel-Prozess einen passenden freien Block im virtuellen Adressraum und mappt dort die selben Pages des physischen Speichers rein wie in dem Quell-Prozess.
Wo wir schon bei einem interessantem Punkt wären. Wenn man Nachrichten mit dynamischer Länge hat, könnte man sagen, das man ab 4kb nicht mehr die Daten kopiert, sondern Pages einblendet.
Die Frage wäre jetzt nur was passiert mit den Pages beim Sender? Ich meine normalerweise werden die Daten ja kopiert oder besser gesagt kannst du als Sender davon ausgehen, wenn du zurück vom Syscall kommst, dass du mit den Daten machen kannst was du willst. Wenn man aber die Pages in einem anderen Prozess einblendet, dann kannst du das nicht mehr einfach so machen.
Eine Lösung wäre COW (copy-on-write).

Zitat von: erik
Naja, man muss eben auf beiden Seiten aufpassen das man keine Message fehlerhaft dekodiert ansonsten verliert man die Synchronisation. Wenn man da z.B. ein fehlertolerantes Protokoll benutzen möchte bringt das eben wieder etwas zusätzliche Komplexität ins Spiel.
Die würde ich halt in den Bibliotheken verstecken.

Zitat von: erik
Wieso, was ist daran schlechter Programmierstiel dem OS klar zu sagen das ich viele Daten lesen will anstatt dem OS etliche male zu sagen (lügen) das ich nur wenige Daten lesen will? Mal vom zusätzlichen Kommunikationsoverhead vieler Anfragen abgesehen.
Ich würde sowas nicht per fread machen sondern die Datei direkt in meinen Prozess mappen lassen (was wohl daran liegt, dass das wohl wesentlich effizienter und schneller sein sollte, auf allen Betriebssystemen).

Wie wäre es denn wenn mal jemand sagen würde wie das unter Windows oder Linux (oder sonst einem OS) gemacht wird?

Zitat von: erik
Benötigt man dieses Pipe-Paar ein mal pro Prozess oder ein mal pro Thread? Kann man über ein Pipe-Paar verschiedene Dinge parallel/verschachtelt erledigen?
Ich bin im Moment für pro Thread, weil du meine Pipe´s nicht wie klassische Pipe´s benutzen kannst. Betrachte sie eher wie eine definierte Kommunikation über SharedMem.

Was meinst du mit dem parallel/verschachtelt erledigen? Also wie gesagt, meine Pipe ist keine Pipe im klassischen Sinne.

Zitat von: erik
Okay, hab ich verstanden. Das bedeutet das Du in jedem Prozess je einen Thread benötigs um die Kommunikation aufrecht zu erhalten auch wenn mal gerade nichts kommuniziert wird. Wenn sehr viele Programme auf Deinem OS laufen die alle mal auf Dateien zugegriffen haben würde das auch bedeuten das in Deinem "Storage-Server" eine ziemlich große Anzahl an Threads nutzlos schläft und Speicher verbraucht.
It´s not a bug, it´s a feature ;)

Ich bin mir gerade nicht sicher was du damit meinst das ich in jedem (User-) Prozess einen Thread brauche um die Kommunikation aufrecht zu erhalten.
Was die Threads in den Servern betrifft, ja.

Aber wenn viele Programme laufen können, die viele Dateien öffnen, dann hat der PC auch genug Power und RAM um das bewältigen zu können.

Zitat von: erik
Das heißt Du möchtest für einen Vorgang gleich 2 CPUs belegen?
So wie du das "sagst" klingt das so negativ ;) Ich will darauf hinaus, das die Daten, in meinem Fall, ja in eine Pipe kopiert werden und daraus auch wieder rauskopiert werden müssen. Am optimalsten wäre es halt wenn das ganze Gleichzeitig stattfinde würde. Dann müssten auch keine Kontextwechsel stattfinden, sondern der Storage-Server kann immer schön in die Pipe kopieren und der Client kann zur gleichen Zeit schön aus der Pipe kopieren, so werden die Daten ohne Kontextwechsel in den Client kopiert.

Ein weiterer Vorteil ist, das meine Methode auch bei knappen virtuellem Speicher funktionieren.

Ich wäre dann auch mal an einer Lösung interessiert wie man die Daten aus dem Server in den Clienten (über IPC) bekommt bzw. wie du/andere es halt besser machen würden.

Anders gefragt, wie machen es denn bekannte/weniger bekannte Mikrokernel?

Edit::

Was mir gerade so durch den Kopf geht. Wenn ich es richtig verstanden habe, dann soll ja beim synchronen Msg-Handling an sich nichts kopiert werden (oder besser im Kernel zwischengespeichert werden), aber wo sind die Daten denn? Wenn sie auf dem Stack sind, auf welchem User oder Kernel? Falls auf dem Userstack, wie bekommt ihr sie da von einem Prozess zum nächsten? Denn normalerweise kann man den Userspace von einem anderen Prozess ja nicht "sehen".
« Letzte Änderung: 28. August 2010, 22:44 von FlashBurn »

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #8 am: 29. August 2010, 09:29 »
Auf Grund von erik´s Fragen, habe ich das mit der Pipe nochmal durchdacht und habe den Code von oben wieder verworfen (zumindest größtenteils). Was ich übernommen habe, ist das man immer in "Wörtern" (auf einer 32bit CPU also 4byte) in die Pipe schreibt und auch aus ihr liest.

Das Problem mit dem Code war, das es nicht möglich war eine Nachricht mit mehreren Aufrufen an "pipeLightRead" zu holen, das habe ich mit meinem aktuellem Code gelöst.

Wäre toll wenn ihr den mal kommentieren könnte, sprich ob das ne gute Idee oder eher ne schlechte ist!

#define PIPELIGHT_STATUS_READER_WAITING 0x1
#define PIPELIGHT_STATUS_WRITER_WAITING 0x2

struct pipeLightKernel_t {
uint32t numPages;
struct list_t *pages;
struct sem_t reader;
struct sem_t writer;
};

struct pipeLight_t {
uint32t size;
uint32t volatile numWords2Read;
uint32t volatile flags;
uint32t volatile ptrRead;
uint32t volatile ptrWrite;
uint32t data[];
}

void pipeLightReaderWait(struct pipeLightKernel_t *pipe, struct pipeLight_t *pipeUser) {
if(pipeUser->numWords2Read == 0) {
if(pipe->writer.count < 0)
semRelease(&pipe->writer);

pipe->flags|= PIPELIGHT_STATUS_READER_WAITING;

semAcquire(&pipe->reader);

pipe->flags&= ~PIPELIGHT_STATUS_READER_WAITING;
}
}

void pipeLightReaderFlush(struct pipe_t *pipe) {
if(pipe->flags & PIPELIGHT_STATUS_WRITER_WAITING)
pipeLightWriterWakeup(pipe);
}

void pipeLightReaderWakeup(struct pipeLightKernel_t *pipe) {
semRelease(&pipe->reader);
}

void pipeLightWriterWait(struct pipeLightKernel_t *pipe, struct pipeLight_t *pipeUser) {
if(pipeUser->numWords2Read == pipeUser->size) {
if(pipe->reader.count < 0)
semRelease(&pipe->reader);

pipe->flags|= PIPELIGHT_STATUS_WRITER_WAITING;

semAcquire(&pipe->writer);

pipe->flags&= PIPELIGHT_STATUS_WRITER_WAITING;
}
}

void pipeLightWriterFlush(struct pipeLightKernel_t *pipe) {
if(pipe->flags & PIPELIGHT_STATUS_READER_WAITING)
pipeLightReaderWakeup(pipe);
}

void pipeLightWriterWakeup(struct pipeLightKernel_t *pipe) {
semRelease(&pipe->writer);
}

void pipeLightReader(struct pipeLight_t *pipe, uint32t *buffer, uint32t count) {
while(true) {
if(!count)
break;

//if there is no data, go into the kernel and wait for the writer to wake us up
while(pipe->numWords2Read == 0)
pipeLightReaderWait(pipe);

*buffer= pipe->data[ptrRead];

atomicSub(&pipe->numWords2Read,1);
buffer++;
count--;

if(++ptrRead == pipe->size)
ptrRead= 0;
}

pipeLightReaderFlush(pipe);
}

void pipeLightWriter(struct pipeLight_t *pipe, uint32t *buffer, uint32t count) {
while(true) {
if(!count)
break;

//if there is no free mem to write to, go into the kernel and wait for the reader to wake us up
while(pipe->numWords2Read == pipe->size)
pipeLightWriterWait(pipe->data);

pipe->data[ptrWrite]= *buffer;

atomicAdd(&pipe->numWords2Read,1);
buffer++;
count--;

if(++ptrWrite == pipe->size)
ptrWrite= 0;
}

pipeLightWriterFlush(pipe->data);
}

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #9 am: 29. August 2010, 09:57 »
Hallo,


Wo wir schon bei einem interessantem Punkt wären. Wenn man Nachrichten mit dynamischer Länge hat, könnte man sagen, das man ab 4kb nicht mehr die Daten kopiert, sondern Pages einblendet.
Ganz genau.

Die Frage wäre jetzt nur was passiert mit den Pages beim Sender?
Der Client würde blockieren, Du hast doch extra das send+recv dafür. Wenn Du von asynchronen IPC schreibst dann wäre kopieren vielleicht grundsätzlich die bessere Variante, den bei rein asynchronem IPC werden ja wohl eher kleine Infos übermittelt (Prozess X ist gekillt worden, Datei Y hat sich verändert oder IRQ Z ist aufgetreten).

Ich würde sowas nicht per fread machen sondern die Datei direkt in meinen Prozess mappen lassen (was wohl daran liegt, dass das wohl wesentlich effizienter und schneller sein sollte, auf allen Betriebssystemen).
Ich bezweifle dass das per Memory-Mapped-File-I/O schneller geht. Nehmen wir mal an wir haben ein Programm das die Datei komplett einlesen und verarbeiten will, als simples Beispiel nehmen wir mal md5sum. Wenn man das per Mapping (in einem Flat-Memory-System mit MMU) macht dann gibt es nur für jede Page eine Excpetion (der Exception-Handler holt dann das entsprechende Stück der Datei in den Speicher), weil das Programm ja linear von vorne nach hinten durch geht. Die ganzen Exceptions erzeugen ein Haufen überflüssigen Overhead. Wenn die Datei von dem Programm immer wieder oder nur teilweise verarbeitet/verändert wird (z.B. bei einer Datenbank) dann ist Mapping klar im Vorteil. Aber wir wollen ja in diesem Thread über IPC diskutieren, für Memory-Mapped-File-I/O darfst Du gerne einen neuen Thread aufmachen aber da dürften nur wenige mit diskutieren denn in welchem Hobby-OS steckt schon Memory-Mapped-File-I/O.

Ich bin mir gerade nicht sicher was du damit meinst das ich in jedem (User-) Prozess einen Thread brauche um die Kommunikation aufrecht zu erhalten.
Ich meinte, wenn auf dem Computer 50 Programme laufen und jedes mal kurz auf eine Datei zugegriffen hat (eine Config-Datei oder was ähnliches) dann laufen (oder eher schlafen) in Deinem Storage-Server 50 Threads und verbrauchen Speicher. Ich finde das irgendwie ungeschickt.

Aber wenn viele Programme laufen können, die viele Dateien öffnen, dann hat der PC auch genug Power und RAM um das bewältigen zu können.
Oder der PC würde es eigentlich gerade so schaffen wird aber mit Deinem Konzept dann doch in die Knie gezwungen (swapping usw.).

Zitat von: erik
Das heißt Du möchtest für einen Vorgang gleich 2 CPUs belegen?
So wie du das "sagst" klingt das so negativ ;)
Das sollte auch negativ klingen, Du versuchst das überflüssige und teure Kopierern billiger zu machen in dem Du 2 CPUs gleichzeitig beschäftigst. Der richtige Weg wäre IMHO das Kopieren möglichst ganz zu vermeiden.


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 #10 am: 29. August 2010, 11:30 »
Der Client würde blockieren, Du hast doch extra das send+recv dafür. Wenn Du von asynchronen IPC schreibst dann wäre kopieren vielleicht grundsätzlich die bessere Variante, den bei rein asynchronem IPC werden ja wohl eher kleine Infos übermittelt (Prozess X ist gekillt worden, Datei Y hat sich verändert oder IRQ Z ist aufgetreten).
Was ist mit asynchronem I/O? Also im Sinn von "schreib mal diese 300 MB (;)) raus und gib mir dann einen Callback, aber ich mach solang schonmal was anderes"?

Zitat
Das sollte auch negativ klingen, Du versuchst das überflüssige und teure Kopierern billiger zu machen in dem Du 2 CPUs gleichzeitig beschäftigst. Der richtige Weg wäre IMHO das Kopieren möglichst ganz zu vermeiden.
Solange das Szenario fread ist, musst du mindestens einmal kopieren, um das nötige Alignment zu bekommen, damit du einfach Pages mappen kannst. Wenn ich es richtig verstanden habe, haben deine Segmente auch gewisse Alignmentvorgaben, insofern hast du dann genau dasselbe Problem.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #11 am: 29. August 2010, 11:45 »
Zitat von: taljeth
Solange das Szenario fread ist, musst du mindestens einmal kopieren, um das nötige Alignment zu bekommen, damit du einfach Pages mappen kannst. Wenn ich es richtig verstanden habe, haben deine Segmente auch gewisse Alignmentvorgaben, insofern hast du dann genau dasselbe Problem.
Wenn ich dich jetzt richtig verstanden habe, dann würdest du halt aus der 1. Page alles in eine neue Page kopieren und diese dann anstatt der originalen mappen und das selbe eventuell mit der letzten Page nochmal?

Da wären wir dann wieder da, das ich sage, nur fixed size Msgs und alles andere über meine Pipe´s bzw. wenn es z.B. mehr als 4MB sind, dann macht man das per SharedMemory.

Ich würde dann sozusagen die Komplexität an die User Programme (bzw. die Bibliotheken) weiterreichen.

Was ich in dem Sinne auch noch nicht habe, was passiert eigentlich wenn der empfangende Prozess nicht genug freien virtuellen Speicher hat um die Nachricht zu mappen?

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #12 am: 29. August 2010, 11:54 »
Wenn ich dich jetzt richtig verstanden habe, dann würdest du halt aus der 1. Page alles in eine neue Page kopieren und diese dann anstatt der originalen mappen und das selbe eventuell mit der letzten Page nochmal?
Vermutlich schon, ja. Aber wenn wir mal kurz von den 300 MB Abstand nehmen, die sicher nicht der Durchschnittsfall sind, dann machen die erste und die letzte Page wahrscheinlich häufig den Großteil oder sogar den ganzen Request aus.

Zitat
Was ich in dem Sinne auch noch nicht habe, was passiert eigentlich wenn der empfangende Prozess nicht genug freien virtuellen Speicher hat um die Nachricht zu mappen?
Dann gibt es halt einen Fehler zurück. Dasselbe wie wenn der Empfänger weggesegfaultet ist oder anderweitig nicht in der Lage ist, Nachrichten zu verarbeiten.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #13 am: 29. August 2010, 12:08 »
Zitat von: taljeth
Dann gibt es halt einen Fehler zurück. Dasselbe wie wenn der Empfänger weggesegfaultet ist oder anderweitig nicht in der Lage ist, Nachrichten zu verarbeiten.
Das war mir klar, aber was passiert mit der Nachricht, bleibt die in der Queue und muss durch einen extra Syscall gelöscht werden oder passiert das gleich?

Auch wäre dann interessant woher man weiß das der Sender auf eine Antwort wartet, denn man muss ihm ja bescheid geben, das der Empfänger die Nachricht nicht entgegen nehmen konnte (da ist dann synchron wieder im Vorteil). Bei mir ist das wieder durch ein Flag gekennzeichnet, das der Sender auf eine Antwort wartet.

Zitat von: taljeth
Vermutlich schon, ja. Aber wenn wir mal kurz von den 300 MB Abstand nehmen, die sicher nicht der Durchschnittsfall sind, dann machen die erste und die letzte Page wahrscheinlich häufig den Großteil oder sogar den ganzen Request aus.
Da wäre dann das optimalste, wenn du alles in eine oder zwei neue Pages kopierst und diese dann mappst. Dann ersparst du dir zumindest den 2. Kopiervorgang.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #14 am: 30. August 2010, 12:19 »
Hallo,


Was ist mit asynchronem I/O? Also im Sinn von "schreib mal diese 300 MB (;)) raus und gib mir dann einen Callback, aber ich mach solang schonmal was anderes"?
Also bei asynchronem IPC hab ich mir das so vorgestellt das der Service die geerbten Speicher-Bereiche so lange behalten darf bis der PopUp-Thread sich beendet oder diese Speicher-Bereiche explizit freigibt. Das bedeutet natürlich das der Client so lange nichts in diesem Speicher verändern darf aber das ist bei asynchronem File-I/O ja so üblich. Bei meinem Konzept müsste der VFS-Server dafür ein zweites Message-Target erstellen (also einen zweiten Port öffnen) um darüber dann asynchrones IPC empfangen zu können.

Solange das Szenario fread ist, musst du mindestens einmal kopieren, um das nötige Alignment zu bekommen, damit du einfach Pages mappen kannst. Wenn ich es richtig verstanden habe, haben deine Segmente auch gewisse Alignmentvorgaben, insofern hast du dann genau dasselbe Problem.
Naja, ich erkaufe mit das nicht Kopieren müssen damit das ich das Alginment nicht ganz einhalte. Ich denke maximal 15 Bytes vorne und hinten sind verschmerzbar, seht Ihr das anders? Wenn ja, warum?


Wenn ich dich jetzt richtig verstanden habe, dann würdest du halt aus der 1. Page alles in eine neue Page kopieren und diese dann anstatt der originalen mappen und das selbe eventuell mit der letzten Page nochmal?
Bei sehr großen Datenmengen dürfte sich das auf jeden Fall lohnen. Bei sehr kleinen Datenmengen ist kopieren wohl eh die effizientere Wahl.

Da wären wir dann wieder da, das ich sage, nur fixed size Msgs und alles andere über meine Pipe´s bzw. wenn es z.B. mehr als 4MB sind, dann macht man das per SharedMemory.
Ich finde "fixed size Msgs" klingt nach wenigen kBytes, von da bis zu den 4MBytes ist ein weites Feld, was machst Du mit Datensätzen in diesem Größen-Bereich? Das Shared-Memory muss aber auch wieder erst initiiert werden und damit bekommst Du dann für eine einzelne Funktionalität (in diesem Fall fread) mehrere verschiedene Wege und damit mehr Komplexität (im fread genauso wie in Deinem Storage-Server).
Eine meiner Hauptmotivationen für mein IPC-Konzept ist das ich nur einen einzigen Mechanismus haben will der für alle Größen optimal funktioniert. Das ich im Kernel kopiere, bei sehr kleinen Datensätzen, macht weder für den Client noch für den Service einen Unterschied, die merken das gar nicht und haben immer das selbe Interface zur Verfügung.

Ich würde dann sozusagen die Komplexität an die User Programme (bzw. die Bibliotheken) weiterreichen.
Womit diese Komplexität trotzdem nicht weg ist und eben immer noch CPU-Takte kostet (und in jedem Prozess etwas Code-Speicher belegt).

Was ich in dem Sinne auch noch nicht habe, was passiert eigentlich wenn der empfangende Prozess nicht genug freien virtuellen Speicher hat um die Nachricht zu mappen?
Das ist in der Tat ein Problem, aber das gibt es auch noch an anderen Stellen in Flat-Memory-Systemen. Mit meinen Segmenten hab ich dieses Problem glücklicherweise nicht, also hab ich noch nie über entsprechende Lösungen nachgedacht.


Aber wenn wir mal kurz von den 300 MB Abstand nehmen, die sicher nicht der Durchschnittsfall sind
Ich wollte eben einfach einen IPC-Fall der mal eine größere Menge an Daten überträgt, den Fall mit den sehr wenigen Daten stellt ja schon fstat dar (und fopen für die unbekannte Message-Größe beim Dateinamen+Path). Wenn Ihr einen anderen Wert für die Größe haben wollt dann gerne aber es wäre schön wenn wir uns dann mal einigen könnten damit wir ein einheitliches Szenario haben an dem wir über IPC diskutieren können.


Grüße
Erik
« Letzte Änderung: 30. August 2010, 12:39 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 #15 am: 30. August 2010, 13:03 »
Zitat von: erik
Ich finde "fixed size Msgs" klingt nach wenigen kBytes, von da bis zu den 4MBytes ist ein weites Feld, was machst Du mit Datensätzen in diesem Größen-Bereich? Das Shared-Memory muss aber auch wieder erst initiiert werden und damit bekommst Du dann für eine einzelne Funktionalität (in diesem Fall fread) mehrere verschiedene Wege und damit mehr Komplexität (im fread genauso wie in Deinem Storage-Server).
Da ich mein Msg-System noch nicht wirklich nutzen muss, bin ich mir mit der Größe noch nicht sicher, aber im Moment sind es gerade mal 32bytes ;)
Ich bin wie gesagt, sowieso eher ein Fan von SharedMem (was auch meine Pipe´s mit einschließt) und die Kommunikation welcher SharedMem wofür genutzt wird, das soll dann über Msgs laufen und alles andere halt über SharedMem (Pipe´s).
Ich denke ab 4kb kann man durchaus SharedMem nutzen, bzw sollte es auch. Sicher für einmalige Sachen ist das zwar scheiße, aber bevor ich das ganze in den Kernel kopiere und von dort aus dann wieder zum Clienten kopiere, finde ich SharedMem schneller (was ich aber nicht bzw. noch nicht beweisen kann) und angenehmer.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #16 am: 31. August 2010, 20:34 »
Hallo,


Da ich mein Msg-System noch nicht wirklich nutzen muss, bin ich mir mit der Größe noch nicht sicher, aber im Moment sind es gerade mal 32bytes
Das ist, äh, ziemlich spartanisch. Damit kann man ja nichts weiter machen als Kommandos zu erteilen.

Sicher für einmalige Sachen ist das zwar scheiße, aber bevor ich das ganze in den Kernel kopiere und von dort aus dann wieder zum Clienten kopiere
Du versuchst immer das Kopieren beim Kernel zu vermeiden aber das Kopieren im User-Space scheint Dich nicht zu kümmern. So weit mir bekannt ist ist das Kopieren im User-Space nicht schneller also wo ist das Problem?

finde ich SharedMem schneller (was ich aber nicht bzw. noch nicht beweisen kann) und angenehmer.
Klar kann Shared-Memory ziemlich schnell sein aber nur wenn man den geschickt benutzt und das sehe ich bei Deinem Konzept noch nicht (sicher ändert sich das noch).

Falls wir uns auf ein Beispiel-Szenario einigen konnten wäre es nicht schlecht wenn Du Dein Konzept daran mal Schritt für Schritt ganz detailliert erklärst, sicher können wir das dann auch besser einschätzen. Ich persönlich habe wohl noch nicht alles da dran verstanden.


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 #17 am: 31. August 2010, 20:52 »
Zitat von: erik
Das ist, äh, ziemlich spartanisch. Damit kann man ja nichts weiter machen als Kommandos zu erteilen.
Naja, die Planung sieht auch so aus, das es für viel mehr nicht genutzt werden soll, aber wie groß würdest du ihn denn machen? Aber bitte davon ausgehen, das ich fixed-size benutze und er nicht zu groß werden soll.

Zitat von: erik
Du versuchst immer das Kopieren beim Kernel zu vermeiden aber das Kopieren im User-Space scheint Dich nicht zu kümmern. So weit mir bekannt ist ist das Kopieren im User-Space nicht schneller also wo ist das Problem?
1. ist der KernelSpace begrenzt (was aber nicht wirklich ein Problem sein sollte) und 2. ist im schlimmsten Fall das Kopieren im UserSpace genauso langsam wie das Kopieren im KernelSpace.
Worauf ich hinaus möchte, im schlimmsten Fall bringt mir mein System gar nichts, aber im besten Fall hoffe ich (und viel mehr ist es leider im Moment auch nicht ;) ) das es auf SMP System schneller ist als das Kopieren im KernelSpace (da es gleichzeitig stattfinden könnte).
Genauso versuche ich so viele Kernelaufrufe wie möglich zu vermeiden und da fällt mir dann doch ein dass das Kopieren im Kernel teurer ist als im UserSpace.
Denn im Kernel muss ich immer vorher gucken, ob der User überhaupt die Zugriffsrechte hat und ob der Speicher gemappt ist, aus dem kopiert werden soll. Wozu? Damit ich ja keine Exception im Kernel bekomme. Im UserSpace ist mir das egal, dann wird das Programm halt beendet.

Zitat von: erik
Falls wir uns auf ein Beispiel-Szenario einigen konnten wäre es nicht schlecht wenn Du Dein Konzept daran mal Schritt für Schritt ganz detailliert erklärst, sicher können wir das dann auch besser einschätzen. Ich persönlich habe wohl noch nicht alles da dran verstanden.
Bleiben wir doch bei der 300MB Datei. Das ich immer nur eine 4kb Page mappe, habe ich schon verworfen, da würden die ständigen Requests an den Service mir ne neue Page zu geben, die Performance auffressen.

Also ich sage dem VFS-Service (über ne Pipe) das ich eine Datei öffnen will und gebe noch den Pfad+Namen an. Hat das geklappt sage ich das ich halt die ganzen 300MB lesen will.
Der VFS-Service weist also den Treiber an die Datei zu laden (das VFS muss dazu im schlimmsten Fall sehr viele Anfragen machen, weil die Datei nicht zusammenhängend ist). Der Treiber macht das halt und erstellt nen SharedMem Bereich, setzt den VFS-Service auf die "allow"-Liste und gibt ihm die ID des SharedMem zurück. Dann "unmapped" er den SharedMem wieder und für ihn ist die Sache dann gegessen.
Der VFS-Service wiederrum setzt dann den Clienten auf die "allow"-Liste und gibt ihm die ID des SharedMem. Der Client kann den Speicher dann mappen (vorrausgesetzt er hat genug virtuellen Speicher frei) und dann werden die Daten in den Buffer des Clienten kopiert (ich kann und will dieses Kopieren nicht vermeiden).

Als ein potentielles Problem sehe ich, dass der Client den SharedMem nicht wieder freigibt. Denn so könnte es passieren, das der VFS-Service den Speicher gerne freigeben würde (ich meine wir reden hier von 300MB), aber das geht nicht (das weiß der Service aber nicht, für ihn funktioniert das ganze) da er ja noch beim Clienten gemappt ist.

Was ich gerade beim Schreiben schon optimiert habe, der VFS-Service muss die Datei noch nicht mal in seinen eigenen virtuellen Speicher mappen, es reicht ja vollkommen aus das er die ID des SharedMem speichert.

Falls du grobe Fehler siehst, dann immer raus damit!

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #18 am: 01. September 2010, 17:40 »
Hallo,


aber wie groß würdest du ihn denn machen? Aber bitte davon ausgehen, das ich fixed-size benutze und er nicht zu groß werden soll.
Sorry, da fragst Du den falschen. Ich persönlich bin der Meinung das es keine optimale Message-Größe gibt, es wird immer Dinge geben wo die Message nicht voll ausgelastet ist (so das Dein Kernel unnützes Zeug kopiert) genauso wie es immer Dinge geben wird wo die Message nicht ausreicht (so das irgendein komplexer Mechanismus her muss um das zu handeln). Aus meiner persönlichen Sicht sind nur flexible Message-Größen das einzig selig machende.

Worauf ich hinaus möchte, im schlimmsten Fall bringt mir mein System gar nichts, aber im besten Fall hoffe ich (und viel mehr ist es leider im Moment auch nicht ;) ) das es auf SMP System schneller ist als das Kopieren im KernelSpace (da es gleichzeitig stattfinden könnte).
Aus meiner Sicht würde ich das anders formulieren: Im Average-Case ist Dein System X-fach langsamer als andere weil Du die Daten (mehrfach sogar, falls ich Dich richtig verstanden habe) kopierst und im Best-Case kommst Du immer noch nicht ganz an die Konkurrenz ran (selbst die nicht Zero-Copy-Konkurrenz dürfte kaum einholbar sein).

Genauso versuche ich so viele Kernelaufrufe wie möglich zu vermeiden
Aha, deswegen ein Haufen extra Syscalls für das Memory-Sharing usw?

und da fällt mir dann doch ein dass das Kopieren im Kernel teurer ist als im UserSpace.
Denn im Kernel muss ich immer vorher gucken, ob der User überhaupt die Zugriffsrechte hat und ob der Speicher gemappt ist, aus dem kopiert werden soll. Wozu? Damit ich ja keine Exception im Kernel bekomme. Im UserSpace ist mir das egal, dann wird das Programm halt beendet.
Okay, das ist ein gutes Argument nicht im Kernel zu kopieren. Auch mein Kernel soll den Speicher der Applikationen nur verwalten aber niemals hineinschauen, Exceptions im System-Mode wären auf meiner CPU tödlich.

Bleiben wir doch bei der 300MB Datei.
Gut. :)

Der VFS-Service weist also den Treiber an die Datei zu laden (das VFS muss dazu im schlimmsten Fall sehr viele Anfragen machen, weil die Datei nicht zusammenhängend ist).
Was ist bei Dir der "Treiber"? Der Dateisystem-Treiber (z.B. für ext2) oder der Block-Device-Treiber (z.B. für SATA)? Falls ersteres, warum sollte der VFS die Anfragen zerstückeln? Der weiß doch gar nicht das die Datei fragmentiert auf der Platte liegt (der VFS hat noch nicht mal ne Ahnung das man Dateien fragmentieren müsste/könnte). Falls letzteres, wo wird das Dateisystem verwaltet? Doch hoffentlich nicht im VFS, dann könnte man nicht mal einfach so ein NFS o.ä. einbinden.

Der Treiber macht das halt und erstellt nen SharedMem Bereich, setzt den VFS-Service auf die "allow"-Liste und gibt ihm die ID des SharedMem zurück.
Okay, ich denke das hab ich verstanden, aber das erscheint mir irgendwie unnötig umständlich.

Dann "unmapped" er den SharedMem wieder und für ihn ist die Sache dann gegessen.
Wer ist "er" und "ihn"? Falls beides der Block-Device-Treiber ist, warum wird der Speicher nicht erst mal vom Dateisystem-Treiber analysiert/ausgelesen?

Der VFS-Service wiederrum setzt dann den Clienten auf die "allow"-Liste und gibt ihm die ID des SharedMem. Der Client kann den Speicher dann mappen (vorrausgesetzt er hat genug virtuellen Speicher frei) und dann werden die Daten in den Buffer des Clienten kopiert
Ich denke das hab ich ebenfalls verstanden, auch wenn ich das immer noch ziemlich umständlich finde.

(ich kann und will dieses Kopieren nicht vermeiden).
Hier kommen wir meiner Meinung nach zum Problem in Deinem Konzept. Warum "willst" Du nicht auf das kopieren verzichten?

Als ein potentielles Problem sehe ich, dass der Client den SharedMem nicht wieder freigibt. Denn so könnte es passieren, das der VFS-Service den Speicher gerne freigeben würde (ich meine wir reden hier von 300MB), aber das geht nicht (das weiß der Service aber nicht, für ihn funktioniert das ganze) da er ja noch beim Clienten gemappt ist.
Es müsste eben ganz klar geregelt sein wann welcher Speicher wem gehört bzw. wer Zugriff drauf hat. In meinem Konzept ist das ganz klar geregelt, der Speicher gehört immer dem Client (außer wenn kopiert wurde natürlich) und der Service kann auf diesen Speicher genau so lange zugreifen wie der IPC-Vorgang dauert also der PopUp-Thread läuft (nur so lange sind die Segmente, die dem Service gegeben wurden, vorhanden). In dieser Zeit kann der Service diesen Speicher oder auch nur Teile davon seinerseits wieder an andere Services weiterreichen (ein Dateisystem-Treiber könnte z.B. einen Teil an den Block-Device-Treiber geben damit dieser dort ein Fragment einer Datei hinein lädt und einen anderen Teil mit einem weiteren IPC-Vorgang um ein weiteres Fragment der Datei zu laden).

Falls du grobe Fehler siehst, dann immer raus damit!
Außer das Du den Speicher vom Service an den Client weiter gibst und auf das Kopieren bestehst sehe ich erst einmal keine grundsätzlichen Fehler, alle anderen Schwächen resultieren aus den zwei genannten.
Sorry, für dieses vernichtende Urteil aber dieses Konzept wirkt auf mich irgendwie extrem umständlich und von der verkehrten Seite angegangen. Falls Du der Meinung bist das ich Dein Konzept missverstanden habe so versuche es mir Bitte anders zu erklären.


Grüße
Erik
« Letzte Änderung: 01. September 2010, 17:42 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 #19 am: 01. September 2010, 18:12 »
Zitat von: erik
Aus meiner Sicht würde ich das anders formulieren: Im Average-Case ist Dein System X-fach langsamer als andere weil Du die Daten (mehrfach sogar, falls ich Dich richtig verstanden habe) kopierst und im Best-Case kommst Du immer noch nicht ganz an die Konkurrenz ran (selbst die nicht Zero-Copy-Konkurrenz dürfte kaum einholbar sein).
Wie kommst du darauf das mein System X-fach langsamer sei? Ich meine wir gehen einfach mal davon aus, dass alle Daten in die Pipe passen ohne das der Writer warten muss, das die Daten wieder daraus gelesen werden.

Im normal Fall läuft es doch so ab, du allokierst einen Speicherbereich für deinen Buffer für eine Nachricht, kopierst die erforderlichen Sachen in den Buffer rein und machst einen Syscall das du eine Nachricht versenden willst. Die Nachricht wird dann in einen Buffer im Kernel kopiert (dieser muss auch vorher allokiert werden, was bei dynamic-size Msgs unter Umständen ne "Weile" dauern kann).
Wenn der Empfänger dann die Nachricht abholt gibt es 2 Möglichkeiten, entweder sein Empfangsbuffer ist für die Nachricht groß genug oder er muss erstmal einen Syscall machen um zu wissen wie groß die Nachricht ist. Dann allokiert er einen Buffer und ruft nen Syscall auf, das er die Nachricht haben will. Die Nachricht wird dann aus dem Kernelbuffer in den Buffer des Clienten kopiert.

So ungefähr sollte das auf allen Systemen aussehen die asynchrones IPC zur Verfügung stellen. Die Nachricht muss in einem Buffer im Kernel zwischengespeichert werden und den Speicher, wo der Buffer drin liegt, kannst du dem Sender auch nicht einfach wegnehmen. Genauso ist es normal das du mit dem Speicher wo du die Nachricht hast reinkopieren lassen, machen kannst was du willst.

Ich bin mir ziemlich sicher dass das IPC von Haiku so aussieht, wie die aber das mit fread und solchen Konsorten gelöst haben, weiß ich nicht, müsste ich mal nachgucken. Die nutzen dynamic-size Msgs.

Ich weiß das man beim synchronen IPC versucht das Kopieren zu vermeiden, aber wie will man das mit sehr großen Nachrichten anstellen? Weil die Nachricht müsste ja in den Kernelstack des Senders passen, damit du ihn einfach vom Kernelstack in den Userstack des Empfängers kopieren kannst.

Zitat von: erik
Aha, deswegen ein Haufen extra Syscalls für das Memory-Sharing usw?
Ich verstehe nicht was du mit den extra Syscalls meinst. Weil SharedMem habe ich sowieso (wie jedes andere OS auch haben sollte) und das nutze ich halt dafür.

Zitat von: erik
Was ist bei Dir der "Treiber"? Der Dateisystem-Treiber (z.B. für ext2) oder der Block-Device-Treiber (z.B. für SATA)? Falls ersteres, warum sollte der VFS die Anfragen zerstückeln? Der weiß doch gar nicht das die Datei fragmentiert auf der Platte liegt (der VFS hat noch nicht mal ne Ahnung das man Dateien fragmentieren müsste/könnte). Falls letzteres, wo wird das Dateisystem verwaltet? Doch hoffentlich nicht im VFS, dann könnte man nicht mal einfach so ein NFS o.ä. einbinden.
Also ich gehe im Moment schon davon aus, dass das (z.B.) ext2 Modul dafür verantworlich ist, die einzelnen Blöcke (LBAs) von der Platte (oder woher auch immer) zu laden.

Ich stelle mir das so vor, das du als Modul (z.B. ext2) dem VFS verschiedene Funktionen zur Verfügung stellst (z.B. open, close, read, write, ...) und dich (damit meine ich das Modul) sehr wohl darum kümmerst welche LBAs zu der Datei gehören.

NFS ist ein Netzwerkfilesystem, oder? Sehe ich jetzt spontan kein Problem, anstatt nen Blockdevice Treiber nach den Daten zu fragen, fragt er (NFS-Modul) einfach den Netzwerk-Treiber nach den Daten.

Es kann sein das ich mir das jetzt zu einfach mache, aber das liegt daran, dass ich bei dem Thema null-Ahnung habe.

Zitat von: erik
Okay, ich denke das hab ich verstanden, aber das erscheint mir irgendwie unnötig umständlich.
Aus meiner Sicht ist das schneller/besser als wenn ich sage, das er die Daten per Msg verschickt.

Zitat von: erik
Wer ist "er" und "ihn"? Falls beides der Block-Device-Treiber ist, warum wird der Speicher nicht erst mal vom Dateisystem-Treiber analysiert/ausgelesen?
Der Blockdevice-Treiber. Was meinst du mit deiner letzten Frage?

Zitat von: erik
Ich denke das hab ich ebenfalls verstanden, auch wenn ich das immer noch ziemlich umständlich finde.
Wieso? kommt dem Zero-copy ziemlich nahe, bzw. ist es sogar. Das dürfte so ähnlich wie dein Konzept aussehen.

Zitat von: erik
Hier kommen wir meiner Meinung nach zum Problem in Deinem Konzept. Warum "willst" Du nicht auf das kopieren verzichten?
Problem ist (in meinem Kopf) das Paging, ich will nicht ohne das es der Prozess weiß, in den Pagingtabellen rumfummeln. Denn das müsste ich machen damit ich anstatt seines (der Empfänger/Client) Buffers, meinen eigenen nehmen kann.

Man könnte jetzt streng genommen sogar sagen, dass fread daran Schuld ist. Denn fread sagt das du den Buffer übergibst wo die Daten rein sollen und nicht das du nen Buffer zurück bekommst wo die Daten drin sind.

Zitat von: erik
Sorry, für dieses vernichtende Urteil aber dieses Konzept wirkt auf mich irgendwie extrem umständlich und von der verkehrten Seite angegangen.
Du hast mich erst überzeugt das mein Konzept richtiger Mist ist, wenn ich ein besser (eventuell mit deiner Hilft ;) ) habe. Aber es sollte auch vernünftig für Paging umzusetzen sein.

 

Einloggen