Autor Thema: Funktionsaufruf Kernel -> Modul  (Gelesen 22813 mal)

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #20 am: 22. August 2010, 14:42 »
Zitat von: taljeth
Wenn du weißt, dass du irgendwo Codeduplikation hast, solltest du dich ranmachen, sie zu eliminieren. Wenn sich nur ein paar Konstanten unterscheiden, gib die Konstanten als Parameter rein und mach die zwei unterschiedlichen Varianten als Wrapper. Wenn du dich um die Performance sorgst, weis den Compiler an, dass er auf jeden Fall inlint.
Das ist leider nicht so einfach :( (wenn ich dir jetzt sage warum, dann werde ich hier geköpft ;) )

Zitat von: erik
Das ist wirklich sehr ungeschickt. Eine der wichtigsten Regeln bei der SW-Entwicklung lautet "Nichts, aber auch wirklich gar nichts, darf es doppelt geben!". Jag den einen Quell-Code 2 mal durch den Compiler, jeweils mit einem unterschiedlichen Define per -D oder nutze sed (das mach ich bei meinem Assembler, ich will ja nicht für ADD, SUB, SUBR, AND, NAND, OR, NOR und XOR alles mehrfach tippen).
So einfach ist das wiederrum nicht. Es ist ungefähr so wie du das mit deinem kmalloc machen willst. Der Code ist in meinem VMM und ich nutze für den Kernel und den UserSpace unterschiedliche Datenstrukturen und die müssen (leider), zwecks Huhn-Ei-Problem, mit extra Code behandelt werden und dort sind halt die Funktionsalgorithmen (mehr oder weniger) gleich, aber es gibt halt Unterschiede die extra behandelt werden müssen.

Die einzige Möglichkeit wäre die For-Schleife in eine Funktion auszulagern, aber ganz ehrlich, ne For-Schleife in eine extra Funktion, das finde ich dann schon overkill.

Zitat von: erik
Auf meiner CPU ist Kernel-Code immer ununterbrechbar (meine CPU ist entweder im User-Mode und von dort kann per Syscall, Exception oder Interrupt in den System-Mode gewechselt werden oder sie ist bereits im System-Mode und dann geht nichts davon, nur per RFS (Return-From-Systemmode) zurück in den User-Mode, wenn im System-Mode doch mal ne Exception auftreten sollte dann geht die CPU in den Double-Error-Panic-Shutdown und braucht externe Hilfe von einer anderen CPU).
Das finde ich wiederum nicht so toll. Dann sollte dein Kernel wirklich sehr sehr mikro sein ;)

Zitat von: erik
Der Vorteil von Signal-Handler ist das sie relativ einfach zu implementieren sind und man keine Threads benötigt (Linux und viele andere Unixe auch können Threads noch gar nicht so lange). Die klassischen Unix-Signal-Handler sind nützlich wenn ein Programm z.B. noch was machen will wenn der User CTRL-C drückt aber als richtiges IPC sind sie denkbar ungeeignet.
Aber man muss auch aufpassen was man in so einem Handler alles macht, da es bestimmt nicht gut ist, wenn gerade malloc läuft und von einem Signal-Handler unterbrochen wird, der dann wiederum malloc aufruft.

Was mich noch interessieren würde, kann man die irgendwie durch Messages emulieren (zwecks POSIX-Kompatibilität)?

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #21 am: 22. August 2010, 15:40 »
Das ist leider nicht so einfach :( (wenn ich dir jetzt sage warum, dann werde ich hier geköpft ;) )
Das müssen wir dann leider in Kauf nehmen. ;)

Zitat
Was mich noch interessieren würde, kann man die [Signale] irgendwie durch Messages emulieren (zwecks POSIX-Kompatibilität)?
Nicht wirklich, Signale müssen eigentlich sofort ankommen (am offensichtlichsten dürfte es bei SIGSEGV sein).
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #22 am: 22. August 2010, 17:53 »
Hallo,


Signale müssen eigentlich sofort ankommen (am offensichtlichsten dürfte es bei SIGSEGV sein).
Warum sofort? Wenn der Thread welcher SIGSEGV ausgelöst hat erstmal geblockt ist (so das der Scheduler ihn nicht aktivieren möchte) dann besteht doch keine so große Eile. Selbst andere Threads in diesem Prozess sollten durchaus normal weiterarbeiten können solange sie nicht den selben Fehltritt ausführen.


So einfach ist das wiederrum nicht. Es ist ungefähr so wie du das mit deinem kmalloc machen willst. Der Code ist in meinem VMM und ich nutze für den Kernel und den UserSpace unterschiedliche Datenstrukturen und die müssen (leider), zwecks Huhn-Ei-Problem, mit extra Code behandelt werden und dort sind halt die Funktionsalgorithmen (mehr oder weniger) gleich, aber es gibt halt Unterschiede die extra behandelt werden müssen.
Scheint ein SW-Design-Problem zu sein. Klingt vielleicht ziemlich arrogant aber wenn Du ein OS programmieren möchtest solltest Du fähig sein soetwas sauber zu lösen. Du muss auch über den Fall nachdenken wenn der User-Code absichtlich etwas falsch macht. Bringt das Deinen Kernel in Gefahr?

Die einzige Möglichkeit wäre die For-Schleife in eine Funktion auszulagern, aber ganz ehrlich, ne For-Schleife in eine extra Funktion, das finde ich dann schon overkill.
Pack diese Funktion, mit der For-Schleife, in 2 verschiedene H-Dateien und includiere jeweils die richtige, wenn die Funktion static ist und nur ein einziges mal benutzt wird wird der Compiler wohl eh inlinen. Wenn Du lieber 2 fertige leicht unterschiedliche C-Dateien haben möchtest dann generiere sie Dir doch per sed, das kann ich wirklich nur wärmstens empfehlen. Mit ein wenig Magie im Make-File geht das sogar automagisch wenn sich die eine Vorlage oder die sed-Steuerungsdateien geändert haben. Ich habe sed schon oft für derartige Probleme benutzt und war damit immer sehr zufrieden.

Zitat von: erik
Auf meiner CPU ist Kernel-Code immer ununterbrechbar (meine CPU ist entweder im User-Mode und von dort kann per Syscall, Exception oder Interrupt in den System-Mode gewechselt werden oder sie ist bereits im System-Mode und dann geht nichts davon, nur per RFS (Return-From-Systemmode) zurück in den User-Mode, wenn im System-Mode doch mal ne Exception auftreten sollte dann geht die CPU in den Double-Error-Panic-Shutdown und braucht externe Hilfe von einer anderen CPU).
Das finde ich wiederum nicht so toll. Dann sollte dein Kernel wirklich sehr sehr mikro sein ;)
Warum? Was genau ist daran nicht so toll wenn ein Micro-Kernel nicht unterbrechbar ist? Ich empfinde das als Vorteil, das schließt viele Fehlerquellen von vornherein zuverlässig aus. Ein reinrassiger Micro-Kernel hat mit den eigentlichen User-Space-Daten nichts zu tun er muss sie nur verwalten. Und wenn er doch irgendwo rein sehen will muss er vorher prüfen ob das gefahrlos möglich ist, auf x86 gibt es doch auch VERR und VERW um zu prüfen wo man hingreifen darf und wo nicht, das wird es bei mir in ähnlicher Form auch geben.


Aber man muss auch aufpassen was man in so einem Handler alles macht, da es bestimmt nicht gut ist, wenn gerade malloc läuft und von einem Signal-Handler unterbrochen wird, der dann wiederum malloc aufruft.
Oh ja! Das ist es ja was taljeth mir hier im Forum (http://forum.lowlevel.eu/index.php?topic=2321.msg26297#msg26297, ließ Dir am besten den ganzen Thread durch) vor ner Weile "vorgejammert" hat. Für Signale wie SIGSEGV ist das Konzept ganz brauchbar aber für richtiges IPC oder gar synchrones RPC (was wohl die überwiegende Mehrheit aller IPC-Nutzungen darstellt) ist es extrem ungeeignet (hier http://forum.lowlevel.eu/index.php?topic=2466 wurden mal die Nachteile verbildlicht). In tyndur kann sogar während eines laufenden Signal-Handlers ein neuer aufgerufen werden, das ist dann ein LIFO-Prinzip das geradezu danach schreit für DoS-Attaken benutzt zu werden.

Was mich noch interessieren würde, kann man die irgendwie durch Messages emulieren (zwecks POSIX-Kompatibilität)?
Ich habe vor das in meiner libc zu emulieren. Es soll bei mir einen allgemeinen prozess-globalen asynchronen Signal-Handler geben, der mit der Prozess-ID als Target angesprochen werden kann und der nur dazu dient allgemeine Informationen vom Kernel und ähnliches entgegen zu nehmen, welcher dann in der libc den registrierten Handler aufruft. Falls der betreffende Thread nicht eh schon deaktiviert ist, wie es bei SIGSEGV sein sollte, dann wird eben einer pausiert und nachdem die eigentliche User-Signal-handler-Funktion zurückkommt wieder reaktiviert. Wenn es keinen "schuldigen" Thread gibt, wie z.B. wenn der User CTRL-C drückt, dann wird eben der Haupt-Thread (mit welchen main aufgerufen wurde) vorübergehend abgeklemmt (falls das überhaupt nötig ist). Ob das so auch wirklich immer funktioniert weiß ich nicht genau, ich hab da erst gerade eben kurz drüber nachgedacht, aber für die meisten Anwendungsfälle klassischer Unix-Signale (so häufig werden die wohl nicht benutzt) sollte es schon reichen.


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

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #23 am: 22. August 2010, 18:19 »
Signale müssen eigentlich sofort ankommen (am offensichtlichsten dürfte es bei SIGSEGV sein).
Warum sofort? Wenn der Thread welcher SIGSEGV ausgelöst hat erstmal geblockt ist (so das der Scheduler ihn nicht aktivieren möchte) dann besteht doch keine so große Eile. Selbst andere Threads in diesem Prozess sollten durchaus normal weiterarbeiten können solange sie nicht den selben Fehltritt ausführen.
Ich weiß, dass du ganz gern den kompliziertest möglichen Fall betrachtet, aber nehmen wir doch zur Abwechslung einfach mal einen ganz einfachen Prozess, der keine weiteren Threads hat. Und "sofort" ist natürlich aus Prozesssicht gemeint, nicht zeitbezogen. Was du in der Tat machen kannst, ist den Thread anzuhalten, einen neuen Thread für den Signalhandler anzulegen und dort weiterzumachen. Aber damit hast du keinen Vorteil gewonnen und machst nur eventuelles Exceptionhandling (z.B. per longjmp) kaputt.

Aber das war eigentlich auch nicht der Hintergrund meiner Antwort, sondern die Frage, ob man das mit Message Passing emulieren kann. Und Message Passing erfordert es, dass irgendjemand die Nachricht abholt. Dazu ist es unter Umständden vorteilhaft, wenn derjenige noch nicht abgestürzt ist. ;)
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #24 am: 22. August 2010, 18:25 »
Zitat von: erik
Scheint ein SW-Design-Problem zu sein. Klingt vielleicht ziemlich arrogant aber wenn Du ein OS programmieren möchtest solltest Du fähig sein soetwas sauber zu lösen. Du muss auch über den Fall nachdenken wenn der User-Code absichtlich etwas falsch macht. Bringt das Deinen Kernel in Gefahr?

