Autor Thema: Wie Ressourcenverwaltung im Micro-Kernel OS?  (Gelesen 12816 mal)

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« am: 31. December 2009, 11:00 »
Hallo,


ich bin beim nachdenken (über mein Micro-Kernel OS) auf ein Problem gestoßen.
Wie werden in einem Micro-Kernel OS offene Ressourcen (Dateien, Sockets u.ä.) geschlossen wenn ein Prozess außerordentlich gekillt wird? :?
Der eigentliche Kernel weiß doch nichts von dem ganzen Zeugs. In einem Monolithen ist es ja oft so das wenn ein Prozess beendet wird (oder sich selbst beendet) das offene Dateien, Sockets usw. vom Kernel geschlossen werden. In einem Micro-Kernel OS hingegen weiß der Kernel ja gar nichts von all diesen Ressourcen und kann sie demzufolge auch nicht selber schließen. Wenn man aber gar keine derartigen Mechanismen einbaut dürften nach ein paar abgestürzten Programmen ein Haufen Ressourcen (und damit vor allem Speicher) noch belegt sein. Das könnte langfristig zum Absturz o.ä. führen.

Da ich davon ausgehe das ich nicht der erste bin dem dieses Problem auffällt hoffe ich mal das es bereits erprobte Lösungswege gibt. Ich finde diese nur nicht.

Hat da jemand ein paar Vorschläge oder Suchbegriffe für mich?


Grüße
Erik


und rutscht gut aber unfallfrei ins Jahr 2010 :-D
Reality is that which, when you stop believing in it, doesn't go away.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 31. December 2009, 11:23 »
Ein ordentlicher Mikrokernel hat zwar keine Ahnung von Dateien, aber er kennt Prozesse und er kennt IPC. Wenn ein Prozess beendet wird, kann der Kernel also eine entsprechende Nachricht senden. Die Frage ist dann nur noch, wer die Nachricht alles bekommt: Nur Prozesse, die sich registriert haben, für diesen einen Prozess benachrichtigt zu werden? Eine Art Verteiler, bei dem sich alle Prozesse registrieren können, die von allen beendeten Programmen erfahren? Oder vielleicht auch direkt ein Broadcast an alle Prozesse, so oft wird das ja nicht vorkommen.
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 #2 am: 31. December 2009, 11:57 »
Hallo,


Wenn ein Prozess beendet wird, kann der Kernel also eine entsprechende Nachricht senden.
Daran hatte ich auch schon gedacht, es erscheint mir nur als Overkill da ja (hoffentlich) die Mehrzahl der Prozesse ihre Ressourcen wieder ordentlich aufräumen. Diese Messages währen (ordentliche SW vorausgesetzt) daher zum überwiegenden Anteil unnütz.

Die Frage ist dann nur noch, wer die Nachricht alles bekommt: Nur Prozesse, die sich registriert haben, für diesen einen Prozess benachrichtigt zu werden? Eine Art Verteiler, bei dem sich alle Prozesse registrieren können, die von allen beendeten Programmen erfahren?
Das klingt nach viel Verwaltungsaufwand im Kernel.

Oder vielleicht auch direkt ein Broadcast an alle Prozesse, so oft wird das ja nicht vorkommen.
Dann schau Dir mal einen normalen Compilerlauf an, da wird für jede Source-Code-Datei ein Compiler-Prozess gestartet. Es gibt sicher noch etliche andere Szenarien wo mit Prozessen nur so geschmissen wird. Und wenn dann noch das System zusätzlich mit Unmengen unnützer (da alle Prozesse ihre Ressourcen brav aufräumen) Messages belastet wird ist IMHO auch niemandem geholfen.

Es geht ja eigentlich nur um fehlerhaft programmierte oder außerordentlich gekillte Prozesse die ihre Ressourcen nicht ordentlich aufgeräumt haben.


Mir ist die Idee des Pollens gekommen. Wenn offene Ressourcen von ihren Services alle paar Stunden (ja wirklich Stunden) Nichtbenutzung geprüft werden sollte damit das System nur äußerst wenig belastet werden. Das könnte z.B. einen offenen TCP-Server-Socket treffen auf dem über einen längeren Zeitraum keine eintreffenden Verbindungen kommen. In dem Szenario mit dem GCC würde dieser Mechanismus gar nicht erst zum tragen kommen (und daher auch keine CPU-Zeit benötigen) da alle Dateien sehr schnell wieder geschlossen werden.


Gibt es noch andere Lösungsansätze für dieses Problem?
Ich würde schon gerne mit möglichst wenig System-Last bei trotzdem hoher Zuverlässigkeit auskommen.


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

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 31. December 2009, 12:37 »
Man muss diese verwaltende Instanz ja nicht weglassen. Die verwaltende Einheit könnte einfach ein weiterer Prozess sein, an die alle anderen Prozesse ihre Anfragen zu richten haben, und der diese an die passenden Server weiterleitet. Dann bleiben die IPC-Kanäle als einzige vom Kernel zu verwaltende Ressource.
« Letzte Änderung: 31. December 2009, 12:39 von PorkChicken »
Dieser Text wird unter jedem Beitrag angezeigt.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 31. December 2009, 13:22 »
Zitat von: erik.vikinger link=topic=2400.msg27027#msg27027
Daran hatte ich auch schon gedacht, es erscheint mir nur als Overkill da ja (hoffentlich) die Mehrzahl der Prozesse ihre Ressourcen wieder ordentlich aufräumen. Diese Messages währen (ordentliche SW vorausgesetzt) daher zum überwiegenden Anteil unnütz.
Je nach Modell könnte sich der Server, der Ressourcen bereitstellt, wieder aus der Liste der zu benachrichtigenden Prozesse austragen, sobald alle Ressourcen geschlossen sind. Dann würde die Nachricht nur geschickt werden, wenn es wirklich etwas zu tun gibt. Voraussetzung wäre natürlich etwas intelligenteres als der simple Broadcast.

