Beiträge anzeigen

Diese Sektion erlaubt es dir alle Beiträge dieses Mitglieds zu sehen. Beachte, dass du nur solche Beiträge sehen kannst, zu denen du auch Zugriffsrechte hast.


Nachrichten - FlashBurn

Seiten: 1 ... 35 36 [37] 38 39 ... 43
721
Lowlevel-Coding / Re:IPC Methoden
« am: 27. August 2010, 22:24 »
Also was das Nachrichten senden/empfangen an sich betrifft, bin ich ein Anhänger von asynchron mit der Möglichkeit des Wartens auf eine Nachricht von einem bestimmten Sender. Das ganze noch gepaar mit Nachrichten fixer Länge.

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

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

Ich weiß allerdings noch nicht ob es besser ist, die Anforderung (sprich ob FILE_OPEN, FILE_CLOSE usw.) per Nachricht und nur der Dateiname+Pfad per Pipe gesendet werden soll oder ob ich alles per Pipe mache.
722
Lowlevel-Coding / Re:Funktionsaufruf Kernel -> Modul
« am: 27. August 2010, 22:18 »
Zitat von: erik
Also in meinem Konzept gibt es dieses Problem nicht und auch das Konzept von tyndur hat dieses Problem nicht (denke ich zumindest da die Daten ja auf den Stack gepackt werden und außer das der Stack knapp werden könnte sind da ja erst mal keine bestimmten Größenbeschränkungen). Das dieses Problem durch das aktive Abholen der Messages entsteht ist Dir ja bewusst. Da musst Du jetzt die richtigen Design-Entscheidungen treffen um damit umgehen zu können.
Wieso gibt es das Problem bei deinem Konzept nicht? Ich meine du sagst ja bei dem Abholen der Nachricht wie groß diese sein darf, meine Frage ist nun was passiert wenn die Nachricht zu groß ist (aus welchem Grund auch immer)?
Was verstehst du unter aktivem und was unter passivem Warten?

Zitat von: erik
Warum sollten auch die Wünsche einer Applikation mit niedriger Priorität jene Wünsche von Applikationen mit hoher Priorität behindern? Interessant wird es z.B. beim AHCI-Treiber, der könnte z.B. dafür sorgen das Jobs von Applikationen mit niedrigerer Priorität erst nach den Jobs von Applikationen mit höherer Priorität von der HDD abgearbeitet werden, das SATA-Protokoll bietet dafür einige Tricks.
KISS? Ich meine wenn du nur "einfach" sagst, dass du die Nachrichten (in deinem Fall die sendenden Threads) nach der Priorität sortierst, dann finde ich das auch ungünstig. Denn dann kann es passieren das eine Nachricht mit niedriger Priorität verhungert, weil ständig Nachrichten mit höherer Priorität ankommen. Wenn du sowas machen willst, dann sollte die Zeit bei der Prioritätenberechnung auch eine Rolle spielen und dann finde ich wird das ganze wieder so komplex das es sich gar nicht mehr lohnt und du mit der einfachen Variante wieder besser dran bist ;)

Zitat von: erik
Ich bin mir ziemlich sicher das sich mein IPC-Konzept auch auf Flat-Memory-Systemen implementieren lässt, ich sehe da jedenfalls keine Probleme. Wenn ich mit taljeth einig geworden wäre hätte ich das möglicherweise für tyndur umgesetzt.
Ich habe mir mal die Beschreibung deines Hardwarekonzepts angeguckt. Daher kann ich sagen, das du bei deinem Konzept entweder ziemlich viel Speicher verschwendest (da deine Segmente ja an 4kb aligned und mindestens 256byte groß sein sollen (habe ich zumindest so gelesen/verstanden) oder du dir ne ziemliche Fragmentierung einhandelst (was übrigens auch mein größter Kritikpunkt an Nachrichten mit dynamischer Länge ist, da müsste dann schon ein verdammt guter MemoryManager dahinter stecken und der dürfte dann wieder nicht der schnellste sein).

Was das designen einer eigenen CPU angeht, das hätte ich wohl besser nicht lesen sollen. Jetzt habe ich richtig lust bekommen mich eventuell auch mal daran zu probieren ;) ("nur" mit Hilfe von Logisim)
723
Lowlevel-Coding / IPC Methoden
« am: 27. August 2010, 15:25 »
Ich starte dann mal eine Diskussion zum Thema der verschiedenen IPC Methoden und wie diese implementiert werden können.

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

Fangen wir mit dem synchronen Message-Handling an.

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

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

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

Als nächstes also das asynchrone Message-Handling.

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

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

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

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

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

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

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

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

