Autor Thema: Allgemeine Fragen  (Gelesen 14001 mal)

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #40 am: 09. September 2010, 11:33 »
Hallo,


Mach das bitte.
Okay.
Was mir am meisten Fehlt ist eine Art Anleitung wie man CDI benutzen muss, quasi ein roter Faden der einem beim Lesen begleitet. Ich weiß dass das doof klingt aber besser beschreiben kann ich es nicht. Wenn man sich die Doxygen-Seite anschaut wird man vor allem mit Fakten erschlagen aber man sieht nur wenig Zusammenhang. Die 2 Bildschirmseiten Haupt-Text sind auf jeden Fall nicht genug um erst mal einen Überblick zu bekommen und eine grobe Vorstellung was man mit CDI eigentlich machen soll.
Es gibt zwar eine Liste was ein Treiber alles benutzen darf aber nirgends ist beschrieben was ein IRQ-Handler alles darf. Meine IRQ-Handler z.B. dürfen alles, sogar selber IPC benutzen (z.B. für fread). Auch sehe ich nicht wann ein IRQ beim IRQ-Controller quittiert wird.
Ich hoffe das ich am WE noch etwas Zeit finde um mir mehr Fragen auszudenken.

Davon, dass du es ein ums andere mal bescheiden und unbrauchbar nennst, wird es jedenfalls nicht besser.
Ja, Du hast recht, Sorry.

Zitat
Wenn ich z.B. einen Pointer in einen Treiber rein gebe welche Ansprüche werden an diesen Pointer gestellt? Mal abgesehen davon das er nicht NULL sein sollte.
Eigentlich keine.
Das klingt sehr gut.
Wird eigentlich garantiert das ein Treiber bei Schreibkommandos nie selber schreibend auf die Nutzdaten zugreift und umgekehrt? (Ich hoffe die Frage ist verständlich) Ich kann meine Segmente nicht nur als RW sondern auch als RO oder als WO markieren.

Der Treiber geht dann aber anschließend ran und verlangt für die cdi_mem_area die passenden Flags, die er gern hätte, z.B. physisch zusammenhängend und auf 16 Bytes alignt. Wenn das dann noch nicht passt, muss die CDI-Implementierung umkopieren oder was auch immer, um die richtigen Bedingungen herzustellen. In der Regel dürfte das aber auf ein nop rauslaufen, wenn die Hardware keine besonderen Wünsche hat.
Okay, das klingt ebenfalls sehr gut. Bei mir wird es solche Restriktionen jedenfalls nicht geben so das mich die Flags in cdi_mem_area wohl nicht interessieren werden.

Und falls es bis dahin nicht erledigt ist, kannst du dich selbst drum kümmern, das Interface zurechtzubiegen.
Gerne, aber das ist noch recht ferne Zukunft.

Die threadbasierte Variante wäre eben, dass wenn du gleichzeitig zwei Requests hast, dass jeder in seinem Thread abläuft und die Synchonisierung z.B. über Mutexe läuft. Vorteil ist, dass der Code für den einzelnen Request immer noch schön sequenziell ist wie bisher, Nachteil, dass man das Locking korrekt hinkriegen muss.
Das mit dem Locking ist meiner persönlichem Meinung nach nicht all zu schwer, auf jeden Fall finde ich den Vorteil des sequenziellen Codes beim Verstehen desselben als überaus Wertvoll.

Callbackbasiert wäre, dass du nur einen Thread hast. Um Requests abzusetzen, rufst du eine Funktion auf, die diesen Request abschickt und direkt zurückkehrt. Wenn der Request irgendwann fertig ist, ruft sie einen Callback auf. Während der Request läuft und der Kontrollfluss wieder in der CDI-Lib ist, kannst du aber schonmal den nächsten Request absetzen. Vorteile und Nachteile sind genau umgekehrt: Du kommst ohne Locking zurecht, weil immer nur ein Stück Code gleichzeitig läuft, aber der Code wird zerrissen und statt einem zusammenhängenden Ablauf bekommst du eine State Machine. Performancemäßig schätze ich ist die Callbackvariante wahrscheinlich auch ein bisschen besser. Aber ich bin mir nicht sicher, ob ich damit beispielsweise einen Dateisystemtreiber schreiben wollte...
Ich denke nicht das die Performance der Callback-Version besser ist, zum einem weil die Logik für die State-Maschine auch CPU-Zeit frisst (das dürfte sich mit den Kontext-Wechseln der Thread-Version ungefähr die Waage halten) und zum anderen skaliert diese Variante nicht mit mehreren CPUs (was IMHO schon wichtig ist und auch immer wichtiger wird), das könnte man zwar ausgleichen in dem man genau so viele Threads für die State-Maschine erstellt wie es CPUs gibt aber dann muss man auch wieder synchronisieren und hat im Endeffekt nur die Nachteile beider Varianten kombiniert. Ein weiterer Nachteil der Callback-Version ist das man den Source-Code nur dann versteht wenn er mit guten graphischen State-Diagrammen und einem fetten Haufen an Erklärungen in Textform dokumentiert ist. Sowas macht richtig viel Arbeit aber wenn man das nicht macht versteht den Code nicht nur kein anderer sondern man läuft Gefahr das man auch selber nicht mehr durchblickt.