Zitat
Die Frage ist dann nur noch, wer die Nachricht alles bekommt: Nur Prozesse, die sich registriert haben, für diesen einen Prozess benachrichtigt zu werden? Eine Art Verteiler, bei dem sich alle Prozesse registrieren können, die von allen beendeten Programmen erfahren?
Das klingt nach viel Verwaltungsaufwand im Kernel.
Nur wenn du so einen Publish/Subscribe-Mechanismus nicht sowieso schon hast. Und eine Liste von Prozessen ist jetzt wirklich kein sehr großer Verwaltungsaufwand.

Ich glaube, dass die Registrierung für Benachrichtigung beim Ende eines bestimmten Prozesses die Methode ist, der sich am ehesten mit deinen Vorstellungen von wegen kleiner Overhead deckt.

Dann schau Dir mal einen normalen Compilerlauf an, da wird für jede Source-Code-Datei ein Compiler-Prozess gestartet.
Im Vergleich zu den Nachrichten, die für I/O draufgehen, und zur CPU-Last beim eigentlichen Kompilieren dürfte die Nachricht bei der Beendigung wirklich keine nennenswerte Rolle spielen.

Zitat
Es geht ja eigentlich nur um fehlerhaft programmierte oder außerordentlich gekillte Prozesse die ihre Ressourcen nicht ordentlich aufgeräumt haben.
So gut wie alle Programme, die komplexer als Hello World sind, sind fehlerhaft.

Zitat
Mir ist die Idee des Pollens gekommen. Wenn offene Ressourcen von ihren Services alle paar Stunden (ja wirklich Stunden) Nichtbenutzung geprüft werden sollte damit das System nur äußerst wenig belastet werden.
Wenn du jetzt aber Swap benutzen musst, weil dein Garbage Collector noch nicht gelaufen ist, hast du die gewonnene Performance aber gleich zigfach wieder verloren. Und du musst dafür sorgen, dass PIDs nicht neu vergeben werden bevor der GC gelaufen ist, weil du sonst beim Pollen nicht mehr eindeutig feststellen kannst, ob ein Prozess noch läuft oder nicht. Dass der GC in den einzelnen Servern verteilt läuft, vereinfacht die Sache auch nicht gerade.

Zitat
Das könnte z.B. einen offenen TCP-Server-Socket treffen auf dem über einen längeren Zeitraum keine eintreffenden Verbindungen kommen.
Also da würde ich mich bedanken, wenn das OS meinen Webserver praktisch abschießt. nur weil mal eine Stunde lang niemand auf die Seite zugegriffen hat. ;)
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 #5 am: 31. December 2009, 16:49 »
Hallo,


Man muss diese verwaltende Instanz ja nicht weglassen. Die verwaltende Einheit könnte einfach ein weiterer Prozess sein, an die alle anderen Prozesse ihre Anfragen zu richten haben....
Das klingt nach ner menge zusätzlicher IPC-Messages, die "verwaltende Instanz" ist ja sicher ein User-Mode-Prozess. Außerdem müsste der auch irgendwie den Überblick behalten welche Ressourcen schon länger offen sind und deren Besitzer-Prozesse eventuell bereits tot sind. Dazu müsste diese "verwaltende Instanz" jegliche IPC-Kommunikation verstehen und überwachen können.


Je nach Modell könnte sich der Server, der Ressourcen bereitstellt, wieder aus der Liste der zu benachrichtigenden Prozesse austragen, sobald alle Ressourcen geschlossen sind.
Da die Server ja gleichzeitig verschiedene Clients bedienen müsste man das für jeden Client-Prozess einzeln verwalten. Außerdem sind das ein-/aus-tragen ja auch wieder 2 IPC-Messages.

Dann würde die Nachricht nur geschickt werden, wenn es wirklich etwas zu tun gibt. Voraussetzung wäre natürlich etwas intelligenteres als der simple Broadcast.
Diese Idee klingt aber trotzdem recht interessant.

Nur wenn du so einen Publish/Subscribe-Mechanismus nicht sowieso schon hast.
Nein, für sowas ist mir bis jetzt noch keine Verwendung eingefallen.

Ich glaube, dass die Registrierung für Benachrichtigung beim Ende eines bestimmten Prozesses die Methode ist, der sich am ehesten mit deinen Vorstellungen von wegen kleiner Overhead deckt.
Darüber werd ich mal ne Nacht schlafen, aber nicht heute.

So gut wie alle Programme, die komplexer als Hello World sind, sind fehlerhaft.
Das stimmt sicher, trotzdem geben die meisten Programme ihre verwendeten Ressourcen wieder ordentlich frei.

Wenn du jetzt aber Swap benutzen musst, weil dein Garbage Collector noch nicht gelaufen ist, hast du die gewonnene Performance aber gleich zigfach wieder verloren.
Das stimmt allerdings. Man könnte natürlich bei Speicherknappheit auch schon mal eher loslegen.
Vielleicht könnte der Kernel so ne Todes-Benachrichtigungs-Message auch immer bei einem außerordentlichem Prozess-Kill (Schutzverletzung u.ä.) abschicken, bei solchen Ereignissen ist die Wahrscheinlichkeit für verwaiste Ressourcen am größten.

Und du musst dafür sorgen, dass PIDs nicht neu vergeben werden
Das wollte ich so wieso. IDs sollen immer erst nach einer gewissen Zeit X recycelt werden.

Dass der GC in den einzelnen Servern verteilt läuft, vereinfacht die Sache auch nicht gerade.
Stimmt.