Ich denke am einfachsten wäre es, wenn jemand einfach mal ein "Problem" in den Raum stellt. Daran kann man dann am besten diskutieren welche Variante die beste ist.
724
Lowlevel-Coding / Re:Funktionsaufruf Kernel -> Modul
« am: 27. August 2010, 14:44 »
Zitat von: erik
So weit ich das weiß bindet eine WIN32-Konsolen-Applikation eine passende DLL ein welche dann das Fenster generiert, dafür einen Message-Handler registriert (ich glaube unter Windows kann man Messages nur an Fenster schicken, würde ja auch Sinn ergeben) und der libc dann cin, cout und cerr zur Verfügung stellt.
So ungefähr habe ich mir das auch vorgestellt. Der Punkt ist nun aber, das dieses Fenster jede Art von Msg bekommen kann (das Fenster hat sich geändert, eine Taste wurde gedrückt, mit der Maus wurde geklickt, ...) und halt auch damit umgehen können muss.

Sicherlich werde ich bei mir davon gebrauch machen, das ich sagen kann auf diesen Port darf nur ein bestimmter Prozess senden, aber das ändert halt an unterschiedlich langen Msgs nichts.

Pass auf, du hast einen allgemeinen Port bei deinem VFS Service und der muss halt verschiedene unterschiedlich lange Nachrichten entgegen nehmen. Du weißt nicht wann welche Nachricht ankommt.

Du holst also eine Nachricht ab mit der Meinung das die angegebene Länge des Buffers für die Nachricht reicht, was ist aber wenn das nicht der Fall ist?

Zu deinem Bsp mit dem TCP Paket an einen Port. Um das jetzt mal auf das umzumünzen was ich meine, stell dir einfach vor das du ja unterschiedlich lange Pakete bekommen kannst, dies aber vor dem Empfang nicht wissen kannst. Einfachste Möglichkeit ist natürlich einfach einen Buffer zu nehmen, der das größte mögliche Paket repräsentiert, dann bist du immer auf der sicheren Seite.

Zitat von: erik
Ich möchte für synchrones IPC den Sheduler gar nicht bemühen. Der Client macht seinen Syscall für das IPC (damit landet die CPU im System-Mode), der passende Syscall-Handler markiert den aktuellen Thread als "blockiert wegen sync. IPC" und sichert seine wichtigsten Register in dessen Thread-Strukt, dann wird ein neuer Thread im Prozess des gewünschten Service erstellt, der mitgegebene Speicher wird in den Service-Prozess eingeblendet, der Stack und die Register für den neuen Thread vorbereitet und als letztes wird dieser PopUp-Thread als running (auf der aktuellen CPU) markiert und hinein gesprungen (damit ist die CPU wieder im User-Mode). Die Zeitscheibe des Callers läuft auch noch weiter (im System-Mode läuft die aber nicht weiter). Der Service-Thread erledigt seine Aufgabe und macht am Ende ein 'syscall_msg_sync_handler_end()', wenn das noch vor Ablauf der geerbten Zeitscheibe passiert wurde eben gar nichts geschedult. Im Syscall für 'syscall_msg_sync_handler_end()' (die CPU ist wieder im System-Mode) wird der geerbte Speicher wieder aus dem Service-Prozess entfernt, der PopUp-Thread gekillt, die gesicherten Register vom Client-Thread wieder hergestellt, die syscall_msg_sync_handler_end()-Parameter in die richtigen syscall_msg_sync_send()-Rückgabewert-Register geladen und als letztes der Client-Thread als running (auf der aktuellen CPU) markiert und hinein gesprungen (damit ist die CPU wieder im User-Mode). Es gab also genau 4 CPU-Mode-Wechsel und der Scheduler wurde nicht bemüht falls die Zeitscheibe des Clients noch gereicht hat. Ich denke das ist der schnellst mögliche Weg für synchrones IPC. Falls der Job beim Service doch länger dauert und dieser unterbrochen wird kommt es natürlich auch auf den Scheduller an wann der Job weiter bearbeitet wird.
Wow ... du willst aber ganz schön in die Prozesse eingreifen. Das klingt ja schon fast nach nem Signal Handler, der halt als Thread implementiert ist.

Das der Service-Thread die Zeit und Priorität vom Client-Thread bekommt gibt es glaub ich auch schon woanders. In irgendeinem Hobby-OS hatte das jemand schonmal probiert und wirklich was gebracht hatte es da glaub ich nicht.

Ich stelle mir das zwar kompliziert vor sowas zu implementieren, aber damit hättest du dann sowas wie eine Prioriäten Warteschlange für Msgs.