Pack diese Funktion, mit der For-Schleife, in 2 verschiedene H-Dateien und includiere jeweils die richtige, wenn die Funktion static ist und nur ein einziges mal benutzt wird wird der Compiler wohl eh inlinen. Wenn Du lieber 2 fertige leicht unterschiedliche C-Dateien haben möchtest dann generiere sie Dir doch per sed, das kann ich wirklich nur wärmstens empfehlen. Mit ein wenig Magie im Make-File geht das sogar automagisch wenn sich die eine Vorlage oder die sed-Steuerungsdateien geändert haben. Ich habe sed schon oft für derartige Probleme benutzt und war damit immer sehr zufrieden.
Du hast nicht richtig verstanden wie ich das meinte.

Also stell dir ne Funktion vor die in einer Bitmap ne "freie" Position sucht und dann etwas in einem Array macht.

Das ist soweit der gemeinsame Nenner der Funktionen. Das Problem ist nun das die Funktionen auf verschiedene Strukturen (wo die Bitmaps/Arrays unterschiedlich groß sind) zugreifen. Du kannst den Code 1:1 kopieren, aber es muss der Pointer-Type, sowie die Adresse worauf der Pointer zeigt, geändert werden und die Länge der For-Schleife.

Wie ich die Länge der For-Schleife per Parameter übergebe ist mir noch klar, aber wie willst du das mit den unterschiedlichen Strukturen lösen?

Der Vorteil, wenn ich für jede Struktur ne eigene Funktion nutze, ist halt das er anstatt ein Register für die Überprüfung, ob das Schleifenende erreicht ist, eine Konstante nutzt. Dass Selbe gilt für die Adresse der Struktur.

Bsp:
int getMeSomething() {
 struct kernelFoo_t *ptr= KERNEL_FOO_ADDR;

 for(int x=0; x < KERNEL_FOO_SLOTS; x++) {
  //finde ne freie Position in der Bitmap ptr->bitmap
 }
 int y= _bsr(ptr->bitmap[x]);
 y+= x*32; //weil er bei jedem Durchlauf der Schleife immer gleich 32bit der Bitmap überprüft
 
 return ptr->array[y];
}
Das wäre jetzt ne ziemlich vereinfachte Variante, aber ich hoffe es kommt rüber was ich meine. Das Problem ist nun, das man zwar "x" als Parameter übergeben kann, nicht aber "struct kernelFoo_t *ptr".

Was das Köpfen betrifft, ich springe, unter einer bestimmten Bedingung, aus der For-Schleife mit "goto" an eine Stelle ziemlich am Ende der Funktion (liest sich zwar scheiße, aber fand ich besser als noch ne Variable zu nutzen, nur um zu gucken ob er nen If-Zweig abarbeiten soll).

Zitat von: erik
Warum? Was genau ist daran nicht so toll wenn ein Micro-Kernel nicht unterbrechbar ist? Ich empfinde das als Vorteil, das schließt viele Fehlerquellen von vornherein zuverlässig aus. Ein reinrassiger Micro-Kernel hat mit den eigentlichen User-Space-Daten nichts zu tun er muss sie nur verwalten. Und wenn er doch irgendwo rein sehen will muss er vorher prüfen ob das gefahrlos möglich ist, auf x86 gibt es doch auch VERR und VERW um zu prüfen wo man hingreifen darf und wo nicht, das wird es bei mir in ähnlicher Form auch geben.
Naja, für mich heißt nicht unterbrechbar, das die Interupts aus sind und damit sollte alles was im Kernel gemacht wird, verdammt schnell ablaufen! D.h. auch das ein busy-waiting eigentlich unmöglich wird. Denn wenn die Ints aus sind und du busy-waiting machst, kann das schonmal nach hinten losgehen (natürlich nur auf einem SMP System).

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #25 am: 22. August 2010, 20:24 »
Hallo,


Ich weiß, dass du ganz gern den kompliziertest möglichen Fall betrachtet
So bin ich eben. Wenn ich nur einfache Probleme lösen wollte wäre die geistige Befriedigung, wenn man es geschafft hat, auch nur von einfacher Natur. ;)
Außerdem bin ich persönlich der Meinung das Threading heutzutage der Normalfall ist. Alle ordentlichen OSe können das, tyndur bemüht sich doch auch in diese Richtung.

aber nehmen wir doch zur Abwechslung einfach mal einen ganz einfachen Prozess, der keine weiteren Threads hat. Und "sofort" ist natürlich aus Prozesssicht gemeint, nicht zeitbezogen. Was du in der Tat machen kannst, ist den Thread anzuhalten, einen neuen Thread für den Signalhandler anzulegen und dort weiterzumachen. Aber damit hast du keinen Vorteil gewonnen und machst nur eventuelles Exceptionhandling (z.B. per longjmp) kaputt.
Falls der Thread oder Task (als simples Äquivalent zum Prozess) irgendeinen Fehler, wie z.B. einen Segfault, verursacht hat dann muss er sowieso angehalten werden. Falls es etwas externes ist, wie eben ein CTRL-C, dann sehe ich keine akute Notwendigkeit irgendetwas anzuhalten. Das mit dem longjmp ist natürlich ein Problem wenn man das Signal mit einem PopUp-Thread (oder einem anderen zusätzlichen Thread der aktiv/wartend Signale abholt) verarbeitet. Wird das mit dem longjmp tatsächlich irgendwo benutzt?

Aber das war eigentlich auch nicht der Hintergrund meiner Antwort, sondern die Frage, ob man das mit Message Passing emulieren kann. Und Message Passing erfordert es, dass irgendjemand die Nachricht abholt. Dazu ist es unter Umständden vorteilhaft, wenn derjenige noch nicht abgestürzt ist.
Da hatte ich wohl die Frage von FlashBurn nicht ganz verstanden, sorry.
Klar mit dem aktivem Abholen gibt es da ein Problemchen. Das wäre ein spezifischer Nachteil dieser Methode es sei denn man lässt für diese System-Signale immer einen extra Thread warten (falls man über komplexe Prozesse verfügt) was aber wieder unnützen Speicherverbrauch darstellt.


Du hast nicht richtig verstanden wie ich das meinte.
Möglich. Deshalb hab ich Dir ja verschiedene Lösungsvorschläge unterbreitet.

Das wäre jetzt ne ziemlich vereinfachte Variante, aber ich hoffe es kommt rüber was ich meine. Das Problem ist nun, das man zwar "x" als Parameter übergeben kann, nicht aber "struct kernelFoo_t *ptr".
Genau für solche Probleme ist sed Dein bester Freund. Du hast eine Vorlage in welcher der Pointer-Typ ein spezielles Wort ist und das wird von sed einfach durch etwas spezifisches ersetzt wenn aus der einen Vorlage zwei (oder mehr) C-Dateien generiert werden. Das funktioniert auch wenn Du eine Zahl einfügen möchtest.
In C++ könnte man dieses Problem vielleicht mit einem Template lösen.

Was das Köpfen betrifft, ich springe, unter einer bestimmten Bedingung, aus der For-Schleife mit "goto" an eine Stelle ziemlich am Ende der Funktion (liest sich zwar scheiße, aber fand ich besser als noch ne Variable zu nutzen, nur um zu gucken ob er nen If-Zweig abarbeiten soll).
Ich hab auch schon mal goto benutzt. Es ist ein mächtiges Werkzeug mit dem man sich aber auch extrem leicht ins Knie schießen kann. Wenn man dieses Werkzeug wirklich nur dann benutzt wenn es keinen anderen vernünftigen Weg gibt und größte Vorsicht walten lässt ist das IMHO Okay.

Naja, für mich heißt nicht unterbrechbar, das die Interupts aus sind und damit sollte alles was im Kernel gemacht wird, verdammt schnell ablaufen!
Richtig, genau so hab ich mir das vorgestellt. Alles wo eventuell für unbestimmte Zeit gewartet werden müsste passiert im User-Space. Ich möchte das alle (wichtigen) Syscalls in vorhersehbarer Zeit abgeschlossen werden also wieder in den User-Mode zurückkehren (was bei RPC aber nicht der selbe Thread ist).

Denn wenn die Ints aus sind und du busy-waiting machst, kann das schonmal nach hinten losgehen (natürlich nur auf einem SMP System).
Hm, gerade auf einem SMP-System sollte es schon mal gehen wenn eine CPU etwas länger im System-Mode bleibt, es sind ja noch andere CPUs da die den Interrupt bearbeiten können. Die Critical-Sections in meinem Kernel will ich jedenfalls mit simplen busy-waiting implementieren, ich sehe da auch kein Problem solange es gewährleistet ist das keine Critical-Section länger als unbedingt erforderlich in Benutzung bleibt. Der Code zwischen Critical_Section_Enter() und Critical_Section_Leave() darf eben unter keinsten Umständen blockieren (Endlosschleife o.ä.).


Grüße
Erik
« Letzte Änderung: 22. August 2010, 20:29 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 #26 am: 22. August 2010, 20:40 »
Zitat von: erik
Falls der Thread oder Task (als simples Äquivalent zum Prozess) irgendeinen Fehler, wie z.B. einen Segfault, verursacht hat dann muss er sowieso angehalten werden. Falls es etwas externes ist, wie eben ein CTRL-C, dann sehe ich keine akute Notwendigkeit irgendetwas anzuhalten.
Was sollte ein Prozess machen wollen, wenn er von "außen" beendet wird? Sprich wozu ist der Signal Handler in dem Fall gut?

Zitat von: erik
Klar mit dem aktivem Abholen gibt es da ein Problemchen. Das wäre ein spezifischer Nachteil dieser Methode es sei denn man lässt für diese System-Signale immer einen extra Thread warten (falls man über komplexe Prozesse verfügt) was aber wieder unnützen Speicherverbrauch darstellt.
Es gibt ja cygwin und die müssen ja auch irgendwie die Signal Handler emuliert haben. Wird das dann nicht auch über Messages gelöst?
Zumal man das ja nur bräuchte wenn man Programme ohne großen Aufwand portieren möchte, sprich die Lösung mit dem zusätzlichem Thread wäre durchaus eine mögliche Lösung.

Zitat von: erik
Genau für solche Probleme ist sed Dein bester Freund. Du hast eine Vorlage in welcher der Pointer-Typ ein spezielles Wort ist und das wird von sed einfach durch etwas spezifisches ersetzt wenn aus der einen Vorlage zwei (oder mehr) C-Dateien generiert werden. Das funktioniert auch wenn Du eine Zahl einfügen möchtest.
In C++ könnte man dieses Problem vielleicht mit einem Template lösen.
An C++ und Template dachte ich auch schon, aber da ich mich damit gar nicht auskenne (noch nicht) und auch kein C++ in meinem Kernel haben will, fällt das leider aus ;)
Was sed betrifft, nutze ich für eine ähnliche Sache schon in einem Makefile. In dieser Situation wäre es aber für mich umständlicher alles in mehrere Dateien zu packen. Ich sags mal so, inzwischen ist (bzw. scheint) der Code fehlerfrei und von daher ist es im Moment eher ein "kosmetisches" Problem.

Zitat von: erik
Ich hab auch schon mal goto benutzt. Es ist ein mächtiges Werkzeug mit dem man sich aber auch extrem leicht ins Knie schießen kann. Wenn man dieses Werkzeug wirklich nur dann benutzt wenn es keinen anderen vernünftigen Weg gibt und größte Vorsicht walten lässt ist das IMHO Okay.
Nicht jeder ist so liberal wie du was das goto betrifft ;)