Also da würde ich mich bedanken, wenn das OS meinen Webserver praktisch abschießt. nur weil mal eine Stunde lang niemand auf die Seite zugegriffen hat. ;)
Ich meinte nicht abschießen sondern "nach schauen" ob der Server-Prozess noch läuft und falls nicht dann wird der Socket geschlossen. Ein Service sollte niemals einen Client abschießen. Das darf nur '/sbin/kill' und auch nur mit root-Rechten.


Also noch einiges zu überlegen, Danke noch mal für Eure Anregungen.

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 #6 am: 31. December 2009, 19:08 »
Je nach Modell könnte sich der Server, der Ressourcen bereitstellt, wieder aus der Liste der zu benachrichtigenden Prozesse austragen, sobald alle Ressourcen geschlossen sind.
Da die Server ja gleichzeitig verschiedene Clients bedienen müsste man das für jeden Client-Prozess einzeln verwalten. Außerdem sind das ein-/aus-tragen ja auch wieder 2 IPC-Messages.
Zwei Syscalls. Es ist der Kernel, der bei Prozessende benachrichtigen muss. Das ist kein großer Overhead und ganz kostenlos bekommt man Korrektheit meistens nicht. ;)

Zitat
Nur wenn du so einen Publish/Subscribe-Mechanismus nicht sowieso schon hast.
Nein, für sowas ist mir bis jetzt noch keine Verwendung eingefallen.
Jetzt hast du eine. Letztendlich ist der Mechanismus sowieso nur eine dumme verkettete Liste von Prozessen, definitiv keine Rocket Science.

Zitat
Darüber werd ich mal ne Nacht schlafen, aber nicht heute.
:-D

Bei dieser Gelegenheit: Guten Rutsch!

Zitat
Also da würde ich mich bedanken, wenn das OS meinen Webserver praktisch abschießt. nur weil mal eine Stunde lang niemand auf die Seite zugegriffen hat. ;)
Ich meinte nicht abschießen sondern "nach schauen" ob der Server-Prozess noch läuft und falls nicht dann wird der Socket geschlossen. Ein Service sollte niemals einen Client abschießen. Das darf nur '/sbin/kill' und auch nur mit root-Rechten.
Ich meinte auch nicht Abschießen im Sinn von kill, sondern eben das serverseitige Freigeben der Verbindung. Für einen Webserver würde das ungefähr auf dasselbe herauslaufen.

Übrigens, du machst keine lustigen Sachen wie Unix von wegen Dateideskriptoren an andere Prozesse vererben und sowas? Dann würde es für den Server nämlich wirklich schwer werden festzustellen, was Sache ist (bzw. man müsste bei der Vererbung eine Nachricht schicken - nicht schwer, aber man muss es berücksichtigen).
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 #7 am: 31. December 2009, 21:20 »
Hallo,


Zwei Syscalls.
Nur zum eintragen und austragen des Services auf der Sterbebenachrichtigungsliste des Client-Prozess.

Es ist der Kernel, der bei Prozessende benachrichtigen muss.
Was ja (meistens) gar nicht eintritt da alle Programme (meistens) fehlerfrei mit Ressourcen umgehen können. Die System-Last im Fehlerfall ist IMHO zweitrangig da dieser die Ausnahme sein soll. Der entscheidende Punkt ist das der Fehlerfall zuverlässig erkannt wird und keine Lecks entstehen.

und ganz kostenlos bekommt man Korrektheit meistens nicht. ;)
Da sprichst Du wahr! Es bleibt nur die Frage wie hoch der Preis sein darf/muss.

Jetzt hast du eine. Letztendlich ist der Mechanismus sowieso nur eine dumme verkettete Liste von Prozessen, definitiv keine Rocket Science.
Ich würde in jedem Prozess-Verwaltungs-Struct ein paar Plätze für, im Sterbefall, zu benachrichtigende PIDs bereithalten und wenn die nicht reichen dann muss auf eine zusätzliche große Liste verwiesen werden (so ähnlich wie im inode von ext2). Ein typisches Programm wird kaum über "file", "console" und "tcp" hinauskommen so das z.B. 6 Direkt-Einträge meistens reichen sollten.

Bei dieser Gelegenheit: Guten Rutsch!
Danke! Dir auch!

Ich meinte auch nicht Abschießen im Sinn von kill, sondern eben das serverseitige Freigeben der Verbindung. Für einen Webserver würde das ungefähr auf dasselbe herauslaufen.
Das würde der TCP-Service aber nur machen wenn der Client-Prozess nicht mehr läuft. Solange der Client-Prozess noch läuft (oder sich aufgehangen hat o.ä.) bleibt der TCP-Socket selbstverständlich unangetastet offen. Der TCP-Service soll nur ab und an mal schauen ob der Client noch da ist.

Übrigens, du machst keine lustigen Sachen wie Unix von wegen Dateideskriptoren an andere Prozesse vererben und sowas? Dann würde es für den Server nämlich wirklich schwer werden festzustellen, was Sache ist (bzw. man müsste bei der Vererbung eine Nachricht schicken - nicht schwer, aber man muss es berücksichtigen).
Musst Du mich eigentlich gleich mit so vielen neuen Problemen überhäufen :? Eigentlich wollte ich heute Abend meinen Kopf zum trinken und nicht zum denken benutzen :wink:
Über Vererbung von Ressourcen hab ich in der Tat noch nicht nachgedacht. Wenn dann würde ich das auf explizite Anweisung der Ersteller-Clients an den Service hin machen wollen, nur UNIX-like ist das sicher nicht. Braucht man sowas überhaupt?