Was ich auch noch wollte. Ansich lassen sich unsere verschiedenen Ansichten eh nicht so einfach miteinander vergleichen, da du keinen "flat address space" hast, was ansich heutezutage die Normalität darstellt.
Ich würde auch sagen, das du dir da ziemliche Probleme mit aufhalst! Ich denke da an Fragmentierung und das es das Auslagern von Speicher auf einen Datenträger eher schwieriger macht bzw. solche Segmente dann gar nicht ausgelagert werden können (genaus wie ich nie SharedMem auslagern würde).
725
Lowlevel-Coding / Re:Funktionsaufruf Kernel -> Modul
« am: 26. August 2010, 22:07 »
Zitat von: erik
Wie kommst Du denn da drauf, ich hab doch eindeutig geschrieben das dem Handler die Größe der Speicherbereiche (die er vom Client geerbt bekommt) erfährt. Schau Dir Bitte noch mal die letzten 8 Parameter im Signal-Handler an, mein letztes Code-Beispiel von heute Mittag, da sind 4 Pointer mit je einer Längenangabe. Der Pointer zeigt direkt in den Speicher des Clients (per Shared-Memory) und die Längenangabe kommt ebenfalls vom Client. Auf diese Art kann der Handler mit jeder beliebigen Größe umgehen, er muss ja nichts dafür vorbereiten, das macht alles der Kernel.
Mir sind deine Parameter schon klar, aber d.h. auch das du davon ausgehst, dass du immer weißt wann was für eine Nachricht kommt.

Zitat von: erik
Die hat das Programm aber auch angefordert, indem aus cin gelesen wird. Wenn das Programm aber nicht mal cin öffnet dann darf es IMHO auch nicht mit Tastatureingaben belästigt werden.
Ich muss ehrlich sagen, das ich nicht genau weiß wie das z.B. unter Windows läuft, aber ich dachte du bekommst halt ständig Nachrichten und die die du verarbeiten willst machst du halt und alle anderen werden gleich wieder verworfen.

Bzw. ich stelle es mir so auch einfacher vor, du sendest dem Programm halt das die Mouse gerade da und da geklickt hat und wenn das Programm daraus was machen will dann macht es was drauß und wenn nicht, dann wird die Nachricht halt gelöscht.

Zitat von: erik
Meinst Du diese Frage ernst? Hast Du etwa zu lange für unsere letzte Familienministerin gearbeitet?
Es ging mir darum, das du immer mit allem rechnen musst. Ansonsten fällt das dann unter die Kategorie Sicherheitslücke/Bug was irgendwie ausgenutzt werden könnte.

Zitat von: erik
Beim synchronen Vorgang blockiert der Client so lange bis er eine Antwort erhält, was natürlich das zustellen der Anfrage beim Service mit einschließt. Beim asynchronen Vorgang kann man es zwar so implementieren das der Sender immer sofort weiter machen kann aber das bedeutet das die gesamte Message irgendwo (im Kernel) gequeuet werden muss (falls der Empfänger gerade nicht bereit ist) und das kostet Speicher. Ich werde es in meinem OS wahrscheinlich so einrichten dass das queuen nur für kleine Nachrichten möglich ist die der Kernel selber senden will, denn kann man ja eben nicht blockieren. Wenn ein User-Mode-Prozess eine große asynchrone Nachricht verschicken will und der Empfänger gerade nicht bereit ist, weil z.B. kein zusätzlicher PopUp-Thread mehr erstellt werden darf, dann wird er eben blockiert, ich sehe da keine negativen Folgen.
Ich sehe da auch erstmal keine negativen Folgen. Ich wollte halt nur darauf hinaus das du synchron damit verbindest das du automatisch auch gleich eine Antwort bekommst und das hat damit nichts zu tun. Denn du musst ja auch die Möglichkeit haben einfach eine Msg zu senden ohne eine Antwort zu wollen (Bsp. Server) und da macht sich dann asynchron einfach besser. Ansonsten ist natürlich aus Programmierersicht synchron viel sympatischer.

Zitat von: erik
Das klingt irgendwie ungünstig langsam. Für eine simple RPC-Anfrage, z.B. die Größe einer Datei ermitteln, wird 2 mal auf den Scheduler gewartet. Ein ls in einem Verzeichnis mit 10000 Dateien dürfte da eine echt harte Geduldsprobe werden.
Ich weiß jetzt nicht wie du das machen willst, aber wenn ein anderer Thread aufgerufen wird (was ja praktisch beim RPC passiert), dann kommt es auf die Scheduling-Strategie an, wann er läuft. Sprich wenn ein anderer Thread eine höhere Priorität hat als der Service-Thread dann darf natürlich erstmal der andere Thread laufen.

Ich weiß nicht was du sonst mit warten auf den Scheduler meinen könntest, kannst das ja mal genauer an einem Bsp erklären.

Zitat von: erik
Nur zu. Ich empfehle Dir trotzdem irgendwann einmal eine mächtige Programmiersprache, wie z.B. C++, zu erlernen und damit auch größere Programm zu entwickeln. Das eröffnet einem auch neue Sichtweisen für viele Dinge.
Ich denke durchaus daran C++ für meine Server zu nutzen, nur im Kernel fällt es halt aus. Im Moment scheitert es noch am mangelnden Wissen und das ich weder eine Libc noch eine Libc++ habe.

edit::