Ich nutze es vorallem um bei Fehlern "aufzuräumen" und um den Code nicht unnötig zu vergrößern. Außerdem habe ich jahrelang in Assembler programmiert und die Denkweise wird man nicht so einfach wieder los und vieles was in Assembler kein Problem ist, sieht in C einfach nur schrecklich aus.

Zitat von: erik
Hm, gerade auf einem SMP-System sollte es schon mal gehen wenn eine CPU etwas länger im System-Mode bleibt, es sind ja noch andere CPUs da die den Interrupt bearbeiten können. Die Critical-Sections im Kernel will ich jedenfalls mit simplen busy-waiting implementieren, ich sehe da auch kein Problem solange es gewährleistet ist das keine Critical-Section länger als unbedingt erforderlich in Benutzung bleibt. Der Code zwischen Critical_Section_Enter() und Critical_Section_Leave() darf eben unter keinsten Umständen blockieren (Endlosschleife o.ä.).
Ich weiß jetzt nicht mit vielen CPUs/Kernen du deine Platform planst, aber wir hatten das Thema ja schonmal ;)

Wenn du 16 CPUs hast die alle im Kernel sind und alle auch noch dummerweise den selben Lock wollen, dann kann das Warten schonmal länger ausfallen und keiner nimmt in dem Moment (bzw. kann) Ints entgegen! (unwahrscheinlich aber möglich, das wäre dann ein Problem was nur selten auftritt und sich wahrscheinlich auch nicht reproduzieren lässt, also der größte Albtraum den man haben kann)

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #27 am: 22. August 2010, 21:41 »
Hallo,


Was sollte ein Prozess machen wollen, wenn er von "außen" beendet wird? Sprich wozu ist der Signal Handler in dem Fall gut?
Dieser Handler könnte eine Ressource freigeben die das OS nicht automatisch freigeben kann, genau dafür hab ich zumindest mal einen solchen Handler implementieren müssen.

Es gibt ja cygwin und die müssen ja auch irgendwie die Signal Handler emuliert haben. Wird das dann nicht auch über Messages gelöst?
Da wirst Du einfach mal in den Quell-Code schauen müssen. Ich denke cygwin kann in vielen Situationen, wo man POSIX emulieren muss, eine interessante Inspirationsquelle sein.

Was sed betrifft, nutze ich für eine ähnliche Sache schon in einem Makefile. In dieser Situation wäre es aber für mich umständlicher alles in mehrere Dateien zu packen. Ich sags mal so, inzwischen ist (bzw. scheint) der Code fehlerfrei und von daher ist es im Moment eher ein "kosmetisches" Problem.
Ich kann es ja nicht wirklich beurteilen aber so wie Du Dein Problem beschreibst erscheint mir sed ziemlich geeignet.

Nicht jeder ist so liberal wie du was das goto betrifft
Ich bin wirklich kein Freund von goto, die paar mal wo ich das in den letzten fast 20 Jahren benutzt habe kann ich an einer Hand abzählen, aber man darf sich nicht immer nur von ideologischen Scheuklappen leiten lassen.

Ich nutze es vorallem um bei Fehlern "aufzuräumen" und um den Code nicht unnötig zu vergrößern.
Ich bin mir sicher das moderne Compiler da auch redundanten Quell-Code gut klein optimieren aber unlesbarer oder unwartbarer Quell-Code weil man x mal das selbe macht ist auch nicht toll. Trotzdem erscheint mir goto nur fürs Aufräumen etwas übertrieben. In C++ gibt es dafür Exceptions und catch.

Außerdem habe ich jahrelang in Assembler programmiert und die Denkweise wird man nicht so einfach wieder los und vieles was in Assembler kein Problem ist, sieht in C einfach nur schrecklich aus.
Da kann ich nur zustimmen. Auch ich ärgere mich oft das man einfache Kleinigkeiten in C manchmal extrem umständlich machen muss, manchmal sieht sogar der vom Compiler erzeugte Assembler-Code einfach nur zum schreien aus weil der Compiler eben nicht immer versteht was man will. Aber wenn man eine Hochsprache benutzen möchte dann muss man eben auch für eine Hochsprache programmieren.

Ich weiß jetzt nicht mit vielen CPUs/Kernen du deine Platform planst
Mindestens 2, weniger geht eigentlich nicht.

Wenn du 16 CPUs hast die alle im Kernel sind und alle auch noch dummerweise den selben Lock wollen, dann kann das Warten schonmal länger ausfallen und keiner nimmt in dem Moment (bzw. kann) Ints entgegen! (unwahrscheinlich aber möglich, das wäre dann ein Problem was nur selten auftritt und sich wahrscheinlich auch nicht reproduzieren lässt, also der größte Albtraum den man haben kann)
In diesem Szenario (das wirklich sehr extrem ist, es soll ja schließlich kein großes Big-Kernel-Lock geben) würde irgendeine der CPUs als erste fertig werden und die nimmt dann eben den Interrupt entgegen. Ich habe nicht geschrieben das ich ein Echt-Zeit-OS mit deterministischem Reaktionsverhalten entwickeln will, das IRQs manchmal eine gewisse Latenz haben ist eben so (typische HW kann damit gut umgehen) aber ich denke das mein Konzept im durchschnittlichen Normal-Fall durchaus eine gute Reaktionszeit vorlegen wird, immerhin wird es bei mir keine starre IRQ-Zuordnung geben sondern es nimmt immer die CPU an die gerade den Code mit der niedrigsten Priorität ausführt.


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: 22. August 2010, 22:28 »
Zitat von: erik
Dieser Handler könnte eine Ressource freigeben die das OS nicht automatisch freigeben kann, genau dafür hab ich zumindest mal einen solchen Handler implementieren müssen.
Ich arbeite gerade daran, das sowas (zumindest für den Kernel) nicht nötig sein wird. Sprich das alle Ressourcen "entzogen" werden können ohne dass das andere Prozesse auch "abstürzen" lässt. Ist zwar bei bestimmten Sachen ganz schön arbeit fehlertoleranten Code zu schreiben, aber ich denke das lohnt sich.

Zitat von: erik
Da wirst Du einfach mal in den Quell-Code schauen müssen. Ich denke cygwin kann in vielen Situationen, wo man POSIX emulieren muss, eine interessante Inspirationsquelle sein.
Ich denke genau das werde ich dann irgendwann mal tun wenn ich an einer "libposix" arbeite. Besser ist aber wenn man dann auch versteht wie etwas funktioniert und nicht nur irgendeine Idee übernimmt.

Zitat von: erik
Ich bin mir sicher das moderne Compiler da auch redundanten Quell-Code gut klein optimieren aber unlesbarer oder unwartbarer Quell-Code weil man x mal das selbe macht ist auch nicht toll. Trotzdem erscheint mir goto nur fürs Aufräumen etwas übertrieben. In C++ gibt es dafür Exceptions und catch.
Also ich kann nur sagen, wenn man "O2" beim GCC einsetzt, dann optimiert er solchen Code nicht weg sondern dazu :( Problem ist halt das er es so schnell wie möglich machen will und Speed über Size stellt, aber wenn ein Fehler aufgetreten ist, dann ist mir die Performance eher egal.
Das nächste ist, das Freigeben von Ressourcen und das Freigeben von Locks, in der richtigen Reihenfolge und das macht sich am einfachsten wenn man dazu "goto" verwendet. Ich hatte das alles schonmal mit "if" gemacht, aber da wären wir wieder bei redundanten Code und Fehler. Denn bei irgendeinem der vielen "if"´s vergisst du dann den neuen Lock wieder freizugeben und schon hast du den Salat. Mit nem "goto" passiert mir das nicht und mein Code ist auch noch ein ganzes Stückchen kleiner.

Zitat von: erik
In diesem Szenario (das wirklich sehr extrem ist, es soll ja schließlich kein großes Big-Kernel-Lock geben) würde irgendeine der CPUs als erste fertig werden und die nimmt dann eben den Interrupt entgegen. Ich habe nicht geschrieben das ich ein Echt-Zeit-OS mit deterministischem Reaktionsverhalten entwickeln will, das IRQs manchmal eine gewisse Latenz haben ist eben so (typische HW kann damit gut umgehen) aber ich denke das mein Konzept im durchschnittlichen Normal-Fall durchaus eine gute Reaktionszeit vorlegen wird, immerhin wird es bei mir keine starre IRQ-Zuordnung geben sondern es nimmt immer die CPU an die gerade den Code mit der niedrigsten Priorität ausführt.
Ich dachte in dem Moment an den pyhsischen Speichermanager. Da finde ich es am wenigsten unwahrscheinlich, das mal alle CPUs gleichzeitig neuen Speicher brauchen.
Aber wenn du deinen Kernel wirklich mikro machen willst, dürfte der gar nicht im Kernel sein (es gibt jedenfalls Mikrokernel die sogar das auslagern) und du hast das Problem nicht.

@back2topic

Ich habe mir den Thread nochmal durchgelesen und wollte mal was zum Thema beitragen ;)

Also ansich reden wir hier von einem Treiber der im UserSpace läuft und es wurde das Beispiel Diskettenlaufwerktreiber gebracht.
Ich stelle mir das so vor, das (wer auch immer) wenn ein Sektor gelesen/geschrieben werden soll, eine Nachricht an den Treiber geschickt wird. Dieser holt immer die Nachrichten ab und blockiert wenn keine vorhanden sind.
Hat er eine Nachricht abgeholt, arbeitet er sie ab, sprich er lädt/schreibt einen oder mehrere Sektoren. Wie die Daten in den Treiber oder aus den Treiber kommen ist erstmal egal (ich würde/mache es über SharedMemory). Ist er fertig versucht er die nächste Nachricht abzuholen.
Da das Diskettenlaufwerk sowieso nur eine Sache gleichzeitig machen kann, hat man so auch schon schön alles serialisiert.
Hat man nun 2 Diskettenlaufwerke, erstellt man einfach 2 Threads und stellt 2 Services mit 2 unterschiedlichen MessageQueues zur Verfügung.

Das OS sollte dann natürlich auch in der Lage sein, auf die Antwort eines bestimmen Threads/Ports warten zu können, sprich alle anderen Messages die vorher in die Queue kommen wecken den Thread nicht auf, sondern erst die Message die von dem bestimmten Thread/Port kommt!

Damit hat man keine Probleme mit irgendeinem Handler der was unterbricht und man braucht sich nicht um irgendwelche Serialisierung kümmern.

Für ein Bsp für parallele Bearbeitung, nehme ich mir mal nen Storage-Server (der kümmert sich um alles was mit Dateien lesen/schreiben zu tun hat).
Der hat einen globalen (bekannten) Port/MessageQueue und bei dieser "meldest" du dich sozusagen an und machst dir mit dem Server einen neuen Port/MessageQueue aus wo du deine Anfragen hinstellst. Für diesen neuen Port/MessageQueue wird ein neuer Thread erstellt. (Die Sache wo ich noch nicht so sicher bin, ist ob ein Port/MessageQueue pro Prozess oder Thread besser ist.)
So geht man sicher das Aufgaben parallel abgearbeitet werden können.

Ich weiß das meine Variante ziemlich viele Threads verursacht, aber das ist gewollt!

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #29 am: 23. August 2010, 09:50 »
Wie ich die Länge der For-Schleife per Parameter übergebe ist mir noch klar, aber wie willst du das mit den unterschiedlichen Strukturen lösen?
Könnte man sicher auch lösen, indem man die Größe der Struktur und die Offsets der interessanten Felder als Parameter mit reingibt. Aber an dieser Stelle würde ich wahrscheinlich eher einfach ein Makro draus machen, dem kann man ja auch Typ- und Feldnamen reingeben. Evtl. Teile in eine static-inline-Funktion auslagern, damit das Makro nicht ganz so groß wird.