>>>>Da ich davon ausgehe das ich nicht der erste bin dem dieses Problem auffällt hoffe ich mal das es bereits erprobte Lösungswege gibt.
Wie macht tyndur das aufräumen der Ressourcen im Fehlerfall eigentlich?
Kann tyndur Ressourcen-Vererbung?


Grüße fürs neue Jahr
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 #8 am: 01. January 2010, 11:35 »
Zwei Syscalls.
Nur zum eintragen und austragen des Services auf der Sterbebenachrichtigungsliste des Client-Prozess.
Richtig. Wenn alles korrekt aufgeräumt wird, bleibt es ja aber auch bei diesen zwei Syscalls, weil beim Prozessende kein Server mehr benachrichtigt werden will.

Zitat
und ganz kostenlos bekommt man Korrektheit meistens nicht. ;)
Da sprichst Du wahr! Es bleibt nur die Frage wie hoch der Preis sein darf/muss.
Vielleicht solltest du mal umgekehrt rangehen: Wie billig soll es denn noch werden? Ein Syscall kostet sowieso schon im Vergleich zu den ganzen IPC-Tätigkeiten des Servers nicht sehr viel. Er würde genau einmal pro open/close passieren, was tendenziell auch nicht sehr oft passiert, wenn man mit read/write-Aufrufen vergleicht. Ich bin mir ziemlich sicher, dass der Syscall schon damit nicht im Ansatz ins Gewicht fällt - das (seltene) open wird wesentlich kompliziertere und teurere Sachen machen.

Ich weiß nicht vor was genau du beim Syscall Angst hast (ist doch nur ein Ringwechsel, kein Kontextwechsel, oder?), aber wenn es um die Anzahl der Syscalls geht, könntest du einen kombinierten Syscall aus IPC und in die Liste ein-/austragen machen. Nicht, dass ich das für sinnvoll hielte, das wäre Design für eine minimale Optimierung geopfert.

Zitat
Ich würde in jedem Prozess-Verwaltungs-Struct ein paar Plätze für, im Sterbefall, zu benachrichtigende PIDs bereithalten und wenn die nicht reichen dann muss auf eine zusätzliche große Liste verwiesen werden (so ähnlich wie im inode von ext2). Ein typisches Programm wird kaum über "file", "console" und "tcp" hinauskommen so das z.B. 6 Direkt-Einträge meistens reichen sollten.
Wozu so kompliziert? Nimm doch gleich eine Liste. Ist genauso schnell wie das Array (du brauchst keinen Direktzugriff auf ein Element per Index) und wesentlich einfacher zu implementieren als eine Mischform.

Zitat
Über Vererbung von Ressourcen hab ich in der Tat noch nicht nachgedacht. Wenn dann würde ich das auf explizite Anweisung der Ersteller-Clients an den Service hin machen wollen, nur UNIX-like ist das sicher nicht. Braucht man sowas überhaupt?
Wenn du POSIX haben willst, ja. exec() gibt eben alle Deskriptoren an die Kindprozesse weiter (es sei denn, FD_CLOEXEC ist gesetzt) und wenn du das nicht machst, laufen viele Programme nicht richtig. Wenn du kein POSIX willst, kannst du die Anwendungsfälle für das Vererben von Dateideskriptoren wohl besser über deine eigene IPC-API abdecken.

Zitat
Wie macht tyndur das aufräumen der Ressourcen im Fehlerfall eigentlich?
Der Kernel kann einen RPC-Aufruf machen, wenn ein Prozess beendet wird. Ich glaube aber nicht, dass das tatsächlich benutzt wird (außer für die Implementierung von waitpid, aber das hat ja mit diesem Thema nichts zu tun).

Zitat
Kann tyndur Ressourcen-Vererbung?
Nein, das ist uns zu spät aufgefallen, dass man das für POSIX braucht und deswegen passt es überhaupt nicht in unser Design. ;)

Wobei wir ja mit LIOv2 die ganze Dateiverwaltung neu schreiben (und nebenbei in den Kernel verschieben). Damit sollte es dann am Ende gehen.
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 #9 am: 01. January 2010, 13:20 »
Hallo,


Vielleicht solltest du mal umgekehrt rangehen: Wie billig soll es denn noch werden?
Okay, Du hast recht. Korrektheit ist mir dann doch wichtiger.

... das (seltene) open wird wesentlich kompliziertere und teurere Sachen machen.
Stimmt auffallend. Das Eintragen/Austragen von der Liste sollte sogar noch seltener passieren, schließ sollte sich der Service nicht für jede geöffnete Datei oder Socket erneut auf die Liste setzen lassen.

Ich weiß nicht vor was genau du beim Syscall Angst hast (ist doch nur ein Ringwechsel, kein Kontextwechsel, oder?)
Naja, eigentlich 2 Ringwechsel (einmal User -> Kernel und einmal Kernel -> User). Aber Du hast völlig recht, der Ringwechsel ist extrem billig (auf meiner Plattform soll ein SYSCALL nur unwesentlich teurer sein als ein normaler CALL), vor allem in Relation zu den anderen Dingen die ein Ressourcen-Service sonst so zu erledigen hat.

.... könntest du einen kombinierten Syscall aus IPC und in die Liste ein-/austragen machen. Nicht, dass ich das für sinnvoll hielte, das wäre Design für eine minimale Optimierung geopfert.
Ja, das ist Quatsch, nicht mal ich währe so verrückt sowas zu machen.