Zitat von: erik
Wenn der Artikel wirklich schön ist wäre es toll wenn Du da mal einen Link geben könntest.
Link kommt sofort ;) http://www.ddj.com/showArticle.jhtml?articleID=189401457
726
Lowlevel-Coding / Re:Funktionsaufruf Kernel -> Modul
« am: 26. August 2010, 13:06 »
Zitat von: erik
Einmal pro was? In meinem Konzept brauch ich nur die Message-Target-ID des VFS und schon kann ich ohne weitere Vorbereitungen ein (oder beliebig viele) fopen machen. Diese ID soll dem Prozess bei seiner Erstellung gleich mitgegeben werden so das in der Init-Funktion der libc diese ID nur noch in die richtige Variable kommt (noch bevor main aufgerufen wird).
Das ist der Punkt der noch ein wenig schwammig ist ;) Ich weiß nicht ob die Initialisierung einmal pro Prozess (was das arbeiten von mehreren Threads aus wieder unschön macht) oder pro Thread (mehr "Aufwand" bzw. MemoryOverhead).

Ich wäre ja dafür einen neuen Thread aufzumachen, wo man sich mal über verschiedene IPC Methoden und eine effiziente Implementierung unterhalten kann!

Zitat von: erik
Kann ich nicht genau sagen, nur Du kennst Dein Konzept gut genug um das sagen zu können. Bis jetzt hast Du von Messages und Ports, von Pipes und von Shared-Memory geschrieben. Für all das musst Du zumindest Verwaltungsfunktionen in Deinen Kernel implementieren.
Hab ich schon, aber (mal die Pipes ausgenommen) müsstest du doch auch haben, oder?

Was die dynamischen (die Größe) Msgs betrifft. Dann gehst du aber immer davon aus das an einem "Port" (oder was du als Abstraktion nimmst) immer nur eine Art von Msg ankommen kann. Sprich die müssen immer die selbe Länge haben.

Zitat von: erik
Ich verstehe zwar nicht was Du damit meinst das die Pipes "fast ausschließlich im User-Space" sind aber zumindest muss der Sender den Empfänger irgendwie (wohl per Syscall) darüber benachrichtigen wenn er neue Daten reingelegt hat, dauerndes Pollen des Empfängers willst du bestimmt nicht. Das selbe noch in die andere Richtung wenn der Buffer voll ist und der Empfänger dem (wartenden weil sendewilligen) Sender mitteilen muss das wieder Platz geschaffen wurde.
Im Moment (da ich gerade nen schönen Artikel über IPC gefunden habe und überlege diese Konzept zu nutzen) kannst du dir meine Pipes wie SharedMem vorstellen. Du schreibst/liest in diesem SharedMem. Wie gesagt, du musst dann halt in den Kernel um dem eventuell wartenden Reader/Writer "bescheid" zu geben.

Zitat von: erik
Eben genau diese Fehleranfälligkeit meinte ich damit das der Empfänger niemals eine Nachricht fehlerhaft decodieren darf, dann erkennt er nämlich nicht mehr die nachfolgenden Nachrichten (er weiß nicht mehr wo diese Anfangen) und die Pipe ist erledigt.
Da wollte ich dann "Dummheit wird sofort bestraft" ;) anwenden. Sprich macht der Client etwas was er nicht durfte/sollte, dann wird die Pipe halt dicht gemacht und die Kommunikation wird abgebrochen.

Zitat von: erik
Du hast meine Frage(n) nicht beantwortet! Wieso kann man eigentlich überhaupt einem reinen Client (z.B. ein simples Hello-World-Programm) eine Message schicken? Diese potentielle Gefahr empfinde ich als groben Design-Fehler.
Dieses Warten auf bestimmte Antworten macht IMHO nur den Kernel-Code noch komplizierter.
Eine Tastatureingabe, wäre z.B. auch ne Msg oder dass das Fenster verkleinert wurde und und und ... Also es gibt genug Gründe wieso man einer Anwendung Msgs schicken kann.
Dass das alles bei einer Konsolenanwendung keinen Sinn macht ist klar. Aber als Gegenfrage würde ich stellen, wieso sollte dir (als PC) jemand ein schädliches Netzwerkpaket schicken?

Mein Code ist deswegen nicht wirklich komplizierter geworden, ich meine es wird einfach jedes Mal wenn eine neue Msg in die Queue kommt, überprüft ob der Sender gleich der ID ist auf die man wartet und wenn dem so ist, wird der wartende Thread aufgeweckt.

Zitat von: erik
Beim asynchronen IPC muss der Client 2 Syscalls machen um das Verhalten von synchronem IPC zu erzielen (einmal Anfrage schicken und einmal auf Antwort warten/abholen).
Also ich würde sagen, das hat nichts mit synchron oder asynchron zu tun! Synchron heißt doch ansich nur, das solange wie der Empfänger die Nachricht nicht entgegen nimmt/kann, blockiert der Sender.
Außerdem habe ich genau für die Situation doch meinen "send+recv"-Syscall.