Zitat
Der Vorteil, wenn ich für jede Struktur ne eigene Funktion nutze, ist halt das er anstatt ein Register für die Überprüfung, ob das Schleifenende erreicht ist, eine Konstante nutzt. Dass Selbe gilt für die Adresse der Struktur.
Das sollte der Compiler auch merken, wenn du ihn die Funktion inlinen lässt. Am Ende sollte dafür keine Variable mehr benutzt sein.

Zitat
Was das Köpfen betrifft, ich springe, unter einer bestimmten Bedingung, aus der For-Schleife mit "goto" an eine Stelle ziemlich am Ende der Funktion (liest sich zwar scheiße, aber fand ich besser als noch ne Variable zu nutzen, nur um zu gucken ob er nen If-Zweig abarbeiten soll).
Und? goto hat seine berechtigten Einsatzzwecke. Zum Beispiel Ferhlerbehandlung bedeutet fast immer ein goto ans Ende der Funktion, wenn mehr als ein return -EDUDOOF zu tun ist. Exceptions für Arme, sozusagen. ;)

Außerdem bin ich persönlich der Meinung das Threading heutzutage der Normalfall ist. Alle ordentlichen OSe können das, tyndur bemüht sich doch auch in diese Richtung.
Das bedeutet nicht automatisch, dass es die Anwendungen auch alle benutzen.

Zitat
Das mit dem longjmp ist natürlich ein Problem wenn man das Signal mit einem PopUp-Thread (oder einem anderen zusätzlichen Thread der aktiv/wartend Signale abholt) verarbeitet. Wird das mit dem longjmp tatsächlich irgendwo benutzt?
Ich habe das beispielsweise schonmal in einem kleinen Testframework benutzt (für unsere libext2). Bei einem Segfault per longjmp rausretten, den Testcase als fehlgeschlagen markieren und weiter mit dem nächsten.

Ich bin mir sicher, dass es auch an wesentlich kritischeren Stellen eingesetzt wird. Wenn ein Feature da ist, wird es auch benutzt.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #30 am: 23. August 2010, 10:03 »
Zitat von: taljeth
Könnte man sicher auch lösen, indem man die Größe der Struktur und die Offsets der interessanten Felder als Parameter mit reingibt. Aber an dieser Stelle würde ich wahrscheinlich eher einfach ein Makro draus machen, dem kann man ja auch Typ- und Feldnamen reingeben. Evtl. Teile in eine static-inline-Funktion auslagern, damit das Makro nicht ganz so groß wird.
Genau solche Lösungen machen mir momentan das Leben schwer. Denn die Offsets müssten dann auch mit Hilfe eines anderen Makros übergeben werden (falls die sich mal ändern sollte, so dass man nicht alle Stellen suchen muss wo man die benutzt hat).

Ich bin nicht wirklich ein Freund vieler Makros (das liegt aber nur daran, das ich die meisten nicht verstehe, bzw. es verdammt aufwendig ist sich alles Makros zusammen zu suchen).

Zitat von: taljeth
Und? goto hat seine berechtigten Einsatzzwecke. Zum Beispiel Ferhlerbehandlung bedeutet fast immer ein goto ans Ende der Funktion, wenn mehr als ein return -EDUDOOF zu tun ist. Exceptions für Arme, sozusagen. Wink
Genau dafür nutze ich sie vorrangig! Wieder etwas Offtopic, aber dein letzter Satz impleziert, dass man das ganze mit Exceptions lösen kann, aber eigentlich ersetzen doch Exceptions nur das Vergleichen des Rückgabewerts einer Funktion mit Fehlercodes, das Aufräumen musst du trotzdem machen, oder habe ich das falsch verstanden?

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #31 am: 23. August 2010, 11:08 »
Genau solche Lösungen machen mir momentan das Leben schwer. Denn die Offsets müssten dann auch mit Hilfe eines anderen Makros übergeben werden (falls die sich mal ändern sollte, so dass man nicht alle Stellen suchen muss wo man die benutzt hat).
offsetof aus stddef.h?

Zitat von: taljeth
Genau dafür nutze ich sie vorrangig! Wieder etwas Offtopic, aber dein letzter Satz impleziert, dass man das ganze mit Exceptions lösen kann, aber eigentlich ersetzen doch Exceptions nur das Vergleichen des Rückgabewerts einer Funktion mit Fehlercodes, das Aufräumen musst du trotzdem machen, oder habe ich das falsch verstanden?
Naja, das Vergleichen mit dem Fehlercode ist der uninteressantere Teil. Was Exceptions vor allem machen, ist irgendwo mitten aus dem try-Block heraus in einen catch-Block zu springen, und das ist nicht ganz unähnlich zu einem goto in einer Sprache, die keine Exceptions kennt. Aufräumen musst du natürlich trotzdem selber.
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 #32 am: 24. August 2010, 08:19 »
Hallo,


Ich arbeite gerade daran, das sowas (zumindest für den Kernel) nicht nötig sein wird. Sprich das alle Ressourcen "entzogen" werden können ohne dass das andere Prozesse auch "abstürzen" lässt. Ist zwar bei bestimmten Sachen ganz schön arbeit fehlertoleranten Code zu schreiben, aber ich denke das lohnt sich.
Das halte ich für ein gänzlich unmögliches Unterfangen, nicht jede Art von Ressource ist dem OS überhaupt bekannt/bewusst. Dieses Forum hier ist ein gutes Beispiel dafür, wenn Du angemeldet bist kannst Du zwar den Browser schließen aber abgemeldet wirst Du dadurch nicht (was auch daran liegt das sich noch nicht mal Dein Browser dessen bewusst ist das Du in diesem Forum angemeldet bist). Wenn der Session-Cookie noch existiert kannst Du diesen sogar einem anderen Browser auf einem anderen Computer injizieren und bist dann plötzlich mit diesem Browser hier im Forum angemeldet. Übertrage dieses Problem mal auf Datenbanken oder den Zustand bestimmter HW-Komponenten, auch davon hat das OS keine Ahnung, den meisten Effekt erreichst Du dann wenn Du den Programmen auch im Fehlerfall die Möglichkeit gibst zumindest zu versuchen selber Aufzuräumen.

Besser ist aber wenn man dann auch versteht wie etwas funktioniert und nicht nur irgendeine Idee übernimmt.
Ideen muss man doch verstehen wenn man sie ordentlich übernehmen möchte, dachte ich zumindest immer. ;)

... Mit nem "goto" passiert mir das nicht und mein Code ist auch noch ein ganzes Stückchen kleiner.
Lerne C++ (oder eine andere moderne OO-Sprache) und Du wirst sehen wie einfach und vor allem elegant sich solche Dinge lösen lasen. Das ist einer der Gründe warum ich seit über 5 Jahren kaum noch reines C programmiere.

Ich dachte in dem Moment an den pyhsischen Speichermanager. Da finde ich es am wenigsten unwahrscheinlich, das mal alle CPUs gleichzeitig neuen Speicher brauchen.
Klar kann es passieren das mal gleich einige CPUs (oder gar alle) gleichzeitig Speicher benötigen aber dann dauert es eben etwas länger bis die erste CPU fertig ist (wegen dem Cache-Kohärenz-Protokoll usw.). Der entscheidende Punkt ist das sie in überschaubarer Zeit fertig wird und nicht für unbestimmte Zeit blockiert, solange das gewährleistet ist ist ununterbrechbarer Kernel-Code kein Negativ-Kriterium.

Aber wenn du deinen Kernel wirklich mikro machen willst, dürfte der gar nicht im Kernel sein (es gibt jedenfalls Mikrokernel die sogar das auslagern) und du hast das Problem nicht.
So extrem mikro möchte ich nun auch wieder nicht. Man soll die Dinge ja immer so einfach wie möglich machen aber auf gar keinen Fall noch einfacher.


Da das Diskettenlaufwerk sowieso nur eine Sache gleichzeitig machen kann, hat man so auch schon schön alles serialisiert.
Das ist zwar richtig aber was ist mit Festplatten? Für die hat man sich extra NCQ ausgedacht.

Hat man nun 2 Diskettenlaufwerke, erstellt man einfach 2 Threads und stellt 2 Services mit 2 unterschiedlichen MessageQueues zur Verfügung.
Ja, ich denke das wäre eine sinnvolle Vorgehensweise. Aber bedenke wie viele (möglicherweise nutzlos) wartende Threads dabei in einem üppig ausgestattetem Computer entstehen.

Das OS sollte dann natürlich auch in der Lage sein, auf die Antwort eines bestimmen Threads/Ports warten zu können, sprich alle anderen Messages die vorher in die Queue kommen wecken den Thread nicht auf, sondern erst die Message die von dem bestimmten Thread/Port kommt!
Das verstehe ich nicht. Du meinst die Antwort soll wieder als aktives IPC an den Client geschickt werden? Also sowas wie das http://forum.lowlevel.eu/index.php?topic=2466? Da wird mit Hilfe zweier asynchroner IPC-Vorgänge ein synchroner IPC-Vorgang emuliert. Klar das funktioniert, tyndur ist der Beweis dafür, aber als besonders toll kann man das nun wirklich nicht bezeichnen.

Damit hat man keine Probleme mit irgendeinem Handler der was unterbricht und man braucht sich nicht um irgendwelche Serialisierung kümmern.
Hm, bei nicht ganz so offensichtlichen Ressourcen wirst Du eventuell trotzdem in irgendeiner Form serialisieren müssen.

....
Ich weiß das meine Variante ziemlich viele Threads verursacht, aber das ist gewollt!
Echt, ich hoffe Dein OS läuft nur auf Computern die mit sehr viel RAM ausgestattet sind. Mit diesem Konzept könnten es extrem viele Threads werden und es ist recht wahrscheinlich das für jeden beliebigen Zeitpunkt die meisten davon gerade schlafen(warten).


Außerdem bin ich persönlich der Meinung das Threading heutzutage der Normalfall ist. Alle ordentlichen OSe können das, tyndur bemüht sich doch auch in diese Richtung.
Das bedeutet nicht automatisch, dass es die Anwendungen auch alle benutzen.
Gewiss, aber es bedeutet das eine OS-spezifische LIB das problemlos nutzen kann.

Bei einem Segfault per longjmp rausretten
Hm, wenn Du ungefähr weist bei welchen Speicherzugriff der Segfault auftritt wäre es dann nicht besser vor dem Speicherzugriff zu prüfen ob dieser überhaupt geht? Außerdem ist ein Segfault IMHO ein Fehler den Du als Programmierer beheben musst und nicht das OS zur Laufzeit.

Ich bin mir sicher, dass es auch an wesentlich kritischeren Stellen eingesetzt wird. Wenn ein Feature da ist, wird es auch benutzt.
Ich hab mich gestern da etwas eingelesen und bin mir nicht ganz sicher ob ich das überhaupt auf meiner Plattform ordentlich unterstützen kann, aber da ich der Meinung bin das man die Szenarien mit setjmp/longjmp auch immer anders lösen kann sehe ich das im Moment nicht als dringendes Problem.


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 #33 am: 24. August 2010, 10:05 »
indest immer. ;)
... Mit nem "goto" passiert mir das nicht und mein Code ist auch noch ein ganzes Stückchen kleiner.
Lerne C++ (oder eine andere moderne OO-Sprache) und Du wirst sehen wie einfach und vor allem elegant sich solche Dinge lösen lasen. Das ist einer der Gründe warum ich seit über 5 Jahren kaum noch reines C programmiere.
Dann erklär mal, wie OO an sich in dieser Situation hilft. ;)