Nimm doch gleich eine Liste. Ist genauso schnell wie das Array (du brauchst keinen Direktzugriff auf ein Element per Index) und wesentlich einfacher zu implementieren als eine Mischform.
Mir ist die Idee gekommen das ganze weitestgehend in den User-Space zu verlegen. Der exec-Prozess (der die anderen Prozesse alle erstellt) könnte eine Liste der Prozesse führen die über den Tod anderer Prozesse benachrichtigt werden wollen. Auf dieser Liste können sich dann alle Services eintragen. Der exec-Prozess bekommt vom Kernel bescheid wenn dieser die Verwaltungsstrukturen toter Prozesse wegräumen möchte und merkt sich die PIDs und ein Hintergrundthread verteilt dann ab und an diese PIDs per IPC an alle registrierten Services. Die Services prüfen dann, im IPC-PopUp-Thread, ob für die gegebenen PIDs noch Ressourcen offen sind (also ein simpler Tabellen-LookUp) und schließen diese gegebenenfalls. Dieser PopUp-Thread kann mit sehr niedriger Priorität laufen und blockiert das System nicht. Rein in CPU-Takten gemessen ist das zwar teurer, als Dein Vorschlag mit einer Liste für jeden Prozess, aber es passiert bequem im Hintergrund, eben doch eine Art GC. Und, was mir recht wichtig ist, ich habe im Kernel keine dynamischen Strukturen (eine verkettete Liste besteht aus vielen kleinen Elementen und für die wird die Speicherverwaltung in meinem Kernel wohl nicht geeignet sein).
Als Optimierung könnte der Hintergrundthread im exec-Prozess auch warten bis mehrere PIDs toter Prozesse angefallen sind und dann in jeden IPC an die Services gleich mehrere PIDs übergeben, womit die Kosten nochmal deutlich sinken. Da das alles User-Mode ist stellt das kein Problem dar.


Wenn du POSIX haben willst, ja.
Nein, will ich nicht.

.... kannst du die Anwendungsfälle für das Vererben von Dateideskriptoren wohl besser über deine eigene IPC-API abdecken.
Ja, das wird wohl das beste sein. Und da andere OSe auch nicht POSIX-Konform sind kann man dort bestimmt schauen wie die verschiedenen Programme modifiziert werden müssen damit man ohne POSIX auskommt.


Nein, das ist uns zu spät aufgefallen, dass man das für POSIX braucht und deswegen passt es überhaupt nicht in unser Design. ;)
Deswegen verstopfe ich lieber vorher irgendwelche Foren mit endlosen Threads um nicht hinter feststellen zu müssen das ich in die falsche Richtung gelaufen bin. :-D

(und nebenbei in den Kernel verschieben)
Adieu Micro-Kernel.


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

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #10 am: 01. January 2010, 14:09 »
Mir ist die Idee gekommen das ganze weitestgehend in den User-Space zu verlegen. Der exec-Prozess (der die anderen Prozesse alle erstellt) könnte eine Liste der Prozesse führen die über den Tod anderer Prozesse benachrichtigt werden wollen. Auf dieser Liste können sich dann alle Services eintragen. Der exec-Prozess bekommt vom Kernel bescheid wenn dieser die Verwaltungsstrukturen toter Prozesse wegräumen möchte und merkt sich die PIDs und ein Hintergrundthread verteilt dann ab und an diese PIDs per IPC an alle registrierten Services. Die Services prüfen dann, im IPC-PopUp-Thread, ob für die gegebenen PIDs noch Ressourcen offen sind (also ein simpler Tabellen-LookUp) und schließen diese gegebenenfalls.
Das wäre mein Broadcast-Ansatz, nur mit zeitlicher Verzögerung, oder? Interessant, dass du ausgerechnet auf diese Variante kommst, wo du in den letzten Beiträgen eher auf Performance aus warst. Das dürfte mit die teuerste Variante sein.

Zitat
Und, was mir recht wichtig ist, ich habe im Kernel keine dynamischen Strukturen (eine verkettete Liste besteht aus vielen kleinen Elementen und für die wird die Speicherverwaltung in meinem Kernel wohl nicht geeignet sein).
Aus vielen kleinen, aber gleich großen Elementen. Daraus einen Speicherpool zu basteln, der eine Page kleinhackt und die Elemente dann ohne Fragmentierung verwaltet, ist nicht schwer. Und eine Page anfordern wirst du in deinem Kernel ja können, oder? ;)

Zitat
.... kannst du die Anwendungsfälle für das Vererben von Dateideskriptoren wohl besser über deine eigene IPC-API abdecken.
Ja, das wird wohl das beste sein. Und da andere OSe auch nicht POSIX-Konform sind kann man dort bestimmt schauen wie die verschiedenen Programme modifiziert werden müssen damit man ohne POSIX auskommt.
Es macht keinen Spaß.

Zitat
Deswegen verstopfe ich lieber vorher irgendwelche Foren mit endlosen Threads um nicht hinter feststellen zu müssen das ich in die falsche Richtung gelaufen bin. :-D
POSIX war ja nicht von Anfang an ein Ziel, und tyndur ist eigentlich alles andere als ein Unix. Aber Programme zum Portieren einfach nur neu durchkompilieren zu müssen, ist schon eine feine Sache.

Zitat
Adieu Micro-Kernel.
Wo Mikrokernel anfängt und aufhört ist Definitionssache. Ich habe auch kein Problem damit, dann von einem (sehr mikrokerneligen) Hybriden zu reden.
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 #11 am: 01. January 2010, 17:07 »
Hallo,


Das wäre mein Broadcast-Ansatz, nur mit zeitlicher Verzögerung, oder?
Ja, naja fast, es ist nur ein Multicast und kein Broadcast. Du hast ja verschiedene Szenarien angedeutet.