Ich hab da in letzter Zeit auch ein bisschen über die Schnittstelle zwischen Dateisystem- und Massenspeicher-Treiber nachgedacht für mein OS. Für sowas wie beispielsweise ext2 dürfte doch die Callback-Basierte Variante relativ schön rauskommen. Im ersten Schritt setze ich Requests an das Backend für die direkten Blocks und die ersten Blocktabellen ab, im zweiten Schritt dann die einfach indirekten Blocks und weitere Tabellen, und so weiter. Das dürfte doch Performance-Mässig relativ gut rauskommen wenn man die Requests fürs Backend irgendwie schön asynchron umsetzen kann.
Das klingt schon verlockend, gerade ein einfacher Block-Device-Treiber sollte nur eine recht kleine State-Maschine benötigen die man tatsächlich noch beherrschen kann (und die Möglichkeiten von SATA/AHCI sind dem parallelen abarbeiten von HDD-Zugriffen auch förderlich). Die Frage ist nur ob der theoretische Performancevorteil wirklich so groß ist das er sowas rechtfertigt oder kommt man mit der Thread-Variante auch schon ganz gut ans Ziel (zum vorauseilenden Einlesen der nächsten Blocklisten könnte man ja einen extra Thread lostreten).


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 #41 am: 11. September 2010, 15:49 »
Wird eigentlich garantiert das ein Treiber bei Schreibkommandos nie selber schreibend auf die Nutzdaten zugreift und umgekehrt? (Ich hoffe die Frage ist verständlich) Ich kann meine Segmente nicht nur als RW sondern auch als RO oder als WO markieren.
Hm, darüber habe ich mir ehrlichgesagt noch keine Gedanken gemacht. Vorgesehen ist es momentan nicht.

Zitat
Ich denke nicht das die Performance der Callback-Version besser ist, zum einem weil die Logik für die State-Maschine auch CPU-Zeit frisst (das dürfte sich mit den Kontext-Wechseln der Thread-Version ungefähr die Waage halten)
Hm, ich glaube nicht, dass da eine großartige zusätzliche Logik dabei ist. Wenn irgendwas erledigt ist, wird halt ein Callback-Funktionspointer aufgerufen, mehr nicht. Wobei "irgendwas erledigt" bei tyndur vermutlich heißen würde, dass ein RPC reinkommt (entweder von einem anderen Service oder vom Kernel für einen IRQ).

Die Callbackmethode kommt mir zumindest mit dieser Implementierung natürlicher vor (und sie verlangt allgemein weniger vom implementierenden OS - nicht jeder hat Threads).

Zitat
und zum anderen skaliert diese Variante nicht mit mehreren CPUs (was IMHO schon wichtig ist und auch immer wichtiger wird), das könnte man zwar ausgleichen in dem man genau so viele Threads für die State-Maschine erstellt wie es CPUs gibt aber dann muss man auch wieder synchronisieren und hat im Endeffekt nur die Nachteile beider Varianten kombiniert.
Hm, ja. Wobei ein Gerätetreiber wohl eher nicht CPU-bound sein sollte.

Zitat
Ein weiterer Nachteil der Callback-Version ist das man den Source-Code nur dann versteht wenn er mit guten graphischen State-Diagrammen und einem fetten Haufen an Erklärungen in Textform dokumentiert ist. Sowas macht richtig viel Arbeit aber wenn man das nicht macht versteht den Code nicht nur kein anderer sondern man läuft Gefahr das man auch selber nicht mehr durchblickt.
Kommt drauf an, wie viele Zustände der Automat denn hat. ;) Aber es ist schon richtig, tendenziell wird es unübersichtlich.