Ich bin mir relativ sicher, dass wenn sich überhaupt was verändert, es nicht an OO, sondern an anderen Sprachfeatures liegt. (Nichts gegen OO, aber es ist kein Allheilmittel)

Zitat
Hm, wenn Du ungefähr weist bei welchen Speicherzugriff der Segfault auftritt wäre es dann nicht besser vor dem Speicherzugriff zu prüfen ob dieser überhaupt geht? Außerdem ist ein Segfault IMHO ein Fehler den Du als Programmierer beheben musst und nicht das OS zur Laufzeit.
Wie gesagt, bei mir war es ein Testframework. Ich muss davon ausgehen, dass der zu testende Code kaputt ist (sonst müsste ich ihn ja nicht testen), aber ich weiß natürlich nicht, an welcher Stelle.

Zitat
Ich hab mich gestern da etwas eingelesen und bin mir nicht ganz sicher ob ich das überhaupt auf meiner Plattform ordentlich unterstützen kann, aber da ich der Meinung bin das man die Szenarien mit setjmp/longjmp auch immer anders lösen kann sehe ich das im Moment nicht als dringendes Problem.
Vermutlich geht es schon anders. Ist nur eine Frage der Portabilität. Wenn du auf POSIX-Programme verzichten kannst, brauchst du es nicht zu implementieren. Wenn du es teilweise implementierst, bekommst du eben auch nur einen Teil der Programme.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #34 am: 24. August 2010, 10:49 »
Zitat von: taljeth
Dann erklär mal, wie OO an sich in dieser Situation hilft. Wink
Das hätte ich auch gerne gewusst ;) Denn 1. (sollte) verwendet man keine Exceptions im Kernel und 2. ändert das am Aufräumen nichts.

Zitat von: erik
Das halte ich für ein gänzlich unmögliches Unterfangen, nicht jede Art von Ressource ist dem OS überhaupt bekannt/bewusst. Dieses Forum hier ist ein gutes Beispiel dafür, wenn Du angemeldet bist kannst Du zwar den Browser schließen aber abgemeldet wirst Du dadurch nicht (was auch daran liegt das sich noch nicht mal Dein Browser dessen bewusst ist das Du in diesem Forum angemeldet bist). Wenn der Session-Cookie noch existiert kannst Du diesen sogar einem anderen Browser auf einem anderen Computer injizieren und bist dann plötzlich mit diesem Browser hier im Forum angemeldet. Übertrage dieses Problem mal auf Datenbanken oder den Zustand bestimmter HW-Komponenten, auch davon hat das OS keine Ahnung, den meisten Effekt erreichst Du dann wenn Du den Programmen auch im Fehlerfall die Möglichkeit gibst zumindest zu versuchen selber Aufzuräumen.
Deswegen habe ich ja dazu geschrieben, das ich das erstmal nur für den Kernel machen will. Im Endeffekt geht es mir nur darum, das ein abgestürzter Prozess nicht beendet wird, wenn er mitten in einer Semaphore oder sowas steckt (diese also gerade hat) und genauso meine ich damit die Threads aus Queues herauszuholen, in denen sie warten (das geht wieder nur für Queues die dem Kernel bekannt sind).

Wo ich gleich mal bei einem Problem bin (mal wieder Offtopic). Im POSIX Standard steht (glaub ich) das die Prozess/Thread IDs nur 1x verwendet werden dürfen. Das hieße ja aber das wenn du 2^32 Prozesse/Threads gestartet hattest, das du dann keinen mehr starten kannst oder habe ich da was falsch verstanden?

Zitat von: erik
Ja, ich denke das wäre eine sinnvolle Vorgehensweise. Aber bedenke wie viele (möglicherweise nutzlos) wartende Threads dabei in einem üppig ausgestattetem Computer entstehen.
Unter meinem Windows laufen gerade 733 Threads und was macht der Computer die meiste Zeit? Idlen! Also kannst du sagen das mindestens 700 Threads gerade auf Arbeit warten.

Gerade bei der Entwicklung hin zu noch mehr Kernen finde ich es besser für jede Ressource (die man auch parallel zu anderen nutzen kann, ansonsten macht es keinen Sinn) einen Thread zu haben.

Der Punkt ist, es macht mir das Leben einfacher, was das serialisieren von solchen Ressourcenzugriffen betrifft und kann das System wesentlich leistungsfähiger machen.

Zitat von: erik
Das ist zwar richtig aber was ist mit Festplatten? Für die hat man sich extra NCQ ausgedacht.
Schonmal Tests dazu gelesen? Ist zwar ein schönes Feature, aber bringt nicht wirklich was! Zumal ich gerade auch nicht ganz verstehe wo das Problem sein soll.

Zitat von: erik
Das verstehe ich nicht. Du meinst die Antwort soll wieder als aktives IPC an den Client geschickt werden? Also sowas wie das http://forum.lowlevel.eu/index.php?topic=2466? Da wird mit Hilfe zweier asynchroner IPC-Vorgänge ein synchroner IPC-Vorgang emuliert. Klar das funktioniert, tyndur ist der Beweis dafür, aber als besonders toll kann man das nun wirklich nicht bezeichnen.
Ich sag jetzt mal nein ;) Ich habe in dem Sinne keine RPC-Handler, sondern einfach nur Msg-Queues (gebunden an einen Port). Der Syscall send+recv ist gerade für solche RPC-Sachen sehr gut geeignet, genauso wie für Server. Denn was machen denn Server, die holen eine neue Nachricht ab, bearbeiten sie, senden die Antwort und dann holen sie die nächste Nachricht ab. Mit diesem Syscall wird 1 Ringwechsel gespart. Das ganze funktioniert natürlich da ich fixed-size Msgs habe, bei dynamic-size Msgs wäre das schwieriger umzusetzen.

Also ich habe 3 Syscalls fürs IPC, send, recv und send+recv. Jedesmal wenn du ein recv machst, kannst du angeben das du auf eine Antwort von dem Port (an den du gerade sendest) warten möchtest. Jede Msg die an dich gesendet wird, wird in die Queue gepackt. Wird aber eine Msg von genau diesem Port empfangen, dann wird diese genommen, der Thread wird wieder aufgeweckt und du bekommst deine Antwort. So habe ich ansich asynchrones IPC, aber die Möglichkeit das ganze auch synchron zu machen (das erleichtert viele Aufgaben)!

So um jetzt nochmal auf das Bsp mit dem Diskettenlaufwerk zurückzukommen. Ich würde, wie gesagt, die Daten die gelesen werden in einen SharedMem-Bereich schreiben und wenn ich (als Treiber) fertig bin, sende ich eine Msg an den Port, der die Anfrage gestellt hatte und dieser wird dann aufgeweckt, bekommt seine Antwort (was in dem Sinne dann der Rückgabewert der Funktion "fread" sein könnte) und kann weiter machen. Die Daten hat dann "fread" in den übergebenden Buffer, aus dem SharedMem, geschrieben. Das sollte sogar sehr performant sein.

Zitat von: erik
Ich hab mich gestern da etwas eingelesen und bin mir nicht ganz sicher ob ich das überhaupt auf meiner Plattform ordentlich unterstützen kann, aber da ich der Meinung bin das man die Szenarien mit setjmp/longjmp auch immer anders lösen kann sehe ich das im Moment nicht als dringendes Problem.
Ich weiß jetzt nicht, aber unter Windows gibt es sowas doch auch nicht und man kommt scheinbar auch ohne aus (nicht das Windows jetzt das OS aller OS ist, aber es macht auch nicht alles falsch).

Edit::

Ich habe gerade mal nachgesehen, also im Moment belegt eine Thread-Struktur bei mir ganze 232byte, dann würde nur noch der Stack dazukommen, den man auch auslagern kann! Das macht bei 1000 Threads gerade mal 226kb Speicher, das finde ich nicht wirklich viel!
« Letzte Änderung: 24. August 2010, 10:52 von FlashBurn »

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #35 am: 24. August 2010, 13:24 »
Hallo,


Dann erklär mal, wie OO an sich in dieser Situation hilft. ;)
Sorry, da hab ich mich ungeschickt ausgedrückt. Ich persönlich kenne keine OO-Sprache die nicht auch gutes Exception-Handling bietet (was nicht bedeutet das es sowas nicht doch gibt). Da hätte ich besser "oder eine andere Sprache mit ordentlichem Exception-Handling" schreiben sollen. Aber ich denke das OO eine Voraussetzung (von mehreren) für gutes Exception-Handling ist von daher glaube ich nicht das es rein funktionale Sprachen gibt die das können (ich persönlich kenne zumindest keine).

Wie gesagt, bei mir war es ein Testframework. Ich muss davon ausgehen, dass der zu testende Code kaputt ist (sonst müsste ich ihn ja nicht testen), aber ich weiß natürlich nicht, an welcher Stelle.
Dann würde ich das Testframework aus einer Schleife in einem Shell-Script aufrufen, mit der Test-Nummer als Parameter, und immer den Rückgabe-Wert prüfen und die Ausgaben von OUT und ERR loggen. Bei einem Segfault sollte das OS auf den Assemblerbefehl genau melden was schief gegangen ist, das ist IMHO sogar sinnvoller als ein einfaches OK/NAK.

Wenn du auf POSIX-Programme verzichten kannst, brauchst du es nicht zu implementieren. Wenn du es teilweise implementierst, bekommst du eben auch nur einen Teil der Programme.
Also ich hab das setjmp/longjmp-Pärchen noch nie in irgendeinem Programm gesehen (von C-Tutorials mal abgesehen), ich glaube nicht das mir so viele Programme entgehen nur weil ich das nicht unterstütze. Bei den paar Programmen die das doch benutzen muss ich eben den Source so modifizieren das es auch ohne geht.


Im Endeffekt geht es mir nur darum, das ein abgestürzter Prozess nicht beendet wird, wenn er mitten in einer Semaphore oder sowas steckt (diese also gerade hat) und genauso meine ich damit die Threads aus Queues herauszuholen, in denen sie warten (das geht wieder nur für Queues die dem Kernel bekannt sind).
Das mit den Semaphoren stelle ich mir sehr aufwendig vor, das bedeutet das Du für jedes Enter und Leave je einen Syscall brauchst um den Kernel zu informieren. Grundsätzlich kann der Kernel nur die Ressourcen berücksichtigen die er auch kennt und das sind bei einem Mikro-Kernel nur sehr wenige. Zu diesem Thema hab ich schon mal diskutiert http://forum.lowlevel.eu/index.php?topic=2400.

Wo ich gleich mal bei einem Problem bin (mal wieder Offtopic). Im POSIX Standard steht (glaub ich) das die Prozess/Thread IDs nur 1x verwendet werden dürfen. Das hieße ja aber das wenn du 2^32 Prozesse/Threads gestartet hattest, das du dann keinen mehr starten kannst oder habe ich da was falsch verstanden?
Das hast Du wohl richtig verstanden. Brauchst Du denn mehr als 2^32 Prozesse/Threads?