Das dürfte mit die teuerste Variante sein.
Das sehe ich nicht so. Der GC-Ansatz sieht auf dem ersten Blick immer deutlich teurer aus bietet aber ganz andere Optimierungsmöglichkeiten (wie z.B. das Bündeln mehrerer Aufräumjobs), außerdem ist es mir sehr wichtig den Kernel dabei außen vor zu lassen. Der GC-Ansatz verbraucht zwar rein rechnerisch (möglicherweise) mehr CPU-Takte aber diese kann man besser einteilen bzw. zu einem späteren Zeitpunkt (wenn das System z.B. gerade auf eine Hardware, z.B. Festplatte, wartet) einschieben. Es geht darum das die eigentliche Arbeit so effizient als möglich ausgeführt werden kann.

Aus vielen kleinen, aber gleich großen Elementen. Daraus einen Speicherpool zu basteln, der eine Page kleinhackt und die Elemente dann ohne Fragmentierung verwaltet, ist nicht schwer. Und eine Page anfordern wirst du in deinem Kernel ja können, oder? ;)
Nunja, nicht immer. Da ich auf Segmentierung setze verwalte ich den einen linearen Speicher (der für alles da ist) im Kernel und der ist nicht zwangsweise in Pages eingeteilt. Nur wenn ich das Paging anschalten muss (z.B. zum swappen) kommt auch eine page-basierte physische Speicherverwaltung dazu (unter den linearen Speicher). Den linearen Speicher möchte ich gerne in 256Byte Häppchen verwalten da dies das natürliche Alignment der Segment-Start-Offsets und das kleinste Alignment der Segment-Größe ist.

Zitat
Und da andere OSe auch nicht POSIX-Konform sind kann man dort bestimmt schauen wie die verschiedenen Programme modifiziert werden müssen damit man ohne POSIX auskommt.
Es macht keinen Spaß.
Du schreibst immer so deprimierende Sachen, muss das sein? :cry:

Wo Mikrokernel anfängt und aufhört ist Definitionssache.
Meine Rede. Anstatt über Definitionshaarspaltereien streiten wir uns doch lieber über wichtigere Sachen. :-D

(sehr mikrokerneligen) Hybriden
Was mit dem Dateisystem anfängt hört dann mit der GUI auf? (so wie bei MAC OS X)
Diesen Weg in den Abgrund möchte ich nicht gehen. Zumindest heute noch nicht.


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 #12 am: 01. January 2010, 20:24 »
Das sehe ich nicht so. Der GC-Ansatz sieht auf dem ersten Blick immer deutlich teurer aus bietet aber ganz andere Optimierungsmöglichkeiten (wie z.B. das Bündeln mehrerer Aufräumjobs), außerdem ist es mir sehr wichtig den Kernel dabei außen vor zu lassen. Der GC-Ansatz verbraucht zwar rein rechnerisch (möglicherweise) mehr CPU-Takte aber diese kann man besser einteilen bzw. zu einem späteren Zeitpunkt (wenn das System z.B. gerade auf eine Hardware, z.B. Festplatte, wartet) einschieben. Es geht darum das die eigentliche Arbeit so effizient als möglich ausgeführt werden kann.
Woher weiß dein GC, wann ein günstiger Zeitpunkt ist? Und ist der Kernel letztendlich wirklich eher außen vor, wenn die Nachricht auf einen Syscall von exec hin geschickt wird, als wenn sich der Kernel selbst entscheidet, die Nachrichten zu schicken?

Zitat
Da ich auf Segmentierung setze verwalte ich den einen linearen Speicher (der für alles da ist) im Kernel und der ist nicht zwangsweise in Pages eingeteilt. Nur wenn ich das Paging anschalten muss (z.B. zum swappen) kommt auch eine page-basierte physische Speicherverwaltung dazu (unter den linearen Speicher). Den linearen Speicher möchte ich gerne in 256Byte Häppchen verwalten da dies das natürliche Alignment der Segment-Start-Offsets und das kleinste Alignment der Segment-Größe ist.
Dann eben s/Page/256-Byte-Häppchen/

Zitat
Zitat
Und da andere OSe auch nicht POSIX-Konform sind kann man dort bestimmt schauen wie die verschiedenen Programme modifiziert werden müssen damit man ohne POSIX auskommt.
Es macht keinen Spaß.
Du schreibst immer so deprimierende Sachen, muss das sein? :cry:
Ich will es dir halt ersparen. Aber vermutlich ist der Lerneffekt größer, wenn du es selbst feststellst.

Zitat
Was mit dem Dateisystem anfängt hört dann mit der GUI auf? (so wie bei MAC OS X)
Diesen Weg in den Abgrund möchte ich nicht gehen. Zumindest heute noch nicht.
Das gefällt mir. Vor allem der letzte Satz. :-D
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 #13 am: 01. January 2010, 23:49 »
Hallo,


Woher weiß dein GC, wann ein günstiger Zeitpunkt ist?
Gute Frage. Er könnte nach der aktuellen CPU-Last fragen. Da der entsprechende Thread mit niedriger Priorität läuft macht er zumindest allen wichtigen Sachen Platz. Und ich denke das bündeln solcher Aktivitäten spart auch ne Menge CPU-Takte.

Und ist der Kernel letztendlich wirklich eher außen vor, wenn die Nachricht auf einen Syscall von exec hin geschickt wird, als wenn sich der Kernel selbst entscheidet, die Nachrichten zu schicken?
Ersteres ist für den Kernel ein ganz normaler IPC wie jeder andere auch. Wenn der Kernel selber irgentwelche Aktivitäten entwickeln soll dann benötigt er wahrscheinlich eigene Threads und sowas will ich auch vermeiden. Mein Kernel soll nur dann etwas tun wenn er dazu aufgefordert wird, entweder per SYSCALL oder per IRQ (und natürlich Exceptions), und dann so schnell als möglich wieder zurückkehren. Vor ner Weile hab ich einiges über QNX gelesen, das ist so ungefähr die grobe Richtung die mir vorschwebt.