Zitat von: erik
Wann läuft der Service-Thread (der den Job bearbeitet) eigentlich los? Wird der Client gleich bei der Anfrage geschedult und die CPU dem Service-Thread übergeben so das der Job möglichst schnell anfangen kann (was bedeutet das der Client erst später dazu kommt seine Antwort abzuholen)? Oder wird der Service-Thread nur auf "runnable" gesetzt und läuft erst dann los wenn der Scheduler mal wieder vorbei schaut.
Also wenn ein Thread auf eine Antwort wartet und diese bekommt wird er wieder in die Ready-Queue des Schedulers gepackt (wann der Scheduler den Thread laufen lässt, kommt auf die Strategie und auf die anderen laufenden Threads an).

Was die Exceptions betrifft, ich bleibe bei meinem goto ;)
Aber es gibt durchaus Sachen wo die sehr hilfreich sein können, in der Libc wird sowas ja mit "errno" gelöst, sprich alle Werte die die Funktion zurückgibt sind "gültig", du musst halt nur in "errno" nachgucken, ob ein Fehler und was für ein Fehler aufgetreten ist.
727
Lowlevel-Coding / Re:Funktionsaufruf Kernel -> Modul
« am: 25. August 2010, 19:13 »
Zitat von: erik
Also wenn Du den Standard richtig verstanden hast dann ist das schon ziemlich mysteriös. Im Prinzip verbraucht ja jedes fork eine ID und wenn die endlich sein sollten dürften so manche Web-Server (solche die für jede ankommende TCP-Verbindung einen fork machen) nicht besonders lange laufen. Ich vermute mal die OSe halten sich in diesem Punkt nicht an den Standard, anders kann ich mir das nicht erklären.
Es gibt Situationen wo das Sinn macht, aber im Großen und Ganzen glaube ich eher das ich den Standard da nicht richtig verstanden habe.

Zitat von: erik
Nein! Man kann einer modernen SATA-HDD, wenn sie an einem Host-Controller hängt der im AHCI-Modus arbeitet (und nicht mehr im klassischen IDE-Modus), wirklich mehrere Jobs geben ohne warten zu müssen bis die vorangegangenen Jobs fertig sind. SATA arbeitet paket-orientiert und da ist es wirklich möglich mehrere Aufträge hintereinander an die Platte zu schicken, ließ Dir am besten mal die SATA-Spezifikation und die AHCI-Spezifikation durch.
Was ich meinte war, das du immer nur einen Job zur gleichen Zeit an die Festplatte geben kannst und dass das nicht parallel, auf mehreren CPUs, gleichzeitig läuft.

Zitat von: erik
oder etwas anderem wo nur von einer (oder vielen) Datei(en) die Größe ermittelt werden soll. Da würdest Du einen Haufen Arbeit machen nur um wenige Infos zu bekommen, das erscheint mir unnötig kompliziert (und auch nicht schneller). Nebst dessen das Du in Deinem Kernel eine Menge Zeugs implementieren musst und ja theoretisch jeder Teil davon Fehler enthalten kann, zumindest generierst Du so eine Menge Code für den Kernel und steigerst damit die Wahrscheinlichkeit für Fehler (ohne nennenswert mehr Funktionalität zu bekommen).
Also die Initialisierung, würde ja nur einmal ablaufen und das würde dann auch von meiner Libc gemacht werden.

Was meinst du genau, was ich da alles im Kernel implementieren müsste?

Zitat von: erik
Das sieht nach ner Menge Syscalls aus. 1. der Client für das Abschicken der Anfrage. 2. der Service für das Abholen der Anfrage (hier eventuell noch ein weiterer weil er erst mal nur die Länge der eigentlichen Anfrage erfahren wollte). 3. der Service für das Schreiben der Antwort (das Flush zähle ich mal nicht extra, das ist ein Flag beim Schreiben). 4. der Client zum Abholen der Antwort. Macht 4 Syscalls mit insgesamt 8 CPU-Mode-Wechseln (+ zusätzliches zur Vorbereitung/Aufräumen). Bei mir sind es nur 2 Syscalls (zum einen macht der Client seine Anfrage und zum anderen meldet der PopUp-Thread das er fertig ist und gekillt werden kann) mit insgesamt 4 CPU-Mode-Wechseln (ohne das irgendwelche Shared-Memorys o.ä. extra vorbereitet/aufgeräumt werden müssen).
Als erstes, wie willst du es denn bei Msgs dynamischer Länge machen? Ich meine an sich müsstes du erstmal fragen wie groß die Msg ist, dann nen Buffer allokieren und dann die Msg abholen, oder?

Dann noch was zu meinen Pipe´s, die sind fast ausschließlich im UserSpace, um daraus was zu lesen musst du halt nicht in den Kernel, das ist ja der ganze Sinn und Zweck der Sache. Du musst nur in den Kernel, wenn du lesen willst und keine Daten vorhanden sind oder du schreiben willst und kein "freier" Speicher mehr da ist.

Zitat von: erik
Woher weiß Dein Dateisystemtreiber wie groß dieser Auftrag ist wenn er die Länge des Dateinamens noch nicht kennt? Byte-orientierte Pipes sind für solche Dinge extrem ungeeignet weil der Empfänger niemals die Synchronisation auf die Message-Grenzen verlieren darf.
Deswegen war es nur ein einfaches Bsp.!