Unter meinem Windows laufen gerade 733 Threads und was macht der Computer die meiste Zeit? Idlen! Also kannst du sagen das mindestens 700 Threads gerade auf Arbeit warten.
Ich vermute das etwa 90% der Threads in irgendeinem blockierendem Syscall hängen und der Rest einfach so schlafen gelegt wurde (um bei anfallender Arbeit geweckt zu werden). Zu der Anzahl als solches: Du darfst nicht vergessen wie viele Programme und Dienste auf einem typischen Windows laufen, in Relation dazu sind die 700 Threads noch recht human, bis eines unserer OSe dieses Komplexitätslevel erreicht vergeht sicher noch einiges an Zeit.

Gerade bei der Entwicklung hin zu noch mehr Kernen finde ich es besser für jede Ressource (die man auch parallel zu anderen nutzen kann, ansonsten macht es keinen Sinn) einen Thread zu haben.
Der Punkt ist, es macht mir das Leben einfacher, was das serialisieren von solchen Ressourcenzugriffen betrifft und kann das System wesentlich leistungsfähiger machen.
Das sehe ich ganz genau so wie Du und deshalb möchte ich dem OS die Kontrolle über die Anzahl der existierenden Threads geben, damit werden nur so viele Threads erzeugt wie auch wirklich benötigt werden.

Ist zwar ein schönes Feature, aber bringt nicht wirklich was! Zumal ich gerade auch nicht ganz verstehe wo das Problem sein soll.
Ich weiß das NCQ nicht so wirklich der Bringer ist aber wenn es bei einem 8-Kern-PC mit 8 parallelen Compiler-Instanzen einige Prozent weniger CPU-IDLE-Zeit bewirken kann dann sollte man das ruhig unterstützen. Aber dafür muss der Festplatten-Treiber in der Lage sein mehrere Anfragen gleichzeitig anzunehmen, in Deinem Szenario würde das bedeuten das mehrere Threads auf diese eine Queue (über die der Treiber seine Dienste anbietet) warten müssen.

Ich habe in dem Sinne keine RPC-Handler, sondern einfach nur Msg-Queues (gebunden an einen Port). Der Syscall send+recv ist gerade für solche RPC-Sachen sehr gut geeignet, genauso wie für Server. Denn was machen denn Server, die holen eine neue Nachricht ab, bearbeiten sie, senden die Antwort und dann holen sie die nächste Nachricht ab. Mit diesem Syscall wird 1 Ringwechsel gespart. Das ganze funktioniert natürlich da ich fixed-size Msgs habe, bei dynamic-size Msgs wäre das schwieriger umzusetzen.

Also ich habe 3 Syscalls fürs IPC, send, recv und send+recv. Jedesmal wenn du ein recv machst, kannst du angeben das du auf eine Antwort von dem Port (an den du gerade sendest) warten möchtest. Jede Msg die an dich gesendet wird, wird in die Queue gepackt. Wird aber eine Msg von genau diesem Port empfangen, dann wird diese genommen, der Thread wird wieder aufgeweckt und du bekommst deine Antwort. So habe ich ansich asynchrones IPC, aber die Möglichkeit das ganze auch synchron zu machen (das erleichtert viele Aufgaben)!
Das Konzept sieht doch recht gut aus, nur für den send+recv würde ich 2 Varianten machen: eine für Clients die eine Anfrage schicken und dann auf ihre zugehörige Antwort warten (irgendetwas anderes will der Client dann eh nicht) und eine für Server die eine Antwort schicken und dann auf eine beliebige neue Anfrage warten.
Das mit den fixed-size Messages empfinde ich irgendwie als ungeschickt, bedeutet dass das der Client nur Anfragen mit einer bestimmten Größe benutzen darf? Das stelle ich mir bei fopen, wo ein unbekannt langer Dateiname mit zusätzlichem unbekannt langem Path (unbekannt aus Sicht des Services) übergeben werden muss, als ungeschickt vor. Ich möchte in mein Konzept auch Obergrenzen für die Größe der übergebenen Speicherbereiche beim erstellen eines Message-Targets angeben können aber das bedeutet nicht das die Anfragen immer exakt genau so groß sein müssen.

So um jetzt nochmal auf das Bsp mit dem Diskettenlaufwerk zurückzukommen. Ich würde, wie gesagt, die Daten die gelesen werden in einen SharedMem-Bereich schreiben und wenn ich (als Treiber) fertig bin, sende ich eine Msg an den Port, der die Anfrage gestellt hatte und dieser wird dann aufgeweckt, bekommt seine Antwort (was in dem Sinne dann der Rückgabewert der Funktion "fread" sein könnte) und kann weiter machen. Die Daten hat dann "fread" in den übergebenden Buffer, aus dem SharedMem, geschrieben. Das sollte sogar sehr performant sein.
Muss der Client auch einen Port öffnen? Wenn ja, warum?
Verstehe ich das richtig das Du den Shared-Memory zusätzlich (mit weiteren Syscalls) einrichten möchtest? Dann muss der Client das doch auch dem Service vorher mitteilen wie die Nutzdaten übermittelt werden sollen. Ich möchte das Memory-Sharing mit in die IPC-Syscalls integrieren, bei einer synchronen RPC-Anfrage werden bis zu vier Speicherbereiche (mit beliebiger Größe) mitgegeben in denen der Service dann die eigentliche Anfrage + zugehörige Nutzdaten (bei Schreibzugriffen) findet und die Antwort (also den Rückgabewert/Status) + zugehörige Nutzdaten (bei Lesezugriffen) ablegen kann. Diese Speicherbereiche soll ein Service auch seinerseits wieder (teilweise) weiterreichen können, z.B. von einem Dateisystem-Treiber an einen Block-Device-Treiber, so das die Nutzdaten möglichst nie kopiert werden müssen.


Ich weiß jetzt nicht, aber unter Windows gibt es sowas doch auch nicht und man kommt scheinbar auch ohne aus (nicht das Windows jetzt das OS aller OS ist, aber es macht auch nicht alles falsch).
Wieso sollte es unter Windows kein setjmp/longjmp geben? Das ist Sache des C-Compilers und nicht Sache des OS, der C-Compiler muss das nur so umsetzen das es der C-Spec genügt und unter dem OS funktioniert. Das setjmp/longjmp nicht richtig funktionieren wenn Multi-Threading ins Spiel kommt ist klar aber in einem Single-Thread-Programm sollte das erstmal tun. Mein Problem ist eher das ich aus fast jedem Programm quasi zwangsläufig ein Multi-Threading-Prozess mache.

Ich habe gerade mal nachgesehen, also im Moment belegt eine Thread-Struktur bei mir ganze 232byte, dann würde nur noch der Stack dazukommen, den man auch auslagern kann! Das macht bei 1000 Threads gerade mal 226kb Speicher, das finde ich nicht wirklich viel!
Klar kann man die vielen Stacks auslagern aber sie belegen im virtuellen Speicher Deiner Prozesse einiges an wertvollem Adressraum (zumindest in einem Flat-Memory-System, mit meinen Segmenten hab ich es da sogar deutlich einfacher).


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 #36 am: 24. August 2010, 13:51 »
Da hätte ich besser "oder eine andere Sprache mit ordentlichem Exception-Handling" schreiben sollen. Aber ich denke das OO eine Voraussetzung (von mehreren) für gutes Exception-Handling ist von daher glaube ich nicht das es rein funktionale Sprachen gibt die das können (ich persönlich kenne zumindest keine).
Funktional ist sowieso ein ganz andere Baustelle... Ich vermute, du meinst prozedural.

Was macht "gutes" Exceptionhandling deiner Meinung nach denn aus, dass es unbedingt OO braucht? In Ada habe ich das jedenfalls schon benutzt ohne irgendwas zu machen, das verdächtig nach OO ausgesehen hätte.

Zitat
Also ich hab das setjmp/longjmp-Pärchen noch nie in irgendeinem Programm gesehen (von C-Tutorials mal abgesehen)
Ich vermeide es ja bei den meisten Programmen, in den Code zu schauen, wenn es nicht sein muss. ;) Aber das eine Programm, das ich wirklich näher kenne, benutzt es zufällig und insofern hab ich dir ein Beispiel: qemu wäre das.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #37 am: 24. August 2010, 14:08 »
Zitat von: erik
Das mit den Semaphoren stelle ich mir sehr aufwendig vor, das bedeutet das Du für jedes Enter und Leave je einen Syscall brauchst um den Kernel zu informieren. Grundsätzlich kann der Kernel nur die Ressourcen berücksichtigen die er auch kennt und das sind bei einem Mikro-Kernel nur sehr wenige.
Du musst doch bei einer Semaphore so oder so in den Kernel, warum dann nicht auch gleich alles im Kernel machen (so wird das möchte ich meinen auch unter Linux/Windows gemacht)?

Zitat von: erik
Das hast Du wohl richtig verstanden. Brauchst Du denn mehr als 2^32 Prozesse/Threads?
Wenn ich das richtig verstanden habe, kann man aber ein POSIX System sehr einfach unbenutzbar machen! Ich denke mal das ich da irgendeinen Denkfehler drin habe, denn ansonsten wären Server die ewig laufen gar nicht möglich. Denn es wäre nur ein Script nötig was einen Prozess startet der nichts macht, sich gleich wieder beendet und das ganze halt so lange bis keine IDs mehr verfügbar sind. Wenn das wirklich so ist, sehe ich das schon als ne ganz schöne Schwachstelle!

Ich habe das so verstanden das du eine neue ID für einen Prozess im Endeffekt über "prozess->id= procID++" holst, sprich wenn ein Prozess die ID 4 hatte, selbst wenn es ihn nicht mehr gibt er also beendet ist, das kein anderer Prozess wieder die ID 4 bekommt.

Zitat von: erik
Ich weiß das NCQ nicht so wirklich der Bringer ist aber wenn es bei einem 8-Kern-PC mit 8 parallelen Compiler-Instanzen einige Prozent weniger CPU-IDLE-Zeit bewirken kann dann sollte man das ruhig unterstützen. Aber dafür muss der Festplatten-Treiber in der Lage sein mehrere Anfragen gleichzeitig anzunehmen, in Deinem Szenario würde das bedeuten das mehrere Threads auf diese eine Queue (über die der Treiber seine Dienste anbietet) warten müssen.
Du konntest mir immernoch nicht sagen, wo das Problem ist, bzw. was du anders machen würdest so das dieses Problem nicht auftritt!

Was ich meine ist, das du dem Festplattentreiber doch "nur" sagst das er mal eben Daten laden soll, z.B. von LBA 123-130 (4kb). Wenn er das gemacht hat, dann holt er die nächste Nachricht ab und lädt wieder ein paar Sektoren. Der Punkt ist aber, das egal wie du es machst, die Festplatte kann immer nur eine Anfrage gleichzeitig entgegen nehmen.

Das einzige was du jetzt meinen könntest, ist das du nicht wartest bis die Festplatte fertig ist mit laden und dann schon die nächste Anfrage sendest.

Zitat von: erik
Das Konzept sieht doch recht gut aus, nur für den send+recv würde ich 2 Varianten machen: eine für Clients die eine Anfrage schicken und dann auf ihre zugehörige Antwort warten (irgendetwas anderes will der Client dann eh nicht) und eine für Server die eine Antwort schicken und dann auf eine beliebige neue Anfrage warten.
Brauch ich nicht. Ob und wie gewartet wird, wird über Flags die man mit übergibt festgelegt!