Dann eben s/Page/256-Byte-Häppchen/
Also einzelne 256Byte Häppchen möchte ich nicht verwalten sondern immer nur zusammenhängende Abschnitte. So genau hab ich mir die Verwaltung des linearen Speichers noch gar nicht überlegt aber sie wird wohl eher an malloc erinnern als an das was sonst so in einem typischen Flat-Memory-OS-Kernel steckt. Ich will ja auch neue, nicht ausgetrampelte, Wege gehen. Als einfachste Variante könnte man vor jedem Bereich einen Header voranstellen, das würde zwar immer 256 Bytes pro Bereich kosten und wär auch sonst sehr lahm aber könnte als ersten Schnellschuss bestimmt funktionieren. DOS hat das auch so gemacht, allerdings mit 16 Byte Alignment und Header-Größe.

Zitat
Du schreibst immer so deprimierende Sachen, muss das sein? :cry:
Ich will es dir halt ersparen. Aber vermutlich ist der Lerneffekt größer, wenn du es selbst feststellst.
Na dann zähl doch mal noch ein paar solcher Nicht-POSIX-OS-Überraschungen auf, heute bin ich eh schon schlecht drauf.

Zitat
Was mit dem Dateisystem anfängt hört dann mit der GUI auf? (so wie bei MAC OS X)
Diesen Weg in den Abgrund möchte ich nicht gehen. Zumindest heute noch nicht.
Das gefällt mir. Vor allem der letzte Satz. :-D
Naja, irgendwann bin ich vielleicht auch mal zu solchen Schandtaten bereit aber heute hindert mich noch mein Idealismus daran.


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 #14 am: 02. January 2010, 00:25 »
Und ist der Kernel letztendlich wirklich eher außen vor, wenn die Nachricht auf einen Syscall von exec hin geschickt wird, als wenn sich der Kernel selbst entscheidet, die Nachrichten zu schicken?
Ersteres ist für den Kernel ein ganz normaler IPC wie jeder andere auch. Wenn der Kernel selber irgentwelche Aktivitäten entwickeln soll dann benötigt er wahrscheinlich eigene Threads und sowas will ich auch vermeiden. Mein Kernel soll nur dann etwas tun wenn er dazu aufgefordert wird, entweder per SYSCALL oder per IRQ (und natürlich Exceptions), und dann so schnell als möglich wieder zurückkehren.
Naja, es wäre ja ein exit-Syscall bzw. eine Exception, die den Prozess killt, die dazu führe würde, dass der Kernel etwas schickt. Nur eben nicht nur an exec, sondern an alle für den Prozess registrierten Server.

Aber ich will dir da nicht reinreden - wenn dir der Weg über exec besser gefällt, mach das.

Zitat
Na dann zähl doch mal noch ein paar solcher Nicht-POSIX-OS-Überraschungen auf, heute bin ich eh schon schlecht drauf.
Naja, überraschend ist daran eigentlich nichts. ;)

Im Prinzip gibt es drei verschiedene Möglichkeiten, Unixprogramme zu portieren. Nummer eins ist die offensichtiche, "einfach" alle Teile von POSIX implementieren, die das Programm braucht. Damit geht das Portieren dann logischerweise am einfachster.

Nummer zwei wäre ganz anderes Interface - ich glaube das ist das, was du vorhast. Vergleichbar wäre das zum Beispiel mit Windows, das ist ja auch was ganz anderes. In der Praxis sieht der Code dann oft so aus:
#ifdef _WIN32
// 1000 Zeilen Code für die Windows-API
#else
// 1000 Zeilen völlig anderer Code, der dieselben Funktionen nochmal mit POSIX implementiert
#endif

Ich denke, ich muss nichts dazu sagen, wieviel Spaß es macht, so ein Programm zu portieren. Die Teile, die auf Systemfunktionen zugreifen, darfst du quasi neuschreiben.

Dann bliebe noch die tyndur-Methode: POSIX ist nicht wirklich von Anfang an berücksichtigt, ist auch nicht die native API, aber viele POSIX-Funktionen sind in einer Kompatibiltätsbibliothek definiert und werden auf die native API abgebildet. Damit kommt man schon recht weit, aber es gibt halt ein paar Fallstricke, wenn das System nicht darauf ausgelegt ist. Beispielsweise passt das getrennte fork/exec von POSIX überhaupt nicht zu der nativen Methode, neue Prozesse zu erstellen. Das Vererbung von Dateideskriptoren an Kindprozesse hatte ich schon genannt - ein Ende einer Pipe zu vererben ist ja eigentlich die klassische IPC-Methode unter Unix und wird entsprechend häufig benutzt. Wehe, du implementierst Umgebungsvariablen nicht wie vorgesehen - dann kriegst du Schwierigkeiten, environ bereitzustellen. Und das geht mit lauter solchen Kleinigkeiten weiter, die jedesmal bedeuten, dass man ein Programm patchen muss statt es einfach zu verwenden.
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 #15 am: 02. January 2010, 16:53 »
Hallo,


Naja, es wäre ja ein exit-Syscall bzw. eine Exception, die den Prozess killt, die dazu führe würde, dass der Kernel etwas schickt. Nur eben nicht nur an exec, sondern an alle für den Prozess registrierten Server.
Dann müsste der Kernel (bei der Multicast-Variante) aber gleich auf ein mal mehrere IPC-Messages losschicken, da die nicht alle auf ein mal gehen heißt das auf jeden Fall queuen und damit Speicherverbrauch oder der Kernel müsste eben pro Prozess diese Liste verwalten (die normalerweise beim Prozess-Tod leer ist) und das möchte ich eigentlich auch nicht. Bei einem Prozess-Tod den GC-Thread von exec aufwecken ist da IMHO deutlich billiger und auch ein deutlich kleinerer Eingriff in den Kernel.