Es gibt die Möglichkeit das ich eine Msg mit nem festgelegten Header habe und der String danach kommt (wobei die Länge gegeben werden kann oder einfach gelesen wird bis "\0" kommt, ich weiß fehleranfällig).

Was du mit der Synchronisation meinst ist mir gerade auch nicht klar.

Zitat von: erik
Ich finde das recht einfach und übersichtlich und es ist völlig egal ob 5 Bytes oder 500Mbytes gelesen werden sollen. Als einzigste Vorbereitung muss 'vfs_msg_id' von der libc einmal beim Prozessstart ermittelt werden, damit man weiß wohin mit den IPC-Anfragen.
D.h. ja das du dich um alles im Clienten kümmern willst, sprich das wenn du 5bytes an der Position 100 lesen willst, dann musst du "wissen" wieviel du von der Datei laden musst und dann musst du die richtigen Daten noch rumkopieren (das Rumkopieren muss ich auch machen).

Ich weiß nicht wie du das mit dem SharedMem machst, aber ich stelle es mir schwierig vor, da dann nen vernünftiges VFS mit Caching aufzusetzen.

Zitat von: erik
Wieso kann man eigentlich überhaupt einem reinen Client (z.B. ein simples Hello-World-Programm) eine Message schicken? Das simple Programm ist doch darauf gar nicht ausgelegt. Ich persönlich sehe es als enormes Risiko an wenn man Prozessen etwas geben kann womit sie nichts anfangen können bzw. was sie gar nicht explizit erlaubt haben. Einem PC im Internet kann man doch auch nur auf die TCP/UDP-Ports was schicken die dieser PCs explizit geöffnet hat (um eben was von außen empfangen zu können).
Was ist eigentlich wenn ein böser Prozess in genau diesem Moment dem Client eine fingierte Antwort schickt? Ich persönlich bin der Meinung das der OS-Kernel es garantieren muss das der Client genau seine Antwort erhält und nichts anderes.
Also bei mir kannst du beim Erstellen des Ports festlegen, ob ein bestimmter Prozess oder alle Prozesse an diesen Port Nachrichten schicken dürfen. Willst du aber als Clienten nur einen Port (was vollkommen ausreichend sein sollte) öffnen, dann musst du alle Prozesse senden lassen.
Ich will ja nicht noch eine Liste an den Port hängen welche Prozesse etwas senden dürfen. (das selbe gilt für das Abholen der Msgs)

Zitat von: erik
Ich bleibe lieber dabei das ich beides anbiete, der zusätzliche Code im Kernel dürfte sehr überschaubar bleiben und den Performance-Verlust um aus etwas Asynchronem etwas Synchrones zu machen will ich nicht (eben weil das Synchrone das signifikant Häufigere und deutlich Wichtigere ist).
Welcher Performanceverlust?

Zitat von: erik
Wenn Du erst für jeden kurzen Datei-Lesevorgang dann ein paar Ebenen tiefer Shared-Memory vorbereiten musst stelle ich mir das nicht besonders performant vor.
Also ich habe schon vor irgendwelches Caching zu nutzen (so dass z.B. der Storage-Server die Daten schon hat und den Festplattentreiber gar nicht erst danach fragen muss, da wird dann auch nichts mit SharedMem initialisiert) und das was du beschrieben hast, ist doch auch nur eine Form des SharedMem!?

Was deine Lösung (die 1.) für die Exceptions betrifft. Das findest du lesbarer und schöner (mal abgesehen von dem ganzen Overhead und das du es im Kernel nicht nutzen kannst)?

Also da bleibe ich lieber bei meinem goto ;)

Wie machst du es eigentlich, wenn eine Funktion zwar einen Fehler zurückgibt, du aber aus dieser Ebene nichts mehr freigeben musst, nur halt aus den äußeren, einen leeren catch-Block?
728
Lowlevel-Coding / Re:Funktionsaufruf Kernel -> Modul
« 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.
729
Lowlevel-Coding / Re:Funktionsaufruf Kernel -> Modul
« 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 ;)
730
Lowlevel-Coding / Re:Funktionsaufruf Kernel -> Modul
« 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!
731
Lowlevel-Coding / Re:Funktionsaufruf Kernel -> Modul
« 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?
732
Lowlevel-Coding / Re:Funktionsaufruf Kernel -> Modul
« 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!
733
Lowlevel-Coding / Re:Funktionsaufruf Kernel -> Modul
« 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)
734
Lowlevel-Coding / Re:Funktionsaufruf Kernel -> Modul
« 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).
735
Lowlevel-Coding / Re:Funktionsaufruf Kernel -> Modul
« 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)?
736
Lowlevel-Coding / Re:Funktionsaufruf Kernel -> Modul
« am: 22. August 2010, 12:11 »
Zitat von: erik
Hab ich gemacht und muss sagen das ich für einen richtigen SlabAllocator wohl keinen Bedarf in meinem Kernel habe (sowas lohnt sich wohl eher in einem Monolithen), außerdem hab ich keinen BuddyAllocator im Hintergrund (ich will Segmente und nicht Pages als Basis für meine Speicherverwaltung).
Deswegen (Segmente) wohl auch die LDT (das würde es zumindest erklären)?! Zu dem das es sich nur für einen Monolithen-Kernel lohnen würde, kommt ganz drauf an, wie mikro dein Mikrokernel werden soll.