Zitat von: erik
Das mit den fixed-size Messages empfinde ich irgendwie als ungeschickt, bedeutet dass das der Client nur Anfragen mit einer bestimmten Größe benutzen darf? Das stelle ich mir bei fopen, wo ein unbekannt langer Dateiname mit zusätzlichem unbekannt langem Path (unbekannt aus Sicht des Services) übergeben werden muss, als ungeschickt vor. Ich möchte in mein Konzept auch Obergrenzen für die Größe der übergebenen Speicherbereiche beim erstellen eines Message-Targets angeben können aber das bedeutet nicht das die Anfragen immer exakt genau so groß sein müssen.
Problem ist einfach, ich will in meinem Kernel kein malloc, sondern nutze nur den SlabAllocator (ja man kann malloc auch mit Hilfe des SlabAllocators implementieren). Außerdem soll das ganz so schnell wie möglich sein.
Gelöst habe ich das so, das die Msgs sowieso nur als Synchronisierung oder kurze Statusmeldung genutzt werden soll (oder für Initialisierung ->).
Das IPC an sich soll über meine UserSpace Pipes laufen! Darüber werden dann auch Pfad und sowas übergeben, man könnte es sogar machen, das man die Anfragen auch darüber sendet (mit dem Gedanken spiele ich auch schon, könnte wesentlich schneller sein).

Zitat von: erik
Muss der Client auch einen Port öffnen? Wenn ja, warum?
Ja! Ich habe auch erst überlegt ob das wirklich sein muss, aber wenn man eine Nachricht verschickt, möchte man eigentlich auch mal eine bekommen. Gerade was den Clienten betrifft, der sendet eine Nachricht und wartet auf die Antwort, sprich er will auch eine Nachricht bekommen.

Zitat von: erik
Verstehe ich das richtig das Du den Shared-Memory zusätzlich (mit weiteren Syscalls) einrichten möchtest? Dann muss der Client das doch auch dem Service vorher mitteilen wie die Nutzdaten übermittelt werden sollen. Ich möchte das Memory-Sharing mit in die IPC-Syscalls integrieren, bei einer synchronen RPC-Anfrage werden bis zu vier Speicherbereiche (mit beliebiger Größe) mitgegeben in denen der Service dann die eigentliche Anfrage + zugehörige Nutzdaten (bei Schreibzugriffen) findet und die Antwort (also den Rückgabewert/Status) + zugehörige Nutzdaten (bei Lesezugriffen) ablegen kann. Diese Speicherbereiche soll ein Service auch seinerseits wieder (teilweise) weiterreichen können, z.B. von einem Dateisystem-Treiber an einen Block-Device-Treiber, so das die Nutzdaten möglichst nie kopiert werden müssen.
Wie gesagt, habe ich meine UserSpace Pipes und im Endeffekt sollen die Nachrichten nur für die Initialisierung genutzt werden.
Sprich das ganze würde dann so ablaufen, das sich ein Client bei einem Server registriert/anmeldet und die beiden machen sich dann aus über welche Pipe gesendet und über welche Pipe empfangen wird.
Meine Pipe ist nichts anderes als SharedMem, der als Ringbuffer genutzt wird (es findet aber fast alles im UserSpace statt).
Was du schon ansprichst, das Weiterreichen von Daten, würde ich auch über SharedMem machen, ist einfach wesentlich schneller.

Zitat von: erik
Wieso sollte es unter Windows kein setjmp/longjmp geben? Das ist Sache des C-Compilers und nicht Sache des OS, der C-Compiler muss das nur so umsetzen das es der C-Spec genügt und unter dem OS funktioniert. Das setjmp/longjmp nicht richtig funktionieren wenn Multi-Threading ins Spiel kommt ist klar aber in einem Single-Thread-Programm sollte das erstmal tun. Mein Problem ist eher das ich aus fast jedem Programm quasi zwangsläufig ein Multi-Threading-Prozess mache.
Ich muss dir ehrlich sagen, das ich nicht genau weiß was setjmp/longjmp ist! Aber ich meinte eigentlich auch mehr die Signalhandler die das dann in irgendeiner Weise nutzen.
Sprich Windows kommt auch ohne Signalhandler aus und trotzdem scheinen alle Programm auch zu laufen (mit den selben Features).

@Exception Handling

Mir ist immernoch nicht klar was mit dem Exception-Handling so viel besser wäre.

Denn wenn ich da an einige meiner Funktionen denke, da hätte man dann viele try/catch Blöcke, aber ich wüsste jetzt auch nicht wie ich dann das goto vermeiden sollte (es gäbe einen Weg, aber der wäre sehr sehr unschön, da wären dann die goto´s wieder besser)?

Edit::

Was die Ressourcen betrifft (Dateien/Sockets).

Ich wollte das so lösen, das sich jeder Server, der Ressourcen eines Prozessen hat, sich im Kernel registriert, das er informiert werden möchte, wenn dieser Prozess beendet wird/ist.

Das kommt dann dem Node-Monitor (man will informiert werden, wenn sich eine Node, bzw. deren Kinder, irgendwie geändert haben, nützlich für einen Dateiexplorer, um informiert zu werden, wenn eine Datei gelöscht (oder ähnliches) wurde) ziemlich nahe.

So wäre das Problem dann auch gelöst ;)
« Letzte Änderung: 24. August 2010, 14:12 von FlashBurn »

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #38 am: 24. August 2010, 19:57 »
Hallo,


Funktional ist sowieso ein ganz andere Baustelle... Ich vermute, du meinst prozedural.
Äh, ja, sorry.

Was macht "gutes" Exceptionhandling deiner Meinung nach denn aus, dass es unbedingt OO braucht? In Ada habe ich das jedenfalls schon benutzt ohne irgendwas zu machen, das verdächtig nach OO ausgesehen hätte.
Bei "gutem" Exceptionhandling kann der ich Exception etwas mitgeben (z.B. einen Fehlertext) eben weil sie ein Objekt (oder wenigstens eine Struktur) ist und ich kann den Typ der Exception feststellen als grobe Gruppe oder ganz exakt (das geht IMHO nur mit Vererbung).

Aber das eine Programm, das ich wirklich näher kenne, benutzt es zufällig und insofern hab ich dir ein Beispiel: qemu wäre das.
Also qemu brauche ich auf meinem Nicht-x86-System vielleicht wirklich mal (um Closed-Source-SW übernehmen zu können), von daher muss ich doch mal schauen was geht. :D Ne, also im Ernst, wenn Dir sonst keine "wichtigen" Programme einfallen dann mach ich mir darüber erstmal keine weiteren Gedanken.


Du musst doch bei einer Semaphore so oder so in den Kernel, warum dann nicht auch gleich alles im Kernel machen (so wird das möchte ich meinen auch unter Linux/Windows gemacht)?
Wozu? Wenn ich keine OS-verwaltete Semaphore brauche, das lohnt sich IMHO nur wenn man da länger drin bleiben will so das die wartenden Threads wirklich geblockt(pausiert) werden sollten, dann möchte ich keine verwenden. Normale Semaphoren kann man auch allein im User-Space implementieren, dazu reicht es das die CPU zuverlässig passende atomare Operationen bereitstellt. Darüber hatten wir doch schon mal vor einem halben Jahr diskutiert, da http://forum.lowlevel.eu/index.php?topic=2468.msg27861#msg27861 hatte ich doch extra mal 4 Varianten aufgelistet.

Wenn ich das richtig verstanden habe, kann man aber ein POSIX System sehr einfach unbenutzbar machen! Ich denke mal das ich da irgendeinen Denkfehler drin habe, denn ansonsten wären Server die ewig laufen gar nicht möglich.
Ich vermute mal die freigebenen IDs werden wieder recycled, ansonsten wären echte Langläufer wirklich kaum möglich.

Der Punkt ist aber, das egal wie du es machst, die Festplatte kann immer nur eine Anfrage gleichzeitig entgegen nehmen.
Genau da irrst Du, das ist eben der Punkt der mit NCQ geändert wurde. Siehe dazu http://de.wikipedia.org/wiki/NCQ. Damit kann man einer aktuellen SATA-HDD bis zu 32 Jobs geben die dann von der HDD in beliebiger Reihenfolge abgearbeitet werden dürfen und wenn ein paar der Jobs fertig sind darf man bereits neue Jobs nach schieben. Es dürften schon ein paar Szenarien existieren wo das tatsächlich etwas zusätzliche Performance bringt. Mit meinem PopUp-Thread-Konzept dürfte sich NCQ fast ohne Mehraufwand (im STAT-Treiber) nutzen lassen, ich muss nur darauf achten nicht mehr als 32 Jobs gleichzeitig an die HDD zu geben, ansonsten sehe ich keine konzeptionellen Beschränkungen.


Brauch ich nicht. Ob und wie gewartet wird, wird über Flags die man mit übergibt festgelegt!
Dann ist gut, das war aus Deiner ersten Erläuterung nur nicht so deutlich erkennbar.

Gelöst habe ich das so, das die Msgs sowieso nur als Synchronisierung oder kurze Statusmeldung genutzt werden soll (oder für Initialisierung ->).
Das IPC an sich soll über meine UserSpace Pipes laufen! Darüber werden dann auch Pfad und sowas übergeben, man könnte es sogar machen, das man die Anfragen auch darüber sendet (mit dem Gedanken spiele ich auch schon, könnte wesentlich schneller sein).
Das klingt nach nem dicken Haufen Arbeit wenn für jeden Kram erst ein Haufen verschiedener Kommunikationskanäle benutzt werden müssen, das müsstest Du mal etwas konkreter an einem Beispiel erklären. Wenn Du dann für das Leseende einer Pipe auch wieder einen Thread brauchst, der auf ankommende Daten wartet, dann werden es immer mehr Threads ohne das tatsächlich mehr gearbeitet wird.

Ja! Ich habe auch erst überlegt ob das wirklich sein muss, aber wenn man eine Nachricht verschickt, möchte man eigentlich auch mal eine bekommen. Gerade was den Clienten betrifft, der sendet eine Nachricht und wartet auf die Antwort, sprich er will auch eine Nachricht bekommen.
Also für mich ist eine Antwort auf eine Anfrage keine eigenständige Nachricht sondern nur eine weitere Phase eines synchronen IPC-Vorgangs. In meinem Kernel will ich auch konkret zwischen synchroner und asynchroner IPC unterscheiden und beides anbieten (wobei die asynchrone Variante wohl nur eine vereinfachte/abgekürzte Form der synchronen Variante ist, also nur wenig zusätzlicher Code erforderlich ist um beides zu unterstützen). Das meiste was in einem Mikro-Kernel-OS gemacht wird ist wohl synchrone IPC, eben RPC, nur wenige Dinge, wie z.B. Signale oder IRQs, müssen als asynchrones IPC behandelt werden.

Wie gesagt, habe ich meine UserSpace Pipes und im Endeffekt sollen die Nachrichten nur für die Initialisierung genutzt werden.
Sprich das ganze würde dann so ablaufen, das sich ein Client bei einem Server registriert/anmeldet und die beiden machen sich dann aus über welche Pipe gesendet und über welche Pipe empfangen wird.
Wie gesagt, das klingt sehr kompliziert.

Meine Pipe ist nichts anderes als SharedMem, der als Ringbuffer genutzt wird (es findet aber fast alles im UserSpace statt).
Was du schon ansprichst, das Weiterreichen von Daten, würde ich auch über SharedMem machen, ist einfach wesentlich schneller.
Also gerade das weiterleiten der Nutzdaten stell ich mir damit nicht so einfach vor, ich schätze da wirst Du in jeder Ebene kopieren müssen. Oder willst du es ermöglichen das 2 Pipes miteinander verbunden werden?