aber viele POSIX-Funktionen sind in einer Kompatibiltätsbibliothek definiert und werden auf die native API abgebildet.
Das ist eher die Lösung die mir vorschwebt. pthead ist da ein gutes Beispiel.

Beispielsweise passt das getrennte fork/exec von POSIX überhaupt nicht zu der nativen Methode, neue Prozesse zu erstellen.
Aber ihr hab die Abstraktion in Tyndur doch recht gut hin bekommen, scheint also ein lösbares Problem zu sein.
Dieses fork/exec-Duo wird mir wohl am meisten Kopfzerbrechen bereiten. Aber das war mir von Anfang an klar. fork lässt sich mit Segmentierung nur umständlich realisieren und möchte ich eigentlich auch nicht haben.

ein Ende einer Pipe zu vererben ist ja eigentlich die klassische IPC-Methode unter Unix und wird entsprechend häufig benutzt.
Okay, Ressourcen-Vererbung steht nun auf meiner Liste, sollte eigentlich nicht wirklich das Problem sein.
Eine Variante, bei der die Services damit nicht behelligt werden, währe das man dem Kindprozess ein IPC-Handle mitgibt mit dem dieser, über den Elternprozess als Vermittler, auf seine geerbten Ressourcen zugreift. Ist zwar keine hochperformante Methode aber man muss dafür nur an der libc drehen.

Wehe, du implementierst Umgebungsvariablen nicht wie vorgesehen - dann kriegst du Schwierigkeiten, environ bereitzustellen.
Umgebungsvariablen wollte ich eigentlich der libc und dem Prozess-Parameter-Block überlassen. Ich muss mir dieses Thema bei Gelegenheit noch mal genauer durchlesen. Steht bereits auf meiner Nachdenk-Liste.

Und das geht mit lauter solchen Kleinigkeiten weiter, die jedesmal bedeuten, dass man ein Programm patchen muss statt es einfach zu verwenden.
Wenn Du etwas Zeit hast währe es echt cool wenn Du noch ein paar dieser Kleinigkeiten nennen könntest.


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 #16 am: 02. January 2010, 17:16 »
Aber ihr hab die Abstraktion in Tyndur doch recht gut hin bekommen, scheint also ein lösbares Problem zu sein.
Naja, wir haben den einfachen Teil davon. Wir haben kein fork/exec, wir haben keine Pipes, die Vererbung der Deskriptoren auch nicht, wir haben kein ordentliches select, kein mmap...

Zitat
Und das geht mit lauter solchen Kleinigkeiten weiter, die jedesmal bedeuten, dass man ein Programm patchen muss statt es einfach zu verwenden.
Wenn Du etwas Zeit hast währe es echt cool wenn Du noch ein paar dieser Kleinigkeiten nennen könntest.
Naja, das läuft in der Regel so: Kleinigkeit entdeckt, ein paar nicht zitierenswerte Wörter gesagt, Programm gepatcht, Kleinigkeit wieder vergessen. Du kannst dir ja lbuilds.git mal anschauen, was wir an den Programmen in der Regel so wegpatchen.
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 #17 am: 02. January 2010, 19:08 »
Hallo,


Wir haben kein fork/exec, wir haben keine Pipes, die Vererbung der Deskriptoren auch nicht, wir haben kein ordentliches select, kein mmap...
Dafür seit ihr aber doch recht weit gekommen, Respekt!
Was für normale/portierungswürdige Programme (außer fremde Treiber) brauchen den eigentlich mmap? Für Gerätetreiber sollte diese Funktionalität doch CDI bieten?

.... Kleinigkeit wieder vergessen.
Na dann ists ja nicht soooo schlimm. Könnte also sogar jemand wie ich hin bekommen.

ein paar nicht zitierenswerte Wörter gesagt
Also eher immer wiederkehrende Ärgernisse als echte Herausforderungen, das beruhigt mich dann doch ein klein wenig.

Du kannst dir ja lbuilds.git mal anschauen, was wir an den Programmen in der Regel so wegpatchen.
Mach ich mal bei Gelegenheit. Geht das auch online? Wenn nein was brauch ich dann alles?


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

XanClic

  • Beiträge: 261
    • Profil anzeigen
    • github
Gespeichert
« Antwort #18 am: 02. January 2010, 19:23 »
Zitat von: erik.vikinger
Geht das auch online?
Jup, und zwar auf git.tyndur.org.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #19 am: 07. January 2010, 12:09 »
Hallo,


Jup, und zwar auf git.tyndur.org.
Danke.
Da sieht man aber nur die Patches, gibt es eine Möglichkeit auch direkt die Original-Datei und die Ziel-Datei gegenüber zu stellen? Da bekommt man vielleicht eine bessere Vorstellung davon wie einschneidend diese Patches sind.

Besonders aufwändig sehen die Patches so jedenfalls nicht aus, an einigen Stellen wird bloß was auskommentiert o.ä. und an anderen Stellen wird schon ein bisschen mehr geändert. Ich vermute mal das aufwändigste an den Patches war die Suche nach der richtigen Stelle im Code um eine beobachtete Fehlfunktion zu korrigieren. Compilerfehler, wegen nicht vorhandener (Library-)Funktionen, waren sicher eher die Ausnahme, zumindest ab einen gewissen Reifegrad der Librarys. Was ich mir auch lustig vorstelle sind Programme die eine Library-Funktion aufrufen (welche erstmal nur als Stub mit Fehlercoderückgabe vorliegt) aber nicht prüfen ob diese wirklich erfolgreich war und dann ein sonderbares Verhalten an den Tag legen.


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

 

Einloggen