Zitat von: erik
Mein kmalloc wird sich wohl mit maximal etwa 5 verschiedenen Größen konfrontiert sehen von daher versuche ich andere Wege zu gehen, vielleicht mach ich für jede Objekt-Größe gleich einen eigenen (passend optimierten) Heap und entsprechend viele kmalloc_??? Funktionen.
Naja, aber falls du dann in Zukunft doch mal noch was hinzufügen willst, würde sich ein allgemeiner Allocator schon anbieten.

Ich nutze bei 2 Sachen auch gleichartigen Code, der sich nur in ein paar Konstanten unterscheidet und jedes Mal wenn ich nen Fehler finde, darf ich in der anderen Funktion den Fehler auch korrigieren und das ist leider sehr fehleranfällig :(

Zitat von: erik
Der Vorteil für SMP ergibt sich vor allem dadurch das ein CPU-lokaler Pool (quasi mehrere kleine L1-Pools vor dem großen globalen L2-Pool) nicht erst gelockt werden muss (vorausgesetzt der Code ist nicht unterbrechbar, was auf meiner Plattform für Kernel-Code immer zutrifft).
Der Code muss gelockt werden, aber so dass du immer den Lock bekommst (es geht im Endeffekt nur darum, das die Ints ausgeschaltet werden, ob das nen Lock ist, darüber lässt sich jetzt streiten).

Zitat von: erik
Das mit dem colorring ist zwar interessant aber ob der Beschleunigungseffekt wirklich so groß ist wage ich etwas zu bezweifeln, vor allem dürfte das nichts mit der TLB-Effizienz zu tun haben da der TLB ja mit Page-Granularität arbeitet.
Beim Coloring ging es glaub ich eher um Cachelines und das hatte nichts mit dem TLB zu tun, aber ich nutze es auch nicht (war mir zu kompliziert es zu implementieren).

Zumal ich den SlabAllocator nicht 1:1 übernommen habe.

@back2topic

Ich muss zugeben, dass ich den "vermeintlichen" Vorteil eines Signal-Handlers (noch) nicht verstanden habe. (Um späteren Problemen aus dem Weg zu gehen, ich bin eh kein Fan vieler Unix-Vorgehensweisen ;) ) Für mich überwiegen eher die Probleme und Nachteile.
737
Lowlevel-Coding / Re:Funktionsaufruf Kernel -> Modul
« am: 20. August 2010, 10:25 »
Zitat von: noob
@Flashburn Du mischt dich in letzter Zeit immer in Themen ein, wenn sie auf bestem Wege sind mehr als 20 Antworten zu bekommen. Wink
Könnte daran liegen das ich jetzt mehr Zeit habe ;) (und dies ein Interessantes Thema werden könnte/ist)

Zitat von: erik
Ich habe keine Ahnung was das ist (aber ich vermute die Suchmaschine meines persönlichen Vertrauens wird mir eine Antwort liefern).
Also den sollte man kennen und wenn nicht dann schnell darüber informieren ;)

Ich hatte mir letztens mal den Code von Haiku (teilweise) angesehen und wenn ich den richtig verstanden habe, dann werden "gelöschte" Threads in eine Liste der Prozessstruktur gepackt (nennen die "dead thread queue" oder so ähnlich), damit sie dann schneller wieder zur Verfügung stehen, wenn der Prozess das nächste Mal einen Thread erstellt.

Zitat von: erik
Außerdem sind IMHO die Threads das wichtigere, die müssen möglichst schnell angelegt und gelöscht werden können.
Ich kann mich nur wiederholen, SlabAllocator ;) Ist im Endeffekt (vereinfach ausgedrückt) nur ne allgemeinere Variante (also sprich für jede beliebige Art von Objekten) der Methode die du beschreibst um den Speicher der Threadstrukturen nicht immer gleich wieder freizugeben, sondern in einen "Pool" zu packen.

In der aktuellesten Fassung (damit meine ich das letzte Paper was dazu veröffentlich wurde) ist das ganze sogar soweit optimiert wurden, das es verdammt gut mit SMP skaliert (und auf 1-CPU-System auch ein wenig schneller geworden ist).
738
Lowlevel-Coding / Re:Funktionsaufruf Kernel -> Modul
« am: 19. August 2010, 22:08 »
Ich will mich auch mal einmischen ;)