Ich muss dir ehrlich sagen, das ich nicht genau weiß was setjmp/longjmp ist!
Ich hab das auch noch nie benutzt oder in irgendeinem Programm-Source gesehen. Dieses Konstrukt würde ich persönlich als noch sehr viel verachtenswürdiger als goto einsortieren.
Anständige C-Coder benutzen soetwas nicht! ;)

Mir ist immernoch nicht klar was mit dem Exception-Handling so viel besser wäre.
Denn wenn ich da an einige meiner Funktionen denke, da hätte man dann viele try/catch Blöcke
Du sollst ja auch nur einen try/catch Block benutzen. In den catch-Abschnit kommt das Aufräumen und in den try-Abschnitt kannst Du den Code packen der möglicherweise Fehler generiert, wenn dort ein Fehler auftritt (also eine Exception geworfen wird) wird sofort in den nächsten catch-Abschnitt gesprungen der sich für diese Exception zuständig fühlt (das throw kannst Du wie ein goto betrachten und das sogar über Funktionsebenen hinweg).

Ich wollte das so lösen, das sich jeder Server, der Ressourcen eines Prozessen hat, sich im Kernel registriert, das er informiert werden möchte, wenn dieser Prozess beendet wird/ist.
Also so wie taljeth das in der genannten Diskussion erklärt hat?

Das kommt dann dem Node-Monitor (man will informiert werden, wenn sich eine Node, bzw. deren Kinder, irgendwie geändert haben, nützlich für einen Dateiexplorer, um informiert zu werden, wenn eine Datei gelöscht (oder ähnliches) wurde) ziemlich nahe.
Also zwischen einer Prozess-Sterbe-Nachricht und einer Veränderungsmitteilung ist IMHO schon ein gewisser Unterschied. Für die Veränderungsmitteilungen bei den Dateien wirst Du wohl einen zusätzlichen (aber ähnlichen) Mechanismus implementieren müssen.


Grüße
Erik
« Letzte Änderung: 24. August 2010, 20:02 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 #39 am: 24. August 2010, 21:26 »
Zitat von: erik
Ich vermute mal die freigebenen IDs werden wieder recycled, ansonsten wären echte Langläufer wirklich kaum möglich.
Das ist halt nicht möglich, so wie ich den Standard verstanden habe. Das eine ID nicht gleichzeitig für 2 Prozesse/Threads verwendet wird, ist ja wohl logisch!

Zitat von: erik
Genau da irrst Du, das ist eben der Punkt der mit NCQ geändert wurde. Siehe dazu http://de.wikipedia.org/wiki/NCQ. Damit kann man einer aktuellen SATA-HDD bis zu 32 Jobs geben die dann von der HDD in beliebiger Reihenfolge abgearbeitet werden dürfen und wenn ein paar der Jobs fertig sind darf man bereits neue Jobs nach schieben. Es dürften schon ein paar Szenarien existieren wo das tatsächlich etwas zusätzliche Performance bringt. Mit meinem PopUp-Thread-Konzept dürfte sich NCQ fast ohne Mehraufwand (im STAT-Treiber) nutzen lassen, ich muss nur darauf achten nicht mehr als 32 Jobs gleichzeitig an die HDD zu geben, ansonsten sehe ich keine konzeptionellen Beschränkungen.
Meine Aussage bleibt trotzdem richtig ;) Denn die Festplatte kann nur eine Anfrage zur gleichen Zeit entgegen nehmen, denn du hast ja nicht 32x Ports um die Festplatte anzusprechen!

Bei dem Fall würde es sich aber wirklich lohnen, wenn man 32 Threads hätte. Dann könnte jeder Thread warten bis neue Arbeit da ist und er könnte auch warten bis die Festplatte fertig ist (ich hoffe du verstehst wie ich das meine).

Zitat von: erik
Das klingt nach nem dicken Haufen Arbeit wenn für jeden Kram erst ein Haufen verschiedener Kommunikationskanäle benutzt werden müssen, das müsstest Du mal etwas konkreter an einem Beispiel erklären. Wenn Du dann für das Leseende einer Pipe auch wieder einen Thread brauchst, der auf ankommende Daten wartet, dann werden es immer mehr Threads ohne das tatsächlich mehr gearbeitet wird.
Ich sehe es erstmal so, das die Initialisierung ruhig ein wenig komplexer/langsamer sein darf, wenn der Vorgang an sich dann schneller geht.

Ist doch ganz einfach ;) Gehen wir mal davon aus, das du weißt in welche Pipe du schreiben sollst und aus welcher du lesen sollst.
Dann holst du dir die Anfrage mit "pipeRead(&msg,sizeof(struct msg_t))" (lohnt sich vorallem bei größeren Msgs, wie z.B. wenn man eine Datei laden soll), arbeitest die Msg ab und schreibst deine Daten dann per pipeWrite(&data,dateLen)". Abschließend musst du dann noch ein "pipeFlush()" aufrufen, damit der Thread der auf die Daten wartet sie lesen kann.
Der Vorteil ist, das diese Pipe´s fast komplett im UserSpace laufen, du also nicht erst die Daten in den Kernel kopierst (Server) um sie dann wieder in den UserSpace zu kopieren (Client).

Mal vom "pipeFlush()" abgesehen, ist das doch nicht viel anders als ein "msgRecv(&msg)" und ein "msgSend(&data,dataLen)".

Zitat von: erik
Also für mich ist eine Antwort auf eine Anfrage keine eigenständige Nachricht sondern nur eine weitere Phase eines synchronen IPC-Vorgangs. In meinem Kernel will ich auch konkret zwischen synchroner und asynchroner IPC unterscheiden und beides anbieten (wobei die asynchrone Variante wohl nur eine vereinfachte/abgekürzte Form der synchronen Variante ist, also nur wenig zusätzlicher Code erforderlich ist um beides zu unterstützen). Das meiste was in einem Mikro-Kernel-OS gemacht wird ist wohl synchrone IPC, eben RPC, nur wenige Dinge, wie z.B. Signale oder IRQs, müssen als asynchrones IPC behandelt werden.
Genau deswegen überlege ich auch ob nicht rein synchrones IPC doch besser ist (siehe L4), aber meine Momentane Lösung ist auch nicht so schlecht.

Obwohl wenn ich gerade mal genauer drüber nachdenke, dann muss ich sagen, dass ich es mir so wie ich es habe einfacher vorstelle.

Folgendes Szenario, ein Client sendet eine Nachricht und der Server kann sie gleich entgegen nehmen. Also kehrt der Client gleich wieder zurück ohne zu blockiert, dann wartet er auf eine Antwort, blockiert also. Aber in der Zwischenzeit sendet, aus welchem Grund auch immer, ein anderer Thread eine Nachricht an den Clienten und dieser wird aufgeweckt und bekommt nun eine Nachricht die er gar nicht haben will (im Moment) bzw. nicht erwartet.
Also müsstest du dort genauso etwas implementieren, das ein Thread auf eine Nachricht von einem bestimmten anderen Thread warten kann und du musst Queues implementieren wo du die Threads reinpacken kannst, falls sie nicht der gewünschte Thread sind.
Da wären wir ja aber praktisch schon wieder beim asynchronen IPC, nur das du halt anstatt Msgs die Threads in die Queue packst.
Wenn ich das von der Seite betrachte, ist asynchrones IPC mit aufgesetztem synchronen (das Warten auf eine Nachricht eines bestimmten Threads) doch besser. Da du dir die Möglichkeit der Asynchronität offen hälst.

Zitat von: erik
Also gerade das weiterleiten der Nutzdaten stell ich mir damit nicht so einfach vor, ich schätze da wirst Du in jeder Ebene kopieren müssen. Oder willst du es ermöglichen das 2 Pipes miteinander verbunden werden?
Ich habe das alles noch nicht 100%ig zu Ende gedacht und wenn der Code dann endlich mal geschrieben wird, wird es auch bestimmt noch verbessert. Aber ansich ist das erstmal nur für den Weg Server->Client gedacht, wo die Daten eine dynamische Länge haben.
Wenn der Festplattentreiber Daten liest und diese an den Storage-Server weitergibt, dann würde ich das natürlich über SharedMem machen, viel einfacher und schneller. Denn da bekommst du ja (meistens) eh Blöcke die sich auf 4kb Pages aufteilen lassen und es muss nicht mehr kopiert werden.
Der Client will dann ja vielleicht nur ein paar Bytes haben und die kopierst du dann per Pipe. Das Einblenden (MemoryMapping) von Dateien wird dann ja eh wieder über SharedMem gemacht.

Zitat von: erik
Du sollst ja auch nur einen try/catch Block benutzen. In den catch-Abschnit kommt das Aufräumen und in den try-Abschnitt kannst Du den Code packen der möglicherweise Fehler generiert, wenn dort ein Fehler auftritt (also eine Exception geworfen wird) wird sofort in den nächsten catch-Abschnitt gesprungen der sich für diese Exception zuständig fühlt (das throw kannst Du wie ein goto betrachten und das sogar über Funktionsebenen hinweg).
Genau dort liegt jetzt wieder das Problem (und der redundante Code). Stell dir vor du musst mehrmals Speicher anfordern, dann wird jedes Mal eine "NotEnoughMemoryException" geschmissen, aber da das Anfordern an verschieden Stellen stattfindet, musst du unterschiedlich Aufräumen, aber desto weiter du in einer Funktion bist, desto mehr musst du "gleich" machen!

Bsp:
try {
 mem= memAlloc4kb();
} catch (NotEnoughMemoryException e) {
 return 0;
}
try {
 id= allocNewID();
} catch (NoFreeIDException e) {
 memFree4kb(mem);
 return 0;
}
try {
 stack= memAlloc4kb();
} catch (NotEnoughMemoryException e) {
 memFree4kb(mem);
 freeID(id);
 return 0;
}
... //das Spiel kannst du jetzt weiter treiben, indem noch mehr Ressourcen benötigt werden
Ich weiß das man den 1. und 2. try-Block zusammenfassen könnte, aber mir ist gerade die Syntax nicht eingefallen. Aber den 3. try-Block musst du doch vom 1. trennen, da du ja ansonsten nicht weißt welche Anforderung nicht geklappt hat und welchen Speicher du wieder freigeben musst.
Ich hoffe du siehst was ich meine.

Zitat von: erik
Also zwischen einer Prozess-Sterbe-Nachricht und einer Veränderungsmitteilung ist IMHO schon ein gewisser Unterschied. Für die Veränderungsmitteilungen bei den Dateien wirst Du wohl einen zusätzlichen (aber ähnlichen) Mechanismus implementieren müssen.
Da ich einen Storage-Server habe, würdest du dich als Client bei dem registrieren, das du eine bestimmte Node beobachten möchtest und jedes Mal wenn sich was ändert, dann bekommst du eine Nachricht. Was in der Nachricht steht, darüber kann man jetzt diskutieren. Sprich ob da schon drin steht was sich genau geändert hat oder ob du das jetzt erst nachfragen musst.

Hier ist jetzt auch asynchrones IPC besser. Denn du willst einfach nur diese Nachricht loswerden und es interessiert dich nicht, ob der Client diese im Moment auch empfangen kann. Auch wäre hier noch ein Syscall nicht schlecht, wo du mehrere Nachrichten mit einmal versenden könntest!

Was den Unterschied betrifft, wenn du dem Unix-Prinzip treu bleiben willst "alles ist eine Datei". Dann bräuchtest du nur diesen Node-Monitor implementieren und alles andere läuft dann über das VFS.

 

Einloggen