Vielleicht sollten wir einfach mal ein Beispiel in beiden Modellen durchspielen. Angenommen wir haben ein Gerät, das zwei Requests hat, eine gewisse Menge Daten rauszuschreiben. Wir können immer nur einen bestimmten Teil davon auf einmal rausschreiben und kriegen einen IRQ, wenn das Gerät wieder bereit ist, mehr Daten anzunehmen. Das dürfte nicht so furchtbar exotisch sein, oder?

Bei der callbackbasierten Variante würde ich die Synchronisation wahrscheinlich so machen, dass ich eine Queue der Requests bastle und bei jedem IRQ daraus die vordersten Einträge rauslese und verarbeite. Wenn die Hardware Vollzug meldet, muss ich in der Lage sein, das wieder dem passenden Request zuzuordnen, um den Callback aufzurufen.

Wie würdest du bei der threadbasierten Variante vorgehen? Irgendwie muss ich jetzt die IRQs auf den passenden Thread mappen. Also im Prinzip die gleiche Vorgehensweise, nur dass ich mir nicht den Callback merke, sondern welchen Thread ich aufwecken 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 #42 am: 14. September 2010, 11:44 »
Hallo,


Zitat
Ich denke nicht das die Performance der Callback-Version besser ist, zum einem weil die Logik für die State-Maschine auch CPU-Zeit frisst (das dürfte sich mit den Kontext-Wechseln der Thread-Version ungefähr die Waage halten)
Hm, ich glaube nicht, dass da eine großartige zusätzliche Logik dabei ist.
Ich behaupte das hängt sehr vom Treiber ab, bei einem Block-Device-Treiber dürfte das wirklich sehr bescheiden bleiben wohingegen ein Dateisystemtreiber da schon deutlich komplexer wird. Ich bin eben der Meinung das der Performanceunterschied nicht so riesig werden wird, bei einer richtig komplexen Statemaschine ist eventuell ein Kontextwechsel in einen anderen Thread billiger und bei einer sehr simplen Statemaschine wohl eher der State-Wechsel, so das ich denke das dies kein wesentliches Kriterium darstellt.

Wobei "irgendwas erledigt" bei tyndur vermutlich heißen würde, dass ein RPC reinkommt (entweder von einem anderen Service oder vom Kernel für einen IRQ).
Micro-Kernel eben, bei einem Monolithen ist das eventuell etwas schwieriger weil man den Call-Back wohl nicht direkt im IRQ-Handler machen will, da bräuchte man dann irgendeinen geeigneten Mechanismus für nichtblockierende verzögerte Call-Backs.

und sie verlangt allgemein weniger vom implementierenden OS - nicht jeder hat Threads
Hm, das sollte etwas genauer erörtert werden. Threads sind sicher keine Anfängerübung aber richtiges Call-Back bestimmt auch nicht. Das Problem ist eben wie so ein Call-Back angenommen wird, die tyndur-Methode hat eben auch so ihre Tücken weil da schnell mal ein Treiber komplett blockieren könnte nur weil er momentan seinen Call-Back nicht los bekommt (das wäre IMHO ein KO-Kriterium).

Wobei ein Gerätetreiber wohl eher nicht CPU-bound sein sollte.
Warum nicht? Was denkst Du warum AHCI/SATA das tolle MSI-(X) unterstützt? Auch viele aktuelle Ethernet-Controller können gerade deswegen jedem Job einen bestimmten von mehreren IRQs zuordnen.

Vielleicht sollten wir einfach mal ein Beispiel in beiden Modellen durchspielen. Angenommen wir haben ein Gerät, das zwei Requests hat, eine gewisse Menge Daten rauszuschreiben. Wir können immer nur einen bestimmten Teil davon auf einmal rausschreiben und kriegen einen IRQ, wenn das Gerät wieder bereit ist, mehr Daten anzunehmen. Das dürfte nicht so furchtbar exotisch sein, oder?
Exotisch ist das sicher nicht aber IMHO eben auch etwas zu simpel um da wirklich spezifische Vorteile oder Nachteile herausarbeiten zu können.

.... Also im Prinzip die gleiche Vorgehensweise, nur dass ich mir nicht den Callback merke, sondern welchen Thread ich aufwecken muss?
Ganz genau. Eben deswegen finde ich das Beispiel etwas zu simpel.

Als komplexeres Beispiel würde ich da schon eher einen Dateisystemtreiber bevorzugen, nur leider hab ich davon kaum Ahnung so das ich mir da auch kein realistisches Szenario ausdenken kann. TCP wäre sicher auch ein sehr interessanter Kandidat. Andere Vorschläge?


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

 

Einloggen