Zitat von: erik
Ich wollte keinen prozesslokalen Thread-Pool, wie das manche (User-Space-)Librarys machen, sondern die eigentliche CreateThread-Funktion im Kernel, die auch vom IPC-Subsystem benutzt wird, holt sich Stacks und Thread-Descriptor-Structs aus zwei vorallozierten Speicher-Pools so das die 2 malloc entfallen können und nur der Stack in die LDT des gewünschten Prozesses und das Thread-Descriptor-Struct in dessen Prozess-Verwaltung integriert werden müssen. Ich hoffe damit das CreateThread sehr billig machen zu können, nur wenn der Pool leer ist wird tatsächlich Speicher alloziert aber dann gleich für X Threads auf ein mal. Jedes mal wenn ein KillThread() aufgerufen wird wird der Stack und das Thread-Descriptor-Struct nicht freigegeben sondern erstmal in den Pool zurückgelegt (wirklich freigegeben wird der Speicher erst wenn der Pool sehr voll ist), auf einem gleichmäßig belastetem System dürfte damit für IPC kaum neuer Speicher für Threads alloziert werden müssen. Damit wäre es auf einem SMP-System sogar möglich das auf zwei CPUs (von Threads aus unterschiedlichen Prozessen) gleichzeitig zwei RPC-Anfragen an den selben  Service gestellt werden, im Kernel gleichzeitig zwei neue Threads erstellt und in den Service-Prozess eingebunden werden und dann auch gleichzeitig loslaufen. Nur das Entnehmen vom Speicher-Pool (jeweils ein Stack-Segment und ein Thread-Descriptor-Struct) und das Einhängen in den Service-Prozess müssen kurz serialisiert werden, in der kernelinternen CreateThread-Funktion.
Klingt irgendwie verdächtig nach einem SlabAllocator ;)

Wimre dann macht Windows etwas ähnliches, was du meinst, für die Prozesse. Ich meine irgendwo mal gelesen zu haben, das alle Prozessstrukturen (es werden glaube ich nur 2048 zugelassen), schon vorher alloziert werden und in eine Art "Ring" gepackt werden. Sprich der Speicher wird nie freigegeben, sondern es wird immer an das Ende herangehängt und am Anfang heraus genommen.
739
Lowlevel-Coding / Re:Port-Verwaltung
« am: 11. August 2010, 17:19 »
Was die Schadprogramme betrifft die sich als Treiber tarnen.

Also ich würde mir darüber an sich eigentlich keine Gedanken machen (weil wieso sollte man ein Schadprogramm für dein OS schreiben?), aber besser jetzt als später.

Allerdings kommt es jetzt noch drauf an, wie du die Treiber lädst. Ich will damit sagen, das es einen unterschied macht, ob sich ein Treiber irgendwo selbst einträgt oder ob z.B. ein Device-Manager einen vermeintlichen Treiber fragt, ob er für diese eine bestimmte Hardware (für die er gerade einen Treiber sucht) ein Treiber ist.
Gut jetzt könnte der Treiber immer sagen das er für die Hardware zuständig ist.

Ich wollte das ganze so lösen, das ich nur 1 oder 2 bestimme Verzeichnisse habe, wo sich die Treiber befinden.
Entweder ich lade jeden Treiber kurz und dieser nennt mir die Hardware welcher er implementiert und ich trage das ganze in eine Liste ein oder jeder Treiber muss eine spezielle Sektion (ich meine hier ein Segment/Sektion einer ELF Datei) haben, in der mind 1 Eintrag einer bestimmten Struktur steht, welche Hardware er implementiert (das wird dann wieder in eine Liste geschrieben).
Die letzte Methode ist die sicherste und auch von der Performance her beste Methode.

Vorteil ist hier das ich einfach ne Art "Beobachter" installieren kann, dieser sagt mir dann jedes Mal bescheid wenn sich in dem Verzeichnis was ändern sollte und ich kann einfach den Treiber aus der Liste nehmen, bzw. ihn neu hinzufügen. Damit wird die Installation von Treiber natürlich verdammt einfach und effizient.
740
Lowlevel-Coding / Re:Port-Verwaltung
« am: 11. August 2010, 16:53 »
Gut dann noch eine Frage, warum sollte ein Treiber auf einen Port zugreifen wollen, auf den er nicht/den er nicht braucht?

Vorallem finde ich den Unterschied krass. Einmal passiert gar nichts und einmal wird der Prozess beendet.

Der Punkt ist doch, ein Treiber der nur das macht was er soll/darf wird nie auf einen Port zugreifen der für ihn nicht freigegeben ist. Also kostet das Nachgucken im UserSpace nur Zeit/Performance.

Das mit stderr kannst du auch aus dem Kernel heraus machen.

Zitat
Auserdem muss ich im Kernel nicht in einer bitmap gucken, sondern in einem Array.
Das ist erstens egal und zweitens ist das Array sogar schnell, verbraucht halt nur mehr Speicher.
Seiten: 1 ... 35 36 [37] 38 39 ... 43

Einloggen