Lowlevel

Lowlevel => OS-Design => Thema gestartet von: FlashBurn am 28. October 2011, 16:33

Titel: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 28. October 2011, 16:33
Also erstmal was gibt es alles, wo ein Thread blockiert?

Mir fallen spontan ein, sleep(), semaphoreAcquire() und wait(). Die ersten beiden machen mir keine Probleme, aber letzterer. ICh weiß gar nicht ob es normalerweise unterstützt wird, das ein Thread ein warten kann bis er wieder geweckt wird? Ich wollte das für eine UserSpace-Sachen nutzen.

Um mal ein ganz einfaches Bsp. für mein eigentliches Problem zu bringen.

CPU A:
Ein Thread will die Semaphore bekommen. Also holt er sich den Lock, dekrementiert den Counter und stellt fest das er warten muss. Der Lock wird wieder freigegeben und der Thread wird blockiert.

CPU B:
Ein Thread will die Semaphore freigeben. Also holt er sich den Lock (der auf CPU A gerade freigeben wurde), inkrementiert den Counter und stellt fest das er einen Thread aufwecken muss. Der Lock wird wieder freigegeben und der Thread wird aufgeweckt (genau der von CPU A).

Problem ist nun, dass es ja passieren könnte, dass CPU B den Thread schneller aufweckt als CPU A den Thread blockieren konnte. Lösung dafür wäre auch ne Art Semaphore, nämlich das Aufwecken wäre ein Freigeben und das Blockieren ein Anfordern.

Mit meinem wait() Syscall (und dem entsprechendem resume()) könnte es nun aber passieren, dass die Aktion resume() mehrmals aufgerufen wurde und der Thread also bei mehreren wait()´s nicht blockiert.

Daher meine Frage, was wäre besser, ne Semaphore zu nutzen wo der Counter beliebig groß werden kann oder ne Semaphore zu nutzen wo der Counter max 1 werden kann?

Oder ist das totaler Schmarrn?
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: bluecode am 28. October 2011, 16:58
Normalerweise werden Semaphores vollständig vom Kernel implementiert, da tritt das Problem wohl nicht auf. Was du suchst ist das was man unter Linux Futex (= Fast Userspace Mutex) nennt. Möglicherweise möchtest du dir dazu den Teil des ersten Papers auf wikipedia (http://de.wikipedia.org/wiki/Futex), in dem es um eben diese geht, anschauen. Ich hab es mir nicht durchgelesen, aber sie scheinen genau das Problem anzusprechen (ich hab aber ehrlich gesagt kein interesse da weiterzuschauen):
Zitat
[...] Due to such race conditions, a lock can receive a wakeup before the waiting process had a chance to enqueue itself into the kernel wait queue. We describe below how various implementation resolved this race condition as part of the kernel service.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 28. October 2011, 17:05
Zitat von: bluecode
Normalerweise werden Semaphores vollständig vom Kernel implementiert, da tritt das Problem wohl nicht auf.
Dumm nur das hier wir den Code dazu schreiben ;)

Mir geht es ja genau darum, wie man das Problem entweder vermeidet oder noch besser damit umgeht.

Zitat von: bluecode
Was du suchst ist das was man unter Linux Futex
Jap, die kenne ich, nur die Sachen mit den Race-Conditions habe ich mir noch nicht angeguckt, werde ich aber gleichmal machen.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: Jidder am 28. October 2011, 17:13
Die Funktionen mset/mclear von BSD gehen wohl auch in diese Richtung. http://docs.freebsd.org/44doc/papers/newvm.html
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 28. October 2011, 17:25
Das finde ich ja gut, genau die selbe Idee (Semaphore im SharedMem für IPC) wollte ich für meine Pipe´s nutzen :D So schlecht kann die also nicht sein.

Trägt nur leider nix zum Thema bei ;) Werde aber trotzdem mal gucken, ob die auch irgendwelche Race-Conditions haben und wie die gelöst wurden.

Edit::

@bluecode
Also soweit ich es verstanden und gefunden habe, machen sie genau dass, was ich in meiner Idee schon meinte. Nen Counter der bei jedem resume() erhöht wird und bei jedem wait() erniedrigt wird. So umgeht man diese Race-Condition.

OT::

Dazu habe ich mal wieder gesehen wieso ich den Linux-Kernel aus Design-Sicht nicht mag ;)
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: Jidder am 28. October 2011, 17:51
Das finde ich ja gut, genau die selbe Idee (Semaphore im SharedMem für IPC) wollte ich für meine Pipe´s nutzen :D So schlecht kann die also nicht sein.

Ich muss meine Aussage leider relativieren. Nach kurzem Googeln scheint es so, dass das in dem Umfang nie im offiziellen Kernel eingeführt wurde.

Das Zitat von der Seite (Section 2.2) wollte ich ursprünglich noch in meinem Post unterbringen:
Zitat
In all the above examples, there appears to be a race condition. Between the time that the process finds that a semaphore is locked, and the time that it manages to call the system to sleep on the semaphore another process may unlock the semaphore and issue a wakeup call. Luckily the race can be avoided. The insight that is critical is that the process and the kernel agree on the physical byte of memory that is being used for the semaphore. The system call to put a process to sleep takes a pointer to the desired semaphore as its argument so that once inside the kernel, the kernel can repeat the test-and-set. If the lock has cleared (and possibly the wakeup issued) between the time that the process did the test-and-set and eventually got into the sleep request system call, then the kernel immediately resumes the process rather than putting it to sleep. Thus the only problem to solve is how the kernel interlocks between testing a semaphore and going to sleep; this problem has already been solved on existing systems.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 28. October 2011, 17:55
Zitat von: jidder
Ich muss meine Aussage leider relativieren. Nach kurzem Googeln scheint es so, dass das in dem Umfang nie im offiziellen Kernel eingeführt wurde.
Im Linux-Kernel ist es drin und ich musste jetzt feststellen, dass die Futex´s doch auch für inter-process-communication genutzt werden und nicht nur für intra.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: Svenska am 28. October 2011, 18:47
Das Problem sollte eigentlich nicht auftreten, wenn der Scheduler selbst weiß, wenn ein Thread auf ein Lock wartet und ihn unblockt, wenn das Lock gerade frei ist. Sollte sich zwischenzeitlich jemand anders bereits das Lock wieder gekrallt haben, kann der gerade geweckte Thread das Lock nicht bekommen und geht halt weiterschlafen.

Allerdings kann ich nicht einschätzen, wieviel Performanceverlust das mit sich bringt, wenn der Scheduler anfängt, Lock-Variablen zu inspizieren...

Vielleicht schreib ich aber auch komplett am Problem vorbei, weiß ich nicht.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 28. October 2011, 18:56
@svenska

Ganz allgemein ist das Problem, wie geht man damit um, dass ein Thread blockiert und bevor er das erfolgreich abschließen kann, wird er von einem anderen Thread aufgeweckt. Da müssen keine Locks im Spiel sein.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: Svenska am 28. October 2011, 19:42
Naja, ein Thread ist blockiert oder er ist nicht blockiert... dazwischen sollte es keine halben Zustände geben. Schlafen tut ein Thread erst, wenn er vollständig eingeschlafen ist, nicht vorher.

Der Rückgabewert der Aufweckfunktion sollte das wiederspiegeln. Wenn der aufweckende Thread weiß, dass der andere Thread eigentlich hätte schlafen müssen, dann kann er das Wecken ja später nochmal probieren und so den Deadlock vermeiden.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 28. October 2011, 19:52
Zitat von: svenska
Wenn der aufweckende Thread weiß, dass der andere Thread eigentlich hätte schlafen müssen, dann kann er das Wecken ja später nochmal probieren und so den Deadlock vermeiden.
Das würde bei mir dann heißen das ich im Kernel so lange probiere den Thread aufzuwecken bis das geklappt hat (weil sowas überlässt man nicht dem User, zumal es dann noch unperformanter wird). Glaubst du wirklich dass das eine gute Idee ist?

Ich denke mit meinem Counter bin ich ganz gut dabei (entspricht auch in etwa der Lösung von Linux und co). Die Frage die ich mir da dann stelle ist halt, sollte man es zulassen das der Counter nen größeren Wert als 1 annimmt oder nicht?

Wie sollte man eigentlich entscheiden ob ein Thread A ein Thread B aufwecken darf?
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: Svenska am 28. October 2011, 20:08
Wenn du einen Thread fünfmal aufweckst, dann geht der die nächsten fünf Male nicht mehr schlafen. Ist (mehrfaches) präventiv-wecken sinnvoll?

Ist es überhaupt sinnvoll, einen Thread aufzuwecken, der garnicht schläft? Um den Deadlock zu vermeiden, reicht ja ein einzelner erneuter Weckversuch, nachdem der betroffene Thread garantiert einmal dran gewesen ist (und die Möglichkeit hatte, endgültig einzuschlafen). Du sollst den Kernel nicht endlos auf den Schlaf eines Userspace-Threads warten lassen. :-)

Außerdem im Fall der Race Condition fällt mir jedenfalls kein Grund ein, warum ein grundloses Aufwecken sinnvoll sein sollte.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 28. October 2011, 20:25
Zitat von: svenska
Ist (mehrfaches) präventiv-wecken sinnvoll?
Das ist ja meine Frage ;)

Zitat von: svenska
Ist es überhaupt sinnvoll, einen Thread aufzuwecken, der garnicht schläft?
Es kann eben den Fall geben das Thread A den wait Syscall (aus welchem Grund auch immer) macht und bevor dieser ausgeführt werden kann kommt der Scheduler und Thread B ist dran, Thread A soll laut Thread´s B infos auf das Aufwecken warten. Thread B erledigt seine Sachen und weckt Thread A auf, dieser schläft noch gar nicht und ein resume() ist in meiner Welt immer erfolgreich (hat keinen Rückgabewert) und damit ist die Sache für Thread B auch beendet. Thread A kommt wieder auf die CPU und legt sich endlich schlafen, dumm nur das es wohl für immer sein wird ;)

Das Problem existiert durchaus, deswegen mussten sich ja auch die Linuxer Gedanken darüber machen.

Zitat von: svenska
Um den Deadlock zu vermeiden, reicht ja ein einzelner erneuter Weckversuch, nachdem der betroffene Thread garantiert einmal dran gewesen ist (und die Möglichkeit hatte, endgültig einzuschlafen).
Was ist, wenn der Thread aber eine so niedrige Priorität hat das er ne Weile nicht mehr dran kommt bzw. eventuell gar nicht mehr, weil der andere Thread ne höhere Priorität hat?

Dann hätten wir da auch noch die Sache damit das man damit ja mehrmals in den Kernel muss, für einen Syscall der eigentlich immer klappen sollte.

Zitat von: svenska
Außerdem im Fall der Race Condition fällt mir jedenfalls kein Grund ein, warum ein grundloses Aufwecken sinnvoll sein sollte.
Ich bin mir nicht sicher ob ich verstanden habe was du damit meinst, aber mit meinem Counter würde der Thread B den Counter von Thread A erhöhen (resume() für Thread A) und wenn Thread A wieder an der Reihe ist (also den wait() Syscall ausführt), dann würde er nicht blockieren, sondern gleich wieder zurück gehen.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 29. October 2011, 11:06
Hallo FlashBurn,


hier zeigt sich IMHO ein weiteres Problem mit Deinen Problemlösungsstrategien. Das bedingungslose wait() ist offensichtlich nicht geeignet Dein Problem zu lösen. Warum schaust Du Dich nicht nach Alternativen um?

Ich würde hierfür Events vorschlagen. In meiner (naiven) Welt hat ein Event (der natürlich im Kernel implementiert sein muss) 2 Syscalls: event_trigger(handle) und event_wait(handle,timeout) und einen internen Zähler der Null (niemand wartet), positiv (ein oder mehrere Threads warten) oder negativ (es wurde ein oder mehrmals getriggert ohne das jemand gewartet hat) sein kann. Beim event_wait() wird der Zähler inkrementiert (falls er >=0 ist) und der Thread hinten an eine geordnete Liste angehängt (und natürlich dem Scheduler entzogen da er ja nun blockiert ist), bei negativem Zähler wird dieser auch inkrementiert aber der Thread wird nicht blockiert (da hiermit nur ein vorangegangenes Trigger abgeholt wird). Das event_trigger() dekrementiert immer den Zähler und prüft dann ob dieser vorher größer 0 war, falls ja wird der vorderste Thread aus der Liste aufgeweckt (und natürlich auch entfernt) da die Liste bei einem Zähler >0 nie leer sein kann. Da beide Syscalls atomar arbeiten kann es auch keine Race-Conditions o.ä. geben. Als zusätzliches Feature kommt beim event_wait() noch das TimeOut hinzu (welches genau diesen Thread wieder aus der Blockierung befreien kann, dabei muss natürlich auch der Zähler dekrementiert und dieser Thread korrekt aus der Liste entfernt werden) welches IMHO sehr sinnvoll ist und dann könnte man noch überlegen ob man die Liste nicht noch nach den Prioritäten der wartenden Threads sortiert damit immer zuerst die mit der höchsten Priorität geweckt werden (das ist aber kein triviales Feature weil es hier u.U. dazu kommen kann das Threads mit niedriger Priorität permanent nach hinten verdrängt werden und so total verhungern). Der einzigste Nachteil dieser Methode ist das Du für so eine User-Mode-Semaphore immer zusätzlich einen zugehörigen Event im Kernel einrichten musst aber das dürfte wohl verschmerzbar sein.

Ansonsten bleibt nur noch zu sagen das wenn man einen Lock üblicherweise so lange benötigt das es sich für die wartenden Threads lohnt sich schlafen zu legen (was ja auch CPU-Zeit kostet) das es dann wohl meistens geschickter ist gleich eine Semaphore zu benutzen die komplett vom Kernel verwaltet wird. Für recht kurze Locks wartet man doch üblicherweise per PAUSE so das die von Dir beschriebenen Probleme eh nicht auftreten.


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: Svenska am 29. October 2011, 11:24
Zitat von: svenska
Ist es überhaupt sinnvoll, einen Thread aufzuwecken, der garnicht schläft?
Es kann eben den Fall geben das Thread A den wait Syscall (aus welchem Grund auch immer) macht und bevor dieser ausgeführt werden kann...
Das ist der Fall der Race-Condition: A geht schlafen, B weckt ihn gleichzeitig wieder auf, Wettlauf.

Ich frage, ob es einen sinnvollen Grund gibt, ein wakeup() for einem wait() zu machen. Nicht gleichzeitig, sondern irgendwann früher. Ich sehe keinen, deswegen habe ich da auch kein Problem. :-)

Zitat von: svenska
Um den Deadlock zu vermeiden, reicht ja ein einzelner erneuter Weckversuch, nachdem der betroffene Thread garantiert einmal dran gewesen ist (und die Möglichkeit hatte, endgültig einzuschlafen).
Was ist, wenn der Thread aber eine so niedrige Priorität hat das er ne Weile nicht mehr dran kommt bzw. eventuell gar nicht mehr, weil der andere Thread ne höhere Priorität hat?
Wenn dein Scheduler einen niedriger priorisierten Thread verhungern lässt, Pech gehabt. Sowas macht man nicht.

Ich bin mir nicht sicher ob ich verstanden habe was du damit meinst, aber mit meinem Counter würde der Thread B den Counter von Thread A erhöhen (resume() für Thread A) und wenn Thread A wieder an der Reihe ist (also den wait() Syscall ausführt), dann würde er nicht blockieren, sondern gleich wieder zurück gehen.
Also präventives Wecken. Naja, wenn du das für sinnvoll erachtest, dann implementier es.

Eriks Variante hat auch was für sich, erlaubt aber auch das präventive Wecken.

Gruß,
Svenska
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 29. October 2011, 12:24
Zitat von: erik
Das bedingungslose wait() ist offensichtlich nicht geeignet Dein Problem zu lösen. Warum schaust Du Dich nicht nach Alternativen um?
Es geht darum auch nen Futex zu implementieren bzw. dem User die Möglichkeit zu geben was eigenes auf basis von wait() und resume() zu entwickeln und vorallem geht es darum nicht für jede Aktion in den Kernel zu müssen.

Zitat von: erik
In meiner (naiven) Welt hat ein Event (der natürlich im Kernel implementiert sein muss) ...
Sorry, schon mal schlecht ;) Ich muss für jede Aktion in den Kernel ohne das es nötig wäre. Erfüllt leider nicht die Anforderungen. Um es nochmal zu sagen, ich habe Anforderungen und die müssen erfüllt werden.
Wenn ein Kunde ein Auto will, kannst du ihm doch nicht einfach nen Flugzeug hinstellen (du kannst schon, aber hast einen Kunden weniger ;)).

Wieso gehst du den unkonventionellen Weg (du machst es genau umgekehrt wie bei einer Semaphore) und nutzt nicht ne Semaphore dafür. Die gibt es schon und du musst nix neues erfinden was es schon gibt ;)

Zitat von: erik
Ansonsten bleibt nur noch zu sagen das wenn man einen Lock üblicherweise so lange benötigt das es sich für die wartenden Threads lohnt sich schlafen zu legen (was ja auch CPU-Zeit kostet) das es dann wohl meistens geschickter ist gleich eine Semaphore zu benutzen die komplett vom Kernel verwaltet wird. Für recht kurze Locks wartet man doch üblicherweise per PAUSE so das die von Dir beschriebenen Probleme eh nicht auftreten.
Ich weiß aber selbst zur Laufzeit nicht wie lange ich solch einen Lock halten werden, was also machen? Du bist doch auch nicht fürs Kopieren, aber wertvolle Zeit beim Spinnen verbraten?!

Spinlocks im UserSpace sind weder fair noch skalieren sie gut (dann kommt noch das nötige Locking des Speicherbuses dazu), sicher um nur 2 Variablen zu ändern funktioniert das, aber sobald ich entweder nicht sagen kann wie lange der Lock gehalten wird oder die Zeitspanne lange wird, ist ne Semaphore besser. Bekommt man allerdings meistens den Lock, lohnt es sich das man sich das in den Kernel Gehen sparen kann.

Zitat von: svenska
Ich frage, ob es einen sinnvollen Grund gibt, ein wakeup() for einem wait() zu machen. Nicht gleichzeitig, sondern irgendwann früher. Ich sehe keinen, deswegen habe ich da auch kein Problem.
Mir fällt kein Sinnvoller Grund ein, ein wakeup() vor einem wait() zu machen. Es kann aber mehr oder weniger Zeitgleich bzw ungünstig (wie schon mehrmals beschrieben) passieren und da hat man dann ne Race-Condition und das muss man ja irgendwie ausmerzen. Dazu halt der Counter und er ist halt einfacher zu implementieren wenn ich viele wakeup()´s vor einem wait() zulassen. Ansonsten müsste ich irgendwie gucken das er nie größer als 1 wird. Kann man wahrscheinlich per cmpxchg machen, bin mir gerade nicht sicher wie ich das genau machen könnte.

Die andere Frage ist dann, würde es denn Probleme geben, wenn man viele wakeup()´s vor einem wait() machen kann? Auch hat sich noch keiner dazu geäußert wie man es am besten löst das nicht jeder Thread jeden x-beliebigen Thread aufwecken kann. Alzu komplexe Rechte-Verwaltung wollte ich eigentlich nicht im Kernel haben.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 29. October 2011, 17:39
Hallo,


Eriks Variante hat auch was für sich
Na klar, meine Ideen sind (fast) immer gut! ;)
Vor allem möchte ich diese Events auch gleich noch vom Kernel automagisch periodisch triggern lassen können (das Konzept hatte ich ja schon mal vorgestellt) dann habe ich einen simplen Mechanismus mit dem man viele tolle Sachen machen kann.

erlaubt aber auch das präventive Wecken.
Genau darum geht es ja. Wenn Du merkst das Du eine Ressource nicht bekommst und dann eben warten möchtest dann kann dazwischen immer mal der Scheduler zuschlagen (dem User-Mode zu erlauben die INTs abzuschalten ist auf gewöhnlichen Plattformen keine gute Idee), von daher kann es eben passieren das erst geweckt wird und dann der Versuch kommt zu schlafen. Entweder man hat so eine Funktionalität oder man muss das immer atomisch machen und das geht meistens nur im Kernel und erfordert damit grundsätzlich mindestens einen Syscall und den möchte man ja wegen der lieben Performance vermeiden.


Sorry, schon mal schlecht ;) Ich muss für jede Aktion in den Kernel ohne das es nötig wäre. Erfüllt leider nicht die Anforderungen....
Ich fürchte Du hast mich missverstanden. Ich meine Du sollst anstatt Deinem wait()/resume() mein event_wait()/event_trigger() zusammen mit Deiner User-Mode-Semaphore benutzen. Da musst Du genau so oft in den Kernel wie mit Deiner Idee nur das Du das Problem mit den Race-Conditions grundsätzlich nicht bekommst.
Übrigens müsstest Du mit Deiner Implementierung auch eine Liste mit den blockierten Threads pflegen damit derjenige der die Semaphore momentan hat auch weiß wen er wecken muss, und wenn Du diese Semaphore z.B. innerhalb von Shared-Memory einrichtest damit diese von mehreren Prozessen benutzt werden kann dann muss auch diese Liste komplett im Shared-Memory liegen (damit jeder eintragen/austragen kann) und dafür möchte in kein malloc bauen müssen (irgendwo her müssen ja die neuen Listen-Elemente kommen). Hast Du dafür schon eine gute Lösung?

Um es nochmal zu sagen, ich habe Anforderungen und die müssen erfüllt werden.
Ganz recht, aber genau das leisten Dein wait()/resume() eben nicht zuverlässig.

Die andere Frage ist dann, würde es denn Probleme geben, wenn man viele wakeup()´s vor einem wait() machen kann?
Der Punkt ist das man sowas sicher nicht absichtlich macht aber das es eben trotzdem mal passieren kann und daher muss das System mit solchen Situationen sinnvoll umgehen können. Bei einem einfachen und unspezifischen wait()/resume() macht es aber IMHO keinen Sinn vorauseilende Resumes zu queuen. Mir fällt für wait()/resume() auch gar keine sinnvolle Verwendung ein, andere unbedingte Blockierungen als sleep() soll es bei meinem OS auch nicht geben. Wenn ein Thread auf ein bestimmtes Ereignis, wie das Freiwerden eines Locks, warten will dann muss er auch eine spezifische Blockierung benutzen um auch gezielt geweckt werden zu können (dazu benötigt man dann auch nicht die ID des blockierten Threads sondern nur den Event-Mechanismus so das sich auch keine Probleme mit den Zugriffsrechten ergeben selbst wenn das über mehrere Prozesse hinweg benutzt wird).

Auch hat sich noch keiner dazu geäußert wie man es am besten löst das nicht jeder Thread jeden x-beliebigen Thread aufwecken kann.
Das würde ich grundsätzlich nur innerhalb eines Prozesses erlauben. Die einzigste Ausnahme würde ich für Prozesse mit root-Rechten machen, sonst kannst Du z.B. keinen anständigen Task-Manager (sowas wie den "Process Explorer" von den Windows Sysinternals, schade das es ein Tool in der Qualität nicht für Linux gibt) programmieren. Eine komplexe Rechte-Verwaltung möchte ich auch nicht im Kernel haben, die möchte ich persönlich lieber im User-Mode haben.


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 29. October 2011, 18:03
Zitat von: erik
Da musst Du genau so oft in den Kernel wie mit Deiner Idee nur das Du das Problem mit den Race-Conditions grundsätzlich nicht bekommst.
Ich fürchte wir reden aneinander vorbei, mal wieder ;)

Auf der einen Seite möchte ich wait()/resume() anbieten, die arbeiten mit den selben Thread-Variablen (Status-Flags und halt der Counter für die Waits) wie meine Semaphoren im Kernel (es geht ja auch bei einer Semaphore darum das ein Thread wartet, da kann diese Situation auch auftreten, wenn auch wesentlich unwahrscheinlicher).

Allgemein wollte ich halt wissen wie man damit umgeht das ein resume() vor einem wait() ankommen kann und das geht auch bei einer Semaphore im Kernel (wenn auch verdammt unwahrscheinlich bei einem nicht unterbrechbaren Kernel), vorallem bei einem der unterbrechbar ist.

Wenn das Problem gelöst ist, habe ich gleichzeitig das wait()/resume() aus dem UserSpace gelöst, da es die selben Thread-Variablen nutzt. Auch muss ich bei einer Futex (auch wenn da eine Semaphore im Kernel dahinter steckt) genau das selbe Problem lösen.

Zitat von: erik
Übrigens müsstest Du mit Deiner Implementierung auch eine Liste mit den blockierten Threads pflegen damit derjenige der die Semaphore momentan hat auch weiß wen er wecken muss, und wenn Du diese Semaphore z.B. innerhalb von Shared-Memory einrichtest damit diese von mehreren Prozessen benutzt werden kann dann muss auch diese Liste komplett im Shared-Memory liegen (damit jeder eintragen/austragen kann) und dafür möchte in kein malloc bauen müssen (irgendwo her müssen ja die neuen Listen-Elemente kommen). Hast Du dafür schon eine gute Lösung?
Jetzt kommen wir zur anderen Seite. Der Counter der Futex ist im SharedMem also genau 4byte. Im Kernel wird dann ne ganz normale Semaphore benutzt, die als ID die physische Adresse des Counters der Futex hat.
Unter Linux wird das sogar so dynamisch gemacht, dass erst wenn ein Thread warten muss, eine Liste erstellt wird. Ich bin mir noch nicht sicher ob ich das auch so machen will, aber da der Thread ja höchstwahrscheinlich eh warten muss, kann man die Zeit auch vernachlässigen.

Zitat von: erik
Das würde ich grundsätzlich nur innerhalb eines Prozesses erlauben. Die einzigste Ausnahme würde ich für Prozesse mit root-Rechten machen, sonst kannst Du z.B. keinen anständigen Task-Manager (sowas wie den "Process Explorer" von den Windows Sysinternals, schade das es ein Tool in der Qualität nicht für Linux gibt) programmieren. Eine komplexe Rechte-Verwaltung möchte ich auch nicht im Kernel haben, die möchte ich persönlich lieber im User-Mode haben.
Schön das wir uns da einig sind ;)

Allerdings ist die Rechte-Sache eigentlich wieder nen eigenes Thema wert. Ich werde also fürs erste resume() nur auf Threads im selben Task zulassen. Gut das ich das transparent zu dem resume() im Kernel machen kann (indem der Syscall das überprüft und kein resume() aufgerufen wird.

Also nochmal ganz genau. Eine Futex ist nur ein Counter im UserSpace. Der Thread der den Wert des Counters ändert entscheidet auch ob er in den Kernel geht (zum Warten oder um einen anderen Thread aufzuwecken) oder nicht. Im Kernel wird nur eine Liste mit wartenden Threads verwaltet.
Wer mir jetzt mit Sicherheitsproblemen kommen möchte, die gleichen haben alle Synchonizationsprimitiven. Denn der Thread muss diese ja nicht nutzen um auf die Daten (lesend oder schreibend) zu zugreifen.
Man stelle sich nur vor ein Thread wartet auf Daten in einer Semaphore im Kernel, aber der andere Thread denkt sich, nee heute nicht und weckt den wartenden Thread halt einfach nicht auf. Dieses Problem ist mMn nicht zu lösen, solange jemand auf etwas wartet.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 29. October 2011, 18:34
Hallo,


Ich fürchte wir reden aneinander vorbei, mal wieder ;)
Dann musst Du mir noch mal genau erklären was Du eigentlich konkret vor hast. Ich hatte Dich so verstanden das Du eine Semaphore im User-Mode hast und falls die jemand haben will aber nicht bekommt das dieser dann einfach schläft bis er vom aktuellen Besitzer geweckt wird (wenn dieser die Semaphore frei gibt). Genau das würde meine Idee auf jeden Fall besser lösen als ein simples unspezifisches wait()/resume().

Auf der einen Seite möchte ich wait()/resume() anbieten, die arbeiten mit den selben Thread-Variablen (Status-Flags und halt der Counter für die Waits) wie meine Semaphoren im Kernel (es geht ja auch bei einer Semaphore darum das ein Thread wartet, da kann diese Situation auch auftreten, wenn auch wesentlich unwahrscheinlicher).
Also wenn Dein wait()/resume() doch mehr machen als nur schlafen legen und aufwecken dann hättest Du das von Anfang an mit dazu schreiben müssen. Im Kernel sollte es grundsätzlich keine Race-Conditions geben weil dort das Prüfen ob die gewünschte Ressource frei ist und das Blockieren (inklusive anhängen an eine Liste) immer atomisch ablaufen kann, so dass das Aufwecken immer konsistent sieht ob und wenn ja welcher Thread wartet. Selbst bei einem unterbrechbaren Kernel kann man ja extra für diesen Vorgang die INTs abschalten (bzw. die resume()/wait()-Syscalls schalten die INTs erst gar nicht frei).

Der Counter der Futex ist im SharedMem also genau 4byte. Im Kernel wird dann ne ganz normale Semaphore benutzt....
Aha, das ist jetzt aber was ganz anderes als am Anfang dieses Threads, oder hab ich da was übersehen? Wir sollten uns erst mal einigen über was wir eigentlich diskutieren wollen.

Wer mir jetzt mit Sicherheitsproblemen kommen möchte ....
Keine Angst, damit komme ich Dir nicht, diese Probleme sind auch mit meinen Events vorhanden (u.a. deswegen hallte ich das TimeOut-Parameter beim event_wait()-syscall für so wichtig).


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 29. October 2011, 19:02
Zitat von: erik
Dann musst Du mir noch mal genau erklären was Du eigentlich konkret vor hast.
Ziel dieses Thema´s war es eine Lösung für die weiter oben oft beschriebene Race-Condition zu finden, was ich ja auch in Form des Counters (fairerweise reicht der eigentlich nicht) habe.

Also nochmal, am Bsp. einer Semaphore im Kernel (die Ints sind aus). CPU A Thread 1 bekommt den Lock für die Semaphore dekrementiert den Counter und stellt fest das er warten muss, packt sich an das Ende der Warteliste, gibt den Lock wieder frei und ruft wait() auf.
CPU B Thread 2 bekommt den Lock genau nach CPU A, inkrementiert den Counter und stellt fest das er jemanden wecken muss, holt den ersten Thread aus der Liste (dummerweise ist das Thread 1) und macht für diesen Thread ein resume().

Jetzt mal in Code-Form:
void
Sem::acquire()
{
    m_Lock.acquire();
   
    if(likely(Atomic::subTestNeg(&m_Count,1))) {
        Thread* t= Thread::getCurrThread();
       
        m_Threads.addTail(t);
       
        m_Lock.release();
       
        t->wait();
    } else {
        m_Lock.release();
    }
}

void
Sem::release()
{
    m_Lock.acquire();
   
    if(likely(Atomic::addTestNegZero(&m_Count,1))) {   
        Thread* t= m_Threads.removeHead();
       
        m_Lock.release();
       
        t->resume();
    } else {
        m_Lock.release();
    }
}

wait() und resume() sehen so aus:
void
Thread::wait()
{
    if(likely(Atomic::subTestNeg(&m_WaitStatus,1))) {
        m_Status= THREAD_STS_WANTS_WAITING;
       
        Scheduler::reschedule();
    }
}

void
Thread::resume()
{
    if(likely(Atomic::addTestZero(&m_WaitStatus,1)))
        Scheduler::addThread(this);
}

Ich gehe mal davon aus, dass Thread 1 auf jeden Fall das Dekrementieren seines m_WaitStatus vor dem resume() von Thread 2 schafft (anders rum wäre auch gar kein Problem).
Das Problem taucht da auf, wo die resume() Funktion Scheduler::addThread(this) aufruft und der Thread noch gar nicht durch den Scheduler durch ist. Das kann jetzt zu Problemen führen. Was ich so gelöst habe, dass diese Funktion den Thread nur hinzufügt, wenn er einen "vernünftigen" Status hat (wozu THREAD_WANTS_WAIT nicht dazu gehört).
Bevor mein Scheduler den Thread wirklich in den THREAD_STS_WAITING Status versetzt, überprüft er nocheinmal m_WaitStatus und ob das Warten wirklich nötig ist.

Könnte es da jetzt noch eine Race-Condition geben? Vorallem wenn man bedenkt, das wir in Richtung jeder Kern hat seine eigene Frequenz gehen und damit auch Code der eigentlich erst später erreicht werden sollte, zeitgleich mit anderem Code erreicht werden kann.

Zitat von: erik
Aha, das ist jetzt aber was ganz anderes als am Anfang dieses Threads, oder hab ich da was übersehen?
Naja, es läuft alles auf obige Situation hinaus, nur um diese geht es mir im Endeffekt. Denn wenn ich das im Griff habe, ist das andere auch kein Problem mehr.

Zitat von: erik
u.a. deswegen hallte ich das TimeOut-Parameter beim event_wait()-syscall für so wichtig
Wenn wir mein Race-Condition Problem geklärt haben, würde ich gerne wissen, wie man sowas vernünftig umsetzt. Ich habe da gar keine Idee, aber sowas wird ja doch ab und zu verwendet. (Gibt es bei einer Pipe unter Linux auch so nen Timeout?)
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 29. October 2011, 19:21
Hallo FlashBurn,


das Problem entsteht IMHO dadurch das Du in acquire() zuerst m_Lock.release() und danach t->wait() machst, sobald Du diese Reihenfolge umkehrst ist Dein Race-Condition-Problem komplett gelöst. Aber ich schätze mal t->wait() kommt nicht sofort sondern erst nach dem Wecken wieder zurück, das ist IMHO eine ungünstige Kernel-Architektur. Bei meinem Kernel wird es möglich sein das ein Thread sich schlafen legt aber nicht sofort die CPU frei gibt (per Scheduler o.ä.) so das er noch andere Dinge erledigen kann bevor er den Scheduler aufruft.

Diese Race-Condition mit irgendwelchen Stati im Thread-Descriptor zu lösen erscheint mir auf jeden Fall viel zu umständlich und zu kompliziert, das bläht IMHO nur den Scheduler unnötig auf.


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 29. October 2011, 19:36
Zitat von: erik
das Problem entsteht IMHO dadurch das Du in acquire() zuerst m_Lock.release() und danach t->wait() machst, sobald Du diese Reihenfolge umkehrst ist Dein Race-Condition-Problem komplett gelöst. Aber ich schätze mal t->wait() kommt nicht sofort sondern erst nach dem Wecken wieder zurück, das ist IMHO eine ungünstige Kernel-Architektur.
Jap, genau das ist das Problem. Wird also nix mit einer schnellen Semaphore.

Zitat von: erik
Diese Race-Condition mit irgendwelchen Stati im Thread-Descriptor zu lösen erscheint mir auf jeden Fall viel zu umständlich und zu kompliziert, das bläht IMHO nur den Scheduler unnötig auf.
Also ob ich nun Code für die Stati habe (welchen ich so oder so brauche) oder die Locks im Scheduler behandeln muss, kommt doch bestimmt aufs gleiche raus.

Ich könnte mir gerade vorstellen, das ich ne spezielle Funktion im Scheduler habe, wo ich ein reschedule durchführe, aber auch die Adresse einer Spinlock mit übergeben und erst wenn der Thread wirklich im Warten-Zustand ist, wird der Lock freigegeben. Damit wird der Lock aber "verdammt lange" gehalten und ich hätte trotzdem noch ein Problem mit wait() und resume() ;)

Was schlägst du denn vor?

Edit::

Wenn ich folgendes verwende:
void
Thread::resume()
{
    if(likely(Atomic::addTestZero(&m_WaitStatus,1) && m_Status == THREAD_STS_WAITING))
        Scheduler::addThread(this);
}

Müsste doch das Problem auch schon weg sein oder?
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 29. October 2011, 19:51
Hallo,


Was schlägst du denn vor?
Mein Vorschlag ist nach wie vor m_Lock.release() und t->wait() zu vertauschen, das dürfte IMHO die Race-Condition am zuverlässigsten beseitigen. Vor allem bleibt so der Scheduler von Locks usw. verschont (ein schlanker Scheduler ohne unnützen Code, der nur selten nötig ist, bringt auch mehr Performance).


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 29. October 2011, 19:58
Zitat von: erik
Mein Vorschlag ist nach wie vor m_Lock.release() und t->wait() zu vertauschen, das dürfte IMHO die Race-Condition am zuverlässigsten beseitigen. Vor allem bleibt so der Scheduler von Locks usw. verschont
Das geht ja leider nicht, da wait() erst zurück kommt, wenn der Thread wieder aufgeweckt wird. Den zweiten Satz verstehe ich gar nicht, was meinst du damit, dass der Scheduler von Locks verschont bleibt? Wenn du dann in den Scheduler gehst (nachdem du den Lock freigegeben hast), hast ja wieder das Problem, das ja der Thread schonwieder in der ReadyQueue sein kann, bevor du überhaupt den Thread abgegeben hast.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 29. October 2011, 20:45
Hallo,


Das geht ja leider nicht, da wait() erst zurück kommt, wenn der Thread wieder aufgeweckt wird.
Das ich das für doof halte hab ich ja schon geschrieben. Dieses wait muss doch nur den Zustand des Threads (also alle Register usw.) passend sichern und den Status auf BLOCKED_FOR_EVENT setzen, danach kann es doch problemlos wieder zurück kommen und muss nicht zwangsläufig den Scheduler bitten einen anderen Thread auf die CPU zu holen. Ich sehe da einfach kein Problem.

Den zweiten Satz verstehe ich gar nicht, was meinst du damit, dass der Scheduler von Locks verschont bleibt? Wenn du dann in den Scheduler gehst (nachdem du den Lock freigegeben hast), hast ja wieder das Problem, das ja der Thread schonwieder in der ReadyQueue sein kann, bevor du überhaupt den Thread abgegeben hast.
Es kann sogar noch schlimmer kommen: nachdem der Thread das m_Lock.release() ausgeführt hat könnte auf einer anderen CPU dieser Thread bereits wieder in die runnable-Liste geholt werden (durch ein Sem::release()) und auf noch einer weiteren CPU sogar ausgeführt werden bevor der Thread auf der ersten CPU dann den Scheduler aufruft (falls die CPU 1 mit einem so extrem geringem Takt läuft dass das alles zwischen einem RET und einem CALL dieser CPU passieren kann). Der Thread könnte also theoretisch zwei mal parallel auf zwei verschiedenen CPUs laufen, das ist aber auch überhaupt gar kein Problem weil der Thread auf der ersten CPU ja nichts kritisches mehr tut (er ändert auf jeden Fall nichts mehr im Thread-Descriptor usw.).
Damit das der Scheduler von den Locks verschont bleibt meine ich das im Scheduler eben keinerlei Code für diese Dinge drin sein muss. Du weißt doch: KISS.


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 29. October 2011, 21:01
Zitat von: erik
Dieses wait muss doch nur den Zustand des Threads (also alle Register usw.) passend sichern und den Status auf BLOCKED_FOR_EVENT setzen, danach kann es doch problemlos wieder zurück kommen und muss nicht zwangsläufig den Scheduler bitten einen anderen Thread auf die CPU zu holen. Ich sehe da einfach kein Problem.
Da wären wir wieder bei sinnlosem Zeit Verbraten ;)

Zumal ich nicht ganz verstehe wie der Thread dann die CPU abgibt und da wäre dann noch ein Problem, welches du eventuell nicht bedacht hast, KernelThreads. Die speichern ihren Zustand auf dem Stack und wenn der gleiche Thread (und damit der gleiche Stack) auf 2 verschiedenen CPUs läuft ist das ganz schlecht ;)

Zitat von: erik
Es kann sogar noch schlimmer kommen: nachdem der Thread das m_Lock.release() ausgeführt hat könnte auf einer anderen CPU dieser Thread bereits wieder in die runnable-Liste geholt werden (durch ein Sem::release()) und auf noch einer weiteren CPU sogar ausgeführt werden bevor der Thread auf der ersten CPU dann den Scheduler aufruft (falls die CPU 1 mit einem so extrem geringem Takt läuft dass das alles zwischen einem RET und einem CALL dieser CPU passieren kann). Der Thread könnte also theoretisch zwei mal parallel auf zwei verschiedenen CPUs laufen, das ist aber auch überhaupt gar kein Problem weil der Thread auf der ersten CPU ja nichts kritisches mehr tut (er ändert auf jeden Fall nichts mehr im Thread-Descriptor usw.).
Wie oben beschrieben ist das leider sehr wohl ein Problem für KernelThreads (und die braucht man auf x86 sowieso, zwecks "hlt"). Für UserThreads ist es für unterbrechbare Kernel ein Problem (wieder der Stack).
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: kevin am 29. October 2011, 21:20
Svenska meinte im IRC, ich soll hier ruhig klugscheißen, also tue ich das hiermit: Ein Semaphor ist männlich und heißt genau so, Semaphore sind mehrere davon. ;)
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 29. October 2011, 21:26
Zitat von: taljeth
Ein Semaphor ist männlich und heißt genau so, Semaphore sind mehrere davon.
Das stimmt so nicht ganz ;) Wenn du von der eingedeutschten Variante redest, dann hast du recht, bei der englischen nicht. Ich versuche immer die englischen Begriffe zu verwendet, da das eingedeutschte mitunter gar nichts mehr mit dem Original zu tun hat (Stack -> Keller).
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 29. October 2011, 21:36
Hallo,


Da wären wir wieder bei sinnlosem Zeit Verbraten ;)
Wieso? Das Sichern der CPU-Register und Setzen des Status muss doch sowieso gemacht werden. Ich mache im Endeffekt genau das selbe wie Du auch, nur in anderer Reihenfolge.

Zumal ich nicht ganz verstehe wie der Thread dann die CPU abgibt
Indem er den Scheduler aufruft und dieser einfach den nächst besten Thread aus der runnable-Liste holt und diesen lädt aber eben ohne vorher irgendwas zu sichern (das wurde ja bereits in wait() gemacht). Ich trenne diese 2 Dinge nur, sonst nichts.

und da wäre dann noch ein Problem, welches du eventuell nicht bedacht hast, KernelThreads.
Doch, bedacht hab ich das, in meinem Konzept sind Kernel-Stacks immer CPU-lokal und das müsste doch eigentlich auch bei x86 gehen. Dafür hab ich aber keine Kernel-Threads, obwohl IMHO auch das lösbar ist (bei x86, inklusive HLT).


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 29. October 2011, 21:47
Zitat von: erik
Indem er den Scheduler aufruft und dieser einfach den nächst besten Thread aus der runnable-Liste holt und diesen lädt aber eben ohne vorher irgendwas zu sichern
Das Sichern wird bei mir entweder beim Kernel-Eintritt (UserThread) oder halt aufm Stack (KernelThread) gemacht. Bei letzterem bin ich mir gerade nicht sicher, ob ich den Teil nicht vllt bei meinem aktuellen Code vergessen habe ;)

Zitat von: erik
Doch, bedacht hab ich das, in meinem Konzept sind Kernel-Stacks immer CPU-lokal und das müsste doch eigentlich auch bei x86 gehen.
Habe ich ja auch, aber halt nicht für KernelThreads, die brauchen ihren eigenen Stack.

Zitat von: erik
Dafür hab ich aber keine Kernel-Threads, obwohl IMHO auch das lösbar ist (bei x86, inklusive HLT).
Wie?
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: Svenska am 29. October 2011, 22:30
Svenska meinte im IRC, ich soll hier ruhig klugscheißen, also tue ich das hiermit: Ein Semaphor ist männlich und heißt genau so, Semaphore sind mehrere davon. ;)
Och, jetzt bin ich wieder schuld oder was? :-P
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 30. October 2011, 09:21
Beim Durchgehen meines Codes ist mir aufgefallen, dass der aktuelle Code unter SMP und KernelThreads noch gar nicht funktionieren würde. Denn der Scheduler braucht seinen eignen Stack, würde er den Stack vom Thread mitbenutzen und der Thread läuft dann miteinmal auf einer anderen CPU bevor der Scheduler fertig ist, würde auf der anderen CPU der Stack vom Scheduler überschrieben werden.
Das hatte ich halt immer so gelöst, dass der Scheduler seinen eigenen Stack bekommt.

Durch diesen Kniff bin ich jetzt auf folgende Lösung gekommen. Ich habe eine Thread::prepareToWait() Methode und diese speichert den aktuellen State des Threads (wenn es ein UserThread ist, direkt in der Thread-Struct und ansonsten halt auf dem Stack) und wechsele zum Scheduler-Stack der CPU. Dadurch befindet sich der Thread dann im Warten-Status, kann auf einer anderen CPU wieder laufen, aber er kann auch noch auf der aktuellen CPU eventuelle Arbeit fertigstellen.

Damit hätte ich dann folgenden Code:
void
Sem::acquire()
{
    m_Lock.acquire();
   
    if(likely(Atomic::subTestNeg(&m_Count,1))) {
        Thread* t= Thread::getCurrThread();
       
        m_Threads.addTail(t);

        t->prepareToWait()
       
        m_Lock.release();
       
        Scheduler::reschedule();
    } else {
        m_Lock.release();
    }
}

Womit ich zwar immernoch ein Problem mit wait()/resume() habe, aber wenn anstatt des einfachen Counters ne komplette Semaphore dafür nutze (was ja leider "mehr" Code bedeutet) dürfte dass dann auch zuverlässig funktionieren.

Oder hat jemand da noch ne andere Idee für?

Edit::

Toll, ist mir jetzt gerade auch an der Variante ein Problem aufgefallen :(

Ich muss irgendwie an die EIP für die Rücksprungadresse von Sem::acquire() rankommen, weil ansonsten der Lock für die Semaphore nochmal freigegeben wird und das kann ja wieder zu Problemen führen. Also nochmal daran pfeilen.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 30. October 2011, 09:50
Hallo,


Och, jetzt bin ich wieder schuld oder was? :-P
Ja, einer muss eben den Kopf hin halten und wenn sich jemand findet der im IRC die Finger nicht still halten konnte (das muss auch niemand abstreiten, ich bin Zeuge) dann darf er im Forum ruhig den Sündenbock spielen. ;)


Das Sichern wird bei mir entweder beim Kernel-Eintritt (UserThread)
Warum? Es gibt doch bestimmt auch viele Syscalls wo gar nicht alle Register gesichert werden müssen. Ich sichere immer nur dann wenn es wirklich nötig ist und auch nur das was die ABI vorschreibt. Ich weiß dass das bei x86 mit dem zentralen Einsprungspunkt (wo alles durch muss) eben anders gelöst wird und die paar Register kosten auch nicht die Welt, aber wirklich als elegant empfinde ich das nicht.

oder halt aufm Stack (KernelThread) gemacht
Warum wird das nicht auch im Thread-Descriptor gesichert? Das ist doch schon wieder eine unnötige Fallunterscheidung.

Zitat von: erik
Dafür hab ich aber keine Kernel-Threads, obwohl IMHO auch das lösbar ist (bei x86, inklusive HLT).
Wie?
Für die spezifischen Kernel-Stacks der Kernel-Threads benötigt man schlussendlich doch eine Fallunterscheidung (das wäre für mich ein wichtiger Grund auf Kernel-Threads gleich ganz zu verzichten) im Scheduler und für HLT muss eben unmittelbar vorher STI und unmittelbar hinterher CLI benutzt werden aber damit hier der Scheduler nicht noch eine Fallunterscheidung wegen dem Sichern/Wiederherstellen von Registern benötigt sollten die Idle-Threads ihre Register selber vor dem HLT im Thread-Descriptor sichern und hinter dem HALT von dort wieder laden aber auch das ist IMHO nicht so arg schlimm.


Das hatte ich halt immer so gelöst, dass der Scheduler seinen eigenen Stack bekommt.
Sorry, wenn ich das wieder so deutlich raus hängen lass aber KISS ist Dir wirklich nicht sonderlich vertraut oder? Wenn Du schon CPU-lokale Kernel-Mode-Stacks hast wozu benötigst Du dann noch extra (auch CPU-lokal?) Scheduler-Stacks? Ich habe wirklich den Verdacht das Du für jedes Problem die kompliziertest mögliche Lösung suchst (und auch findest) nur um dann noch einen extra dicken Haufen zusätzlicher Probleme zu bekommen (für die Du dann auch wieder die maximalst umständlichste Lösung suchst).

Durch diesen Kniff bin ich jetzt auf folgende Lösung gekommen. Ich habe eine Thread::prepareToWait() Methode und diese ....
Das heißt das Du aus 2 Funktionsaufrufen 3 gemacht hast ohne dadurch an Funktionalität zu gewinnen? Ich nehme an Du kannst Dir meine Begeisterung geradezu bildlich vorstellen. ;)
Zum Fertigstellen von Arbeiten gibt es doch den CPU-lokalen Kernel-Mode-Stack, das heißt das nach wait() die CPU in gar keinem Thread-Kontext mehr läuft (das einzigste was daran noch erinnert ist der Wert in CR3 aber das ist im Kernel wurscht weil der Kernel ja in allen PDs identisch ist).

Womit ich zwar immernoch ein Problem mit wait()/resume() habe
Genau dagegen würde meine Idee mit den Events nachhaltig helfen.

Ich muss irgendwie an die EIP für die Rücksprungadresse von Sem::acquire() rankommen, weil ansonsten der Lock für die Semaphore nochmal freigegeben wird und das kann ja wieder zu Problemen führen. Also nochmal daran pfeilen.
Entweder darfst Du aus wait() nicht zwei mal raus kommen (so will ich das in meinem Kernel machen) oder Du machst das mit einem Rückgabewert wie bei fork().


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 30. October 2011, 11:46
Zitat von: erik
Warum? Es gibt doch bestimmt auch viele Syscalls wo gar nicht alle Register gesichert werden müssen.
Es werden auch nur die wichtigsten Register gesichert (die, die die ABI vorschreibt), was auf alle Syscalls zutrifft.

Zitat von: erik
Warum wird das nicht auch im Thread-Descriptor gesichert? Das ist doch schon wieder eine unnötige Fallunterscheidung.
Weil wir dann ja wieder unnötiges Kopieren hätten. Denn die Daten liegen ja schon auf dem Stack, von dort würde man sie dann in den Thread-Discriptor kopieren um sie dann später wieder aus dem Thread-Discriptor zurück auf den Stack kopieren würde (und somit effektiv die gleichen Daten mit den gleichen Daten überschreibt).
Zumal ich die Fallunterscheidung trotzdem bräuchte, weil die Position wo auf den CPU-lokalen-Stack kopiert wird ist immer die gleiche, bei dem KernelThread nicht. Bzw. muss ich mir bei UserThreads immmer den CPU-lokalen-Stack holen und bei KernelThreads steht der Stack-Pointer ja im Thread-Discriptor.

Zitat von: erik
das wäre für mich ein wichtiger Grund auf Kernel-Threads gleich ganz zu verzichten
Geht halt nicht, bei mir läuft z.B. immer ein Thread-Killer-Thread im Hintergrund mit, der Threads (und wenn nötig ganze Tasks) komplett aus dem Speicher entfernt, was halt am einfachsten in einem extra Thread ist. Wie willst du das eigentlich lösen?

Zitat von: erik
Sorry, wenn ich das wieder so deutlich raus hängen lass aber KISS ist Dir wirklich nicht sonderlich vertraut oder?
Ich kenne es, aber ob ich immer auf KISS komme, steht auf nem ganz anderem Blatt. Zumal diese Lösung noch von meinem "alten" Kernel stammt.

Zitat von: erik
Das heißt das Du aus 2 Funktionsaufrufen 3 gemacht hast ohne dadurch an Funktionalität zu gewinnen?
Wieso habe ich nicht an Funktionalität gewonnen? Es ist dann so, dass der eigentliche Thread nicht mehr läuft, sondern seinen neuen Status (in dem Fall blockiert) hat und auch auf einer anderen CPU wieder laufen kann.

Zitat von: erik
Genau dagegen würde meine Idee mit den Events nachhaltig helfen.
Ich sehe immer noch nicht, was deine Events von ganz normalen Semaphoren unterscheidet? Ich will diese auch genau dafür benutzen.

Zitat von: erik
Entweder darfst Du aus wait() nicht zwei mal raus kommen (so will ich das in meinem Kernel machen) oder Du machst das mit einem Rückgabewert wie bei fork().
Spontan habe ich keine Idee, wie ich das mit den Rückgabewerten machen sollte. Wie willst du es denn verhindern nicht zwei mal aus wait() zurück zukommen? Ich werde das halt über die Rücksprungadresse lösen. Im Falle der KernelThreads würde die Rücksprungadresse die von Sem::acquire() sein und im Falle von UserThreads würde er sogar gleich wieder zurück in den UserMode springen (da kann ich mir ja das ganze zurück gespringe, aus dem Syscall wieder raus, sparen).

Ich bin gerade dabei, meinen kompletten Ansatz wie ich mit Threads im Scheduler umgehe, umzuschreiben. Mein neuer Scheduler (es geht da nur um den eigentlichen IRQ Handler) holt nur noch einen neuen Thread und packt einen Thread nur zurück in eine Queue, wenn der Status des Threads Running ist. Bei allen anderen Threads wird davon ausgegangen das sie schon an der richtigen Stelle sind, das wiederum wird schon im Kernel vor dem Scheduler gemacht, so dass der Scheduler wieder ein Stückchen kürzer und einfacher ist.

Dein Hinweis mit, dass ich ja schon nen CPU-lokalen-Stack habe und nicht extra nen Scheduler-lokalen-Stack brauche, werde ich nochmal überdenken. Ich brauche auf jeden Fall die Fallunterscheidung zw Kernel- und UserThreads. Problem ist einfach, das der IRQ-Handler (vom Timer) die Register ja auf dem gerade aktuellen Stack sichert und das kann einmal ein CPU-lokaler-Stack oder ein KernelThread-Stack sein. Um dann nicht noch nachgucken zu müssen (im IRQ-Stub-Code, bevor der eigentliche Scheduler aufgerufen wird) um was es sich handelt, ist es halt einfacher nen "anderen" Stack zu nehmen. Ich sollte vllt dazu sagen, dass ich dafür nicht extra nen Stack bzw. Speicher allozieren. Ich habe ja meine Idle-Threads und die brauchen nicht mal annähernd die 4kb Stack, also nutze ich die eine Hälfte für den KernelThread-Stack und die andere Hälfte ist der Scheduler-Stack (geht da ich ja für jede CPU nen Idle-Thread habe).
Wenn du dann wieder sagst, dazu habe ich ja den CPU-lokalen-Stack, dann müsste ich ja wieder ne Unterscheidung machen oder der Thread müsste seinen State selbst sichern (was auch einer Unterscheidung gleich kommt). So ist das nen ganz normaler KernelThread wo ich auf nix achten muss.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 30. October 2011, 16:04
Ich hoffe jetzt endlich ein halbwegs vernünftige Lösung gefunden zu haben.

Also das man nicht zwei mal aus wait() zurückkommt, habe ich nicht hinbekommen und wüsste auch nicht wie ich das vernünftig (ohne die komplette wait() Funktion in Assembler zu schreiben) lösen könnte.
Bleibt ja nur noch die Variante das ich das ganze, wie bei fork(), durch den Rückgabewert löse. Auch das war nicht wirklich einfach, hat ne ganze Weile gedauert bis ich festgestellt habe, dass das unter einer Hochsprache nicht wirklich (oder doch?) zu lösen ist.

Mein neuer Code sieht jetzt so aus:
void
Sem::acquire()
{
    m_Lock.acquire();
   
    if(likely(Atomic::subTestNeg(&m_Count,1))) {
        Thread* t= Thread::getCurrThread();
       
        m_Threads.addTail(t);
       
        if(Scheduler::changeCurrThreadStatus(THREAD_STS_WAITING)) {
            m_Lock.release();
           
            Scheduler::reschedule();
            unreachable();
        }
    } else {
        m_Lock.release();
    }
}

void
Thread::wait()
{   
    m_Wait.acquire();
}

void
Thread::resume()
{
    m_Wait.release();
}

Thread::m_Wait ist jetzt eine Semaphore. Die Funktion Scheduler::changeCurrThreadStatus() ändert den Status des gerade laufenden Threads, im Falle von UserThreads wird mit dem Stack gar nix weiter gemacht (was auch heißt, das sobald man eine Semaphore im Kernel verwendet, danach wieder in den UserMode gesprungen wird, sprich ich darf im Kernel keine Semaphoren zur Synchronisation von Kernel-Code einsetzen) und im Falle von KernelThreads (das eigentliche Problem) wird der komplette Stack (nur das was wirklich in Benutzung ist) in den CPU-lokalen-Stack kopiert, auf diesen umgestellt und auf dem alten Stack wird ein Interrupt-Stack-Frame erstellt (damit der Scheduler ohne Probleme und Verrenkungen zurückspringen kann). Letzteres musste in einer extra, in Assembler geschriebenen, Funktion gemacht werden.

Der Grund warum ich diese in Assembler schreiben musste ist einfach, ich weiß nicht was für Code der Compiler erzeugt und wie genau der Stack aussieht. Der Code ist auch recht kurz:
schedChangeToNoContextThread:
    push ebp
    mov ebp,esp
   
    mov esp,[ebp+8]
    ;create the stack frame for the scheduler
    pushfd
    push dword 0x8
    push dword .endOriginalThread
    pushad
    push ds
    push es
    push gs
       
    mov eax,esp
   
    mov esp,ebp
    pop ebp
    ret
;----------------------------
align 0x10
.endOriginalThread:
    xor eax,eax
   
    mov esp,ebp
    pop ebp
    ret
;----------------------------
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 30. October 2011, 16:46
Hallo,


Weil wir dann ja wieder unnötiges Kopieren hätten. Denn die Daten liegen ja schon auf dem Stack
Wieso kopieren? Wieso legst du die Register überhaupt auf den Stack und nicht gleich in den Thread-Descriptor? Okay, bei nem HW-Interrupt oder einer Exception muss man alle Register sichern aber dazu benötigt man ein oder zwei Register um eine Adresse laden zu können und die 2 Register müssen ersteinmal auf den Stack und von da aus dann in den Thread-Descriptor aber die 2 zusätzlichen PUSHs und POPs kosten auf einer halbwegs aktuellen CPU nicht viel (ich würde mit insgesamt 2 Takten rechnen).

Zumal ich die Fallunterscheidung trotzdem bräuchte, weil die Position wo auf den CPU-lokalen-Stack kopiert wird ist immer die gleiche, bei dem KernelThread nicht. Bzw. muss ich mir bei UserThreads immmer den CPU-lokalen-Stack holen und bei KernelThreads steht der Stack-Pointer ja im Thread-Discriptor.
Ich verstehe absolut nicht wozu hier eine Fallunterscheidung nötig ist, PUSH und POP arbeiten immer korrekt egal wo der Stack-Pointer steht.

bei mir läuft z.B. immer ein Thread-Killer-Thread im Hintergrund mit, der Threads (und wenn nötig ganze Tasks) komplett aus dem Speicher entfernt, was halt am einfachsten in einem extra Thread ist. Wie willst du das eigentlich lösen?
Also das Erstellen und Löschen von ganzen Prozessen werde ich wohl direkt beim jeweiligen Syscall (oder im Exception-Handler) machen, ansonsten hab ich fürs Defragmentieren (was auf jeden Fall nicht mehr am Stück passieren kann) noch einen DoIdle-Syscall vorgesehen der immer vom Idle-Thread aufgerufen wird und prüft ob in der Job-Queue was drin ist, falls ja wird das (teilweise) abgearbeitet und der Rückgabewert signalisiert dem Idle-Thread das er kein HLT ausführen soll (sondern per YIELD die CPU wieder abgeben soll) oder die Queue ist leer und der DoIdle-Syscall kehrt sofort zurück und der Rückgabewert signalisiert das der Idle-Thread diese Zeitscheibe lang die CPU per HLT-Befehl schlafen lassen soll.

Ich sehe immer noch nicht, was deine Events von ganz normalen Semaphoren unterscheidet? Ich will diese auch genau dafür benutzen.
Ich hatte doch schon mehrmals geschrieben das Du diesen Event-Mechanismus anstatt wait()/resume() mit Deinem Semaphor verwenden sollst. Das war aber auch noch als ich dachte das Du von einem Semaphor im User-Mode ausgehst, müsste aber innerhalb des Kernels auch funktionieren. Falls der Semaphor frei ist wird natürlich weder Dein wait() noch mein event_wait() benötigt, auch das ist unabhängig davon ob der Semaphor im Kernel oder im User-Mode liegt.

Wie willst du es denn verhindern nicht zwei mal aus wait() zurück zukommen?
In meinem event_wait() wird einfach der Zustand des Threads (die Register) im Thread-Descriptor gesichert und sein Status von RUNNING auf BLOCKED_FOR_EVENT gesetzt. Bei den Registern wird für IP (ist bei mir R63) die Adresse im User-Mode hinter dem Syscall-Befehl benutzt und an sonsten werden die Register im Thread-Descriptor so belegt als würde der Thread gerade aus dem Syscall raus kommen. Damit ist event_wait() fertig und kehrt zurück, die CPU läuft damit nicht mehr im Kontext dieses Threads. Beim event_trigger() wird dann nur noch das Register mit dem Rückgabewert im Thread-Descriptor passend gesetzt und der Status auf RUNNABLE geändert und dann der Thread in die runnable-Liste des Schedulers eingetragen (die Blockade ist damit vorbei). Der Scheduler lädt dann irgendwann diesen Thread und er kommt unmittelbar hinter dem Syscall zurück und die Register sind genau so wie sie nach diesem Syscall sein sollen.

Bei Deinen Kernel-Threads würde ich das ganz genau so machen nur das die Rücksprungadresse nicht hinter einem INT/SYSCALL/SYSENTER sondern hinter einem CALL liegt.


Das verdrehen des Stack wird wohl kaum in einer Hochsprache funktionieren, dazu musst Du zumindest Teile von wait() in Assembler implementieren, aber das sind doch nur ne gute Hand voll Zeilen (zusammen mit einem anständigen Beschreibungstext sollte das auch in vielen Jahren noch wartbar sein und dürfte nicht mehr als eine Bildschirmseite belegen).


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 30. October 2011, 17:27
Zitat von: erik
Wieso kopieren? Wieso legst du die Register überhaupt auf den Stack und nicht gleich in den Thread-Descriptor? Okay, bei nem HW-Interrupt oder einer Exception muss man alle Register sichern aber dazu benötigt man ein oder zwei Register um eine Adresse laden zu können und die 2 Register müssen ersteinmal auf den Stack und von da aus dann in den Thread-Descriptor aber die 2 zusätzlichen PUSHs und POPs kosten auf einer halbwegs aktuellen CPU nicht viel (ich würde mit insgesamt 2 Takten rechnen).
Naja, ich muss erstmal an den Thread State rankommen, an den Thread-Discriptor komme ich leicht ran, aber dann muss ich ja noch einen gewissen Wert da drauf addieren um an die Adresse für den State zu kommen und das wird halt unter C++ mehr schwierig als leicht. Denn das ganze passiert ja in Assembler und da habe ich keinen Zugriff auf die Offsets von den class Membern.

Dann kommt noch hinzu, warum bei einem HW-IRQ oder einigen Exceptions die Daten in den Thread-Discriptor schreiben, ich pushe sie einfach auf den Stack und wenn der Code fertig ist, wird er wieder vom Stack gepopt (und ich brauche nicht auf den Thread-Discriptor zugreifen, was schonmal ein Cache-Miss sparen kann). Viel einfacher als wenn ich mich mit dem Kopieren rumquählen müsste. Zumal ist das Kopieren nicht langsamer als ein "pushad"?

Zitat von: erik
Ich verstehe absolut nicht wozu hier eine Fallunterscheidung nötig ist, PUSH und POP arbeiten immer korrekt egal wo der Stack-Pointer steht.
Richtig, mir ging es darum wie ich die Daten dann wieder aus dem Thread-Discriptor auf den Stack bekomme und da habe ich eine Stelle wo ich den CPU-lokalen-Stack herbekomme und eine andere Stelle wo ich den KernelThread-Stack herbekomme. Also eine Unterscheidung.

Zitat von: erik
Also das Erstellen und Löschen von ganzen Prozessen werde ich wohl direkt beim jeweiligen Syscall (oder im Exception-Handler) machen
Das Thema hatten wir ja schon, dass mir das zu lange dauert um die Ints dabei auszulassen. Zumal ich halt mit dem Löschen immer so meine Probleme hatte (bei nem unterbrechbaren Kernel), da man dem Thread ja nicht so einfach seinen Stack wegnehmen kann. Auch das Freigeben des PD´s ist eher schwierig oder sagen wir grob fahrlässig, während es noch in Benutzung ist.

Zitat von: erik
ansonsten hab ich fürs Defragmentieren (was auf jeden Fall nicht mehr am Stück passieren kann) noch einen DoIdle-Syscall vorgesehen der immer vom Idle-Thread aufgerufen wird und prüft ob in der Job-Queue was drin ist, falls ja wird das (teilweise) abgearbeitet und der Rückgabewert signalisiert dem Idle-Thread das er kein HLT ausführen soll (sondern per YIELD die CPU wieder abgeben soll) oder die Queue ist leer und der DoIdle-Syscall kehrt sofort zurück und der Rückgabewert signalisiert das der Idle-Thread diese Zeitscheibe lang die CPU per HLT-Befehl schlafen lassen soll.
Und das nennst du KISS, ich verstehe dabei nur Bahnhof ;) und hört sich aufwendig an. (OT: Mein Idle-Thread läuft solange bis er unterbrochen wird, wie lange das ist, weiß ich vorher gar nicht unbedingt)

Zitat von: erik
Ich hatte doch schon mehrmals geschrieben das Du diesen Event-Mechanismus anstatt wait()/resume() mit Deinem Semaphor verwenden sollst.
Das habe ich schon so verstanden und mache ich ja jetzt auch. Ich wollte nur von dir wissen, wieso du genau den umgekehrten Weg einer Semaphore gehst (was den Counter betrifft). Zumal sich das bei dir so anhört als wenn du nen extra Typ event hast und das verstehe ich halt nicht, weil dein event ist nix anderes als ne Semaphore (so habe ich das jedenfalls verstanden).

Zitat von: erik
Bei Deinen Kernel-Threads würde ich das ganz genau so machen nur das die Rücksprungadresse nicht hinter einem INT/SYSCALL/SYSENTER sondern hinter einem CALL liegt.
Jap und genau das war das Problem, welches in einer Hochsprache nicht zu lösen ist. Deswegen habe ich mir ja eine kleine Assembler-Funktion geschrieben die einen anderen Rückgabewert hat (0 für den eigentlichen Thread und den Pointer zum KernelThread-Stack bei dem Thread der ohne Kontext läuft).

Zitat von: erik
Das verdrehen des Stack wird wohl kaum in einer Hochsprache funktionieren, dazu musst Du zumindest Teile von wait() in Assembler implementieren, aber das sind doch nur ne gute Hand voll Zeilen (zusammen mit einem anständigen Beschreibungstext sollte das auch in vielen Jahren noch wartbar sein und dürfte nicht mehr als eine Bildschirmseite belegen).
Naja, den Stack würde ich nicht innerhalb von wait() verdrehen (weiß auch nicht ob man das vernünftig hinbekommt), sondern halt in einer extra Funktion (in Assembler).
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 31. October 2011, 15:19
Hallo,


Naja, ich muss erstmal an den Thread State rankommen ....
Ja, das ist allerdings ein schwieriges Problem das Du da durch die von Dir verwendete Hochsprache bekommst. Ist es nicht möglich dieses Offset in einem C++-Modul zu ermitteln und als Wert zu exportieren so das zur Link-Zeit dieses Offset auch in den Assembler-Modulen verfügbar ist? Ich weiß nicht ob klar ist was ich meine aber ich denke das dürfte ein eleganter Weg sein Dein Problem zu lösen. Auch aus diesem Grund werde ich bei meinen Kernel wohl doch einige Teile komplett in Assembler haben. Ich habe als Programmierer damit zwar einen höheren Aufwand, insbesondere was die Verwaltung der Thread-Zustände (die bei meiner CPU etwa 90 Register umfassen) angeht, aber dafür sind viele Probleme erst gar nicht existent. Eventuell definierst du den Thread-State aber auch noch mal als dediziertes Struct (mit fest definiertem Layout) und bindest das als Member in Deine Klasse ein und speicherst Dir immer auch direkt die virtuelle Adresse dieses Structs zum aktuellen Thread ab (z.B. im local-APIC, siehe unten).

Dann kommt noch hinzu, warum bei einem HW-IRQ oder einigen Exceptions die Daten in den Thread-Discriptor schreiben, ich pushe sie einfach auf den Stack und wenn der Code fertig ist, wird er wieder vom Stack gepopt (und ich brauche nicht auf den Thread-Discriptor zugreifen, was schonmal ein Cache-Miss sparen kann).
Wenn Du sicher bist das Du nach dem HW-IRQ oder der Exception noch den selben Thread weiterlaufen lässt ist es natürlich eine minimal schnellere Alternative direkt auf den CPU-lokalen Kernel-Mode-Stack zu sichern (Du sparst dadurch das Ermitteln der richtigen Adresse aber das eigentliche Abspeichern der Register dürfte in etwa gleich schnell sein). Auf meiner CPU habe ich großen Wert darauf gelegt das die CPU das möglichst optimal unterstützt und deswegen benötige ich auch nur einen einzigen Assembler-Befehl um an die Adresse des Thread-Descriptors zu kommen (der wird vom Scheduler immer in einem extra CPU-lokalen Controll-Register abgelegt, schau doch mal bei der local-APIC-Spec ob es da nicht irgendein Register gibt das Du bei x86 für diesen Zweck missbrauchen/gebrauchen kannst, auf den jeweils lokalen APIC sollte man doch von jeder CPU aus identisch zugreifen können ohne erst ermitteln zu müssen auf welcher CPU man gerade ist) und beim Wechsel vom User-Mode in den Kernel-Mode werden bei meiner CPU auch immer 8 Register in Schattenregister gesichert so das ich sofort (zumindest ein paar) freie Register habe (das hab ich mir bei ARM abgeschaut und ist IMHO extrem nützlich).

Das Thema hatten wir ja schon, dass mir das zu lange dauert um die Ints dabei auszulassen.
Mir ist klar was Du meinst und für mein System hab ich da auch schon drüber nachgedacht solche Dinge in den Hintergrund zu verlegen (wobei gerade das Erstellen neuer Prozesse eher nicht in den Hintergrund sollte damit das möglichst zügig geht), aber ich denke darüber mach ich mir erst dann konkrete Gedanken wenn ich belastbare Performance/Latenz-Messungen habe.

Zumal ich halt mit dem Löschen immer so meine Probleme hatte (bei nem unterbrechbaren Kernel), da man dem Thread ja nicht so einfach seinen Stack wegnehmen kann. Auch das Freigeben des PD´s ist eher schwierig oder sagen wir grob fahrlässig, während es noch in Benutzung ist.
Das mit dem Stack ist doch kein Problem da Du ja eigentlich immer den CPU-lokalen Kernel-Mode-Stack nutzen solltest, auch das mit dem PD ist nicht gar so schlimm denn schließlich ist doch der Kernel in allen PDs identisch (und nur den brauchst Du doch noch um nach dem Löschen per Scheduler einen anderen Thread auf die CPU zu holen) so das Du hier jedes beliebige PD nutzen kannst (ich würde dafür das PD Deines idle-Prozesses empfehlen, oder auch von init je nach dem was bei Dir sicher immer läuft).

Und das nennst du KISS
Ja, eine Job-Queue (per Lock gesichert damit nix kaputt geht) und den Syscall-Rückgabewert davon abhängig machen ob ein Job verfügbar war (und auch abgearbeitet wurde also die Ilde-Zeitscheibe im Kernel genutzt wurde) ist doch nun wahrlich keine Rocket-Science. Und im User-Mode im idle-Prozess je nach Rückgabewert entweder HLT (Job-Queue im Kernel war leer also aktuelle Zeitscheibe verschlafen) oder YIELD (Kernel hat gearbeitet also erst mal wieder anderen Thread holen) auszuführen ist auch nicht so wild.

Mein Idle-Thread läuft solange bis er unterbrochen wird, wie lange das ist, weiß ich vorher gar nicht unbedingt
Ist bei meinem nicht anders, aber innerhalb des Kernels (im DoIdle-Syscall) kann nicht unterbrochen werden da mein Kernel ja nicht unterbrechbar ist.

wieso du genau den umgekehrten Weg einer Semaphore gehst (was den Counter betrifft)
Hä, hier verstehe ich nur Bahnhof. ;)

Zumal sich das bei dir so anhört als wenn du nen extra Typ event hast und das verstehe ich halt nicht, weil dein event ist nix anderes als ne Semaphore (so habe ich das jedenfalls verstanden).
Diese Events sind bei meinem Kernel eine eigene Art von Service, sowas kenne ich auch von einigen anderen Embedded-OSen und finde das auch sehr praktisch. Wie Du das mit einem Semaphor vergleichen kannst verstehe ich nicht, es wird doch hierbei nichts beschützt o.ä. sondern es kann einfach eine beliebige Anzahl an Threads auf ein Ereignis warten und es wird dann immer genau ein Thread geweckt wenn ein Ereignis auftritt. Das nicht nur Threads sondern auch Ereignisse gequeued werden können liegt einfach daran das sonst eben die von Dir beschriebene Race-Condition auftreten kann, die hierbei benutzte Lösung erachte ich als sehr zuverlässig. Ich habe diesen Mechanismus (auf anderen OSen) schon oft in der Praxis benutzt und hatte damit auch nie Probleme.


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 31. October 2011, 16:05
Zitat von: erik
Ist es nicht möglich dieses Offset in einem C++-Modul zu ermitteln und als Wert zu exportieren so das zur Link-Zeit dieses Offset auch in den Assembler-Modulen verfügbar ist?
Das geht wohl (ich weiß dass das andere OS auch so machen), aber ich habe keine Ahnung wie, zumal ein "pushad" wesentlich einfacher ist als ein oder 2 Register zu pushen um dann die restlichen Register und die gepushten im Thread-State zu sichern.
Fairerweise muss ich zugeben, dass ich bei den Syscalls z.B. doppelt sichere, einmal auf dem Stack und dann nochmal vom Stack in die Thread-Struct. Vorallem um das Speichern in der Thread-Struct in einer Hochsprache machen zu können.

Zitat von: erik
Wenn Du sicher bist das Du nach dem HW-IRQ oder der Exception noch den selben Thread weiterlaufen lässt ist es natürlich eine minimal schnellere Alternative direkt auf den CPU-lokalen Kernel-Mode-Stack zu sichern (Du sparst dadurch das Ermitteln der richtigen Adresse aber das eigentliche Abspeichern der Register dürfte in etwa gleich schnell sein).
Sicher bin ich da nicht und du weist mich da auf ein Problem in meinem Design hin (was ich mit dem neuen Kernel noch nicht bedacht habe). Bei einem HW-IRQ wird ja ein Thread aufgeweckt oder erstellt, was zwangsläufig (da ich die Treiber-IRQ-Threads mit Realtime-Priorität laufen lassen möchte) den Scheduler auf den Plan ruft. Bei mir wird das auf einem SMP-System so gehandhabt, dass ich überprüfe ob die Priorität des neuen Threads größer der des aktuellen Threads ist (dann wird sofort der Scheduler nur auf dieser CPU aufgerufen und ich speichere den State im Moment in der Situation noch nicht :roll:) oder nicht (dann wird eine lowest-priority IPI-Nachricht an alle CPUs - exklusive der wo der Code läuft - gesendet).
Das Empfangen der IPI-Nachricht kommt ja durch den APIC als normaler Interrupt an, was ich nochmal nachgucken muss, ob ich da dann auch nen EOI senden muss oder nicht, weil im Moment mache ich das.

Das Sichern der Register dürfte per "popad" schneller sein, ersten denke ich mal das es optimiert ist und zweitens erfordert es weniger Speicherzugriffe und es müssen wesentlich weniger Instruktionen verarbeitet werden. Das ist allerdings auf MikroKontrollern (bei ARM weiß ich es nicht) schön gelöst, da sind die Register auch nochmal im Speicher gemappt, womit man das ganze leichter bewerkstelligen kann.

Zitat von: erik
der wird vom Scheduler immer in einem extra CPU-lokalen Controll-Register abgelegt, schau doch mal bei der local-APIC-Spec ob es da nicht irgendein Register gibt das Du bei x86 für diesen Zweck missbrauchen/gebrauchen kannst, auf den jeweils lokalen APIC sollte man doch von jeder CPU aus identisch zugreifen können ohne erst ermitteln zu müssen auf welcher CPU man gerade ist
Geht sogar viel einfacher (die APIC Register sollte man für sowas nicht nutzen), man kann dafür einfach nen Debugging-Register nutzen.

Zitat von: erik
Das mit dem Stack ist doch kein Problem da Du ja eigentlich immer den CPU-lokalen Kernel-Mode-Stack nutzen solltest
Jetzt ja, aber auf nem unterbrechbaren Kernel hast du sowas ja nicht.

Zitat von: erik
Hä, hier verstehe ich nur Bahnhof.
Du queuest in deinem Event Threads und es wird immer nur einer aufgeweckt. Fällt dir da nix auf? Das klingt für mich verdammt nach einer Semaphore. Was ich mit dem Counter meine, ist dass du fire()->release() als dekrementieren implementierst und bei einer Semaphore ist es inkrementieren. Wieso gehst du da genau den umgekehrten Weg? Hat das nen speziellen Grund?

Wenn du eine Semaphore nicht so siehst das sie zum Schützen, sondern zum Synchronisieren da ist, macht das schon Sinn nen Event mit einer Semaphore zu verlgeichen.
Deswegen nutze ich ja jetzt eine für wait()/resume() und auch mein waitForThread() nutzt eine.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 31. October 2011, 19:36
Hallo,


zumal ein "pushad" wesentlich einfacher ist als ein oder 2 Register zu pushen um dann die restlichen Register und die gepushten im Thread-State zu sichern.
Einfacher mit Sicherheit, aber den Performance-Vorsprung von PUSHAD schätze ich minimal ein. Das teuerste dürfte sein die Adresse des Thread-Descriptors zu holen.

Fairerweise muss ich zugeben, dass ich bei den Syscalls z.B. doppelt sichere, einmal auf dem Stack und dann nochmal vom Stack in die Thread-Struct. Vorallem um das Speichern in der Thread-Struct in einer Hochsprache machen zu können.
Hm, ich bin mir gar nicht mal so sicher ob das wirklich eine schlechte/unperformante Lösung ist, das Kopieren (vom Stack zum Thread-Descriptor) ließt doch aus Speicher der mit extrem hoher Wahrscheinlichkeit noch im L1-Cache ist und das Schreiben wird beide male (beim PUSHAD wie beim MOVSD) per Write-Allocation bequem und unsichtbar im Hintergrund erledigt. Da es bei x86 ja wohl nicht ganz so einfach ist die Adresse des richtigen Thread-Descriptors schnell zu bekommen und man auch immer erst mal ein oder zwei Register aus dem Weg schaffen muss könnte die Variante mit PUSHAD im INT-Assembler-Stub und dann Kopieren im Hochsprachenteil tatsächlich sogar die Schnellste aller möglichen Lösungen auf x86 sein.

.... dann wird eine lowest-priority IPI-Nachricht an alle CPUs - exklusive der wo der Code läuft - gesendet
Also das klingt wirklich unelegant, dagegen ist doch meine Job-Queue für die Idle-Aufgaben des Kernels noch ein Kinderspiel. Generierst Du für einen IRQ wirklich X andere IRQs (IPIs)? Hast Du schon mal die IRQ-Latenz in Deinem OS gemessen?

Übrigens sind bei ARM (und den meisten anderen GP-CPUs auch) die Register nicht noch zusätzlich im Speicher gemappt, ich kenne eigentlich auch nur die 8Bit-AVRs wo es das gibt.

Geht sogar viel einfacher (die APIC Register sollte man für sowas nicht nutzen), man kann dafür einfach nen Debugging-Register nutzen.
Also ich bin schon der Meinung das der local-APIC für genau solche Dinge da ist (oder zumindest sein sollte). Das mit dem Debug-Register ist zwar ne nette Idee (kannte ich noch gar nicht) aber spätestens wenn Du mal vernünftig Debuggen willst wird Dir das Register fehlen. Ich bin mir aber sicher das man auch bei den MSRs (kommt den lokalen Controll-Registern auf meiner CPU auch am nächsten) einige Register finden wird die sich für sowas benutzen lassen und das ganz ohne das man da auf irgendeine wichtige Funktionalität verzichten muss. Spontan würden mir da z.B. die MTRRs einfallen, von den flexiblen MTRRs gibt es einige und ich denke das es recht unwahrscheinlich ist dass das BIOS wirklich alle davon benötigt (und selbst wenn doch hat es zumindest keine funktionalen Einschränkungen wenn man eines davon missbraucht sondern man beeinflusst nur die Perfomance ;)).

Du queuest in deinem Event Threads und es wird immer nur einer aufgeweckt. Fällt dir da nix auf?
Nö, da fällt mir nix auf! Warum auch? Wenn z.B. ein Event gleich mehrere Male kurz hintereinander getriggert wird dann werden auch kurz hintereinander mehrere Threads geweckt (falls den genügend warten).

Das klingt für mich verdammt nach einer Semaphore.
Also für mich nicht aber vielleicht hab ich auch nur ein Problem mit meinen Ohren. ;) SCNR

Was ich mit dem Counter meine, ist dass du fire()->release() als dekrementieren implementierst und bei einer Semaphore ist es inkrementieren. Wieso gehst du da genau den umgekehrten Weg? Hat das nen speziellen Grund?
Das der Counter im positiven die wartenden Threads zählt und im negativen die gequeueten Events ist purer Zufall (würde andersherum schließlich ganz genauso funktionieren), ich werde mich schwer hüten daraus irgendeine höhere Bedeutung abzuleiten.

Wenn du eine Semaphore nicht so siehst das sie zum Schützen, sondern zum Synchronisieren da ist, macht das schon Sinn nen Event mit einer Semaphore zu verlgeichen.
Selbst zum Synchronisieren ist diese Art von Event IMHO nicht wirklich geeignet, höchstens zum Serialisieren.


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 31. October 2011, 20:23
Zitat von: erik
Hm, ich bin mir gar nicht mal so sicher ob das wirklich eine schlechte/unperformante Lösung ist, das Kopieren (vom Stack zum Thread-Descriptor) ließt doch aus Speicher der mit extrem hoher Wahrscheinlichkeit noch im L1-Cache ist und das Schreiben wird beide male (beim PUSHAD wie beim MOVSD) per Write-Allocation bequem und unsichtbar im Hintergrund erledigt. Da es bei x86 ja wohl nicht ganz so einfach ist die Adresse des richtigen Thread-Descriptors schnell zu bekommen und man auch immer erst mal ein oder zwei Register aus dem Weg schaffen muss könnte die Variante mit PUSHAD im INT-Assembler-Stub und dann Kopieren im Hochsprachenteil tatsächlich sogar die Schnellste aller möglichen Lösungen auf x86 sein.
Das Kopieren passiert so kurz danach, das die Daten auf jeden Fall noch im L1-Cache sind (selbst bei älteren CPUs). Den Thread-Discriptor zu bekommen ist nicht mal das Problem, wenn ich es genau überlege könnte ich sogar den Pointer für den Thread-State sehr einfach bekommen, indem ich den auch in meiner CPU-lokalen-Struct packe (sind es halt 4bytes mehr, aber das macht da gar nix). Auf den Stack müsste ich es aber trotzdem pushen, weil ich die gesicherten Werte später in genau den gleichen Registern wieder brauche (beim Verlassen des Syscalls zurück in den UserMode).

Zitat von: erik
Also das klingt wirklich unelegant, dagegen ist doch meine Job-Queue für die Idle-Aufgaben des Kernels noch ein Kinderspiel. Generierst Du für einen IRQ wirklich X andere IRQs (IPIs)? Hast Du schon mal die IRQ-Latenz in Deinem OS gemessen?
Ich habe das Gefühl du sprichst von was anderem als ich ;)

Was ich beschrieben habe, ist der Weg wie die anderen CPUs (bzw. eine) erfahren das es einen neuen Thread gibt, der eine potentiell höhere Priorität hat als die gerade laufenden Threads. Das hat nix mit dem Idle-Thread zu tun.

Zitat von: erik
Also ich bin schon der Meinung das der local-APIC für genau solche Dinge da ist (oder zumindest sein sollte). Das mit dem Debug-Register ist zwar ne nette Idee (kannte ich noch gar nicht) aber spätestens wenn Du mal vernünftig Debuggen willst wird Dir das Register fehlen. Ich bin mir aber sicher das man auch bei den MSRs (kommt den lokalen Controll-Registern auf meiner CPU auch am nächsten) einige Register finden wird die sich für sowas benutzen lassen und das ganz ohne das man da auf irgendeine wichtige Funktionalität verzichten muss. Spontan würden mir da z.B. die MTRRs einfallen, von den flexiblen MTRRs gibt es einige und ich denke das es recht unwahrscheinlich ist dass das BIOS wirklich alle davon benötigt (und selbst wenn doch hat es zumindest keine funktionalen Einschränkungen wenn man eines davon missbraucht sondern man beeinflusst nur die Perfomance ;)).
Das ist ne ganz schlechte Idee, du weißt weder beim APIC noch bei den MSRs (da sogar noch weniger), welche Register auf welcher CPU frei sind und ob sie sich nicht mit der nächsten Generation schon geändert haben.
Ich weiß das du mehrere Debugging-Register hast und nicht unbedingt alle brauchst, von daher sollte das nicht das Problem sein, ansonsten bietet sich halt ne CPU-lokale-Struktur an (die ich über Segmente und, in meinem Fall, das gs-Register ansprechen kann).

Zitat von: erik
Nö, da fällt mir nix auf! Warum auch? Wenn z.B. ein Event gleich mehrere Male kurz hintereinander getriggert wird dann werden auch kurz hintereinander mehrere Threads geweckt (falls den genügend warten).
Ähm, das ist genau das was passiert wenn du mehrere Male kurz hintereinander bei einer Semaphore release() aufrufst ;)

Also wenn du bei deinem Event event_wait() machst, wird der Thread der das macht, gequeuet, passiert bei einer Semaphore die man mit acquire() aufruft auch. Bei deinem Event wird dann von irgendwem event_trigger() aufgerufen und dieser Thread wird wieder "frei gelassen", passiert bei der Semaphore auch, wenn du release() aufrufst.

Ich sehe da immer noch keinen Unterschied.

Zitat von: erik
Selbst zum Synchronisieren ist diese Art von Event IMHO nicht wirklich geeignet, höchstens zum Serialisieren.
Gut möglich das ich da im Moment etwas mit dem Vokabular durcheinander komme, aber ist das im Endeffekt nicht das gleiche?
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 01. November 2011, 18:11
Hallo,


indem ich den auch in meiner CPU-lokalen-Struct packe
Und wie kommst Du an den? Bzw. ganz generell, wie ermittelst Du die aktuelle CPU-Nummer (am besten linear von 0 bis Anzahl-1 durchnummeriert)?

Ich habe das Gefühl du sprichst von was anderem als ich ;)
Nein, mir ist schon klar das es bei Dir um das weitergeben von Aufgaben (egal ob Threads oder IRQs) geht. Ich finde das extrem kompliziert, generell halte ich nicht sehr viel von IPIs, ich sehe darin keinen echten Nutzen bzw. eine extrem komplizierte Lösung für Probleme die auf einer anständig designten Plattform einfacher zu lösen wären oder erst gar nicht vorhanden wären. Mir ist klar das es bei x86 einige Dinge gibt die man ohne IPIs nur noch umständlicher gelöst bekommt, z.B. das globale TLB-Löschen auf allen CPUs, aber trotzdem empfinde ich dieses Konzept als ziemlich unelegant.

Mich würde trotz allem mal interessieren was Dein OS für eine IRQ-Latenz bietet.

Was ich beschrieben habe, ist der Weg wie die anderen CPUs (bzw. eine) erfahren das es einen neuen Thread gibt, der eine potentiell höhere Priorität hat als die gerade laufenden Threads. Das hat nix mit dem Idle-Thread zu tun.
Warum leitest Du IRQs nicht immer zu der CPU welche aktuell die SW mit der niedrigsten Priorität ausführt? Ich dachte eigentlich das es genau dafür dieses SW-Priority-Register im local-APIC gibt.

Das ist ne ganz schlechte Idee, du weißt weder beim APIC noch bei den MSRs (da sogar noch weniger), welche Register auf welcher CPU frei sind und ob sie sich nicht mit der nächsten Generation schon geändert haben.
Das ist natürlich richtig, aber viele der MSRs sind seit dem ersten Pentium unverändert (wozu wimre auch die MTRRs gehören) und die bestehende SW verlässt sich darauf dass das auch so bleibt, von daher denke ich dass das kein allzu großes Problem ist.

Ich weiß das du mehrere Debugging-Register hast und nicht unbedingt alle brauchst, von daher sollte das nicht das Problem sein
Schon klar das man von den Debugging-Registern nicht unbedingt alle braucht aber wenn Du sie doch mal nutzen möchtest und eines fehlt ist das einfach ziemlich doof, nebst dessen das sich die existierenden Debugger auch auf die spezifizierte Anzahl verlassen und vielleicht kräftig auf die Nase fliegen wenn das unerwartet nicht klappt.

ansonsten bietet sich halt ne CPU-lokale-Struktur an (die ich über Segmente und, in meinem Fall, das gs-Register ansprechen kann).
Stimmt, das ist auch noch ne gute Variante. Leider sind dafür nur 2 Segment-Register verfügbar und eines davon wirst Du schon für TLS nutzen wollen. Trotz allem wäre es cool wenn Intel/AMD in die MSRs einfach 2 bis 4 frei nutzbare Register integrieren würde die dem OS zuverlässig nach eigenem Ermessen zur Verfügung stehen, das tät kaum nennenswert Transistoren kosten aber auf jeden Fall einige Dinge erheblich vereinfachen.


Das Deine Vorstellung von einem Semaphor meiner Vorstellung von einem Event-Mechansimus (bei einigen Embedded/RT-OSen firmiert das auch unter dem Namen "Signal-Slot") ungefähr entspricht mag ja sein aber ich finde nicht das es deswegen das Selbe ist oder Beides gegeneinander ersetzbar/austauschbar wäre.


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 01. November 2011, 19:00
Zitat von: erik
Und wie kommst Du an den? Bzw. ganz generell, wie ermittelst Du die aktuelle CPU-Nummer (am besten linear von 0 bis Anzahl-1 durchnummeriert)?
Ich nutze das fs-Register für TLS und das gs-Register für die CPU-lokale-Struct, wo auch die CPU-Nummer drin steht (aber die würdest du auch durch den APIC bekommen und der ist immer an der selben Stelle gemappt).

Zitat von: erik
Ich finde das extrem kompliziert, generell halte ich nicht sehr viel von IPIs, ich sehe darin keinen echten Nutzen bzw. eine extrem komplizierte Lösung für Probleme die auf einer anständig designten Plattform einfacher zu lösen wären oder erst gar nicht vorhanden wären. Mir ist klar das es bei x86 einige Dinge gibt die man ohne IPIs nur noch umständlicher gelöst bekommt, z.B. das globale TLB-Löschen auf allen CPUs, aber trotzdem empfinde ich dieses Konzept als ziemlich unelegant.
Wie löst du das denn auf deiner Architektur (andere CPUs benachrichtigen)?

Zitat von: erik
Mich würde trotz allem mal interessieren was Dein OS für eine IRQ-Latenz bietet.
Mich auch ;)

Was verstehst du denn darunter? Die Zeit vom feuern des IRQ´s bis zum Senden des EOI? Wie würde man sowas überhaupt messen?

Zitat von: erik
Warum leitest Du IRQs nicht immer zu der CPU welche aktuell die SW mit der niedrigsten Priorität ausführt? Ich dachte eigentlich das es genau dafür dieses SW-Priority-Register im local-APIC gibt.
Wie soll ich es sagen ... genau das mache ich ;)

Wenn ich so darüber nachdenke, kann die Situation das die anderen CPUs eine IPI-Nachricht bekommen müssen, im Falle von IRQs eh nie auftreten. Allerdings muss ich bei der If-Abfrage noch eine kleine Änderung vornehmen.
Wenn die Priorität des aktuellen Threads kleiner des neuen Threads ist, wird nur die aktuelle CPU in den Scheduler geschickt, ist die Priorität größer als die des neuen Threads, wird eine IPI-Nachricht versendet und wenn sie gleich ist wird gar nichts gemacht (und das ist dann für die IRQs wichtig).
Weil im Falle der IRQs kann es nur zu der Situation kommen, dass entweder der Thread der aktuellen CPU kleiner oder gleich ist, womit keine IPI-Nachricht nötig ist.

Da kommen wir aber zu einem Thema, was besser einem neuem Thread diskutiert wird, nämlich Scheduling-Strategien und sowas. Da zählt dann auch rein, wann man eine CPU im Ruhezustand lässt und wann man sie besser aufweckt. Denn es ist ja nicht immer nötig eine CPU aufzuwecken, bei einem IRQ z.B. würde ich lieber eine CPU nutzen die eh schon läuft als eine aufzuwecken.

Zitat von: erik
Das Deine Vorstellung von einem Semaphor meiner Vorstellung von einem Event-Mechansimus (bei einigen Embedded/RT-OSen firmiert das auch unter dem Namen "Signal-Slot") ungefähr entspricht mag ja sein aber ich finde nicht das es deswegen das Selbe ist oder Beides gegeneinander ersetzbar/austauschbar wäre.
Was ist denn mit deinem Event möglich, was nicht mit einer Semaphore geht?
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 01. November 2011, 20:16
Hallo,


wo auch die CPU-Nummer drin steht (aber die würdest du auch durch den APIC bekommen und der ist immer an der selben Stelle gemappt).
Kann bei Dir auch der User-Space an die CPU-Nummer ran? Ist die CPU-Nummer in den local-APICs wirklich linear durchgezählt (das wäre sehr nützlich um diese als Index für ein Array nutzen zu können)?

Wie löst du das denn auf deiner Architektur (andere CPUs benachrichtigen)?
Gar nicht. Diese Funktionalität ist auf meiner Plattform nicht vorgesehen, ich wüsste auch nicht wozu.

Was verstehst du denn darunter? Die Zeit vom feuern des IRQ´s bis zum Senden des EOI? Wie würde man sowas überhaupt messen?
Für die IRQ-Latenz gibt es zwei Definitionen: einmal vom HW-IRQ-Signal bis der erste Befehl vom tatsächlichen IRQ-Handler ausgeführt wird (diese Definition hat einen großen Jitter weil IRQs ja auch ab und an mal gesperrt sein können, aber gerade hierbei ist der Worst-Case für harte Echt-Zeit-Systeme relevant) und zum anderen vom Ausliefern durch den IRQ-Controller an eine CPU bis der erste Befehl vom tatsächlichen IRQ-Handler ausgeführt wird (hierbei spielt die Effizienz der IRQ-Verarbeitung des OS die entscheidende Rolle, da dürften eigentlich monolithische OSe klar im Vorteil sein). Für Dein OS würde mich mal der zweite Wert interessierten aber der ist dem Best-Case des ersten Wertes sehr ähnlich so das er relativ einfach zu ermitteln sein müsste. Bei beiden Definitionen ist aber gerade das mit dem ersten Befehl vom tatsächlichen IRQ-Handler (womit der des eigentlichen Geräte-Treibers gemeint ist) ein schwieriges Problem weil das in modernen OSen oft etwas verzerrt ist. Unter Windows soll die eigentliche Bearbeitung des IRQ erst im sogenannten DPC-Handler passieren und da kann zwischen dem vorgelagerten kurzen IRQ-Handler (an dessen Ende auch das EOI an den IRQ-Controller gemeldet wird) und diesem DPC-Handler doch schon mal einiges an Zeit vergehen. Linux und wimre auch MAC OS X haben ähnliche Mechanismen die vor allem wegen den shared IRQs nötig sind. Ein Micro-Kernel-OS das auf einer Plattform ohne IRQ-Sharing läuft hat also gute Chancen besser zu sein als die etablierten OSe auf x86.


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 01. November 2011, 20:56
Zitat von: erik
Kann bei Dir auch der User-Space an die CPU-Nummer ran?
Jein, nicht über diese CPU-lokale-Struct, aber durch die CPUID Instruktion.

Zitat von: erik
Ist die CPU-Nummer in den local-APICs wirklich linear durchgezählt (das wäre sehr nützlich um diese als Index für ein Array nutzen zu können)?
Auch wieder jein ;)

Praktisch schon, theoretisch solltest du nicht darauf bauen. "Problem" ist, falls ein Kern defekt oder was weiß ich nicht ist, hat er trotzdem seine Nummer. Du hast ja z.B. bei ACPI eine Liste mit lokalen APICs und dort ist ja jedes Mal nen Flag ob die CPU in Ordnung ist. Praktisch fällt mir kein Szenario ein wo das mal vorkommen sollte, aber theoretisch ist es halt so.

Aber wieso willst du über ein Index in ein Array gehen, wenn du genau dafür doch sehr gut Segmente nutzen kannst?

Zitat von: erik
Gar nicht. Diese Funktionalität ist auf meiner Plattform nicht vorgesehen, ich wüsste auch nicht wozu.
Also auf x86 brauchst du es für die TLBs und ich brauche es (in meiner naiven Sicht) für den Scheduler, weil ich der Meinung bin, das immer die Threads mit der höchsten Priorität laufen sollten. Also brauche ich die Möglichkeit die anderen CPUs zu benachrichtigen das es neue Threads mit höherer Priorität gibt. Etwas was ich bisher nur an einer Stelle brauche, sind Rendez-Vous Punkte und da machen sich IPI´s auch ganz gut, z.B. beim Panic um alle CPUs zu stoppen.

Zitat von: erik
Für Dein OS würde mich mal der zweite Wert interessierten aber der ist dem Best-Case des ersten Wertes sehr ähnlich so das er relativ einfach zu ermitteln sein müsste.
Naja, der Handler im Kernel macht nix anderes als Nachrichten zu verschicken (und da kann man nur hoffen, das nicht zu viele Geräte den gleichen IRQ nutzen), was wiederrum ja pro Nachricht eine Thread Erstellung bedeutet und dann wird sofort auf diesen Thread gewechselt (sofern nicht ein anderer Treiber-Thread schon läuft). Wobei man natürlich noch "kurz" durch den Scheduler muss.

Interessant wäre es, ob es nicht für die Latenz gut wäre, wenn man bei mehreren Nachrichten die anderen CPUs in den Scheduler (per IPI) zwingt. Weil so würde die anderen CPUs erstmal bis zum Ende der Zeitscheibe arbeiten und dann feststellen das es neue Threads mit höherer Priorität gibt (da wären wir dann wieder bei Scheduling-Strategien).

Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 02. November 2011, 08:26
Hallo,


Jein, nicht über diese CPU-lokale-Struct, aber durch die CPUID Instruktion.
Aber CPUID liefert wimre nur eine physische CPU-Position, in der Art von Sockel/Core/Thread, und keine lineare Nummer, oder?

Praktisch schon, theoretisch solltest du nicht darauf bauen. "Problem" ist, falls ein Kern defekt oder was weiß ich nicht ist, hat er trotzdem seine Nummer.
Gut, dann hätte man eben bei einer defekten CPU einen unbenutzten Eintrag im Array, da das doch ziemlich selten ist denke ich das man diese kleine Speicherverschwendung problemlos akzeptieren kann.

Aber wieso willst du über ein Index in ein Array gehen, wenn du genau dafür doch sehr gut Segmente nutzen kannst?
Ich denke da z.B. an den CPU-lokalen Slot-Mechanismus eines SLAB-Allocators (auch im User-Mode) für den es essenziell wichtig ist möglichst performant an die CPU-Nummer zu kommen und auch das diese linear durchgezählt ist damit man eben die zugehörigen Datenstrukturen in einem simplen und schnellen Array ablegen kann. Für jeden Kram gleich wieder ein eigenes Segment zu erstellen hallte ich nicht für sehr geschickt, nebst dessen das auch meine 16 Segmentregister irgendwann aus gehen wenn man zu viele davon für feste Aufgaben reserviert (ich hab da bis jetzt nur TLS vorgesehen ansonsten benötige ich sowas weder im User-Mode noch im Kernel-Mode).

und ich brauche es (in meiner naiven Sicht) für den Scheduler, weil ich der Meinung bin, das immer die Threads mit der höchsten Priorität laufen sollten.
Schon klar aber eigentlich gibt es doch nur 2 Quellen für neue Threads (die dann potentiell eine höhere Priorität haben können): einmal HW-IRQs und dann der CreateThread()-Syscall. Bei der zweiten Möglichkeit wird eben anhand der Priorität entschieden welcher Thread als nächstes auf der aktuellen CPU läuft, falls der neue Thread eine höhere Priorität haben soll als der der ihn erstellt dann startet die CPU eben als erstes den neuen Thread und der Ersteller-Thread wird als RUNNABLE markiert und in die runnable-Liste des Schedulers eingetragen (falls auf einer anderen CPU ein Thread mit einer niedrigeren Priorität läuft dann wird der Scheduler das spätestens am Ende von dessen Zeitscheibe merken). Bei der erstem Möglichkeit kann es sein das für einen HW-Geräte-IRQ ein neuer PopUp-Thread mit hoher Priorität erstellt wird und dann läuft dieser eben auf der aktuellen CPU weiter und der unterbrochene Thread kommt in die runnable-Liste des Schedulers (wenn die IRQ-Priorität mit der Priorität des IRQ-Handler-Threads übereinstimmt und das prioritätsbasierende IRQ-Routing gut funktioniert dann sollte der IRQ ja eh nur von einer CPU angenommen werden die aktuell einen Thread mit einer niedrigeren Priorität ausführt). Es kann aber auch sein das durch einen IRQ, z.B. vom Timer, ein existierender Thread geweckt wird und ob dieser Thread den aktuellen Thread verdrängt hängt eben davon ab ob der geweckte Thread eine höhere Priorität als der unterbrochene Thread hat, in jedem Fall wird der unterlegene Thread in die runnable-Liste des Schedulers eingetragen.

Viele Worte, wenig Sinn: ich sehe keinen Grund warum man dafür IPIs benötigt. Klar gibt es Szenarien wo ein Thread mal nicht dran kommt obwohl auf einer anderen CPU gerade ein Thread mit einer niedrigeren Priorität läuft (es könnte z.B. passieren das während eine CPU für einen IRQ einen neuen Thread erzeugt oder einen bestehenden aufweckt auf einer anderen CPU gerade ein Syscall ausgeführt wird (so das diese CPU den IRQ eben nicht annehmen kann) aber der Syscall-verursachende Thread eine noch niedrigere Priorität hat als der auf der ersten CPU unterbrochene Thread, dann bleibt der unterbrochene Thread eben solange untätig in der runnable-Liste des Schedulers liegen bis auf der anderen CPU die Zeitscheibe für den niedrig priorisierten Thread abgelaufen ist) aber was solls Du entwickelst doch kein hartes Echtzeit-OS oder? Solange Deine Zeitscheiben nicht zu lang sind (bei Threads mit recht niedriger Priorität kannst Du die ja noch zusätzlich verkürzen) dürfte für ein normales Desktop-System keinerlei Problem daraus entstehen.

Also brauche ich die Möglichkeit die anderen CPUs zu benachrichtigen das es neue Threads mit höherer Priorität gibt.
Das ist etwas was IMHO nur dann Sinn ergibt wenn jede CPU eine eigene runnable-Liste hat und der übergeordnete Scheduler die Last mal wieder fair über alle CPUs verteilen muss, solange es nur eine globale runnable-Liste gibt wird sich doch eh recht zügig ein Gleichgewicht einstellen.

Etwas was ich bisher nur an einer Stelle brauche, sind Rendez-Vous Punkte und da machen sich IPI´s auch ganz gut, z.B. beim Panic um alle CPUs zu stoppen.
Hm, darüber hab ich noch gar nicht wirklich nachgedacht, eigentlich bin ich der Meinung keine Rendez-Vous-Punkte zu benötigen aber das mit dem Panic erscheint mir dann doch wichtig. Beim normalen Shutdown wollte ich es eigentlich so machen das alle normalen Prozesse beendet werden und dann auch die ganzen Treiber entladen werden (wobei auch die Dateisysteme entmountet werden) und zum Schluss der init-Prozess einfach das Netzteil abschaltet, meinem OS-Kernel und der innersten Personality will ich gar nicht die Fähigkeit zum kontrollierten Abschalten geben.

Interessant wäre es, ob es nicht für die Latenz gut wäre, wenn man bei mehreren Nachrichten die anderen CPUs in den Scheduler (per IPI) zwingt. Weil so würde die anderen CPUs erstmal bis zum Ende der Zeitscheibe arbeiten und dann feststellen das es neue Threads mit höherer Priorität gibt (da wären wir dann wieder bei Scheduling-Strategien).
Wenn pro IRQ gleich mehrere Threads erstellt/geweckt werden müssen, was bei IRQ-Sharing ja leider gegeben ist, dann ergibt es natürlich Sinn diese mehreren Threads möglichst schnell auf mehrere CPUs zu verteilen aber dazu würde es doch eigentlich reichen wenn man die Restzeitscheibe der anderen CPUs auf 0 setzt so das diese direkt in den Scheduler gehen (so als wäre dort ein Yield aufgerufen worden). Da man auf x86 ja auch immer an die anderen local-APICs ran kommt sollte das doch kein Problem sein. Für echte IPIs sehe ich noch immer keinen Grund, von dem TLB-Flush bei x86 mal abgesehen.


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 02. November 2011, 09:24
Zitat von: erik
Aber CPUID liefert wimre nur eine physische CPU-Position, in der Art von Sockel/Core/Thread, und keine lineare Nummer, oder?
Weiß ich gerade auch nicht, aber braucht man die CPU-Nummer im UserSpace? Als Index für ein Array kannst du es schonmal nicht benutzen, denn die CPU könnte ja, während du den Index benutzt, gewechselt werden. Von daher wird ja z.B. beim Slab-Allocator im UserSpace die Thread-ID für sowas genommen.

Zitat von: erik
Ich denke da z.B. an den CPU-lokalen Slot-Mechanismus eines SLAB-Allocators (auch im User-Mode) für den es essenziell wichtig ist möglichst performant an die CPU-Nummer zu kommen und auch das diese linear durchgezählt ist damit man eben die zugehörigen Datenstrukturen in einem simplen und schnellen Array ablegen kann.
Richtig, da war ja was ;) Genau dafür nutze ich das ja auch.

Zitat von: erik
Schon klar aber eigentlich gibt es doch nur 2 Quellen für neue Threads (die dann potentiell eine höhere Priorität haben können): einmal HW-IRQs und dann der CreateThread()-Syscall.
Es gibt noch einen 3. Grund, nämlich wenn ein Thread "aufgeweckt" wird, vom Schlafen oder vom Warten (z.B. Semaphore).

Zitat von: erik
falls auf einer anderen CPU ein Thread mit einer niedrigeren Priorität läuft dann wird der Scheduler das spätestens am Ende von dessen Zeitscheibe merken
Ich weiß nicht wie lange deine Zeitscheiben so sind, aber wir reden da schon von mehreren ms und wenn du dann Audio und Video machst, könnte das schon zu dem Problem führen was ich ja unter Windows habe (knacksen im Sound und Bildstocken, auf nem QuadCore).

Zitat von: erik
Das ist etwas was IMHO nur dann Sinn ergibt wenn jede CPU eine eigene runnable-Liste hat und der übergeordnete Scheduler die Last mal wieder fair über alle CPUs verteilen muss
Der Vorteil einer globalen runnable-Liste liegt für mich genau da, dass immer die Threads mit der höchsten Priorität laufen. Wenn ich das da auch nicht umsetze, kann ich ja gleich mehrere Listen (pro CPU eine) nehmen (was ich ja später auch noch vor habe).

Zitat von: erik
Hm, darüber hab ich noch gar nicht wirklich nachgedacht, eigentlich bin ich der Meinung keine Rendez-Vous-Punkte zu benötigen aber das mit dem Panic erscheint mir dann doch wichtig. Beim normalen Shutdown wollte ich es eigentlich so machen das alle normalen Prozesse beendet werden und dann auch die ganzen Treiber entladen werden (wobei auch die Dateisysteme entmountet werden) und zum Schluss der init-Prozess einfach das Netzteil abschaltet, meinem OS-Kernel und der innersten Personality will ich gar nicht die Fähigkeit zum kontrollierten Abschalten geben.
Ich habe für sowas ne STOP-IPI. Damit kann man entweder alle, außer der sendenden, CPUs anhalten und kann halt z.B. nen kontrollierten Panic machen oder diese eine CPU den PC runterfahren lassen.

Zitat von: erik
Wenn pro IRQ gleich mehrere Threads erstellt/geweckt werden müssen, was bei IRQ-Sharing ja leider gegeben ist, dann ergibt es natürlich Sinn diese mehreren Threads möglichst schnell auf mehrere CPUs zu verteilen aber dazu würde es doch eigentlich reichen wenn man die Restzeitscheibe der anderen CPUs auf 0 setzt so das diese direkt in den Scheduler gehen
Das halte ich ersten für Overhead (da ich durch eine Liste durchgehen muss) und zweitens, desto länger die Liste ist, desto schneller ist eine IPI-Nachricht. Dann kommt noch hinzu, dass es bei mir eher schwierig wird auf den APIC der anderen CPUs zu zugreifen (geht das überhaupt, dazu müsste man mindestens die physikalische Adresse der einzelnen APICs unterschiedlich setzen, aber ich glaube dass das trotzdem nicht geht).
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: kevin am 02. November 2011, 09:52
Praktisch schon, theoretisch solltest du nicht darauf bauen. "Problem" ist, falls ein Kern defekt oder was weiß ich nicht ist, hat er trotzdem seine Nummer. Du hast ja z.B. bei ACPI eine Liste mit lokalen APICs und dort ist ja jedes Mal nen Flag ob die CPU in Ordnung ist. Praktisch fällt mir kein Szenario ein wo das mal vorkommen sollte, aber theoretisch ist es halt so.
Wenn ich mich nicht täusche, hat Homix letztens erzählt, dass er auf seinem System nur jeden zweiten Index genutzt hat, weil bei seiner CPU das Hyperthreading deaktiviert ist.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 02. November 2011, 11:11
Zitat von: taljeth
Wenn ich mich nicht täusche, hat Homix letztens erzählt, dass er auf seinem System nur jeden zweiten Index genutzt hat, weil bei seiner CPU das Hyperthreading deaktiviert ist.
Wäre bei mir auch kein Problem, da ich die ID´s selbst vergebe. Mein SMP-Startup Code serialisiert die Ausführung des Init Codes der CPUs und da wird einfach der Wert von nem Counter genommen. Von daher sollte man sich darüber keinen Kopf machen.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: Svenska am 02. November 2011, 16:55
Zitat von: erik
Aber CPUID liefert wimre nur eine physische CPU-Position, in der Art von Sockel/Core/Thread, und keine lineare Nummer, oder?
Weiß ich gerade auch nicht, aber braucht man die CPU-Nummer im UserSpace?
Wenn man einen Prozess/Thread auf eine CPU festpinnen können möchte, muss man die CPU eindeutig identifizieren können. Von daher wäre das schon sinnvoll.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 02. November 2011, 17:06
Zitat von: svenska
Wenn man einen Prozess/Thread auf eine CPU festpinnen können möchte, muss man die CPU eindeutig identifizieren können. Von daher wäre das schon sinnvoll.
Das könnte man ja per Syscall lösen, aber ich stehe diesem Festpinnen eher skeptisch gegenüber. Wozu soll das gut sein, außer um Multithreading-Probleme zu beseitigen und dass ist aus meiner Sicht der falsche Weg dafür.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: Svenska am 02. November 2011, 17:09
Wenn du einen Prozess auf eine CPU festpinnst und alle anderen Prozesse von dieser CPU entfernst, dann hast du plötzlich die Möglichkeit, weiche Echtzeitanforderungen zu erfüllen. Außerdem kann man durch das Festpinnen den Scheduler entlasten.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: Jidder am 02. November 2011, 17:11
Und der Thread wandert dann nicht zwischen den CPUs (und der Inhalt der Caches mit ihm).
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 02. November 2011, 17:25
Zitat von: svenska
Wenn du einen Prozess auf eine CPU festpinnst und alle anderen Prozesse von dieser CPU entfernst, dann hast du plötzlich die Möglichkeit, weiche Echtzeitanforderungen zu erfüllen.
Hmm, ich sage jetzt mal (ohne konkrete Erfahrung zu haben), das muss durch den Scheduler und einer geeigneteten Wahl der Thread-Priorität zu regeln sein.

Zitat von: svenska
Außerdem kann man durch das Festpinnen den Scheduler entlasten.
Meinen (einfachen/primitiven) Scheduler würde es eher noch komplexer machen und ihn nicht wirklich entlasten (was meinst du damit genau?).

Zitat von: jidder
Und der Thread wandert dann nicht zwischen den CPUs (und der Inhalt der Caches mit ihm).
Auch das sollte Aufgabe des Schedulers sein. Genau dieses Argument kann ich nur für Single-Threaded Anwendungen gelten lassen, weil wenn wir die Caches optimal ausnutzen wollen, dann sollten alle Threads auf der selben CPU laufen, was wieder Multithreading ad absurdum führen würde. Wimre sind die Caches beim Bulldozer pro "Dual"-Core gleich (was eins der Probleme unter Windows ist) und damit wäre das nicht so schlimm.

Zumal wir wieder sehr zum Thema Scheduling-Strategien abtriften ;) Und ich bin immer noch nicht wirklich vom Festpinnen überzeugt. Wieviel macht denn der Cache aus und wieviel kann ein guter Scheduler retten?
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: Svenska am 02. November 2011, 17:59
Ich hab mal als Faustregel gelesen, dass man einen RAM-Zugriff so behandeln sollte, wie man früher einen HDD-Zugriff für Swap behandelt hatte, weil es so extrem viel langsamer ist als über den Cache zu gehen. Das heißt, dass Cache extrem wichtig ist.

Und du kannst einen optimal genialen ultimativ komplexen Scheduling-Algorithmus haben und ich behaupte, dass er für gewisse Verteilungen äußerst schlecht performt. In dem Fall ist es sinnvoll, ein bestimmtes Verhalten erzwingen zu können. Das wird dich zwar auch nicht überzeugen, aber dafür ist es dein Betriebssystem.

Ich mag es nicht, wenn ein OS alles besser weiß als ich und mir die Möglichkeiten nimmt, darauf einwirken zu können. Selbst dann, wenn ich es im Normalfall nie tun werde.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 02. November 2011, 18:29
Zitat von: svenska
Ich hab mal als Faustregel gelesen, dass man einen RAM-Zugriff so behandeln sollte, wie man früher einen HDD-Zugriff für Swap behandelt hatte, weil es so extrem viel langsamer ist als über den Cache zu gehen. Das heißt, dass Cache extrem wichtig ist.
Daraus lese ich heraus, des es also Sinn macht den Speicherverbrauch von Software eher zu verringern als zu vergrößern ;) Weil so ist das gewünschte ja eher im Cache.

Zitat von: svenska
Und du kannst einen optimal genialen ultimativ komplexen Scheduling-Algorithmus haben und ich behaupte, dass er für gewisse Verteilungen äußerst schlecht performt. In dem Fall ist es sinnvoll, ein bestimmtes Verhalten erzwingen zu können. Das wird dich zwar auch nicht überzeugen, aber dafür ist es dein Betriebssystem.
Ich bin der Meinung, dass der genial ultimativ komplexe Scheduling-Algo so lange braucht, dass es wieder keinen Sinn macht ihn zu benutzen ;) Und ja, ich mag das mit dem Festpinnen nicht. Macht die Sache mit dem Scheduler eher komplexer. Wieso ist da KISS nicht in Ordnung?

Zitat von: svenska
Ich mag es nicht, wenn ein OS alles besser weiß als ich und mir die Möglichkeiten nimmt, darauf einwirken zu können. Selbst dann, wenn ich es im Normalfall nie tun werde.
Ist das nicht Over-engineering?
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 02. November 2011, 18:46
Hallo,


Weiß ich gerade auch nicht, aber braucht man die CPU-Nummer im UserSpace?
Ich bin mir da auch nicht ganz sicher ob es abseits eines SLAB-Allocators noch andere Dinge gibt die auch im User-Mode von der CPU-Nummer abhängen aber das alleine war für mich Grund genug dem User-Space eine lineare und eine physische CPU-Nummer per Befehl anzubieten (ich nutze dazu den normalen Befehl der die lokalen Controll-Register, mein MSR-Äquivalent, liest und erlaube dem User-Mode genau diese 2 Werte).

Als Index für ein Array kannst du es schonmal nicht benutzen, denn die CPU könnte ja, während du den Index benutzt, gewechselt werden. Von daher wird ja z.B. beim Slab-Allocator im UserSpace die Thread-ID für sowas genommen.
Dem kann ich ja glücklicherweise vorbeugen indem ich es erlaube das auch der User-Mode (ohne Kernel-Mitwirkung) für kurze und begrenzte Zeit die INTs komplett abschalten kann, was auch das Ablaufen der Zeitscheibe hinausschiebt. Somit kann ich selbst im User-Mode die aktuelle CPU-Nummer für den SLAB-Allocator nutzen, die Thread-ID ist da IMHO völlig ungeeignet da diese eher zufällig ist (auf jeden Fall nicht linear numeriert), bei jedem neuen Thread auch eine neue Thread-ID vergeben wird und es vor allem sehr viel mehr Threads in einem Prozess als CPUs geben kann.

Es gibt noch einen 3. Grund, nämlich wenn ein Thread "aufgeweckt" wird, vom Schlafen oder vom Warten (z.B. Semaphore).
Das Aufwecken ist so ähnlich wie das neu Erstellen und lässt sich auf jeden Fall in die 2 Kategorien einsortieren. Entweder wird durch einen IRQ geweckt (z.B. wenn der Timer ein Event triggert auf das bereits ein Thread wartet) oder es wird durch eine Software-Aktion ausgelößt (so wie auch Dein Beispiel mit dem Freigeben eines Semaphors und gerade hier kann ja im release() geprüft werden ob ein Thread mit höherer Priorität als der aktuelle wartet und wenn ja dann wird dieser sofort auf die aktuelle CPU geholt und der der gerade den Semaphor freigegeben hat kommt in die runnable-Liste).

Ich weiß nicht wie lange deine Zeitscheiben so sind, aber wir reden da schon von mehreren ms und wenn du dann Audio und Video machst, könnte das schon zu dem Problem führen was ich ja unter Windows habe (knacksen im Sound und Bildstocken, auf nem QuadCore).
Ja, meine Zeitscheiben sollen auch mehrere ms lang sein (ich dachte so an 10 ms bis etwa 20 ms) und, ja, das ist keine harte Echtzeit mehr. Normalerweise sollten die SW-Buffer bei Audio und Video deutlich mehr als 100 ms überbrücken können aber ich weiß auch das man im professionellen Umfeld auch Applikationen hat wo möglichst kurze Latenzen und damit auch kleine Buffer benutzt werden, wer sowas benötigt benötigt eben auch ein passendes OS oder das OS bietet die Möglichkeit spezielle Scheduler-Features anzuschalten (wie z.B. das bei neuen Threads immer eine Optimierung über alle CPUs gefahren wird) was dann zwar mehr CPU-Leistung kostet aber eben auch kürzere Latenzen für die wichtigen Dinge ermöglicht (die weniger wichtigen Dinge werden dafür noch stärker verdrängt).

Der Vorteil einer globalen runnable-Liste liegt für mich genau da, dass immer die Threads mit der höchsten Priorität laufen.
Das sehe ich ganz genauso, nur ist eben eine Frage wie schnell dieses automatische Gleichgewicht sein soll. Hier ist es eben so das wenn man kürzere Latenzen haben will (vor allem auch im Worst-Case der ja sonst bis zu einer kompletten Zeitscheibe dauern kann) das man dafür zusätzliche CPU-Leistung opfern muss.

Ich habe für sowas ne STOP-IPI. Damit kann man entweder alle, außer der sendenden, CPUs anhalten und kann halt z.B. nen kontrollierten Panic machen oder diese eine CPU den PC runterfahren lassen.
Also über die Kernel-Panic hab ich mir noch nicht allzu genau nen Kopf gemacht (steht jetzt aber auf meiner ToDo-Liste)  aber das Runterfahren des PC (inklusive Ausschalten) soll bei mir eigentlich die User-Mode-Personality machen (also der init-Prozess).

Das halte ich ersten für Overhead (da ich durch eine Liste durchgehen muss) und zweitens, desto länger die Liste ist, desto schneller ist eine IPI-Nachricht.
Nö, ich würd einfach die nächsten X CPUs in den Scheduler zwingen, wobei X die Anzahl der neuen Threads ist welche eventuell noch einen kleinen Angstfaktor dazu bekommt. Das ist O(n), genauso wie das Verschicken von X IPIs.

dass es bei mir eher schwierig wird auf den APIC der anderen CPUs zu zugreifen (geht das überhaupt, dazu müsste man mindestens die physikalische Adresse der einzelnen APICs unterschiedlich setzen, aber ich glaube dass das trotzdem nicht geht).
Ich kenne mich bei x86 ja nicht mehr so gut aus aber wimre hat jeder APIC (auch die lokalen) eine individuelle physische Adresse, es gibt nur die zusätzliche Möglichkeit das von einer CPU aus der eigene local-APIC über eine zusätzliche physiche Alias-Adresse erreichbar ist. Diese zusätzliche Alias-Adresse ist auch nicht von außerhalb der CPU nutzbar, so das die nicht z.B. von einem PCI-Gerät benutzt werden kann.


Wenn ich mich nicht täusche, hat Homix letztens erzählt, dass er auf seinem System nur jeden zweiten Index genutzt hat, weil bei seiner CPU das Hyperthreading deaktiviert ist.
Das ist zwar doof aber auch kein Beinbruch wenn dadurch nur jeder zweite Eintrag in einem Array, das üblicherweise pro Element nur ein paar Controll-Variablen und Pointer enthält, ungenutzt bleibt. Es wäre aber auch sehr schön wenn sich in solchen Situationen das BIOS besser verhalten würde.


Wenn du einen Prozess auf eine CPU festpinnst und alle anderen Prozesse von dieser CPU entfernst, dann hast du plötzlich die Möglichkeit, weiche Echtzeitanforderungen zu erfüllen.
Das festpinnen mag sicher gut funktionieren aber ich bin mir nicht sicher ob es auch Möglichkeiten gibt alle anderen Thread von dieser CPU zu verbannen. Dabei entstehen IMHO einige Probleme. Was ist wenn dieser eine Thread gerade blockiert? Bleibt die CPU dann unbenutzt? Geht die dann in einen Stromsparzustand (was eventuell längere Aufwachzeiten und damit Latenzen verursacht)? Wie viele CPUs in einem System kann man den ruhigen Gewissens frei räumen? Ich persönlich würde maximal die Hälfte zulassen und das auch nur wenn mindestens 4 CPUs verfügbar sind. Wenn dieser spezielle Thread z.B. einen synchronen IPC-Vorgang initiiert wäre es doch praktisch wenn der Thread der diesen IPC bearbeitet dafür so lange die CPU haben könnte, schon damit die zu bearbeitenden Daten auch im richtigen Cache bleiben. Ich bin zwar auch der Meinung das festpinnen eine interessante Option darstellt aber ich sehe noch einige Probleme die gelöst werden müssen damit das auch in der Praxis eine wirklich zufriedenstellende Angelegenheit wird.

Ich gebe Dir auf jeden Fall recht das zu häufiges wechseln der CPU eher kontraproduktiv ist, wegen dem Umladen der Caches obwohl das auch erst auf Mehr-Sockel-Systemen zum Problem wird (innerhalb einer CPU sind die Cache-to-Cache-Transfers ziemlich schnell und bei Intel auch gar nicht nötig weil der L3-Cache ja "inklusive" ist).

Außerdem kann man durch das Festpinnen den Scheduler entlasten.
Also das sehe ich noch nicht.

Ich mag es nicht, wenn ein OS alles besser weiß als ich und mir die Möglichkeiten nimmt, darauf einwirken zu können. Selbst dann, wenn ich es im Normalfall nie tun werde.
Auch hier kann ich Dir uneingeschränkt zustimmen (und betrachte sowas auch nicht als Over-Engineering) aber ob das Festpinnen wirklich eine gute Lösung darstellt wage ich zu bezweifeln. Aus meiner Sicht könnte es eine gute Lösung sein wenn man den Scheduler zwingt nach jeder Änderung der Anzahl aktiver Threads oder der Priorität eines aktiven Threads die optimale Verteilung über alle CPUs neu zu berechnen (also den großen Scheduler aufzurufen), das kostet zwar einiges an zusätzlicher CPU-Leistung kann aber bestimmt etwas helfen das die wichtigen Jobs auch immer eine CPU abbekommen. Wobei der große Scheduler natürlich auch aufpassen muss das er Thread nur möglichst selten von einer CPU auf eine andere Wechselt. Auf meiner Plattform habe ich vor die maximale Aufrufhäufigkeit des großen Schedulers mit HW-Hilfe zu begrenzen so das wenn z.B. ein Thread kurz erstellt wird aber recht schnell wieder verschwindet (z.B. ein IRQ-Handler-PopUp-Thread) das der große Scheduler eventuell gar nichts tut weil sich die große Gesamtsituation nicht geändert hat. Diese Maximal-Frequenz könnte man signifikant erhöhen wenn z.B. eine Applikation explizit ein besseres Echtzeit-Verhalten anfordert (das wäre aus meiner Sicht eine sinnvolle Stellschraube die sicher auch einiges bewirken kann und wenn die Applikation auch genau weiß welche Latenzen sie akzeptieren kann könnte man das OS auf genau diesen Wert trimmen so das eben nicht zu viel zusätzliche CPU-Leistung für sowas geopfert wird).


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: Svenska am 02. November 2011, 18:50
Festpinnen ist meistens keine gute Lösung, aber es bietet eine Möglichkeit, um auf einen eventuell schlecht oder fehlerhaft funktionierenden Automatismus einzuwirken. Den Automatismus zu fixen ist die bessere Lösung, aber nicht immer möglich oder praktikabel.

Und FlashBurn: Ich glaube, du hast das KISS-Prinzip nicht so richtig verstanden. ;-)
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 02. November 2011, 19:14
Zitat von: erik
Ja, meine Zeitscheiben sollen auch mehrere ms lang sein (ich dachte so an 10 ms bis etwa 20 ms)
Was genau meinst du eigentlich mit Zeitscheibe, weil man da ja schon zw. periorischen Timer-IRQs und One-Shot-IRQs unterscheiden muss. Denn ich habe im Mom glaube eine max. Zeitscheibe für einen Thread von 62ms. Wenn du bei einem periodischen IRQ eine Zeitscheibe von 1ms hast, könnte man damit noch leben.

Zitat von: erik
Nö, ich würd einfach die nächsten X CPUs in den Scheduler zwingen, wobei X die Anzahl der neuen Threads ist welche eventuell noch einen kleinen Angstfaktor dazu bekommt. Das ist O(n), genauso wie das Verschicken von X IPIs.
Der Punkt ist, das versenden einer IPI Nachricht um alle CPUs in den Scheduler zu zwingen ist nicht O(n), sondern O(1). Denn du versendest genau eine Nachricht. Genau ein paar CPUs in den Scheduler zu zwingen ist hingegen wieder O(n). Obwohl man auch das durch geschickten Einsatz einiger APIC Features bestimmt auf O(1) drücken kann.

Zitat von: erik
Ich kenne mich bei x86 ja nicht mehr so gut aus aber wimre hat jeder APIC (auch die lokalen) eine individuelle physische Adresse
Nope, sind standardmäßig alle an die selbe Adresse gemappt. Wäre aber mal interessant zu wissen ob man durch verschiedene Adressen an die APICs der anderen CPUs ran kommt, aber da sehe ich ohnehin keinen Sinn drin.

Zitat von: erik
und betrachte sowas auch nicht als Over-Engineering
Um ein wenig zu sticheln, also ist das jetzt doch Abhängig von der Sichtweise ;)

Zitat von: svenska
Den Automatismus zu fixen ist die bessere Lösung, aber nicht immer möglich oder praktikabel.
Zitat von: Wikipedia
Das KISS-Prinzip entstammt ursprünglich dem Bereich der Informatik. Als Designprinzip beschreibt es im Gegensatz zu einer Problemlösung in der Form eines Workarounds die möglichst einfache, minimalistische und leicht verständliche Lösung eines Problems, welche meistens als optimal angesehen wird.
Festpinnen ist also ein Workaround und ist laut dieser Definition nicht KISS.

Zumal man an dieser Definition auch schön sieht, dass KISS sehr wohl im Auge des Betrachters liegt:
Zitat
leicht verständliche Lösung eines Problems, welche meistens als optimal angesehen wird
Also leicht verständlich ist mehr als nur subjektiv und "meistens" hilft da auch nicht wirklich ;)
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 02. November 2011, 19:50
Hallo,


Was genau meinst du eigentlich mit Zeitscheibe
Das ist bei mir die Zeit die ein Thread jedes mal neu zugewiesen bekommt wenn er vom Scheduler auf eine CPU geladen wird, so lange kann ein Thread maximal laufen bevor wieder der Scheduler dran kommt. Meine CPUs haben alle einen eigenen Zeitscheiben-Counter (ein simpler Down-Counter mit fester Frequenz) der bei Ablauf der Zeit eine Yield-Exception für die eigene CPU auslöst, das entspricht quasi einem One-Shot-Timer. Fürs erste möchte ich den Wert, der vom Scheduler in diesen Timer geladen wird, als fest deklarierte Konstante realisieren aber später kann ich mir vorstellen das dieser Wert eventuell von der Priorität des jeweiligen Threads abhängt. Der richtige Timer, den es nur einmal gibt (und dem x86-HPET sehr ähnlich ist), wird bei mir nicht für die Zeitscheiben benutzt (schon allein deswegen weil er nicht eine bestimmte sondern immer nur eine zufällige CPU unterbrechen kann, der Timer-IRQ also nur sehr selten genau die CPU erreicht auf der der Thread läuft dessen Zeitscheibe gerade abgelaufen ist).

Der Punkt ist, das versenden einer IPI Nachricht um alle CPUs in den Scheduler zu zwingen ist nicht O(n), sondern O(1).
Okay, das wusste ich nicht. Das ist dann natürlich ein Argument für die IPIs.

Nope, sind standardmäßig alle an die selbe Adresse gemappt.
Um ehrlich zu sein glaube ich das nicht so wirklich, das erscheint mir irgendwie arg daneben, selbst für x86-Verhältnisse.

Um ein wenig zu sticheln, also ist das jetzt doch Abhängig von der Sichtweise ;)
Das sehe ich anders. Es ist eben absolut unmöglich einen Automatismus zu schaffen der unter allen Bedingungen ein optimales oder wenigstens befriedigendes Ergebnis liefert. Gerade deswegen ist es IMHO eine sinnvolle Idee wenn das OS den Programmen ein paar Stellschrauben anbietet damit diese den Automatismus zumindest etwas auf die Sprünge helfen können.

Also Workarounds mag ich persönlich auch grundsätzlich nicht, trotzdem kann (nicht muss) die Umsetzung eines Workarounds durchaus dem KISS-Prinzip entsprechen. Es kann auch sein das eine echte Problemlösung zu komplex oder zu aufwendig ist so das ein Workaround durchaus zu einer insgesamt akzeptablen Lösung wird.


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 02. November 2011, 20:02
Zitat von: erik
Um ehrlich zu sein glaube ich das nicht so wirklich, das erscheint mir irgendwie arg daneben, selbst für x86-Verhältnisse.
Wieso eigentlichen? Das Ding heißt nicht umsonst local-APIC. Der ist doch für die anderen CPUs total uninteressant und er ist direkt auf der CPU und mir ist jetzt nix bekannt, wie man direkt auf eine andere CPU von einer CPU aus, zugreifen kann. Zumal du ja auch nicht auf die Idee kommst, MSRs von anderen CPUs manipulieren zu wollen.

Zitat von: erik
Gerade deswegen ist es IMHO eine sinnvolle Idee wenn das OS den Programmen ein paar Stellschrauben anbietet damit diese den Automatismus zumindest etwas auf die Sprünge helfen können.
Dafür sind mMn Prioritäten da. Zumal hieße das nicht auch, das man, im Falle des Bulldozers, die Programme extra dafür programmieren soll, weil der Scheduler unter Windows diese benachteiligt? Was hieße das für den Code?

Zitat von: erik
trotzdem kann (nicht muss) die Umsetzung eines Workarounds durchaus dem KISS-Prinzip entsprechen
Pass auf, mein Problem ist einfach, wer legt fest ob solch ein Workaround noch dem KISS-Prinzip entspricht? Es scheint ja keine knallharten Kriterien zu geben nach denen jeder zum gleichen Ergebnis kommt. Von daher ist es halt subjektiv und nur darum geht es mir hier.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 02. November 2011, 20:29
Hallo,


Wieso eigentlichen?
Weil es selbst für x86-Verhältnisse schon ziemlich strange ist das mehrere unterschiedliche Ressourcen hinter der selben physischen Speicheradresse stecken.

Zumal du ja auch nicht auf die Idee kommst, MSRs von anderen CPUs manipulieren zu wollen.
Doch, auf genau diese Idee bin ich gekommen. Bei meiner Plattform kann jede CPU auf die lokalen Controll-Register jeder anderen CPU zugreifen. Ohne dem könnte der Boot-Loader gar nicht die anderen CPUs finden und aktivieren (deswegen bin ich mir auch relativ sicher dass das auch bei x86 möglich ist). Auch andere nützliche Dinge lassen sich damit anstellen wenn jede CPU jeder anderen mal genau auf die Finger schauen kann.

Dafür sind mMn Prioritäten da.
Du hast doch selber festgestellt das dafür die Prioritäten alleine nicht ausreichen, die werden frühestens nach Ablauf der aktuellen Zeitscheibe wirksam, aber Du willst es doch schneller haben also musst Du auch zusätzliche Mechanismen einbauen (ob das festpinnen oder IPIs oder irgendwas anderes ist ist ja erstmal egal).

Zumal hieße das nicht auch, das man, im Falle des Bulldozers, die Programme extra dafür programmieren soll, weil der Scheduler unter Windows diese benachteiligt?
Ich weiß es nicht genau aber das die Bulldozers Probleme mit dem geteilten Cache haben wird wohl eher am gegenseitigen Verdrängen durch Aliasing liegen, dieses Problem hatte Intel mit den ersten P4's mit Hyperthreading auch, da kann der Scheduler vom OS gar nix gegen tun (hier könnte höchstens besseres ASLR helfen), sowas muss in der HW gefixt werden.

Von daher ist es halt subjektiv und nur darum geht es mir hier.
Alles ist letztendlich (zumindest teilweise) subjektiv, und persönliche Meinungen erst recht.


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 02. November 2011, 20:48
Zitat von: erik
Ohne dem könnte der Boot-Loader gar nicht die anderen CPUs finden und aktivieren (deswegen bin ich mir auch relativ sicher dass das auch bei x86 möglich ist).
Also das BIOS weckt die CPUs genauso auf, wie das OS es später machen soll. Es wird eine (oder zwei) IPI-Init Nachrichten an alle CPUs gesendet und dann eine IPI-Startup Nachricht an alle und mit Hilfe eines Counters "zählt" man dann die CPUs. So stand es in einem Dokument von Intel.

Zitat von: erik
ob das festpinnen oder IPIs oder irgendwas anderes ist ist ja erstmal egal
Stimmt, nur ist das Aufrufen des Schedulers nicht einfacher als den Algo des Schedulers irgendwie zu modifizieren?

Zitat von: erik
Ich weiß es nicht genau aber das die Bulldozers Probleme mit dem geteilten Cache haben wird wohl eher am gegenseitigen Verdrängen durch Aliasing liegen, dieses Problem hatte Intel mit den ersten P4's mit Hyperthreading auch, da kann der Scheduler vom OS gar nix gegen tun (hier könnte höchstens besseres ASLR helfen), sowas muss in der HW gefixt werden.
Was genau meinst du mit "Aliasing"?

Soweit ich es vestanden habe, ist der Scheduler von Windows 7 sehr wohl ein Problem. Aus dem Grund warum Hyperthreading mit ihm besser funktioniert als vorher ;) Denn der Scheduler arbeitet so, dass erst alle geraden Kerne (bei 0 startend) ausgelastet werden bis die ungeraden ausgelastet werden. Das soll die Effizienz bei Hyperthreading verbessern, aber da der L2-Cache bei einem "Dual"-Core beim Bulldozer immer gesharet wird, führt das zu Problem. Denn so werden immer genau die Kerne zu erst ausgelastet, die keinen gemeinsamen Cache haben.

Was mir daran ein wenig suspekt vorkommt, es scheint ja auf Mehrkern-CPUs ohne Hyperthreading auch zu funktionieren, dass alle Kerne ausgelastet werden. Also denke ich mal das AMD die ungeraden Kerne alle als Hyperthreading-Kerne (per CPUID) meldet und deswegen der Scheduler solche Entscheidungen trifft. Ist eigentlich ein Problem von AMD und nicht vom Windows-Scheduler. Ich finde es auch irgendwie behämmert, wenn man für jede CPU (und damit meine ich nicht mit oder ohne Hyperhtreading und Mehrkern CPUs) einen eigenen Scheduler braucht. Denn das Problem wäre ja durch eine einfache Änderung im CPUID-Befehl zu lösen.

Dafür wäre es interessant ob CPUID Mikrocode ist (wovon ich eigentlich ausgehe). Denn dann wäre es ja theoretisch möglich ein Update nachzuschieben.

Zitat von: erik
Alles ist letztendlich (zumindest teilweise) subjektiv, und persönliche Meinungen erst recht.
Das stimmt wohl. Nur kann man sich z.B. nicht darüber streiten das 1+1 = 2 ist, oder ;)

Das Problem mit KISS ist ja auch, eine Lösung ist nur solange KISS, bis eine noch einfachere gefunden wurde. Ist doch mit fast allen Sachen so, es ist so lange unmöglich bis jemand bewiesen hat das es möglich ist ;)
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 02. November 2011, 22:01
Hallo,


Es wird eine (oder zwei) IPI-Init Nachrichten an alle CPUs gesendet und dann eine IPI-Startup Nachricht an alle und mit Hilfe eines Counters "zählt" man dann die CPUs. So stand es in einem Dokument von Intel.
Dazu müssen doch aber die anderen local-APIC bereits irgendwie konfiguriert sein damit diese die IPI's auch korrekt empfangen können, oder? Ich sehe da irgendwie eine Art von Henne-Ei-Problem. Wie bekommt man den eigentlich mit wie viele CPUs überhaupt vorhanden sind? Werden einfach alle möglichen durchprobiert und nur die die auch tatsächlich antworten gezählt?

Auf meiner Plattform soll das per Hardware gehen, ich habe einen CPU-Indentify-Ring-Bus vorgesehen, der beim Chipsatz anfängt und auch dort wieder aufhört. Der Chipsatz wartet bis alles aus dem Reset raus ist und triggert dann das Durchzählen der CPUs per Ring-Bus an, dabei werden die CPUs linear und auch physisch durchgezählt und der Chipsatz erhält dann am Ende das Ergebnis und weiß wie viele CPUs es insgesamt gibt und wie viele Sockel es insgesamt gibt. Erst danach weckt der Chipsatz automatisch die CPU 0 (indem er einfach in deren lokalen Controll-Registern eine Start-Adresse reinschreibt an der diese CPU dann anfängt den ersten Befehl zu holen). Damit das Zugreifen auf fremde Controll-Register überhaupt funktioniert ist es ja auch erforderlich das jede CPU weiß welche sie ist, sonst könnten diese Zugriffe ja nicht korrekt geroutet werden.

nur ist das Aufrufen des Schedulers nicht einfacher als den Algo des Schedulers irgendwie zu modifizieren?
Also ein simpler Funktionsaufruf ist IMHO schon einfacher als das Verschicken und vor allem Empfangen von IPI's oder gar als das Entwickeln eines komplexen mehrstufigen Schedulers.

Was genau meinst du mit "Aliasing"?
Das Problem damals bei den P4's mit Hyperthreading war das diese beiden logischen CPUs sich ja den einen physischen L1-Cache (Code und Daten) geteilt haben und das wenn z.B. verschiedene Programme auf die selbe libc zugreifen ist ja die Wahrscheinlichkeit hoch das diese libc in beiden Programmen an der selben virtuellen Adresse liegt und damit sich diese Zugriffe ständig gegenseitig aus dem L1-Cache verdrängen. Ein anderes Problemszenario war wenn mehrere Threads vom selben Prozess die selbe Aufgabe erledigen (z.B. in einem Grafik-Renderer) das dann u.a. die Stackzugriffe ein sehr ähnliches Zugriffsmuster haben und damit in den selben Cache-Lines gelandet sind so das hier auch wieder permanente gegenseitige Verdrängung vorherrschte. Nach Ende des P4-Experiments gab es ja ne Weile lang keine Hyperthreading-CPUs mehr von Intel und bei den Core-CPUs hat Intel, soweit ich das verstanden habe, das L1-Cache-Indexierungssystem für die beiden logischen CPUs unterschiedlich gemacht.

.... Denn so werden immer genau die Kerne zu erst ausgelastet, die keinen gemeinsamen Cache haben.
Okay, das ist ein anderes Problem, aber auch irgendwie doof. Vielleicht hätte AMD an Microsoft ein paar Vorabexemplare schicken sollen damit die mal schauen ob es irgendwo Probleme gibt, eventuell wäre den MS-Leuten sowas noch rechtzeitig aufgefallen.

Was ich mich frage ist wie das mit dem Energiesparen zusammen arbeitet. Wenn wir eine 4-Kern-CPU mit Hyperthreading und 3 mäßig gierige Threads haben würde der MS-Scheduler ja 3 Kerne unter Dampf setzen obwohl eigentlich 2 reichen könnten (die dafür eventuell sogar mit einem minimal höherem Takt laufen). Wenn diese Threads dann auch noch wenig effizienten oder nur wenig anspruchsvollen Code ausführen (so dass das Hyperthreading gar keine Bremse wäre) ist der MS-Scheduler ja eine echte Energieverschwendung.

eine Lösung ist nur solange KISS, bis eine noch einfachere gefunden wurde
Ja, deswegen kann ich ja auch ruhigen Gewissens behaupten das meine Segmente mehr KISS sind als Flat-Memory. ;)


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 02. November 2011, 22:17
Zitat von: erik
Dazu müssen doch aber die anderen local-APIC bereits irgendwie konfiguriert sein damit diese die IPI's auch korrekt empfangen können, oder? Ich sehe da irgendwie eine Art von Henne-Ei-Problem. Wie bekommt man den eigentlich mit wie viele CPUs überhaupt vorhanden sind? Werden einfach alle möglichen durchprobiert und nur die die auch tatsächlich antworten gezählt?
Du sollst, laut Intel, die IPI-Init Nachricht an alle und die IPI-Startup Nachricht nur an die CPUs schicken, von denen dir gesagt wurde das sie Ok sind (z.B. ACPI). Das BIOS schickt einfach die IPI-Startup Nachricht an alle CPUs (und ich vermute mal, wartet dann ne gewisse Zeitspanne) und die CPUs "registrieren" sich mit ihrer APIC-ID und das BIOS weiß dann wie viele CPUs es gibt.

Zitat von: erik
Also ein simpler Funktionsaufruf ist IMHO schon einfacher als das Verschicken und vor allem Empfangen von IPI's
Dem kann ich nur zustimmen, das Senden ist ja noch einfach, aber das Empfangen bzw. vernünftige Abarbeiten von IPI´s ist nicht mehr so einfach.

Zitat von: erik
Das Problem damals bei den P4's mit Hyperthreading war das diese beiden logischen CPUs sich ja den einen physischen L1-Cache (Code und Daten) geteilt haben und das wenn z.B. verschiedene Programme auf die selbe libc zugreifen ist ja die Wahrscheinlichkeit hoch das diese libc in beiden Programmen an der selben virtuellen Adresse liegt und damit sich diese Zugriffe ständig gegenseitig aus dem L1-Cache verdrängen. Ein anderes Problemszenario war wenn mehrere Threads vom selben Prozess die selbe Aufgabe erledigen (z.B. in einem Grafik-Renderer) das dann u.a. die Stackzugriffe ein sehr ähnliches Zugriffsmuster haben und damit in den selben Cache-Lines gelandet sind so das hier auch wieder permanente gegenseitige Verdrängung vorherrschte. Nach Ende des P4-Experiments gab es ja ne Weile lang keine Hyperthreading-CPUs mehr von Intel und bei den Core-CPUs hat Intel, soweit ich das verstanden habe, das L1-Cache-Indexierungssystem für die beiden logischen CPUs unterschiedlich gemacht.
Interessant zu wissen!

Zitat von: erik
Okay, das ist ein anderes Problem, aber auch irgendwie doof. Vielleicht hätte AMD an Microsoft ein paar Vorabexemplare schicken sollen damit die mal schauen ob es irgendwo Probleme gibt, eventuell wäre den MS-Leuten sowas noch rechtzeitig aufgefallen.
Ich habe keine Ahnung seit wann AMD an Bulldozer entwickelt, aber ich denke mal der Windows 7 Scheduler war wohl zeitiger fertig und MS hat halt für Hyperthreading optimiert (und das war schon einige Zeit bevor Windows 7 erschienen ist, bekannt).

Zitat von: erik
Was ich mich frage ist wie das mit dem Energiesparen zusammen arbeitet. Wenn wir eine 4-Kern-CPU mit Hyperthreading und 3 mäßig gierige Threads haben würde der MS-Scheduler ja 3 Kerne unter Dampf setzen obwohl eigentlich 2 reichen könnten (die dafür eventuell sogar mit einem minimal höherem Takt laufen). Wenn diese Threads dann auch noch wenig effizienten oder nur wenig anspruchsvollen Code ausführen (so dass das Hyperthreading gar keine Bremse wäre) ist der MS-Scheduler ja eine echte Energieverschwendung.
Ich weiß es natürlich nicht genau, aber es muss schon ne bestimmte Auslastung vorhanden sein bevor die logischen Core´s genutzt werden (das sehe ich immer sehr gut auf meinem Laptop). Ich denke also mal MS wird das schon halbwegs bedacht haben.

Zitat von: erik
Ja, deswegen kann ich ja auch ruhigen Gewissens behaupten das meine Segmente mehr KISS sind als Flat-Memory.
Ich glaube da werden dir wohl fast alle wiedersprechen ;)
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: Svenska am 03. November 2011, 00:01
Ich weiß ja, dass du ein großer Fan von optimalen, überall perfekten Vollautomatismen und wunderbaren algorithmischen Lösungen bist, da brauchst du mir nicht die KISS-Definition herzitieren. Dennoch halte ich es für sinnvoll, den Automatismus abschaltbar machen zu können (oder ihn in ein bestimmtes Verhalten zwingen zu können).

Ein Autopilot in einem Flugzeug ist immer abschaltbar. Auch große Industrieanlagen kann man in der Regel manuell fahren (meist mit reduzierter Leistung). Und auch ein Not-Aus-Knopf an der Wand ist eine Implementation einer manuell abschaltbaren Automatik.

Du gehst im übrigen immer von "der Lösung" aus. Die gibt es nicht. Es gibt i.A. mehrere Ansätze, die zum Ziel führen, wobei jeder Ansatz Vor- und Nachteile hat. Universallösungen sind in der Regel over-engineered, funktionieren nur mäßig gut oder sind tierisch komplex.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: kevin am 03. November 2011, 09:25
Ja, meine Zeitscheiben sollen auch mehrere ms lang sein (ich dachte so an 10 ms bis etwa 20 ms) und, ja, das ist keine harte Echtzeit mehr.
Hm? Du kannst Zeitscheiben von 10 bis 20 Minuten nehmen und immer noch harte Echtzeit (http://de.wikipedia.org/wiki/Echtzeitsystem#Harte_und_weiche_Echtzeit) haben. Ob das nützlich ist, wäre natürlich noch die andere Frage, aber wenn du einen Reaktionszeit von zwei Tagen garantierst und das immer einhalten kannst, ist es harte Echtzeit.

Zitat
Das festpinnen mag sicher gut funktionieren aber ich bin mir nicht sicher ob es auch Möglichkeiten gibt alle anderen Thread von dieser CPU zu verbannen. Dabei entstehen IMHO einige Probleme. Was ist wenn dieser eine Thread gerade blockiert? Bleibt die CPU dann unbenutzt? Geht die dann in einen Stromsparzustand (was eventuell längere Aufwachzeiten und damit Latenzen verursacht)? Wie viele CPUs in einem System kann man den ruhigen Gewissens frei räumen? Ich persönlich würde maximal die Hälfte zulassen und das auch nur wenn mindestens 4 CPUs verfügbar sind.
Da landen wir bei Mechanism und Policy (http://en.wikipedia.org/wiki/Separation_of_mechanism_and_policy). Der Kernel sollte nur den Mechanismus anbieten, aber nicht die Policy vorschreiben. Du hast vielleicht ganz andere Einschränkungen für deinen Einsatzzweck im Sinn als FlashBurn für seinen. Ob du jetzt den Ablauf durch einen (ersetzbaren) Userspaceteil abhandelst oder nur ein paar Stellschrauben für den Scheduler einplanst, ist deine Designentscheidung, aber ein paar Konstanten fest zu verdrahten, die dir gerade gut reinlaufen, ist mit Sicherheit falsch.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 03. November 2011, 11:53
Zitat von: svenska
Ich weiß ja, dass du ein großer Fan von optimalen, überall perfekten Vollautomatismen und wunderbaren algorithmischen Lösungen bist, da brauchst du mir nicht die KISS-Definition herzitieren. Dennoch halte ich es für sinnvoll, den Automatismus abschaltbar machen zu können (oder ihn in ein bestimmtes Verhalten zwingen zu können).
Mir ist bewusst, dass es nicht den perfekten Algo gibt und ich finde Linux ist der beste Beweis dafür ;) Zumal du mir ja immer mit KISS kommst.

Nur ist das mit dem Festpinnen nicht so einfach umzusetzen, aber wie gesagt, das ist eigentlich Stoff für ein anderes Thema.

Zitat von: svenska
Ein Autopilot in einem Flugzeug ist immer abschaltbar. Auch große Industrieanlagen kann man in der Regel manuell fahren (meist mit reduzierter Leistung). Und auch ein Not-Aus-Knopf an der Wand ist eine Implementation einer manuell abschaltbaren Automatik.
Willst du mir damit sagen, dass man den Scheduler abschalten können sollte? Das ganze muss wieder mit einer Rechteverwaltung gelöst werden, wo ich noch keine richtige gute Idee habe.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: Svenska am 03. November 2011, 12:13
Dennoch halte ich es für sinnvoll, den Automatismus abschaltbar machen zu können (oder ihn in ein bestimmtes Verhalten zwingen zu können).
Der Teil in Klammern ist wichtig. Den Scheduler komplett abzuschalten ist eher nicht sinnvoll. :-D

Festpinnen heißt übrigens nicht nur, dass ein Thread ausschließlich auf einer CPU ausgeführt werden darf, sondern auch, dass ein Thread auf einer bestimmten CPU nicht ausgeführt werden darf. Also eine Festlegung pro Thread, welche CPUs zulässig sind.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 03. November 2011, 13:22
Zitat von: svenska
Festpinnen heißt übrigens nicht nur, dass ein Thread ausschließlich auf einer CPU ausgeführt werden darf, sondern auch, dass ein Thread auf einer bestimmten CPU nicht ausgeführt werden darf. Also eine Festlegung pro Thread, welche CPUs zulässig sind.
Und du willst immernoch behaupten, dass das Festpinnen den Scheduler einfacher macht ;)

Für gewöhnlich (also rede ich von Windows ;)) darf das Programm doch nicht selbst entscheiden, ob es nur auf einer bestimmten CPU läuft (??). Der User muss das also machen, aber ich wüsste nicht dass man das für jeden Thread extra festlegen könnte, sondern immer nur für ganze Prozesse und damit macht es eh nur Sinn für Singel-Threaded Anwendungen. Gut man könnte den Scheduler jetzt zwingen jeden Thread auf einer extra CPU laufen zu lassen. Ich kann mir nur einfach nicht vorstellen, dass das bei Multithreading sinnvoll ist.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: Svenska am 03. November 2011, 16:00
Dann lässt du es halt sein. Ich klinke mich an der Stelle aus. :-)
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: kevin am 03. November 2011, 22:59
Von Windows habe ich keine Ahnung aber sched_setaffinity(2) unter Linux braucht wohl keine speziellen Berechtigungen, wenn man sich selbst pinnen will. Und man kann natürlich auch einzelne Threads pinnen, wenn man das will.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 04. November 2011, 11:59
Zitat von: taljeth
Von Windows habe ich keine Ahnung aber sched_setaffinity(2) unter Linux braucht wohl keine speziellen Berechtigungen, wenn man sich selbst pinnen will. Und man kann natürlich auch einzelne Threads pinnen, wenn man das will.
Naja, bei sich selbst sollte das auch kein Problem darstellen, aber kann man auch Threads anderer Prozesse pinnen? Und ist es unter Linux so wie svenska meinte, dass man auch sagen kann auf welchen CPUs der Thread nicht laufen darf?

Was ich unter Windows meinte, ist die Sache die man im Taskmanager festlegen kann, wie das im Programmcode funktioniert weiß ich auch nicht.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: kevin am 04. November 2011, 12:52
Ich hab dir doch extra den Funktionsnamen gepostet, damit du die Manpage (http://linux.die.net/man/2/sched_setaffinity) selber nachlesen kannst. Aber bitteschön, ich kann die entscheidende Passage auch für dich kopieren: "The caller needs an effective user ID equal to the real user ID or effective user ID of the process identified by pid, or it must possess the CAP_SYS_NICE capability."

Und wenn man festlegen kann, auf welchen CPUs der Thread laufen darf, dann legt man damit logischerweise auch unvermeidlich fest, auf welchen CPUs er nicht laufen darf.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 04. November 2011, 13:04
Zitat von: taljeth
Ich hab dir doch extra den Funktionsnamen gepostet, damit du die Manpage selber nachlesen kannst.
Es ist Freitag ;)

Zitat von: taljeth
Und wenn man festlegen kann, auf welchen CPUs der Thread laufen darf, dann legt man damit logischerweise auch unvermeidlich fest, auf welchen CPUs er nicht laufen darf.
Ich dachte man kann nur festlegen auf welcher CPU und nicht CPUs der Thread laufen darf. Am Bsp. des Bulldozers, würde es ja Sinn machen immer 2 Kerne anzugeben, wo ein Thread drauf laufen darf.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: kevin am 04. November 2011, 13:56
Mensch, klick doch endlich auf den Link und schau dir die Manpage an. ;)

sched_setaffinity() nimmt einen cpu_set_t*, nicht eine einzelne CPU-Nummer.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 04. November 2011, 14:17
Mensch, klick doch endlich auf den Link und schau dir die Manpage an. ;)

sched_setaffinity() nimmt einen cpu_set_t*, nicht eine einzelne CPU-Nummer.
Hab ich doch schon längst gemacht, deswegen habe ich ja auch in einer Vergangenheitsform geschrieben ;)
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 04. November 2011, 20:35
Hallo,


Das BIOS schickt einfach die IPI-Startup Nachricht an alle CPUs (und ich vermute mal, wartet dann ne gewisse Zeitspanne) und die CPUs "registrieren" sich mit ihrer APIC-ID und das BIOS weiß dann wie viele CPUs es gibt.
Ich kann mich irren aber ich vermute das wird nicht reichen, bei modernen CPUs wo der Speicher selber auch an der CPU hängt muss das BIOS schon etwas konkreter über die physische Verschaltung und auch den Funktionsumfang der beteiligten CPUs informiert sein damit es z.B. auch das Routing von Speicherzugriffen innerhalb des CPU-Netzwerkes korrekt konfigurieren kann (das ist auch deswegen erforderlich weil ja nicht jeder Sockel mit jedem anderen Sockel verbunden ist so das mache Anfragen eben mehrere Hops benötigen).

Zitat von: erik
Ja, deswegen kann ich ja auch ruhigen Gewissens behaupten das meine Segmente mehr KISS sind als Flat-Memory.
Ich glaube da werden dir wohl fast alle wiedersprechen ;)
Nur zu, wenn ich mal soweit bin das ich meinen Standpunkt auch ordentlich beweisen kann dürfte der Widerspruch verstummen. ;)


Und auch ein Not-Aus-Knopf an der Wand ist eine Implementation einer manuell abschaltbaren Automatik.
Hm, wo wohnst Du denn das bei Dir an den Wänden ein Not-Aus-Knopf ist? Bitte erzähle jetzt nicht das die Wände bei Dir nicht nur mit einem Not-Aus-Knopf sondern auch mit flauschig weicher Polsterung ausgestattet sind. ;) SCNR

Universallösungen sind in der Regel over-engineered, funktionieren nur mäßig gut oder sind tierisch komplex.
Oder alles zusammen.


aber wenn du einen Reaktionszeit von zwei Tagen garantierst und das immer einhalten kannst, ist es harte Echtzeit.
Ja, ich weiß, soviel zur Theorie. Ich dachte aber eigentlich immer das Du eher der praktisch denkende Mensch wärst. ;)

aber ein paar Konstanten fest zu verdrahten, die dir gerade gut reinlaufen, ist mit Sicherheit falsch.
Ich nehme mal an das Du damit meinst das ich nur maximal die Hälfte der CPUs für die exklusive Nutzung eines einzelnen Threads reservieren würde. Natürlich ist das eine willkürliche Festlegung die mir so einfach in den Kram passt, trotzdem steckt da aber auch ein praktischer Gedanke dahinter, ich will ja mein System nicht tot machen (in dem Punkte sollte noch nicht einmal root das Recht haben sein System an die Wand zu hauen). Der nächste Aspekt ist der das sich die anderen Programme ja auch implizit auf eine Policy verlassen und das darf ein General-Purpose-OS eben auch nicht ganz vergessen. Aber das ist alles natürlich nur meine persönliche Meinung die auch nicht für jeden Anwendungsfall zutreffend sein muss.


Ob ich in mein OS jemals einen Mechanismus zum festpinnen implementiere würde ich aus heutiger Sicht eher bezweifeln, das kann in Zukunft natürlich auch anders werden. Über den Sinn oder Unsinn dieses Mechanismus will ich auch gar nicht streiten (das ist IMHO völlig aussichtslos) aber über die praktische Realisierung würde ich schon ganz gerne mal diskutieren.


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: kevin am 04. November 2011, 23:44
Ja, ich weiß, soviel zur Theorie. Ich dachte aber eigentlich immer das Du eher der praktisch denkende Mensch wärst. ;)
Aber auch jemand, der auf saubere Terminologie Wert legt.

Wenn du meine praktische Meinung zum Thema wissen willst: In meinem Hobby-OS brauche ich keine harte Echtzeit. ;)

Zitat
Natürlich ist das eine willkürliche Festlegung die mir so einfach in den Kram passt, trotzdem steckt da aber auch ein praktischer Gedanke dahinter, ich will ja mein System nicht tot machen (in dem Punkte sollte noch nicht einmal root das Recht haben sein System an die Wand zu hauen). Der nächste Aspekt ist der das sich die anderen Programme ja auch implizit auf eine Policy verlassen und das darf ein General-Purpose-OS eben auch nicht ganz vergessen. Aber das ist alles natürlich nur meine persönliche Meinung die auch nicht für jeden Anwendungsfall zutreffend sein muss.
Es spricht ja nichts dagegen, gute Defaults zu haben, und solange es nur Defaults sind, kannst du sie auch mehr oder weniger beliebig so wählen, wie du es gerade für richtig hältst. Aber ein paar willkürliche Zahlen fest zu verdrahten ist einfach falsch.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 05. November 2011, 11:58
Hallo,


In meinem Hobby-OS brauche ich keine harte Echtzeit.
Das sehe ich für mein Projekt ganz genau so, trotzdem hätte ich z.B. gerne das normale Audio/Video-Programme ohne knacksen und ruckeln arbeiten können, das war es doch schließlich was FlashBurn an Windows gestört hat. Das Problem ist wenn man Latenzen zusichern möchte die kürzer als eine Zeitscheibe (plus ein wenig Reserve) sind dann muss man den Scheduler schon mit ein paar Tricks extra ausstatten, ob dass das Festpinnen von Threads ist oder gar das Freiräumen von einzelnen CPUs für einen einzigen Thread oder auch was ganz anderes ist dann wieder ein Implementierungsdetail und hängt auch davon ab wie das Ergebnis konkret aussehen soll. Das Freiräumen einer CPU zur exklusiven Nutzung durch einen einzigen Thread ist IMHO durchaus eine gute Option weil somit gewährleistet ist das dieser Thread immer sofort seine CPU haben kann (nebst dessen das ihm niemand seinen Cache-Inhalt trasht) aber das ist auch eine extrem teure Lösung weil man ja üblicherweise nur ein paar wenige CPUs zur Verfügung hat so dass das restliche System durch den Wegfall von ganzen CPUs durchaus erheblich beeinträchtigt wird (nebst dessen dass das Freiräumen von CPU wegen der erheblichen Einbußen für das restliche System auch nur mit besonderen Rechten passieren darf). Das bloße Festpinnen einzelner Threads oder Prozesse auf bestimmte CPUs bringt im Punkt Latenz IMHO eher wenig, auf einem normalen System dürfte es immer noch genügend andere Threads geben die auch mal auf einer der zugeordneten CPUs laufen und damit eben trotzdem im Weg sein können. Das Festpinnen bringt eher ein klein wenig für die Performance, z.B. wenn ein Prozess genau so viele Workerthreads startet wie CPUs vorhanden sind und diese unterschiedliche Datensätze verarbeiten kann es von Vorteil sein das jeder dieser Workerthreads immer nur auf einer CPU laufen kann und so das kostspielige hin und her transferieren von Cache-Inhalten entfallen kann, solange das restliche System alle CPUs ungefähr gleichmäßig zusätzlich beansprucht dürften die Workerthreads davon tatsächlich profitieren wenn aber z.B. die HW-IRQs immer nur von einer CPU bearbeitet werden dann wäre der Workerthread der auf dieser CPU läuft erheblich benachteiligt.

Aber ein paar willkürliche Zahlen fest zu verdrahten ist einfach falsch.
Es gibt in einem normalen OS sicher unzählige solcher Defaults und etliche davon kann man auch verändern aber wie viele Prozent der normalen User tun das? Auf wie vielen Linux-Systemen darf ein normaler User mehr als 95%-Plattenplatz belegen (per Default werden doch 5% für root reserviert, was auf einer aktuellen Multi-TB-Platte schon einiges ist) obwohl dieses Default relativ einfach zu ändern ist. Natürlich ist selbst das kein Grund so ein Default einfach im Datei-System-Treiber oder gar im VFS fest zu verdrahten aber es macht IMHO durchaus Sinn für solche Stellschrauben Grenzen festzulegen. Wenn ich definiere das z.B. mindestens 2 CPUs oder mindestens 10% aller CPUs (also das was größer ist) nicht zur exklusiven Zuweisung an einzelne Threads benutzt werden dürfen dann mag das zwar willkürlich erscheinen kann aber einfach auch eine untere Grenze sein die mein OS benötigt um sauber laufen zu können und die noch nicht mal root verletzen darf. Stellschrauben können immer auch negative Effekte haben und ich denke ein guter Programmierer sollte sich bemühen sinnvolle Grenzen festzulegen, wobei sinnvoll eben bedeutet das zum einen die Stellschraube auch was nützt aber trotzdem wenigstens der gröbste Schaden verhindert wird.


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 05. November 2011, 12:32
Zitat von: erik
trotzdem hätte ich z.B. gerne das normale Audio/Video-Programme ohne knacksen und ruckeln arbeiten können, das war es doch schließlich was FlashBurn an Windows gestört hat.
Problem unter Windows sind schlechte Treiber und die Architektur des Kernels. Während bestimmte Teile von Treibern abgehandelt werden (das DPC Zeugs), sind die Ints aus und jeder Treiber sollte nicht viel machen (als wenn das so klappt, da bin ich z.B. dafür das man dazu gezwungen wird), da es ansonsten zu dem geschilderten Problemen kommt.

Ich möchte das ja so lösen, das Treiber in Form von Real-Time-Threads laufen (die bekommen immer die CPU, allerdings auch in Round-Robin Manier, wenn es genug gibt). So ist es auf jeden Fall nicht möglich das ein Thread/Treiber das ganze System unbenutzbar macht (es geht nix mehr, auch kein Maus/Tastatur).

Wegen dem Cache-Trashing dachte ich daran, in jeder Thread-Struktur die ID der CPU zu speichern, auf der der Thread zu letzt gelaufen ist und wenn die Liste der Ready-Threads (allerdings immer nur pro Priorität) durchgegangen wird, wird vorzugsweise ein Thread genommen, der als letztes genau auf der CPU gelaufen ist. Um das ganze nicht alzu lang (aber auch das ist relativ) werden zu lassen, wollte ich festlegen, dass max "Anzahl vorhandener CPUs" Threads angefasst werden und dann einer genommen wird.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: kevin am 05. November 2011, 13:04
Klar, das Festpinnen bringt nur in einigen bestimmten Fällen was. Wenn du nur verhindern willst, dass die Musik Aussetzer hat, dann bringt dir das gar nichts. An dieser Stelle würde ich dir stattdessen einfach empfehlen, den Soundtreiber so zu schreiben, dass er anders als Windows Puffer benutzt, die länger als ein paar Millisekunden sind... Das merkt man auch in VMs recht deutlich: Mit Windows-Gästen gibt es immer wieder Aussetzer, Linux benutzt größere Puffer und tut problemlos, auch wenn es nicht die ganze physische CPU für sich hat. (Hängt natürlich vermutlich vom konkreten Treiber ab, aber bei intel-hda ist es so und das hat ja schon ein gewisse Verbreitung)
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 05. November 2011, 15:03
Zitat von: taljeth
An dieser Stelle würde ich dir stattdessen einfach empfehlen, den Soundtreiber so zu schreiben, dass er anders als Windows Puffer benutzt, die länger als ein paar Millisekunden sind... Das merkt man auch in VMs recht deutlich: Mit Windows-Gästen gibt es immer wieder Aussetzer, Linux benutzt größere Puffer und tut problemlos, auch wenn es nicht die ganze physische CPU für sich hat. (Hängt natürlich vermutlich vom konkreten Treiber ab, aber bei intel-hda ist es so und das hat ja schon ein gewisse Verbreitung)
Da wären wir wieder bei Workaround ;)

Ich finde es kann nicht sein, das man größere Puffer braucht, immerhin reden wir hier von einem QuadCore@3.2GHz. Zumal ich ja weiß das es unter Windows funktioniert (nur halt nicht unter Win7 x64).
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: kevin am 05. November 2011, 15:36
Das hat nichts mit Workaround zu tun. Winzige Puffer sind immer langsam, ob das jetzt Sound, Netzwerk oder Festplatte ist. Wenn überhaupt, dann ist ein Scheduler, der darauf optimiert ist, dass solche CPU-fressenden Treiber trotzdem einigermaßen laufen, ein Workaround.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 05. November 2011, 16:01
Hallo,


Während bestimmte Teile von Treibern abgehandelt werden (das DPC Zeugs), sind die Ints aus und jeder Treiber sollte nicht viel machen (als wenn das so klappt, da bin ich z.B. dafür das man dazu gezwungen wird), da es ansonsten zu dem geschilderten Problemen kommt.
Also Dein Satzbau ist mal wieder, äh, kreativ. Ich versuche einfach mal so gut es geht zu interpretieren. Der Vorteil an den DPCs von Windows ist ja gerade das dabei die INTs an sind, die DPCs sind nämlich die zweite Ebene bei der IRQ-Verarbeitung. Ich bin mir ziemlich sicher dass das zumindest bis einschließlich Windows XP noch genau so war, denn dafür hab ich schon mal Treiber programmiert. Das Erzwingen von möglichst kurzen IRQ-Handlern (erste Ebene) stelle ich mir schwierig bis unmöglich vor, ich glaube auch nicht dass das wirklich was bringen würde (die Treiber-Programmierer sind sicher kreativ genug um da irgendwie drumrum zu kommen, hier wäre IMHO eine strengere Qualitätskontrolle bei der Treiber-Zertifizierung zielführend, aber das will MS ja gar nicht weil man damit die ganzen Billig-HW-Anbieter aus dem Markt drängen würde und die im schlimmsten Falle noch anfangen Linux-Treiber für ihre HW zu schreiben).

Ich möchte das ja so lösen, das Treiber in Form von Real-Time-Threads laufen (die bekommen immer die CPU
Alle Treiber? Auch z.B. für einen USB-Ethernet-Controller? Wie ist das generell für USB-Devices oder Dateisystem-Treiber (diese Treiber haben ja eigentlich keinen eigenen HW-Zugriff und daher auch keine IRQ-Handler)? Werden Real-Time-Threads von Deinem Scheduler immer bevorzugt? Was im Endeffekt auch nur eine höhere Priorität darstellt. Wenn alle Treiber die selbe (Real-Time-)Priorität haben kann es eben doch wieder passieren das (schlechte) Treiber für unwichtige Hardware das System blockiert oder zumindest stärker belastet als der User will. Ich würde eher vorschlagen das Du bei Deinem IPC-System vorsiehst das die Priorität des Caller an den Callee vererbt werden kann (nicht muss). Damit könnte z.B. ein wichtiger Prozess, der vom User eine hohe Priorität bekommen hat, auch seine I/O-Aktivitäten mit hoher Priorität laufen lassen. Damit wäre es auch egal ob das Video-Programm von einer SATA-HDD oder von einer USB-HDD streamt, das System würde diesen Stream auf jeden Fall mit hoher Priorität behandeln so dass das Programm die gegebene Hardware zu gut wie möglich ausschöpfen kann und zwar egal wie viele andere niedriger priorisierte Programme auch noch an die selbe HDD wollen oder anderweitig Last verursachen.

So ist es auf jeden Fall nicht möglich das ein Thread/Treiber das ganze System unbenutzbar macht (es geht nix mehr, auch kein Maus/Tastatur).
Ich würde sagen das wenn Du jedem beliebigen Treiber Real-Time-Priorität gibst es erst recht möglich ist das weder Maus noch Tastatur gehen.

Wegen dem Cache-Trashing dachte ich daran ....
Hm, die Idee gefällt mir irgendwie. Ich habe zwar das ungute Bauchgefühl das dadurch der Scheduler teurer wird aber der erhoffte Vorteil nur zu selten wieder rein kommt (die meisten Programme sind nicht so stark Cache-Abhängig). Ist aber auf jeden Fall mal eine genauere Betrachtung wert, kommt gleich in meine ToDo-Liste, Danke.


Klar, das Festpinnen bringt nur in einigen bestimmten Fällen was. Wenn du nur verhindern willst, dass die Musik Aussetzer hat, dann bringt dir das gar nichts.
Es könnte was bringen wenn man dem Audio-Thread eine CPU exklusiv zuweist aber das bedeutet eben das man alle anderen Threads auf die verbleibenden CPUs festpinnen muss was ganz klar wieder ein Problem mit der Rechteverwaltung bringt und auch nur für eine sehr begrenze Anzahl an wichtigen Threads machbar ist (ich denke hier muss in jedem Fall eine vernünftige Reserve an frei verfügbaren CPUs für das restliche System übrig bleiben).

An dieser Stelle würde ich dir stattdessen einfach empfehlen, den Soundtreiber so zu schreiben, dass er anders als Windows Puffer benutzt, die länger als ein paar Millisekunden sind...
Gerade das will man aber im (semi-)professionellen Umfeld bei der Audio/Video-Produktion nicht. Da möchte man z.B. das der SW-Synthesizer mit möglichst kurzer Latenz auf die Tastatur reagiert (damit der Musiker einigermaßen in Echtzeit spielen kann) und benutzt deswegen möglichst kurze Puffer. Hierfür würde es eben helfen wenn der Scheduler bei jeder Änderung an der Anzahl oder den Prioritäten der lauffähigen Threads eine neue optimale Verteilung dieser Threads auf die CPUs vornimmt, damit könnte man recht zuverlässig verhindern das ein Thread mit hoher Priorität (bis zu) eine ganze Zeitscheibe lang warten muss bis der Thread mit niedriger Priorität endlich vom Scheduler unterbrochen wird damit der Thread mit der hohen Priorität laufen kann. Klar kostet sowas permanent etwas zusätzliche CPU-Leistung (der Scheduler wird teurer) aber im Fall eines "QuadCore@3.2GHz" kann ich natürlich verstehen das FlashBurn (völlig zu recht) sich über das OS ärgert weil er diese CPU-Leistung gerne investieren würde um dafür eine flüssige Wiedergabe zu haben.


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 05. November 2011, 16:32
Zitat von: erik
Der Vorteil an den DPCs von Windows ist ja gerade das dabei die INTs an sind, die DPCs sind nämlich die zweite Ebene bei der IRQ-Verarbeitung. Ich bin mir ziemlich sicher dass das zumindest bis einschließlich Windows XP noch genau so war, denn dafür hab ich schon mal Treiber programmiert.
Ich dachte das die INTs da aus sind und deswegen schlechte Treiber zu Problemen führen.

Zitat von: erik
Alle Treiber?
Hmm, also zumindest die IRQ-Handler (die ja als Popup-Thread gestartet werden).

Zitat von: erik
Wie ist das generell für USB-Devices oder Dateisystem-Treiber (diese Treiber haben ja eigentlich keinen eigenen HW-Zugriff und daher auch keine IRQ-Handler)?
Da würde ich entweder den Aufrufer (Callee bekommt selbe Priorität wie Caller) "entscheiden" lassen oder halt den Treiberprogrammierer nach der Latenz, die benötigt wird.

Zitat von: erik
Ich würde sagen das wenn Du jedem beliebigen Treiber Real-Time-Priorität gibst es erst recht möglich ist das weder Maus noch Tastatur gehen.
Der Punkt ist, dass die INTs an sind und so IRQs noch ankommen und die Real-Time-Priorität auch "nur" eine gewisse Zeitscheibe bekommt (da bin ich mir noch nicht sicher wie groß die sein sollte) und ansonsten ja Round-Robin greift, so dass zumindest jeder Treiber mal dran kommt.
Das ist ja gerade der Vorteil, das ein Treiber nicht das ganze System außer gefecht setzen kann.

Zitat von: erik
Ich habe zwar das ungute Bauchgefühl das dadurch der Scheduler teurer wird aber der erhoffte Vorteil nur zu selten wieder rein kommt (die meisten Programme sind nicht so stark Cache-Abhängig).
Das Problem an der Lösung ist, dass der Scheduler desto teurer wird, desto mehr CPUs man hat (er also in dem Fall O(n) wird, wo meiner bisher O(1) ist). Allerdings wird das bei mir nicht so schlimm werden, da ich eh mehrere Scheduler habe (im Moment 2) und zur Bootzeit, je nach CPU Anzahl, entschieden wird welcher genommen wird. Dadurch kann ich ab einer gewissen Zahl von CPUs ein ganz anderes Konzept für den Scheduler wählen.

Man muss bei diesem Problem auch ganz deutlich zw Server (Mehrsockel-Systeme) und normalem PC unterscheiden. Ich will kein neues Server-OS entwickeln, sondern eher in die Richtung Multimedia-OS (auch wenn ich ein paar Sachen vorhabe, die man eher im Embedded- und Server-Bereich braucht). Da dürfte die Cache-Problematik sowieso nicht so schlimm sein (zumindest bei Intel CPUs).

Wie es z.B. auf ARM aussieht weiß ich gar nicht.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: kevin am 05. November 2011, 16:59
Es könnte was bringen wenn man dem Audio-Thread eine CPU exklusiv zuweist aber das bedeutet eben das man alle anderen Threads auf die verbleibenden CPUs festpinnen muss
Das ist aber dummerweise nicht so richtig praktikabel, solange wir beim durchschnittlichen Rechner bei 2 bis maximal 8 CPUs sind. Und du wolltest die Diskussion ja unter praktischen Gesichtspunkten führen. ;)

Zitat
Gerade das will man aber im (semi-)professionellen Umfeld bei der Audio/Video-Produktion nicht.
Moment, professionelles Umfeld - wenn davon bisher die Rede war, dann hab ich das verpasst.

Zitat
Da möchte man z.B. das der SW-Synthesizer mit möglichst kurzer Latenz auf die Tastatur reagiert (damit der Musiker einigermaßen in Echtzeit spielen kann) und benutzt deswegen möglichst kurze Puffer.
Ich muss zugeben, dass ich die Hardware an dieser Stelle nicht gut genug kenne, aber ich bin mir relativ sicher, dass man einen angefangenen Puffer nicht zwingend zu Ende spielen muss. Ob es im allgemeinen ein definiertes Ergebnis gibt, wenn man den Puffer ändert, während die Hardware schon spielt oder man besser auf einen neuen Puffer wechselt, weiß ich nicht.

Auf jeden Fall gibt es nur dann einen Grund, keinen vollständigen langen Puffer zu spielen, wenn irgendein Ereignis eintritt. Für mich heißt das, dass man anstreben sollte, Kosten auch nur mit diesem Ereignis zu verbinden. Solange ich völlig unprofessionell nur ein bisschen Musik im Hintergrund hören will, sehe ich es nicht ein, warum das unnötig viel CPU-Zeit kosten sollte und dafür womöglich auch noch ein schlechteres Ergebnis bekomme, weil nicht schnell genug nachgeliefert wird.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 06. November 2011, 12:35
Hallo,


Ich dachte das die INTs da aus sind und deswegen schlechte Treiber zu Problemen führen.
Nein, innerhalb der DPCs sind die INTs an aber die DPCs stellen wimre die höchste Priorität dar so das selbst Prozesse mit sehr hoher Priorität von unwichtiger HW (bzw. von deren schlechten Treibern) verdrängt werden können.

Hmm, also zumindest die IRQ-Handler (die ja als Popup-Thread gestartet werden).
Damit würdest Du aber alle IRQs auf eine einzige Prioritätsstuffe stellen und das ist IMHO nicht sehr geschickt. Die Priorität von HW-Geräten und deren IRQs sollte meiner Meinung nach von der Priorität der gerade erfüllten Aufgaben abhängen. Wenn Du z.B. Daten über die RS232-Schnittstelle übertragen willst dann sollte der IRQ-Handler und auch der IRQ für den UART genau eine Prioritätsstufe höher sein als die Anwendung die die Daten überträgt so das wenn daneben noch andere Anwendungen mit wesentlich höherer Priorität laufen diese nicht mal durch den IRQ bzw. IRQ-Handler der RS232-Schnittstelle verdrängt werden können. Kompliziert wird dieses Konzept eigentlich nur wenn ein HW-Gerät für verschiedene Anwendungen (mit unterschiedlicher Priorität) Aufgaben erfüllen muss. IRQ-Sharing steht diesem Konzept auch etwas im Wege.

Da würde ich entweder den Aufrufer (Callee bekommt selbe Priorität wie Caller) "entscheiden" lassen
Natürlich muss der Caller die Möglichkeit zum Entschieden haben.

oder halt den Treiberprogrammierer nach der Latenz, die benötigt wird.
Der Treiberprogrammierer weiß doch noch gar nicht welche Anforderungen später mal zur Laufzeit zu erfüllen sind. Siehe mein Beispiel oben mit der RS232-Schnittstelle oder auch beim Beispiel mit der Soundkarte macht es einen Unterschied ob man einfach nur im Hintergrund mit etwas MP3 berieselt werden will (hier spielt die Latenz gar keine Rolle weil der MP3-Player ja mehrere riesige Puffer im voraus berechnen kann) oder ob jemand am Computer ein SW-Instrument spielen möchte (hier sind möglichst kurze Latenzen sehr wichtig damit sich das Instrument gut spielen lässt aber dafür ist der Musiker sicher auch bereit keine andere CPU-intensive Anwendung parallel laufen zu lassen usw).

Der Punkt ist, dass die INTs an sind und so IRQs noch ankommen
Das heißt also das bei Dir die IRQ-Handler für wichtige HW-Geräte auch durch IRQs von weniger wichtigen HW-Geräten unterbrochen werden können? Also in meinem Konzept ist das konsequent ausgeschlossen, weil die Priorität des IRQs immer mit der Priorität des IRQ-Handler identisch ist (sichert der Kernel zu).

und die Real-Time-Priorität auch "nur" eine gewisse Zeitscheibe bekommt (da bin ich mir noch nicht sicher wie groß die sein sollte) und ansonsten ja Round-Robin greift, so dass zumindest jeder Treiber mal dran kommt.
Schon klar, weil alle IRQ-Handler gleich sind kommen alle auch irgendwie vorwärts. Die Frage ist aber: ist es das was Du wirklich willst? Wäre es nicht besser wenn die die wichtigen HW-Geräte eine höhere Priorität als die weniger wichtigen HW-Geräte haben? Und wäre es nicht praktisch wenn die Priorität der HW-Geräte sich dynamisch nach der Wichtigkeit der von ihnen momentan bearbeiteten Aufgaben richtet?
Was machst Du eigentlich wenn die gewünschte Latenz kleiner als die Zeitscheibe ist? Bei Round-Robin kann die Latenz auch mal das X-fache einer Zeitscheibe werden.

Das Problem an der Lösung ist, dass der Scheduler desto teurer wird, desto mehr CPUs man hat (er also in dem Fall O(n) wird, wo meiner bisher O(1) ist).
Ich würde eher sagen das der Scheduler dann O(n/2) o.ä. ist weil es ja doch recht wahrscheinlich ist das einer der nächsten Threads auf der gerade aktuellen CPU als letztes gelaufen ist, ich denke da an das Geburtstagsproblem (bin aber aber auch nicht so sicher dass das hier wirklich zutrifft).

Man muss bei diesem Problem auch ganz deutlich zw Server (Mehrsockel-Systeme) und normalem PC unterscheiden.
Stimmt, die Cache-Problematik sollte bei einem Single-Sockel-System gar nicht mal so schlimm werden da ja bei Intel und auch bei AMD die letzte Cache-Ebene immer für alle Cores gemeinsam gilt, kritisch werden wohl erst Mehr-Sockel-Systeme. Insofern könnte man den Scheduler oben auch soweit vereinfachen das er auch Threads nimmt die nur auf dem selben Core (bei Hyperthreading) oder auf dem selben Sockel gelaufen sind (je nach dem wo z.B. ein gemeinsamer L2-Cache vorhanden ist).

Die meisten ARM-MultiCore-SoCs haben üblicherweise pro Core einen kleinen L1-Daten-Cache und einen kleinen L1-Code-Cache aber zusätzlich noch einen großen und gemeinsamen L2-Cache für alles, von daher denke ich dass das Cache-Problem bei den heutigen ARM-MultiCore-SoCs nicht allzu bedeutend ist.


Das ist aber dummerweise nicht so richtig praktikabel
Ach ne, darauf will ich doch die ganze Zeit hinaus, diese Lösung hat zwar ihren Reitz ist aber einfach zu teuer. Wenn irgendwann einmal selbst der billigste ALDI-PC mindestens 16 echte Cores hat und das mitgelieferte OS aber auch mit 20% davon zufriedenstellend läuft dann ist diese Methode eine Überlegung wert (an der Erfüllung des zweiten Kriteriums wird aber MS erheblich dagegen arbeiten).

Moment, professionelles Umfeld - wenn davon bisher die Rede war, dann hab ich das verpasst.
Ob nun unbedingt professionell oder nicht ist erst mal egal aber es ging mir eindeutig nicht um nen MP3-Player für die Hintergrundberieselung.

aber ich bin mir relativ sicher, dass man einen angefangenen Puffer nicht zwingend zu Ende spielen muss.
Gewiss, aber wenn Du kein Knacksen haben willst musst Du aufs Sample genau wissen an welcher Stelle der Puffer abgebrochen wird und auch rechtzeitig einen neuen Puffer bereit stellen der stattdessen weitergespielt werden soll. Das erscheint mir recht aufwendig, da würde ich es bevorzugen einfach in einem festen Raster zu arbeiten, obwohl das dynamische auch einen interessanten Eindruck macht.

Ob es im allgemeinen ein definiertes Ergebnis gibt, wenn man den Puffer ändert, während die Hardware schon spielt oder man besser auf einen neuen Puffer wechselt, weiß ich nicht.
Einen einmal übergebenen Puffer zu ändern halte ich für eine ganz schlechte Idee weil Du nie genau weiß wie weit die HW diesen Puffer schon eingelesen hat (und die Daten in der HW puffert). Kontrolliert Wechseln ist da IMHO eher machbar aber eben mit dem potentiellen Problem des Knacksens.


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 06. November 2011, 13:07
Zitat von: erik
Nein, innerhalb der DPCs sind die INTs an aber die DPCs stellen wimre die höchste Priorität dar so das selbst Prozesse mit sehr hoher Priorität von unwichtiger HW (bzw. von deren schlechten Treibern) verdrängt werden können.
Das macht das Problem für mich noch unverständlicher. Ich weiß das es unter WinXP definitiv nicht auftritt, bei Win7 x86 bin ich mir nicht sicher, aber ich bin mir sicher das es ein Treiber-Problem ist. Auf meinem Laptop wo auch Win7 x64 läuft, habe ich diese Probleme nicht. Diese Latency Probleme sind aber von Gerät zu Gerät unterschiedlich. Deswegen liegt es irgendwo am Konzept bzw. den Treibern (unter Linux scheint es die Probleme ja nicht zu geben).

Zitat von: erik
Die Priorität von HW-Geräten und deren IRQs sollte meiner Meinung nach von der Priorität der gerade erfüllten Aufgaben abhängen.
Hmm, darüber müssen wir diskutieren ;)

Also ich bin der Meinung alle IRQs sollten erstmal behandelt werden, damit es halbwegs schnell weitergehen kann. Allerdings habe ich bei mir auch die Möglichkeit die Priorität der IRQs festzulegen. Da würde es dann schon Sinn machen diese Priorität auch an die IRQ-Handler weiterzugeben.

Zitat von: erik
Der Treiberprogrammierer weiß doch noch gar nicht welche Anforderungen später mal zur Laufzeit zu erfüllen sind.
Da bin ich halt anderer Meinung. Um mal dein Bsp. mit dem MP3-Player, der im Hintergrund läuft, aufzugreifen. Ich möchte eigentlich auch da das es zu keinen Aussetzern kommt, dann läuft halt z.B. die Kompilierung langsamer, aber die Musik läuft durchgehend. Das meine ich mit Latenz und das der Treiberprogrammierer die kennen sollte.
An diesem Bsp. sieht man halt gut, worauf jeder Wert legt. Mir ist es wichtig das die Daten dann zum entsprechenden Zeitpunkt in die Hardware kommen, auch wenn dafür ein Prozess mit höherer Priorität (ausgehend von einem MP3-Player im Hintergrund und einer Kompilierung um Vordergrund) verdrängt wird.

Ich muss aber zugeben, dass ich absolut keine Ahnung habe wie das genau mit den Treibern (am Bsp von Soundkarten) genau funktioniert. Sprich ob man z.B. wie du es immer meinst, einfach nen riesigen Buffer an den Treiber übergeben kann oder nicht.

Um es nochmal deutlich zu sagen, es geht mir dabei um den Treiber, nicht um das Programm welches die Daten für den Treiber erzeugt.

Zitat von: erik
Die Frage ist aber: ist es das was Du wirklich willst? Wäre es nicht besser wenn die die wichtigen HW-Geräte eine höhere Priorität als die weniger wichtigen HW-Geräte haben?
Im Prinzip passiert das ja schon so automatisch. Denn die Threads der IRQ-Handler werden definitiv in der Reihenfolge ausgeführt in der die IRQs auftreten (FIFO). Die Zeitscheibe sollte dann im Schnitt lang genug sein, dass die meisten Handler schon vorher abgeben (da sollten doch eigentlich ein paar ms reichen oder?).

Zitat von: erik
Wäre es nicht besser wenn die die wichtigen HW-Geräte eine höhere Priorität als die weniger wichtigen HW-Geräte haben? Und wäre es nicht praktisch wenn die Priorität der HW-Geräte sich dynamisch nach der Wichtigkeit der von ihnen momentan bearbeiteten Aufgaben richtet?
Jein ;) Wie gesagt, ich betrachte z.B. Audio/Video als verdammt wichtig, da macht es schon nen Unterschied ob da alles zur richtigen Zeit passiert. Wenn die Tastatur/Maus die Daten mal ein "wenig" (ms, aber ich denke eher weniger) später zur Anwendung schicken kann, ist eher verschmerzbar. Genauso HDD/ODD. Deswegen sollte halt der Treiberprogrammierer entscheiden können (bzw. halt die Priorität der IRQs) wie wichtig für das Gerät die Latenz ist.

Problem dabei ist natürlich, dass man entweder nen QM für Treiber braucht (was für ein Hobby-OS vllt noch geht, aber wenn man mal davon ausgeht das man kleiner als MS ist und nicht alle Treiber kontrollieren kann, geht es halt nicht) oder den Programmierern alles vorschreiben muss (und das geht auch nicht wirklich).
Gleiche Prinzip ist, dass ein Programm sich einfach in die höchste Priorität packen kann und schon setzt man die meisten/alle Mechanizmen aus, die eigentlich greifen sollen für ne gute User Experience.
Deswegen sollte man halt auch ganz deutlich wissen worauf man sein OS spezialisieren will (was auch ein Problem von Linux auf dem Desktop ist und wo Windows es teilweise besser macht).

Zitat von: erik
Ich würde eher sagen das der Scheduler dann O(n/2) o.ä. ist weil es ja doch recht wahrscheinlich ist das einer der nächsten Threads auf der gerade aktuellen CPU als letztes gelaufen ist
Ist O(n/2) nicht gleich O(n)?

Ich komme bei der O-Notation immer durcheinander und finde die auf nem Rechner sowieso eher unpraktikabel, weil wenn man streng nach der Theorie geht, ist aufm Rechner alles O(1).

Zitat von: erik
Stimmt, die Cache-Problematik sollte bei einem Single-Sockel-System gar nicht mal so schlimm werden da ja bei Intel und auch bei AMD die letzte Cache-Ebene immer für alle Cores gemeinsam gilt, kritisch werden wohl erst Mehr-Sockel-Systeme.
Das will ich bei mir durch "einige" Scheduler lösen (im Mom sind es 2, werden aber mind. 3). Zur Bootzeit wird dann entschieden welcher Scheduler genommen wird, das wird aber automatisch gemacht und kann vom User nicht so wirklich beeinflusst werden (es sei denn er deaktiviert im BIOS irgendwelche CPUs oder Kerne).

Zitat von: erik
Die meisten ARM-MultiCore-SoCs haben üblicherweise pro Core einen kleinen L1-Daten-Cache und einen kleinen L1-Code-Cache aber zusätzlich noch einen großen und gemeinsamen L2-Cache für alles, von daher denke ich dass das Cache-Problem bei den heutigen ARM-MultiCore-SoCs nicht allzu bedeutend ist.
Ich sollte mich so langsam echt mal in ARM einlesen. Denn ich stelle mir gerade die Frage, woher weiß man auf ARM eigentlich wie viele Cores vorhanden sind? Es gibt ja nicht sowas wie ACPI.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: bluecode am 06. November 2011, 13:43
Ist O(n/2) nicht gleich O(n)?
Doch natürlich. Er wollte damit wohl nur genauer sagen wie man sich den konstanten Faktor vorzustellen hat.

Zitat
Ich komme bei der O-Notation immer durcheinander und finde die auf nem Rechner sowieso eher unpraktikabel, weil wenn man streng nach der Theorie geht, ist aufm Rechner alles O(1).
Nur wenn man mit der Annahme, dass die Eingabe beschränkt ist, arbeitet. Unpraktikabel ist die O-Notation nur dann, wenn man mit ihr nicht das analysiert was wirklich von Interesse ist: Es zwingt ja niemand dich nur die Länge der Eingabe als Variable herzunehmen (das zieht man in aller Härte eigentlich nur für Sachen auf der Turingmaschine heran), sondern ein oder mehrere sinnvolle Größenmaße die aus der Eingabe resultieren (Anzahl Knoten oder Kanten bei Graphen, Höhe eines Baumes, maximaler Ein/Ausgrad, Baumweite etc.) und teilweise kommen da schon auch Konstanten vor, um genauer zu sagen wie die Laufzeit aussieht. ZB wäre es für eine praktische Analyse unsinnig ein 2^(2^k) wegzulassen (für konstantes k). Abgesehen davon muss man auch nicht die worst-case Laufzeit abschätzen, man kann auch average/best-case abschätzen.
Das alles sagt am Ende natürlich immernoch nichts über die konkrete praktische Performance. Es ist aber ein ziemlich gutes Maß dafür wie stark die praktische Performance bei Vergrößerung der Eingabe abnimmt.Insofern ist es schon nützlich, wenn man nicht vorher genauer weiß wie groß die Eingabe sein wird und nicht die Zeit hat/investieren will alle Datenstrukturen die einem so einfallen zu implementieren und gegeneinander zu testen.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 06. November 2011, 13:54
Zitat von: bluecode
Das alles sagt am Ende natürlich immernoch nichts über die konkrete praktische Performance. Es ist aber ein ziemlich gutes Maß dafür wie stark die praktische Performance bei Vergrößerung der Eingabe abnimmt.Insofern ist es schon nützlich, wenn man nicht vorher genauer weiß wie groß die Eingabe sein wird und nicht die Zeit hat/investieren will alle Datenstrukturen die einem so einfallen zu implementieren und gegeneinander zu testen.
Genau da liegt das eigentliche Problem bei der Anwendung der O-Notation.

Du kannst nicht einfach davon ausgehen das O(1) immer schneller ist als O(n) (nur ein Bsp), was aber eigentlich jeder macht, der entweder nicht viel Erfahrung hat oder es nicht besser gelernt hat (was an Unis leider der Fall ist).

Man muss sich immer genau angucken, wie lange dauert O(1) und wie lange dauert O(n) (meinet wegen auch nur im worst-case). Es kann nämlich durchaus sein, dass der O(n) in der Praxis, für die jeweilige Anwendung, schneller ist als O(1).
Was nämlich leider nicht so wirklich in der Uni rübergebracht wird ist, dass die O-Notation nix mit der eigentlichen Laufzeit zu tun hat.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: bluecode am 06. November 2011, 15:24
[...], was aber eigentlich jeder macht, der entweder nicht viel Erfahrung hat oder es nicht besser gelernt hat (was an Unis leider der Fall ist).
Du kannst es nicht einfach auf die Theorie schieben, wenn jemand sie missversteht. Genauso schiebe ich es nicht auf die Datenstruktur, wenn jemand sie schlecht implementiert. Andererseits ist die verkürzte/missverstandene Version der Theorie eine für die meisten Situationen bei weitem ausreichende Grundlage, soll heißen für die meisten UseCases (variable Größe der Eingabe im nicht-trivialen Bereich) und die bekanntesten Datenstrukturen stimmt das auch mit der Realität überein.

Ich denke übrigens schon, dass es an der Uni gelehrt wird. Ob es dann auch gelernt wird, ist eine andere Frage (und eine die nicht unbedingt den Profs in die Schuhe zu schieben ist). Aber selbst wenn es nicht gelernt wird, ich halte die O-Notation für eine der besseren Entscheidungsgrundlagen (im speziellen besser als alles für Eingabegröße k für kleines k zu testen, wenn in der Realität auch größere k auftreten, das es beim osdev häufig nur kleine k's gibt ist ein anderes Thema). Aber um es gleich mit dazu zu sagen, wenn man es richtig machen will, dann abstrahiert man das was man als Datenstruktur braucht so, dass man die Implementierung in der richtigen Phase des Entwicklungszyklus durch etwas in der Praxis performanteres austauschen kann (das man dann aber auch auf Praxisfällen testet! Genau hier wirds ja dann beim OSDev zB richtig schwierig, denn man kann jahrelang nichts praxisrelevantes richtig testen und im speziellen nicht die vielen tollen Spezialfälle die ihr hier so gerne an den Haaren herbeizieht). Deswegen würde ich lieber anfangs asymptotisch auf der sicheren Seite sein, wenn ich nichts/wenig an Daten (was Eingabegrößen und Laufzeitvergleiche diverser Datenstrukturen für diese Größe angeht) über das konkrete Anwendungsgebiet habe.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 06. November 2011, 15:38
Zitat von: bluecode
Du kannst es nicht einfach auf die Theorie schieben, wenn jemand sie missversteht.
Da hast du natürlich recht, aber der Punkt bleibt, dass theoretisch alle Algos auf dem Rechner O(1) sind ;)

Zitat von: bluecode
Ich denke übrigens schon, dass es an der Uni gelehrt wird. Ob es dann auch gelernt wird, ist eine andere Frage (und eine die nicht unbedingt den Profs in die Schuhe zu schieben ist).
Also ich habe den Spaß jetzt an 2 verschiedenen Unis hören dürfen und es wurde beides Male nicht darauf hingewiesen. Ich gehe aber davon aus, dass die Profs einfach voraussetzen, dass man das weiß oder sich denken kann ;)
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: bluecode am 06. November 2011, 15:55
Zitat von: bluecode
Du kannst es nicht einfach auf die Theorie schieben, wenn jemand sie missversteht.
Da hast du natürlich recht, aber der Punkt bleibt, dass theoretisch alle Algos auf dem Rechner O(1) sind ;)
Die Annahme ist genauso sinnvoll wie "in der Praxis gibt es nur endlich viele natürliche/reelle Zahlen". Ich denke wir sind uns beide einig, dass das kein hilfreiches Konstrukt wäre und keine weltbewegenden Aussagen möglich machen würde.
Es macht halt keinen Sinn Annahmen zu machen, die die Analyse zu grobkörnig ausfallen lässt (also alles als gleich zu behandeln was man eigentlich unterscheiden wollte). Im Endeffekt ist die obige Folgerung ja genauso viel wert wie "alle terminierenden Algorithmen terminieren". Und das einzige wonach man sinnvoll rechnerunabhängig unterscheiden kann ist halt nunmal das Wachstum der Laufzeit in Abhängigkeit der Eingabegröße (bis auf Konstanten, genau wegen der Rechnerunabhängigkeit). Und die Praxis zeigt imho überwältigend, dass diese Art der Analyse sehr wertvoll ist.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 06. November 2011, 16:02
Zitat von: bluecode
Ich denke wir sind uns beide einig, dass das kein hilfreiches Konstrukt wäre und keine weltbewegenden Aussagen möglich machen würde.
Jap, nur leider durfte ich schon einige Diskussionen erleben wo halt genauso argumentiert wurde, dass wenn das größte n bekannt ist, der Algo in dem Fall O(1) ist und das war in einem anderen OS-Dev Forum ;)
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 06. November 2011, 17:00
Hallo,


Das macht das Problem für mich noch unverständlicher.
Soweit ich weiß können Windows-Sound-Treiber gewisse Vorgaben für die Puffergröße machen an die sich die Programme aber nicht sklavisch halten müssen. Vielleicht hat sich da von einer Treiber-Revision zur nächsten Revision was geändert. Ansonsten kann ich zu Deinem Problem gar nichts sagen weil ich Dein Problem ja nicht kenne. Vielleicht solltest Du das in einem Forum posten wo es um die von Dir benutze Hardware oder OS-Version geht.

Hmm, darüber müssen wir diskutieren ;)
Sehr gerne. ;)
Hauptsache ich muss nicht über O's diskutieren.

Also ich bin der Meinung alle IRQs sollten erstmal behandelt werden, damit es halbwegs schnell weitergehen kann.
Das sehe ich grundlegend anders. Ich finde alles mit niedriger Priorität muss solange warten bis alles mit hoher Priorität abgearbeitet ist, dazu sollte noch eine kleine Fairnessregel kommen damit auch die Dinge mit niedriger Priorität nicht ganz verhungern. Wenn Du alle IRQs auf die CPU lässt ohne nach Priorität zu sortieren dann werden die wichtigen Sachen eventuell zu stark gebremst und es kommt zu genau dem was Du eigentlich nicht möchtest: Aussetzern.

Allerdings habe ich bei mir auch die Möglichkeit die Priorität der IRQs festzulegen. Da würde es dann schon Sinn machen diese Priorität auch an die IRQ-Handler weiterzugeben.
Genau.

Da bin ich halt anderer Meinung. Um mal dein Bsp. mit dem MP3-Player, der im Hintergrund läuft, aufzugreifen. Ich möchte eigentlich auch da das es zu keinen Aussetzern kommt, dann läuft halt z.B. die Kompilierung langsamer, aber die Musik läuft durchgehend. Das meine ich mit Latenz und das der Treiberprogrammierer die kennen sollte.
Du verwechselst eindeutig "zugesicherte Latenz" mit "zugesichertem Durchsatz". Im Falle des MP3-Player ist nur der Durchsatz entscheidend bzw. die akzeptable Latenz ist so groß das diese kein Problem darstellt. Ein MP3-Player arbeitet üblicherweise nach dem Double-Buffer-Prinzip: er nimmt 2 Puffer und füllt als erstes beide mit Daten (Sequenz 1 und 2) dann startet er Puffer A und kurz darauf legt er Puffer B in die Queue für den Sound-Treiber und sagt das er geweckt werden möchte wenn Puffer A abgearbeitet wurde. Sobald Puffer A abgearbeitet wurde wird der Play-Thread wieder geweckt und er befüllt Puffer A mit neuen Daten (Sequenz 3) und hängt ihn an die Sound-Queue an und lässt sich wecken wenn Puffer B leer ist. Sobald Puffer B fertig ist wird der Player-Thread erneut geweckt und befüllt Puffer B mit neuen Daten (Sequenz 4) und hängt diesen wieder an die Sound-Queue an und lässt sich wecken wenn Puffer A fertig ist. Wie das dann weiter geht kann sich sicher jeder selbst ausmalen. Zu Aussetzern kommt es wenn für das Aufwecken, befüllen eines Puffers und anschließendes Anhängen an die Sound-Queue mehr Zeit vergeht als ein Puffer lang ist (Durchsatz). Deswegen nutzen MP3-Player gerne möglichst große Puffer damit es eben auch nichts macht wenn vom Ende eines Puffers bis der Player-Thread dann tatsächlich wieder auf einer CPU läuft mal ein bisschen Zeit vergeht (Latenz) was ja aufgrund der Länge der Zeitscheiben usw. durchaus mal passieren kann. Wenn die Puffer zu kurz sind kann es passieren das unerwartet lange Latenzen beim Aufwecken zu Aussetzern führen und wenn die CPU zu schlapp ist wird es passieren das selbst bei langen Puffern nicht schnell genug nachgefüllt werden kann (wir gehen jetzt mal davon aus das die Zeit die für das Befüllen eines Puffers benötigt wird direkt linear proportional zu dessen Größe ist, was bei MP3 ziemlich zutreffend sein sollte).

An diesem Bsp. sieht man halt gut, worauf jeder Wert legt.
Eben nicht, man sieht nur ein einziges Szenario von vielen. Ich kann mir z.B. eine Messdatenerfassung per USB (mit sehr hoher Baudrate) und das direkte abspeichern auf die HDD vorstellen und so lange das läuft ist es mir absolut egal ob der Ton kurz aussetzt oder der Bildaufbau mal ein paar Frames länger dauert. Mir ist wichtig das ich als User sagen kann was wichtig ist und eben nicht der Treiberprogrammierer, der weiß doch gar nicht was ich mit meinem PC machen will.

es geht mir dabei um den Treiber, nicht um das Programm welches die Daten für den Treiber erzeugt.
Das darf man nicht voneinander trennen. Der Treiber ist nur ein untergeordneter Dienstleister, der User arbeitet mit Programmen und von denen möchte er bestimmte Aufgaben erfüllt bekommen. Diese Programme benutzen ihrerseits wieder die Treiber aber hier ist es wichtig dass das Programm sagen kann welche (Teil-)Aufgabe wie wichtig ist. Stell Dir vor während einer meiner Datenerfassungen würde irgendein Ereignis passieren das einen kurzen Ton ausgibt und nur weil der Sound-Treiber grundsätzlich mit extrem hoher Priorität läuft würde der Datenstrom kurz unterbrochen, ich nehme an Du kannst Dir vorstellen wie begeistert ich dann über den Sound-Treiber wäre. Wie taljeth schon sagte "Der Kernel sollte nur den Mechanismus anbieten, aber nicht die Policy vorschreiben.", wobei hier jetzt "Kernel" gegen "OS mitsamt Treiber" auszutauschen ist.

Denn die Threads der IRQ-Handler werden definitiv in der Reihenfolge ausgeführt in der die IRQs auftreten (FIFO).
Die IRQs kommen mehr oder weniger zufällig, daraus sollte man nichts schließen. Und wenn Du streng nach FIFO arbeitest könnte es doch passieren das Treiber für nachfolgende IRQs warten müssen nur weil ein Treiber weiter vorne ewig braucht.

Die Zeitscheibe sollte dann im Schnitt lang genug sein, dass die meisten Handler schon vorher abgeben (da sollten doch eigentlich ein paar ms reichen oder?).
Also ich kann mir auch mal komplexe Workloads vorstellen wo der Handler mal deutlich mehr als eine Zeitscheibe braucht. Du machst da wieder Annahmen und Vereinfachungen wo es nicht angebracht ist.

ich betrachte z.B. Audio/Video als verdammt wichtig, da macht es schon nen Unterschied ob da alles zur richtigen Zeit passiert.
Ich habe aber auch Situationen, z.B. Messdatenerfassung, wo mir Audio/Video absolut scheiß egal sind, wenn ich aber gerade ein Full-HD-Video schaue ist mir Audio/Video (vor allem auch das beides Synchron ist) doch wieder wichtig. Deswegen will ich ein OS das sich meinen Anforderungen anpassen lässt. Was eine "gute User Experience" ist hängt sehr davon ab was der User machen will, ein OS das nur mit ein paar willkürlich handverlesenen Workloads zurecht kommt dürfte sich nicht lange am Markt halten können.

das wird aber automatisch gemacht und kann vom User nicht so wirklich beeinflusst werden
Warum nicht? Da eine kleine Stellschraube vorzusehen kostet Dich fast nichts und würde dem User die Möglichkeit geben für spezielle Situationen auch mal spezielle Vorgaben zu machen.

Denn ich stelle mir gerade die Frage, woher weiß man auf ARM eigentlich wie viele Cores vorhanden sind?
Das weiß normalerweise der Board-spezifische Bootloader und der sagt es dem OS.


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 06. November 2011, 17:59
Zitat von: erik
Soweit ich weiß können Windows-Sound-Treiber gewisse Vorgaben für die Puffergröße machen an die sich die Programme aber nicht sklavisch halten müssen. Vielleicht hat sich da von einer Treiber-Revision zur nächsten Revision was geändert. Ansonsten kann ich zu Deinem Problem gar nichts sagen weil ich Dein Problem ja nicht kenne. Vielleicht solltest Du das in einem Forum posten wo es um die von Dir benutze Hardware oder OS-Version geht.
Der Versuch das zu klären ist schon vor längerer Zeit gescheitert. Es hat auch nix mit dem Audio-Treiber zu tun, der tut hier auf meinem Laptop auch seinen Dienst. Zumal auch der Video-Treiber davon betroffen scheint (da in dem Moment auch das Bild kurz "stockt" bzw. es werden Frames ausgelassen).

Was mir so beim Lesen deines Beitrags aufgefallen ist, wir reden mal wieder von unterschiedlichen Dingen. Bei mir laufen (bisher) alle IRQ-Handler mit der selben Real-Time-Priorität. Das ist ja nur die eine Richtung, nämlich das Empfangen von Daten/Ereignissen. Diese Handler sollten ungefähr ähnlich denen von einem Monolithen aussehen.

Um weiter das Bsp. mit dem MP3-Player zu nutzen und wo im Vordergrund ne Kompilierung läuft (am besten eine CPU, macht das ganze einfacher). Unter Windows ist es ja so, dass Anwendungen die im Vordergrund sind einen gewissen Bonus bei der Zeitscheibe bekommen. Funktioniert der Scheduler so, dass immer die Threads mit höchster Priorität laufen und am Ende ihrer Zeitscheibe wieder hinten an die Ready-Liste gepackt werden, wird der MP3-Player entweder nie oder nur zu sporadisch drankommen.
Dem könnte man entkommen, in dem z.B. der Worker-Thread ne höhere Priorität als Normal bekommt. Nun funktioniert mein Scheduler noch anders, bei mir wird ein Thread der seine Zeitscheibe aufgebraucht hat nicht hinten an die Ready-Liste, sonder an die Run-Liste gehängt. Erst wenn die Ready-Liste einmal abgearbeitet wurde, werden die beiden Listen vertauscht (wobei die Ready-Liste ja leer ist) und es wird wieder von Vorne angefangen. So verhungert zumindest kein Thread. Da bräuchte man das mit der höheren Priorität z.B. gar nicht. (Ob das so ne gute Idee ist, steht auf nem anderen Blatt.)

Zitat von: erik
Ich kann mir z.B. eine Messdatenerfassung per USB (mit sehr hoher Baudrate) und das direkte abspeichern auf die HDD vorstellen und so lange das läuft ist es mir absolut egal ob der Ton kurz aussetzt oder der Bildaufbau mal ein paar Frames länger dauert. Mir ist wichtig das ich als User sagen kann was wichtig ist und eben nicht der Treiberprogrammierer, der weiß doch gar nicht was ich mit meinem PC machen will.
Der Treiberprogrammierer weiß schon wie wichtig was ist, schließlich legt er auch fest welche Priorität der IRQ bekommt.
Problem auf Empfangsseite ist, dass du die Priorität des direkten IRQ-Handlers nur einmal festlegen kannst (da wo der Treiber sich registriert) und dann kannst du eigentlich keinen Einfluss mehr darauf nehmen. Denn du weißt ja auch nicht wer die Daten dann haben möchte und wie wichtig die nun sind.

Auf Sender-Seite (Daten werden vom User an den Treiber gegeben), sieht das schon anders aus. Dass kann man dadurch beeinflussen, dass man versucht durchgehend die Priorität des Caller´s weiter zu nutzen. Ich würde da auch gerne die Variante von Windows mit nutzen, sprich das ein Programm das im Vordergrund ist, nen Priorität-Boost bekommt. Die Frage ist aber auch da, wie kann man das vernünftig umsetzen. Man müsste dafür ja eigentlich nen Syscall machen, dass der GUI-Server (oder auch TUI-Server) einem Prozess einen gewissen Prioritäts-Boost geben kann (was dann für alle Threads des Prozesses gelten würde). Mir fällt im Mom keine Anwendung ein, aber was ist wenn eine Anwendung einen Thread hat, der die Idle-Priorität hat (der Thread läuft nur, wenn normalerweise der Idle-Thread laufen würde) und der bekommt miteinmal solch einen Boost. Das ist dann ja nicht mehr im Sinne des Programmierers.

Zitat von: erik
Die IRQs kommen mehr oder weniger zufällig, daraus sollte man nichts schließen. Und wenn Du streng nach FIFO arbeitest könnte es doch passieren das Treiber für nachfolgende IRQs warten müssen nur weil ein Treiber weiter vorne ewig braucht.
Das die anderen Treiber nicht verhungern, soll ja durch eine entsprechend "kurze" Zeitscheibe gesichert werden. Deswegen sollte der Treiberprogrammierer auch so wenig wie nötig direkt im IRQ-Handler machen.

Zitat von: erik
Warum nicht? Da eine kleine Stellschraube vorzusehen kostet Dich fast nichts und würde dem User die Möglichkeit geben für spezielle Situationen auch mal spezielle Vorgaben zu machen.
Naja, was heißt kostet mich nix, mein Bootloader müsste geändert werden und ich müsste dafür etwas in meiner Config-Datei für den Bootloader vorsehen ;)

Ich meine gut, man könnte den User in dieser Config-Datei wählen lassen und wenn der Scheduler nicht verfügbar ist, wird wieder automatisch gewählt. Ein anderes Problem ist aber, das mein Bootloader in dem Sinne keine Ahnung hat, welcher Scheduler was kann und was nicht, das code ich fest ein. So kann es also passieren das der User den Scheduler für Single-CPU Systeme ausgewählt hat, aber auf einem SMP-System funktioniert das nicht und das OS wird crashen. Das finde ich eher nicht so toll.

Zur Laufzeit umstellen will ich mir nicht antun.

Zitat von: erik
Das weiß normalerweise der Board-spezifische Bootloader und der sagt es dem OS.
Da muss ich mal wieder Linux-Bashing betreiben. Einige in der Linux-Welt regen sich im Moment über UEFI und secure-boot auf, aber das die ganzen ARM-Boards mit Linux-spezifischen Bootloader versehen werden, stört keinen ;) (ganz speziell fällt mir da jetzt der Bootloader vom Raspberry Pi ein)
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: Svenska am 06. November 2011, 19:23
Hallo,

Und auch ein Not-Aus-Knopf an der Wand ist eine Implementation einer manuell abschaltbaren Automatik.
Hm, wo wohnst Du denn das bei Dir an den Wänden ein Not-Aus-Knopf ist? Bitte erzähle jetzt nicht das die Wände bei Dir nicht nur mit einem Not-Aus-Knopf sondern auch mit flauschig weicher Polsterung ausgestattet sind. ;) SCNR
Ich dachte eher an die Labore in der Uni, aber auch hier ist auf der anderen Seite der Wand ein großer Kasten... mit Sicherungen drin. Auch die darf man für gewöhnlich als Not-Aus bezeichnen. ;-)

Auf wie vielen Linux-Systemen darf ein normaler User mehr als 95%-Plattenplatz belegen (per Default werden doch 5% für root reserviert, was auf einer aktuellen Multi-TB-Platte schon einiges ist) obwohl dieses Default relativ einfach zu ändern ist.
Auf jedem, bei dem der User bewusst ist, dass es so einen Default gibt. Das gilt überall.

Wenn ich definiere das z.B. mindestens 2 CPUs oder mindestens 10% aller CPUs (also das was größer ist) nicht zur exklusiven Zuweisung an einzelne Threads benutzt werden dürfen dann mag das zwar willkürlich erscheinen
Das mag sogar sinnvoll erscheinen. Aber ebenfalls ist es sinnvoll, diese Zahl änderbar zu machen. Schließlich kann dein System auch an stellen eingesetzt werden, wo du extrem viele Threads mit extrem geringen Latenzanforderungen hast, während gleichzeitig CPU-fressende Threads laufen. Dann sind vielleicht 25% angemessen. Der Administrator, der so ein System betreiben möchte, wird dann den Default kennen und eventuell auch ändern wollen. Für den Durchschnittsnutzer reicht der Default aus.

Zitat von: erik
Warum nicht? Da eine kleine Stellschraube vorzusehen kostet Dich fast nichts und würde dem User die Möglichkeit geben für spezielle Situationen auch mal spezielle Vorgaben zu machen.
Naja, was heißt kostet mich nix, mein Bootloader müsste geändert werden und ich müsste dafür etwas in meiner Config-Datei für den Bootloader vorsehen ;)
Ja, weil du dein Design so verbockt hast, dass die Flexibilität jetzt fehlt. :-P Aber wenn du der Meinung bist, dass dein System besser ist als jedes andere, dann musst du es nicht einstellbar machen. Die Praxis zeigt, dass Stellschrauben immer gut sind.

Zitat von: erik
Das weiß normalerweise der Board-spezifische Bootloader und der sagt es dem OS.
Da muss ich mal wieder Linux-Bashing betreiben. Einige in der Linux-Welt regen sich im Moment über UEFI und secure-boot auf, aber das die ganzen ARM-Boards mit Linux-spezifischen Bootloader versehen werden, stört keinen ;) (ganz speziell fällt mir da jetzt der Bootloader vom Raspberry Pi ein)
Also die GPU zum Booten zu benutzen ist schon heftig, da stimme ich dir zu. Aber du darfst auch gerne 25% auf den Kaufpreis und 10% auf die mechanische Größe drauflegen und ein kleines Boot-Flash anbauen... die Frage ist, ob du dazu bereit bist.

Linux (neben Windows CE) ist auf den meisten ARM-Systemen ein Standard und danach haben sich die Betriebssysteme zu richten. Was beim PC ein Teil von ACPI ist, sind in der ARM-Welt z.B. FTDs (Flatted Device Trees). Ein Hobby-OS muss damit klarkommen. Reg dich lieber drüber auf, dass man einen WindowsCE-Bootloader in der Regel nicht dazu überreden kann, etwas anderes zu booten, einen üblichen Linux-Bootloader (RedBoot, U-Boot) dagegen schon eher. Und die findet man häufiger.

PS: Bei MIPS ist das noch viel schlimmer. Da ist ja nichtmal die Architektur festgelegt. Guck dir mal Projekte wie z.B. Linux auf der Playstation 2 an... außerdem wollen die Hersteller oft genug keine anderen Betriebssysteme, im Gegensatz zum PC, vergleiche hierbei die Diskussion von TomTom damals.

Gruß,
Svenska
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 06. November 2011, 19:50
Zitat von: svenska
Ja, weil du dein Design so verbockt hast, dass die Flexibilität jetzt fehlt.
Was meinen Bootloader betrifft, definitiv :cry: Der müsste mal komplett überarbeitet werden. Allerdings wären wir da wieder bei KISS ;) Im Mom brauche ich keine speziellen Sachen für meine Kernel-"Module" und so wollte ich es eigentlich lassen. Sowas kann man immernoch ändern, ohne das es Probleme gibt (außer das mit dem Wechseln zur Laufzeit).

Zitat von: svenska
Aber wenn du der Meinung bist, dass dein System besser ist als jedes andere, dann musst du es nicht einstellbar machen. Die Praxis zeigt, dass Stellschrauben immer gut sind.
Ich will kein zweites Linux machen! Was ich mir wünsche ist erstmal ein DAU-kompatibles System (und ich denke das ist schwieriger als nen System für Freaks) und da muss man keine Scheduler per Hand wechseln können. Mein OS soll auch nicht auf jedem Toaster laufen ;)

Zitat von: svenska
Linux (neben Windows CE) ist auf den meisten ARM-Systemen ein Standard und danach haben sich die Betriebssysteme zu richten.
Dann sollen die Linux-Leute auch nicht zwecks secure-boot rumheulen, Windows ist auf PC-Systemen nun mal Standard ;) Ok, ist auch scheiße für uns.

Zitat von: svenska
Reg dich lieber drüber auf, dass man einen WindowsCE-Bootloader in der Regel nicht dazu überreden kann, etwas anderes zu booten, einen üblichen Linux-Bootloader (RedBoot, U-Boot) dagegen schon eher.
Dafür bräuchte man an der richtigen Stelle nen dislike-Button ;)

Welche Geräte gibt es heute die nur WindowsCE laden können (gut gibt ein paar billig Netbooks aus Fernost)? Ich möchte meinen der Fastboot-Bootloader von Android ist auch nicht so das wahre, aber ich würde mir viel lieber eine allgemeine Firmware wünschen, wo ich nicht schon im Bootloader alles mögliche an Treiber haben muss (ein BIOS nur in besser ;)).

Zitat von: svenska
außerdem wollen die Hersteller oft genug keine anderen Betriebssysteme, im Gegensatz zum PC, vergleiche hierbei die Diskussion von TomTom damals.
Ich denke das würde sich mit einer allgemeinen Firmware auch lösen lassen. Problem außerhalb des PCs ist doch das man das System relativ einfach zerschießen kann, aber es relativ schwer wieder in Ordnung bekommt.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: kevin am 06. November 2011, 21:08
Zitat von: svenska
Ja, weil du dein Design so verbockt hast, dass die Flexibilität jetzt fehlt.
Was meinen Bootloader betrifft, definitiv :cry: Der müsste mal komplett überarbeitet werden.
Nein, sowas! Wie konnte das nur passieren? Ein selbstentwickelter Bootloader mit kaputtem Design, der die Flexibilität einschränkt. Also damit konnte ja wirklich niemand rechnen!!11elf! (Hier steht es im Wiki (http://www.lowlevel.eu/wiki/GRUB#Warum_GRUB.3F))
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 06. November 2011, 21:19
Nein, sowas! Wie konnte das nur passieren? Ein selbstentwickelter Bootloader mit kaputtem Design, der die Flexibilität einschränkt. Also damit konnte ja wirklich niemand rechnen!!11elf! (Hier steht es im Wiki (http://www.lowlevel.eu/wiki/GRUB#Warum_GRUB.3F))
Wie soll ich es nur sagen ... das was GRUB mir bieten kann funktioniert ;) Das was mir GRUB nicht bieten kann, ist halt nicht so flexibel wie es sein könnte. Ich habe mich für einen eigenen Bootloader entschieden, weil ich da alles kontrollieren kann, Erfahrung sammle, es spaß macht daran rumzubasteln (du weißt schon, das viele alleine die Bastelei als das fertige Ergebnis interessiert?) und weil ich mir so die 3.-Stage im Bootvorgang sparen konnte.

Interessant finde ich aber folgendes:
Zitat von: Wiki
Falls man allerdings von Anfang an intensiv seinen Bootloader plant – was einem Anfänger aufgrund mangelnder Erfahrung verwehrt bleibt
Soll heißen, eigentlich sollte man auch kein OS programmieren (oder planen). Denn das kann ja ein Anfänger nicht aufgrund mangelnder Erfahrung. Henne-Ei-Problem (vorallem was den Bootloader betrifft, da man heutzutage ja nix mehr im Real-Mode macht).
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: kevin am 06. November 2011, 22:27
Ist ja kein Zufall, dass die meisten ihren Kernel x-mal neuschreiben.

Was kann dein Bootloader denn, was GRUB nicht kann? Und was man bräuchte, um den Benutzer eine Option für den Scheduler übergeben zu lassen?
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 06. November 2011, 22:40
Zitat von: taljeth
Ist ja kein Zufall, dass die meisten ihren Kernel x-mal neuschreiben.
Es kommt halt drauf an, ob man basteln will oder ob man ein Ergebis benutzen will. Ich bin eher der bastel-Typ ;)

Zitat von: taljeth
Was kann dein Bootloader denn, was GRUB nicht kann?
Ich linke meinen Kernel erst zur Bootzeit fertig zusammen. Das geht so unter GRUB nicht (??). Man kann zwar Module laden lassen, aber man kann nicht anhand vorhandener Hardware einen Kernel zusammenlinken lassen.
Ich kann bei GRUB nicht so wirklich beeinflussen wo die Module hinkommen und ich kann zu einem Modul nicht noch einen festen String dazuschreiben. Meine Config-Datei soll nen Kernel, nen VFS-Server, nen Device-Server und Module laden können und ich muss dann ganz genau wissen welches Modul was ist. Erst dadurch kann ich dann den VFS-Server und den Device-Server starten. Die anderen Server brauchen mind. diese beiden und deswegen ist das so wichtig. Zumal ich ja flexibel sein will ;) und die Dateinamen für die Server sollen nicht fest gecodet sein.

Zitat von: taljeth
Und was man bräuchte, um den Benutzer eine Option für den Scheduler übergeben zu lassen?
Das Übergeben der Option ist kein wirkliches Problem. Das Problem besteht darin zu entscheiden ob ein Scheduler wirklich für die vorhandene Hardware geeignet ist. Das mache ich im Moment statisch im Code, sprich ich gucke ist nur eine CPU vorhanden, dann nimm /scheduler/smp.ko, sind mehrere CPUs vorhanden dann nimm /scheduler/smp.ko.
Willst du den Benutzer entscheiden lassen, müsste ich in den einzelnen Modulen Code einbauen, die überprüfen ob die entsprechende Hardware vorhanden ist. Dieser Code müsste PIC sein und dürfte keine weiteren Funktionen nutzen (würde die Sache nur noch komplizierter machen). Extra für sowas ne Skriptsprache zu verwenden/einzubauen halte ich für richtig Overkill.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: Svenska am 07. November 2011, 00:43
Allerdings wären wir da wieder bei KISS ;) Im Mom brauche ich keine speziellen Sachen für meine Kernel-"Module" und so wollte ich es eigentlich lassen. Sowas kann man immernoch ändern, ohne das es Probleme gibt (außer das mit dem Wechseln zur Laufzeit).
(a) KISS wäre GRUB zu benutzen.
(b) Wenn es einfach zu ändern wäre, würdest du es tun. Das Problem wegerklären heißt, dass es eben nicht einfach ist.

Zitat von: svenska
Aber wenn du der Meinung bist, dass dein System besser ist als jedes andere, dann musst du es nicht einstellbar machen. Die Praxis zeigt, dass Stellschrauben immer gut sind.
Ich will kein zweites Linux machen!
Stellschrauben sind blöd, äh, weil, naja, Linux hat sowas und Linux will ich nicht. Ich will Windows, das kann alles, aber nichts richtig. Nichtmal Audio.

Was ich mir wünsche ist erstmal ein DAU-kompatibles System (und ich denke das ist schwieriger als nen System für Freaks) und da muss man keine Scheduler per Hand wechseln können.
Das Argument, dass Stellschrauben ein System DAU-unfreundlich machen, ist Blödsinn. Das weißt du so gut wie ich.
Nicht umsonst geistern elendig viele Registry-Hacks rum, die Windows in bestimmten Bereichen verbessern (eben weil der Default doof ist).

Aber gut, leb in deiner eigenen Welt. Du kannst offensichtlich jedes Problem in jedem Zusammenhang optimal lösen.
(PS: Stellschrauben sind auch gut geeignet, um gute Default-Werte zu finden.)

Zitat von: svenska
Reg dich lieber drüber auf, dass man einen WindowsCE-Bootloader in der Regel nicht dazu überreden kann, etwas anderes zu booten, einen üblichen Linux-Bootloader (RedBoot, U-Boot) dagegen schon eher.
Welche Geräte gibt es heute die nur WindowsCE laden können (gut gibt ein paar billig Netbooks aus Fernost)?
Alle Geräte, die mit Windows CE ausgeliefert werden. Dazu gehört so ziemlich jedes Navigationssystem außer TomTom, einige Settop-Boxen und viel mehr. Die Welt besteht aus mehr als Computern und computerähnlichen Geräten, besonders bei ARM und MIPS.

Ich möchte meinen der Fastboot-Bootloader von Android ist auch nicht so das wahre, aber ich würde mir viel lieber eine allgemeine Firmware wünschen, wo ich nicht schon im Bootloader alles mögliche an Treiber haben muss (ein BIOS nur in besser ;)).
Das Konzept "BIOS" ist kaputt. Also gibt es auch kein "BIOS in richtig". Denk lieber an z.B. coreboot, das wäre doch mal was.

Das Konzept "BIOS" ist langsam, weil sich das BIOS per se erstmal um alle Geräte kümmern können muss und später für das Betriebssystem auch noch Funktionen bereithält; bei EFI sogar noch Hardwaretreiber. Normalerweise sollte die Firmware das OS starten können, vielleicht ne minimale Konsole bieten (Bootgerät) und nicht mehr.

Dafür bräuchte man an der richtigen Stelle nen dislike-Button ;)
Ich hab was gegen Facebook, danke.

Zitat von: svenska
außerdem wollen die Hersteller oft genug keine anderen Betriebssysteme, im Gegensatz zum PC, vergleiche hierbei die Diskussion von TomTom damals.
Ich denke das würde sich mit einer allgemeinen Firmware auch lösen lassen.
Nein. Du kannst soziale Probleme nicht technisch lösen.

Problem außerhalb des PCs ist doch das man das System relativ einfach zerschießen kann, aber es relativ schwer wieder in Ordnung bekommt.
Wenn du das verhindern willst, brauchst du etwas, was jeder PC hat: Einen eigenen Flash-Baustein für das Rescue-System (beim PC auch BIOS genannt). Den wird es aus Platz- und Kostengründen nie überall geben können.

Gruß,
Svenska
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: kevin am 07. November 2011, 09:40
Ich linke meinen Kernel erst zur Bootzeit fertig zusammen. Das geht so unter GRUB nicht (??). Man kann zwar Module laden lassen, aber man kann nicht anhand vorhandener Hardware einen Kernel zusammenlinken lassen.
Moment... Das heißt, dein Bootloader erkennt automatisch die Hardware, damit er weiß, was er laden soll? Das heißt für das Minimum an Funktionalität, das man erwarten kann, dass er PCI kann (okay, das mag noch vertretbar sein) und dass er einen kompletten USB-Stack hat. Wahrscheinlich muss er für einige Treiber zusätzliches Probing machen, d.h. einfach nur einen zusätzlichen Treiber installieren reicht nicht, sondern man muss den Bootloader ergänzen. Wenn es mehrere Treiber für eine Hardware gibt (vgl. vesa/nv/nvidia), dann muss sich der Bootloader irgendwie automatisch entscheiden, welche er nehmen will.

Außerdem halte ich es für genauso fragwürdig, in den Bootloader einen Linker einzubauen. Alles, was im Bootloader ist, wird nur während dem Booten benutzt und kann zur Laufzeit nicht wiederverwendet werden. Im Prinzip müsstest du in deinen Bootloader nur noch einen Scheduler einbauen und schon hättest du ein komplettes OS mitsamt Treibern...

tl;dr: Bist du des Wahnsinns?

Zitat
Ich kann bei GRUB nicht so wirklich beeinflussen wo die Module hinkommen und ich kann zu einem Modul nicht noch einen festen String dazuschreiben. Meine Config-Datei soll nen Kernel, nen VFS-Server, nen Device-Server und Module laden können und ich muss dann ganz genau wissen welches Modul was ist. Erst dadurch kann ich dann den VFS-Server und den Device-Server starten. Die anderen Server brauchen mind. diese beiden und deswegen ist das so wichtig. Zumal ich ja flexibel sein will ;) und die Dateinamen für die Server sollen nicht fest gecodet sein.
Also die richtige Vorgehensweise wäre es, dass die Binaries die Information selber mitbringen, welche Abhängigkeiten sie haben.

Wenn du es einfacher machen willst, kannst du ja in die Modul-Kommandozeile irgendwelche Flags packen wie "das hier vor dem ganzen Rest laden". Wenn du nicht auf Dateinamen prüfen willst, wird es ja auch bei deinem eigenen Bootloader in der Config stehen müssen, oder? Spricht also nichts dagegen, das auch bei Multiboot so zu machen.

Zitat
Das Übergeben der Option ist kein wirkliches Problem. Das Problem besteht darin zu entscheiden ob ein Scheduler wirklich für die vorhandene Hardware geeignet ist. Das mache ich im Moment statisch im Code, sprich ich gucke ist nur eine CPU vorhanden, dann nimm /scheduler/smp.ko, sind mehrere CPUs vorhanden dann nimm /scheduler/smp.ko.
Willst du den Benutzer entscheiden lassen, müsste ich in den einzelnen Modulen Code einbauen, die überprüfen ob die entsprechende Hardware vorhanden ist. Dieser Code müsste PIC sein und dürfte keine weiteren Funktionen nutzen (würde die Sache nur noch komplizierter machen). Extra für sowas ne Skriptsprache zu verwenden/einzubauen halte ich für richtig Overkill.
Hä? Wieso muss der Code PIC sein und darf nichts anderes benutzen? Wenn ich sage, dass ich ein bestimmtes Modul haben will, dann wird es geladen, ohne irgendwas zu prüfen, und wenn es nicht funktionieren kann, krieg ich halt einen Panic. Selber schuld, wenn ich explizit eine nichtfunktionierende Konfiguration angebe.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 07. November 2011, 11:51
Zitat von: svenska
KISS wäre GRUB zu benutzen.
Dann müsste ich ne 3. Stage programmieren, diese als Kernel laden lassen, dann den Kernel linken und an die richtige Stelle laden, die ganze Hardware-Abfage müsste auch (nochmal) gemacht werden. Also im Endeffekt das meiste was meinen Bootloader ausmacht.

Zitat von: svenska
Wenn es einfach zu ändern wäre, würdest du es tun. Das Problem wegerklären heißt, dass es eben nicht einfach ist.
Es ist wirklich einfach, wäre nur ne zusätzliche Option in meiner Config-Datei, die ich dem Parser hinzufügen müsste, aber das macht an einer anderen Stelle Probleme.

Zitat von: svenska
Stellschrauben sind blöd, äh, weil, naja, Linux hat sowas und Linux will ich nicht. Ich will Windows, das kann alles, aber nichts richtig. Nichtmal Audio.
Nein, das Problem was ich habe, ist dass du immer mit irgendwelchen Sachen ankommst, die halt Linux kann. Oft sind das Features die ich brauche wenn ich nen Server betreiben will oder nen System für Freaks schreiben will oder es auf einem Toaster laufen soll ;)

Zitat von: svenska
Alle Geräte, die mit Windows CE ausgeliefert werden. Dazu gehört so ziemlich jedes Navigationssystem außer TomTom, einige Settop-Boxen und viel mehr. Die Welt besteht aus mehr als Computern und computerähnlichen Geräten, besonders bei ARM und MIPS.
Genau das gleiche wie oben. Ich möchte ein Desktop-Betriebssystem schreiben, da interessieren mich andere geräte wie z.B. Settop-Boxen fürs erste nicht, sondern Computer und computerähnliche Geräte.

Zitat von: svenska
Das Konzept "BIOS" ist kaputt. Also gibt es auch kein "BIOS in richtig". Denk lieber an z.B. coreboot, das wäre doch mal was.
Ist coreboot nicht ein BIOS Ersatz?

Zitat von: svenska
Wenn du das verhindern willst, brauchst du etwas, was jeder PC hat: Einen eigenen Flash-Baustein für das Rescue-System (beim PC auch BIOS genannt). Den wird es aus Platz- und Kostengründen nie überall geben können.
Das ist klar, aber wenn man in den Consumer-Bereich will (Desktop/Laptop und Co) dann sollte man dort sowas haben. Auf nem Mikrokontroller finde ich auch hat ne Firmware nix zu suchen.

Zitat von: taljeth
Moment... Das heißt, dein Bootloader erkennt automatisch die Hardware, damit er weiß, was er laden soll? Das heißt für das Minimum an Funktionalität, das man erwarten kann, dass er PCI kann (okay, das mag noch vertretbar sein) und dass er einen kompletten USB-Stack hat. Wahrscheinlich muss er für einige Treiber zusätzliches Probing machen, d.h. einfach nur einen zusätzlichen Treiber installieren reicht nicht, sondern man muss den Bootloader ergänzen. Wenn es mehrere Treiber für eine Hardware gibt (vgl. vesa/nv/nvidia), dann muss sich der Bootloader irgendwie automatisch entscheiden, welche er nehmen will.
Nein. Er erkennt alles was für den Kernel wichtig ist, also eine CPU oder mehrere, kann der Kernel den APIC nutzen oder muss er den PIT nutzen, wie kann man den FPU State sichern und was für Syscall-Methoden werden unterstützt. Auch die CPU Features werden abgefragt (für z.B. Paging-Flags) und für den Scheduler wäre es dann noch interessant zu wissen, was für eine CPU-Topologie vorliegt (mehrere Sockel, Hyperthreading usw.).

Zitat von: taljeth
Außerdem halte ich es für genauso fragwürdig, in den Bootloader einen Linker einzubauen. Alles, was im Bootloader ist, wird nur während dem Booten benutzt und kann zur Laufzeit nicht wiederverwendet werden. Im Prinzip müsstest du in deinen Bootloader nur noch einen Scheduler einbauen und schon hättest du ein komplettes OS mitsamt Treibern...
Ist das nicht bei GRUB und uboot der Fall (Treiber und sowas und eigentlich schon mehr OS als Bootloader)?

Was sagst du dann erst zu erik´s Bootloader, er will ja eine lebend Geburt seines Kernels vornehmen, da dürfte also auch mehr drin sein, als dir lieb ist ;)

Zitat von: taljeth
Also die richtige Vorgehensweise wäre es, dass die Binaries die Information selber mitbringen, welche Abhängigkeiten sie haben.
Es gibt einfach eine feste Reihenfolge in welche das System bzw. bestimmte Komponenten gestarten werden müssen, da es dort Abhängigkeiten gibt. Das ist halt bei einem MikroKernel so. Wie soll ich das in die Binaries packen?

Zitat von: taljeth
Wenn du nicht auf Dateinamen prüfen willst, wird es ja auch bei deinem eigenen Bootloader in der Config stehen müssen, oder?
Nicht ganz. Meine Config sieht ungefähr so aus:
kernel="/system/kernel"
vfs-server="/system/server/vfs-server"
device-server="/system/server/device-server"
runtime-loader="/system/runtime-loader"
module="/system/driver/foo"
...
Das ganze wird dann in einem gewissen Format an den Kernel bzw. Device- und VFS-Server weitergeleitet und entsprechend gestartet.

Zitat von: taljeth
Hä? Wieso muss der Code PIC sein und darf nichts anderes benutzen? Wenn ich sage, dass ich ein bestimmtes Modul haben will, dann wird es geladen, ohne irgendwas zu prüfen, und wenn es nicht funktionieren kann, krieg ich halt einen Panic. Selber schuld, wenn ich explizit eine nichtfunktionierende Konfiguration angebe.
Ich möchte diese Einstellung aber nicht bei mir. Du überprüfst die Eingabe von einem User in deinem Programm doch auch? Oder soll jedes Programm abschmieren, weil nicht überprüft wurde, ob es eine Zahl war oder nicht?
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: kevin am 07. November 2011, 13:21
Nein. Er erkennt alles was für den Kernel wichtig ist, also eine CPU oder mehrere, kann der Kernel den APIC nutzen oder muss er den PIT nutzen, wie kann man den FPU State sichern und was für Syscall-Methoden werden unterstützt. Auch die CPU Features werden abgefragt (für z.B. Paging-Flags) und für den Scheduler wäre es dann noch interessant zu wissen, was für eine CPU-Topologie vorliegt (mehrere Sockel, Hyperthreading usw.).
Und warum kann der Kernel das nicht alles selber nachschauen? Wenn du Speicher sparen willst (ob sich das lohnt...?) link die einzelnen Module page-alignt rein, dann kannst du die nicht benötigten zur Laufzeit freigeben, wenn die Initialisierung fertig ist.

Ich glaube, dieser Bootloader ist mal wieder dein Hang zur möglichst komplizierten Lösung...

Zitat
Ist das nicht bei GRUB und uboot der Fall (Treiber und sowas und eigentlich schon mehr OS als Bootloader)?
Ja, GRUB 2 ist im Prinzip ein OS, nur fehlt leider ein gescheiter Bootloader.

Zitat
Was sagst du dann erst zu erik´s Bootloader, er will ja eine lebend Geburt seines Kernels vornehmen, da dürfte also auch mehr drin sein, als dir lieb ist ;)
Ich hoffe, dass er seinen Kernel nicht im Bootloader zusammenlinken will... Ansonsten gebe ich dafür gern ein k-Attribut raus.

Zitat
Es gibt einfach eine feste Reihenfolge in welche das System bzw. bestimmte Komponenten gestarten werden müssen, da es dort Abhängigkeiten gibt. Das ist halt bei einem MikroKernel so. Wie soll ich das in die Binaries packen?
Wenn eine bestimmte ELF-Sektion vorhanden ist, parst du die, und da steht dann sowas drin wie "ich habe eine Abhängigkeit auf 'vfs'".

Zitat
Nicht ganz. Meine Config sieht ungefähr so aus: [...]
Naja, also, da steht ja explizit drin, was was ist. Ist konzeptionell nichts anderes als wenn du die Multiboot-Kommandozeile hernimmst.

Zitat
Ich möchte diese Einstellung aber nicht bei mir. Du überprüfst die Eingabe von einem User in deinem Programm doch auch? Oder soll jedes Programm abschmieren, weil nicht überprüft wurde, ob es eine Zahl war oder nicht?
Ich sagte nicht, der Kernel soll hart auf die Schnauze fliegen, wenn er auf ein Gerät zugreift, das es gar nicht gibt. Sondern dass der Kernel gestartet werden soll und dann ganz ohne PIC und mit so vielen Funktionsaufrufen wie er will feststellt, ob die Hardware da ist oder nicht. Wenn nein, gibt es eine Fehlermeldung.

Was du willst, ist nicht, dass es eine Fehlermeldung gibt, sondern dass (nehmen wir mal einen Browser als Beispiel) der Browser gar nicht erst startet, um mir einen 404 anzuzeigen. Stattdessen prüft eine als PIC geschriebene Funktion, die das OS aus dem Browser extrahiert, ob es die Webseite überhaupt gibt. Und wenn nicht, dann wird der eigentliche Browser gar nicht erst gestartet.

Klingt bescheuert? Ja, schon irgendwie...
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: Svenska am 07. November 2011, 14:18
Zitat von: svenska
KISS wäre GRUB zu benutzen.
Dann müsste ich ne 3. Stage programmieren, [...]
Nö, du hättest nur von vornherein ein einfaches (KISS) Design entwickeln müssen.

aber das macht an einer anderen Stelle Probleme.
Also doch nicht so einfach.

Zitat von: svenska
Stellschrauben sind blöd, äh, weil, naja, Linux hat sowas und Linux will ich nicht. Ich will Windows, das kann alles, aber nichts richtig. Nichtmal Audio.
Nein, das Problem was ich habe, ist dass du immer mit irgendwelchen Sachen ankommst, die halt Linux kann.
(a) Tut mir Leid, dass ich mich mit Windows seit längerem nicht mehr tiefgehend befasse und daher lieber mit Systemen vergleiche, die mir persönlich wesentlich besser gefallen. Das ist neben Linux gelegentlich auch mal ein BSD.
(b) Halte ich Benutzer entweder für kompetent oder für inkompetent. Inkompetent heißt, es gibt überall gut funktionierende Standardwerte. Kompetent heißt, wer es besser weiß, darf sein Wissen auch benutzen. Das unterscheidet ein Betriebssystem von Spielzeug...
(c) "Einstellbar" heißt nicht "kompliziert". Wenn du explizit ein debugfs mounten musst, dann sieht das ein inkompetenter Nutzer die Stellschrauben nichtmal...

Ich möchte ein Desktop-Betriebssystem schreiben, da interessieren mich andere geräte wie z.B. Settop-Boxen fürs erste nicht, sondern Computer und computerähnliche Geräte.
Darum ging es an der Stelle aber nicht... ich wollte dir nur zeigen, dass der Horizont auch weiter weg sein kann... und dass es dort Probleme gibt, ist offensichtlich. Solange diese aber ignoriert werden (weil ist für mich ja nicht relevant), ändert sich nichts.

PS: Eine Settop-Box mit USB-Anschluss und eingebauter Festplatte ist potentiell ein vollwertiger Computer.

Ist coreboot nicht ein BIOS Ersatz?
Ja, es ist aber kein BIOS. Coreboot initialisiert die notwendige Hardware und startet dann einen Payload. Das kann ein BIOS sein (dann wird SeaBIOS verwendet), muss es aber nicht.

Zitat von: svenska
Einen eigenen Flash-Baustein für das Rescue-System (beim PC auch BIOS genannt).
Das ist klar, aber wenn man in den Consumer-Bereich will (Desktop/Laptop und Co) dann sollte man dort sowas haben.
Vergiss es. Guck dir ein Android-Gerät deiner Wahl an, da ist weder Platz noch Geld für sowas vorhanden. Gleiches gilt für Notebooks. Desktops auf ARM-Basis hab ich, abgesehen von Terminals, noch nicht gesehen.

Auf nem Mikrokontroller finde ich auch hat ne Firmware nix zu suchen.
ARM-Bausteine sind Mikrocontroller... die werden auch dann nicht zu Mikroprozessoren, wenn man sie in Netbooks einbaut...

Ist das nicht bei GRUB und uboot der Fall (Treiber und sowas und eigentlich schon mehr OS als Bootloader)?
U-Boot enthält nur Treiber für CPU, RAM, Flash (evtl. HDD) und Splash-Screen und kann Dateisysteme recht ineffizient lesen. Mehr kannst du einkompilieren, wenn du willst, genug Platz und (Boot-)Zeit hast und die entsprechende Unterstützung überhaupt existiert.

Es gibt einfach eine feste Reihenfolge in welche das System bzw. bestimmte Komponenten gestarten werden müssen, da es dort Abhängigkeiten gibt. Das ist halt bei einem MikroKernel so.
Nö. Wenn der Kernel selbstständig die bereits vorgeladenen Module starten kann, dann kann er auch die Reihenfolge festlegen, in der er sie startet. Und normalerweise macht man eine Abhängigkeitsverwaltung in der Form "ich brauch dies, du brauchst das" usw. Ist flexibler und wird bei Erweiterungen wichtig...

Du überprüfst die Eingabe von einem User in deinem Programm doch auch? Oder soll jedes Programm abschmieren, weil nicht überprüft wurde, ob es eine Zahl war oder nicht?
Darum sollte jemand, der keine Ahnung hat, ja auch nicht am Bootloader rumspielen... geht ja auch nicht jeder Windows-Nutzer an die Registry und pfuscht da wahllos rum. Das tun die, die genug Ahnung haben - und für die ist sowas da.

Gruß,
Svenska
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 07. November 2011, 14:31
Zitat von: taljeth
Und warum kann der Kernel das nicht alles selber nachschauen? Wenn du Speicher sparen willst (ob sich das lohnt...?) link die einzelnen Module page-alignt rein, dann kannst du die nicht benötigten zur Laufzeit freigeben, wenn die Initialisierung fertig ist.
Warum sollte er? Zumal ich dann ja den Linker in den Kernel packen müsste und da will ich den ja nicht haben. Im Endeffekt "spare" ich mir dabei eine indirektion bei den Function-Calls.

Zitat von: taljeth
Ich glaube, dieser Bootloader ist mal wieder dein Hang zur möglichst komplizierten Lösung...
Wieso? So wie der Linux-Kernel Module nachladen kann, lasse ich das vom Bootloader machen, weil ich im Kernel keinen Linker habe und auch nicht haben will.

Zitat von: taljeth
Ja, GRUB 2 ist im Prinzip ein OS, nur fehlt leider ein gescheiter Bootloader.
Kann ich nicht beurteilen, habe ich mir noch nicht angeguckt. Die Frage wäre, was unterscheidet eigentlich nen Bootloader von einem OS?

Zitat von: taljeth
Wenn eine bestimmte ELF-Sektion vorhanden ist, parst du die, und da steht dann sowas drin wie "ich habe eine Abhängigkeit auf 'vfs'".
Wer parst die? Der Runtime-Loader? Für den findet eine lebend-Geburt statt, genauso für den Device-Server. Der VFS-Server hat ein wenig Init-Code, da er die ganzen Module startet.

Ich verlagere damit eine gewisse Komplexität bzw. Funktionalität aus dem OS in den Bootloader um eine halbe lebend-Geburt zu machen.

Zitat von: taljeth
Naja, also, da steht ja explizit drin, was was ist. Ist konzeptionell nichts anderes als wenn du die Multiboot-Kommandozeile hernimmst.
Die dann meine 3.-Stage parsen müsste, um die komme ich halt bei meiner Architektur nicht drumrum.

Zitat von: taljeth
Ich sagte nicht, der Kernel soll hart auf die Schnauze fliegen, wenn er auf ein Gerät zugreift, das es gar nicht gibt.
Zitat von: taljeth
Wenn ich sage, dass ich ein bestimmtes Modul haben will, dann wird es geladen, ohne irgendwas zu prüfen, und wenn es nicht funktionieren kann, krieg ich halt einen Panic.
Wenn ich jetzt den Scheduler nehme den der User vorgibt (1-CPU Scheduler) und dann das System starte (mehrere CPUs), dann wird es nen Panic geben, aber der wird nicht auf den ersten Blick nach dem Scheduler aussehen.
Will ich dieses Problem umgehen, muss ich vorher prüfen ob die Angabe des Users Sinn macht und das müsste dann im Bootloader passieren. Dazu müsste jedes Modul ne Funktion haben, die überprüft ob das Modul auf dieser Hardware überhaupt laufen kann.

Wie sieht denn der Bootvorgang bei tyndur aus? Zumal die meisten MikroKernel haben einen eigenen Bootloader (in irgendeiner Form), weil es halt einfach bestimmte Sachen im Bootvorgang und einer gewissen Reihenfolge gibt, auf die man setzt.

Zitat von: svenska
Nö, du hättest nur von vornherein ein einfaches (KISS) Design entwickeln müssen.
Ich wollte ein gewisses Feature haben und daraus resultiert das halt.

Zitat von: svenska
Also doch nicht so einfach.
Das hinzufügen ist einfach, aber dann funktioniert es nicht so wie ich es mir wünsche. Ich kann immer irgendwas zusammen hacken, was zwar einfach wäre, aber es ist halt nicht das was ich will.

Zitat von: svenska
Coreboot initialisiert die notwendige Hardware und startet dann einen Payload.
Wo bekommt es diesen Payload her? Dieser Payload müsste dann ja nen gewisse Art von Firmware sein, die es ermöglich das Bootloader geladen werden können. Sicher man könnte als Payload auch gleich nen Bootloader haben, aber sollte solch eine Firmware nicht einfach sein und nicht schon so speziell wie nen Bootloader?

Zitat von: svenska
Vergiss es. Guck dir ein Android-Gerät deiner Wahl an, da ist weder Platz noch Geld für sowas vorhanden. Gleiches gilt für Notebooks. Desktops auf ARM-Basis hab ich, abgesehen von Terminals, noch nicht gesehen.
Du meinst die Hersteller wollen das nicht, aber es wäre besser und einfacher für alle. Entweder man will nur Linux drauf laufen lassen oder es muss halt sowas wie ne Firmware geben. Ein Problem ist doch das man genau deswegen jedes Mal Linux und Co (wo Windows mit eingeschlossen ist) auf jedes Board angepasst werden müssen.
Bei Notebooks und Desktops sollte das Geld da sein. Wieviel soll das denn ausmachen?
Was ist denn der Unterschied zw. nem Terminal und nem Desktop? Mir fällt spontan Trimslice ein.

Zitat von: svenska
Wenn der Kernel selbstständig die bereits vorgeladenen Module starten kann, dann kann er auch die Reihenfolge festlegen, in der er sie startet.
Mein Kernel kann das aber nicht ;) Das ist eine Design-Entscheidung gewesen, genauso wie ich mich für einen MikroKernel entschieden habe.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: Svenska am 07. November 2011, 15:29
Wieso? So wie der Linux-Kernel Module nachladen kann, lasse ich das vom Bootloader machen, weil ich im Kernel keinen Linker habe und auch nicht haben will.
Bei einem Mikrokernel sind die Module üblicherweise Tasks. Prozessverwaltung ist für mich ein elementarer Teil des Kernels, also kann der Kernel auch (vom Bootloader in den Speicher gelegte) Tasks starten. Wenn ein Task einfach blockiert, weil seine Vorbedingung noch nicht erfüllt ist (z.B. ein Socket oder ein bestimmter IPC-Dienst), dann spielt auch die Ladereihenfolge keine Rolle mehr.

Wenn du deinen Kernel zur Initialisierungszeit aus Einzelteilen zusammenlinkst, ist es ein Makrokernel, kein Mikrokernel mehr. Darum ist Linux, trotz aller Module, ein modularisierter Monolith.

Zitat von: taljeth
Naja, also, da steht ja explizit drin, was was ist. Ist konzeptionell nichts anderes als wenn du die Multiboot-Kommandozeile hernimmst.
Die dann meine 3.-Stage parsen müsste, um die komme ich halt bei meiner Architektur nicht drumrum.
Kaputte Architektur? :-D

Wenn ich jetzt den Scheduler nehme den der User vorgibt (1-CPU Scheduler) und dann das System starte (mehrere CPUs), dann wird es nen Panic geben, aber der wird nicht auf den ersten Blick nach dem Scheduler aussehen.
Dann ist dein System nicht in der Lage, vernünftig mit Fehlern umzugehen. Ein Kernel ohne Scheduler ist nicht lauffähig, soweit richtig. Aber warum sollte ein UMP-Scheduler auf einem SMP-System einen Panic werfen? Reicht eine Warnung nicht? Warum sollte ein SMP-Scheduler auf einem UMP-System einen Panic werfen? Wie wird denn vom Bootloader entschieden, ob jetzt der (SMP-fähige) Scheduler A oder der (SMP-fähige) Scheduler B verwendet wird? Anhand der vom Benutzer vorgegebenen Konfigurationsdatei? Dann kann der Benutzer auch den Scheduler vorgeben und wenn der nicht geladen werden kann, wird der Standardscheduler als Fallback geladen...

Dazu müsste jedes Modul ne Funktion haben, die überprüft ob das Modul auf dieser Hardware überhaupt laufen kann.
Ja, ein USB-Modul sollte auf einem USB-freien PC einen Fehler werfen... und ein PAE-Modul sollte das auf einem i586 ebenfalls tun. Wenn der Kernel nach der Initialisierung aller Module feststellt, dass irgendwas elementares fehlt (z.B. das Root-Dateisystem), dann gibt es einen Panic. Machst du sicherlich ähnlich, wenn die Konfigurationsdatei kaputt ist.

Zitat von: svenska
Coreboot initialisiert die notwendige Hardware und startet dann einen Payload.
Wo bekommt es diesen Payload her? Dieser Payload müsste dann ja nen gewisse Art von Firmware sein, die es ermöglich das Bootloader geladen werden können. Sicher man könnte als Payload auch gleich nen Bootloader haben, aber sollte solch eine Firmware nicht einfach sein und nicht schon so speziell wie nen Bootloader?
Payloads sind im Baustein mit drin und können alles mögliche sein, z.B. ein Linux-Kernel mit Initrd (als Rescue-System), ein BIOS, ein DOS, ein GRUB-artiger Bootloader, was immer du möchtest.

Flexibilität ist das Zauberwort. In einem festplattenlosen Terminal braucht die Firmware keine Festplattenunterstützung haben, die kostet nur Zeit, Platz und damit Geld. In einer eierlegenden Wollmilchsau kommt ein full-featured sonstwas zum Einsatz, auf einem Tablet ein Kernelloader.

Du meinst die Hersteller wollen das nicht, aber es wäre besser und einfacher für alle. Entweder man will nur Linux drauf laufen lassen oder es muss halt sowas wie ne Firmware geben.
Eine Firmware brauchst du immer, ob du nun Linux drauf haben willst oder was anderes. Und nein, die Hersteller wollen das nicht. Das ist Aufwand, Aufwand ist teuer und Geld ist ein Problem. Und man könnte ja auf Bastler treffen, die dann ihre eigene Software drauf laufen lassen wollen.

Ein Problem ist doch das man genau deswegen jedes Mal Linux und Co (wo Windows mit eingeschlossen ist) auf jedes Board angepasst werden müssen.
Das ist falsch. Der Kernel und die Treiber sind zwingend hardwarespezifisch. Solange du die Treiber nicht in die Firmware stopfen möchtest (was das Betriebssystem erst recht festnagelt, vgl. DOS setzt BIOS voraus), muss der Kernel auf die Hardware zugeschnitten sein.

Bei Notebooks und Desktops sollte das Geld da sein. Wieviel soll das denn ausmachen?
Das Geld ist nicht da, das nennt man nämlich Gewinnspanne. Die ist im Consumerbereich ohnehin schon sehr klein.

Was ist denn der Unterschied zw. nem Terminal und nem Desktop? Mir fällt spontan Trimslice ein.
Terminals sind nicht oder nur sekundär auf lokale Arbeiten ausgelegt, im Gegensatz zu Desktops. Das heißt meist: Schnelles Netzwerk, langsame CPU, wenig RAM, einfache GPU. Trimslice kannte ich übrigens noch nicht, würde ich aber auch nicht als Desktop bezeichnen.

Zitat von: svenska
Wenn der Kernel selbstständig die bereits vorgeladenen Module starten kann, dann kann er auch die Reihenfolge festlegen, in der er sie startet.
Mein Kernel kann das aber nicht ;) Das ist eine Design-Entscheidung gewesen, genauso wie ich mich für einen MikroKernel entschieden habe.
Tja, dann ist das eben schlechtes Design. ;-) Aber wenn du den Kernel zusammenlinkst, ist es eher ein Makrokernel als ein Mikrokernel.

Gruß,
Svenska
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: kevin am 07. November 2011, 15:32
Warum sollte er? Zumal ich dann ja den Linker in den Kernel packen müsste und da will ich den ja nicht haben. Im Endeffekt "spare" ich mir dabei eine indirektion bei den Function-Calls.
Wenn du einen Linker im Mikrokernel brauchst, machst du was falsch. Mein Vorschlag war, beim Bauen alle Module in den Kernel zu linken und die nicht benötigten zur Laufzeit freizugeben. Dafür sparst du dir den kompletten Linker, sowohl im Bootloader als auch im Kernel.

Damit brauchst du auch keine bestimmten Funktionen in den Modules als PIC und mit eingeschränkter Lib oder in einer Skriptsprache schreiben, sondern kannst bei der Initialisierung den Code von allen Modulen ganz normal benutzen. Wenn der Benutzer etwas ungünstiges verlangt, kannst du sogar einen sicheren Fallback wählen anstatt einen Panic zu machen. Weniger Komplexität, mehr Funktionalität - für mich klingt das überzeugend.

Zitat
Wieso? So wie der Linux-Kernel Module nachladen kann, lasse ich das vom Bootloader machen, weil ich im Kernel keinen Linker habe und auch nicht haben will.
Bei Linuxmodulen geht es darum, zur Laufzeit nachzuladen, bei dir geht es darum, zur Bootzeit auszuwählen. Aber lassen wir das einmal außer Acht: Warum in aller Welt sollte ein Mikrokernel dynamisch ladbare Module brauchen? Das ist vollkommen overengineert.

Zitat
Kann ich nicht beurteilen, habe ich mir noch nicht angeguckt. Die Frage wäre, was unterscheidet eigentlich nen Bootloader von einem OS?
Ich glaube, diese Frage zu beantworten hilft uns nicht so richtig weiter, aber mein Versuch wäre: Ein Bootloader läuft selber nicht mehr weiter, nachdem er den Kernel gestartet hat, während ein OS die Kontrolle behält, wenn es ein Programm startet.

Zitat
Die dann meine 3.-Stage parsen müsste, um die komme ich halt bei meiner Architektur nicht drumrum.
Die stage3 wäre aber Teil des Kernels und würde wesentlich einfacher werden als ein kompletter Bootloader mit integriertem Linker.

Zitat
Wenn ich jetzt den Scheduler nehme den der User vorgibt (1-CPU Scheduler) und dann das System starte (mehrere CPUs), dann wird es nen Panic geben, aber der wird nicht auf den ersten Blick nach dem Scheduler aussehen.
Will ich dieses Problem umgehen, muss ich vorher prüfen ob die Angabe des Users Sinn macht und das müsste dann im Bootloader passieren. Dazu müsste jedes Modul ne Funktion haben, die überprüft ob das Modul auf dieser Hardware überhaupt laufen kann.
Warum muss das im Bootloader passieren? Das ist genau mein Browser-Beispiel von oben (schade dass du nicht drauf eingegangen bist).

Aus dem Zufallspanic einen Panic zu machen, der nach Scheduler aussieht, ist einfach. Dazu musst du prüfen, ob die Angabe des Users sinnvoll ist, ja. Das kannst du bei der Initialisierung des Schedulers machen. Wenn du magst, kannst du statt dem aussagekräftigen Panic sogar einen sicheren Falback auswählen und damit weitermachen.

Zitat
Wie sieht denn der Bootvorgang bei tyndur aus? Zumal die meisten MikroKernel haben einen eigenen Bootloader (in irgendeiner Form), weil es halt einfach bestimmte Sachen im Bootvorgang und einer gewissen Reihenfolge gibt, auf die man setzt.
GRUB ist ursprünglich der Bootloader eines Mikrokernels (Hurd).

Bei tyndur läuft das so: Der Kernel startet init (das ist per Definition das erste Multiboot-Modul) und mappt alle anderen Module in den Adressraum von init. init startet dann den Rest der Multiboot-Module, die sich wiederum bei init anmelden, sobald sie initialisiert sind, und wenn sie Abhängigkeiten haben, informieren sie sich bei init, ob die Abhängigkeit schon da ist.

An dieser Stelle sind in der Regel pci und jeweils ein Block- und Dateisystemtreiber geladen, plus servmgr. servmgr liest eine Konfiguration vom Rootdateisystem und lädt alle übrigen Module, die darin angegeben sind (inklusive weiteren Abhängigkeiten usw.).
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 07. November 2011, 16:28
Zitat von: svenska
Prozessverwaltung ist für mich ein elementarer Teil des Kernels, also kann der Kernel auch (vom Bootloader in den Speicher gelegte) Tasks starten.
Das passiert in meinem momentanen Konzept nur für den VFS-Server, der bekommt die ganzen Module (also den Speicher) und startet dann den Device-Server usw.

Zitat von: svenska
Wenn du deinen Kernel zur Initialisierungszeit aus Einzelteilen zusammenlinkst, ist es ein Makrokernel, kein Mikrokernel mehr. Darum ist Linux, trotz aller Module, ein modularisierter Monolith.
Jetzt bin ich verwirrt. Der fertige Linux-Kernel mit dynamischen Modulen bleibt trotzdem ein Monolith, aber mein Kernel, der aus Einzelteilen zusammengelinkt wird (und dann ein MikroKernel wäre), ist ein MarkoKernel?
Ist ja eigentlich auch egal. Reicht es nicht wenn man zw. MikroKernel und Monolith (und eventuell noch NanoKernel) unterscheidet?

Zitat von: svenska
Kaputte Architektur?
Ist Linux für mich auch ;) Liegt also wieder im Auge des Betrachters.

Zitat von: svenska
Aber warum sollte ein UMP-Scheduler auf einem SMP-System einen Panic werfen?
Aus einem ganz ganz einfachen Grund, kein Locking! Zumal mir gerade noch einfällt, dass der SMP-Scheduler auf einigen UMP-Systemen sogar ne Exception auslösen wird (der greift nämlich direkt auf den APIC zu).

Zitat von: svenska
Wie wird denn vom Bootloader entschieden, ob jetzt der (SMP-fähige) Scheduler A oder der (SMP-fähige) Scheduler B verwendet wird? Anhand der vom Benutzer vorgegebenen Konfigurationsdatei?
Nein, das sagte ich doch schon, das ist fest im Code drin. Alles was den Kernel betrifft ist fest gecodet. Im Moment unterscheide ich nur zw. UMP und SMP, will später aber noch nen Scheduler für viele CPUs (keine Ahnung, vllt ab 16 oder so) und wollte mal gucken wie man das mit Hyperthreading umsetzen kann (aber das hat erstmal Zeit).

Zitat von: svenska
Wenn der Kernel nach der Initialisierung aller Module feststellt, dass irgendwas elementares fehlt (z.B. das Root-Dateisystem), dann gibt es einen Panic. Machst du sicherlich ähnlich, wenn die Konfigurationsdatei kaputt ist.
Das ist der Punkt, aus der Sicht des Kernel läuft einfach alles ;) Ich wollte genau diesen ganzen Schmarrn aus dem Kernel haben. Die ganzen wirklich groben Fehler fängt bei mir schon der Bootloader ab, macht den Kernel einfacher.

Zitat von: svenska
Der Kernel und die Treiber sind zwingend hardwarespezifisch. Solange du die Treiber nicht in die Firmware stopfen möchtest (was das Betriebssystem erst recht festnagelt, vgl. DOS setzt BIOS voraus), muss der Kernel auf die Hardware zugeschnitten sein.
Ich rede davon das im Prinzip jedes Board seine eigene Platform ist und Linux und Co müssen an jede Platform angepasst werden. Genau darauf wollte ich mit einer allgemeinen Firmware hinaus, dass es eine Platform gibt die man unterstützen muss. Das wäre halt wesentlich angenehmer und auch einfacher für Linux und Co.

Zitat von: svenska
Terminals sind nicht oder nur sekundär auf lokale Arbeiten ausgelegt, im Gegensatz zu Desktops. Das heißt meist: Schnelles Netzwerk, langsame CPU, wenig RAM, einfache GPU. Trimslice kannte ich übrigens noch nicht, würde ich aber auch nicht als Desktop bezeichnen.
Wo wir wieder dabei wären, liegt im Auge des Betrachters ;) Ich würde Trimslice schon als Desktop (da es definitiv kein Netbook ist) einstufen. Was du als Terminal bezeichnest, bezeichnet Intel als Nettop.

Zitat von: taljeth
Wenn du einen Linker im Mikrokernel brauchst, machst du was falsch. Mein Vorschlag war, beim Bauen alle Module in den Kernel zu linken und die nicht benötigten zur Laufzeit freizugeben. Dafür sparst du dir den kompletten Linker, sowohl im Bootloader als auch im Kernel.
Ich wollte aber das Feature haben, dass sich der Kernel erstens um so wenig Initialisationsarbeit wie nötig kümmern muss und vorallem wollte ich den ganzen Nachgucken-Was-Für-Hardware-Vorhanden-Ist Schmarrn aus dem Kernel raus haben.
Wenn ich es mir recht überlege, wenn erik ne lebend-Geburt machen will, muss er das ja auch schon alles im Bootloader machen (mal davon abgesehen, dass er ja nicht unterschiedliche Hardware unterstützen möchte).

Zitat von: taljeth
Damit brauchst du auch keine bestimmten Funktionen in den Modules als PIC und mit eingeschränkter Lib oder in einer Skriptsprache schreiben, sondern kannst bei der Initialisierung den Code von allen Modulen ganz normal benutzen. Wenn der Benutzer etwas ungünstiges verlangt, kannst du sogar einen sicheren Fallback wählen anstatt einen Panic zu machen. Weniger Komplexität, mehr Funktionalität - für mich klingt das überzeugend.
Problem ist hier der Auftraggeber (also ich  :-P) und der will bestimmte Features haben und damit muss man leben. Anderes Bsp. ist dass ich 4MB Pages unterstützen will, was wiederrum einige Anforderungen an den PMM stellt, da kannst du noch so lange kommen und sagen, dann lass doch einfach die 4MB Pages weg ;)

Zitat von: taljeth
Warum in aller Welt sollte ein Mikrokernel dynamisch ladbare Module brauchen? Das ist vollkommen overengineert.
Warum sollte ein Monolith das brauchen oder Programme? Wieso ist das da nicht overengineert? Man "spart" auf der einen Seite Speicher und der Kernel wird einfacher.
Zumal ich eher sagen würde, statisch ladbare Module, weil das passier nur einmal und man kann sie nicht mehr entladen.

Zitat von: taljeth
Ein Bootloader läuft selber nicht mehr weiter, nachdem er den Kernel gestartet hat, während ein OS die Kontrolle behält, wenn es ein Programm startet.
Wenn ein OS also als Bootloader für ein anderes OS missbraucht werden würde, wäre es kein OS mehr? Ich mein wenn ich mir GRUB2 so angucke, dann sehe ich sehr viel gleichen Code, ich würde sogar so weit gehen und behaupten das GRUB2 komplexer und umfangreicher als die meisten Hobby-OS´e ist.

Zitat von: taljeth
Warum muss das im Bootloader passieren? Das ist genau mein Browser-Beispiel von oben (schade dass du nicht drauf eingegangen bist).
Weil ich das so will ;) Ansonsten siehe oben.

Zitat von: taljeth
Bei tyndur läuft das so: Der Kernel startet init (das ist per Definition das erste Multiboot-Modul)
Interessant, so kann man es natürlich auch machen, aber meins ist dafür flexibler :P Jeder entscheidet sich halt anders. Ihr habt euch für diesen Weg entschieden und konntet euch so die "Arbeit" (ich habe dabei viel gelernt und es hat auch Spaß gemacht) einen Bootloader schreiben zu müssen sparen.
Aber warum muss ich mich an irgendwelche Gegebenheiten anpassen, die ich auch relativ einfach (einfacher als ne neue Architektur umzusetzen) ändern kann, wenn diese Gegebenheiten nicht ganz meinem Design entsprechen?
Bitte komm mir jetzt nicht mit Rad neu erfinden, das hatten wir schon und den Spruch darf keiner bringen, der ein OS entwickelt ;)

Zitat von: taljeth
init startet dann den Rest der Multiboot-Module, die sich wiederum bei init anmelden, sobald sie initialisiert sind, und wenn sie Abhängigkeiten haben, informieren sie sich bei init, ob die Abhängigkeit schon da ist.
Ihr habt also einfach das Überprüfen der Abhängigkeiten in eure Module/Programme verlagert (was es so bei mir nicht geben wird, da dass durch die festgelegte Reihenfolge abgefangen wird). Ist init eigentlich ein Programm oder nur ein Skript?

Ich will das dann so lösen, dass ich nur ein Skript und kein Programm habe (was ich wieder flexibler finde).
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: kevin am 07. November 2011, 16:46
Eine Antwort auf einzelne Absätze spare ich mir. Du hast dich auf die Position "ich will das aber so" zurückgezogen, und das ist okay. Gerade in einem Hobby-OS kann man Dinge auch mal anders machen, weil man eben was ausprobieren will. Du solltest dann nur aufhören, zu glauben, dass dein Modell das einzig wahre ist, vor allem wenn du keine Begründung dafür hast. ;)

Objektiv gesehen ist genau das passiert, wovor die Wiki-Seite eben warnt: Dein Bootloader schränkt das Design deines Kernels ein. Du kannst oder willst ein Feature (Stellschrauben für den Benutzer) nicht implementieren, weil es mit deinem Bootloader-Design zu umständlich wäre. Ich glaube, dass es vernünftig funktionierende Alternativen gibt, die das erlauben würden, habe ich gezeigt. Dass du sie nicht haben willst, ist eine andere Sache.

init in tyndur ist eine Binary, um das noch zu beantworten. Ein Skript bräuchte ja wieder einen Interpreter und das wird für den allerersten Prozess im System schwierig.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 07. November 2011, 16:57
Zitat von: taljeth
Du hast dich auf die Position "ich will das aber so" zurückgezogen, und das ist okay. Gerade in einem Hobby-OS kann man Dinge auch mal anders machen, weil man eben was ausprobieren will. Du solltest dann nur aufhören, zu glauben, dass dein Modell das einzig wahre ist, vor allem wenn du keine Begründung dafür hast.
Ich habe doch 2 Gründe genannt, einmal "spare" (es ist nicht viel, ich weiß) ich Speicher und ich spare mir ein paar Cache-Misses, da ich eine indirektion weniger bei den Calls habe.
Ich glaube auch nicht das mein Modell das einzige wahre ist, es gibt immer viele Lösungen für ein Problem.

Zitat von: taljeth
Objektiv gesehen ist genau das passiert, wovor die Wiki-Seite eben warnt: Dein Bootloader schränkt das Design deines Kernels ein.
Das verstehe ich wiederrum nicht. Wieso schränkt mein Bootloader das Design meines Kernels ein? Ich habe doch das was ich will. Um es mal so zu sagen, ihr wollt mir ja einreden, dass die Möglichkeit, dass der User wählen kann verdammt wichtig ist, aber wieso ist sie das? Wieso muss ich das drin haben?

Ich sags mal so, so wie svenska von Linux und BSD geprägt ist, so bin ich besonders von einem OS geprägt (BeOS) und meines Wissens nach, gibt es dort auch nicht wirklich viele Stellschrauben (wenn überhaupt welche, die mit dem vergleichbar sind, was ihr meint) und es hat trotzdem funktioniert und war mMn seiner Zeit voraus!

Zitat von: taljeth
Ich glaube, dass es vernünftig funktionierende Alternativen gibt, die das erlauben würden, habe ich gezeigt. Dass du sie nicht haben willst, ist eine andere Sache.
Deine Alternative, wie oben geschrieben, verbietet mir aber ein anderes Feature was ich haben will.

Zitat von: taljeth
init in tyndur ist eine Binary, um das noch zu beantworten. Ein Skript bräuchte ja wieder einen Interpreter und das wird für den allerersten Prozess im System schwierig.
Du sagst also euer Design schränkt euch da ein. Ich würde sagen es ist kaputt ;)
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: Svenska am 07. November 2011, 17:08
Reicht es nicht wenn man zw. MikroKernel und Monolith (und eventuell noch NanoKernel) unterscheidet?
Reicht es nicht auch, wenn man den Begriff "Kernel" komplett weglässt und von "Computer" spricht? ;-)

Zitat von: svenska
Aber warum sollte ein UMP-Scheduler auf einem SMP-System einen Panic werfen?
Aus einem ganz ganz einfachen Grund, kein Locking!
Hä? Ein UMP-Scheduler sollte nur eine CPU benutzen. Also statt den vorhandenen 4 Kernen wird halt nur einer benutzt. Dann braucht es auch kein SMP-fähiges Locking...

Ich rede davon das im Prinzip jedes Board seine eigene Platform ist und Linux und Co müssen an jede Platform angepasst werden. Genau darauf wollte ich mit einer allgemeinen Firmware hinaus, dass es eine Platform gibt die man unterstützen muss. Das wäre halt wesentlich angenehmer und auch einfacher für Linux und Co.
Ja. Und es ist größer, aufwändiger und teurer, weil eine allgemeingültige Plattform immer auch Dinge unterstützen muss, die das reale System dann nicht braucht. Beim PC wäre das beispielsweise ein Tastaturcontroller, der auf einem legacy-freien System umständlich emuliert werden muss. Und darum wollen die Hersteller das nicht.

Ich würde Trimslice schon als Desktop (da es definitiv kein Netbook ist) einstufen. Was du als Terminal bezeichnest, bezeichnet Intel als Nettop.
"Alles, was kein Netbook ist, ist ein Desktop". Naja, wie du willst. Der Begriff "Terminal" stammt übrigens aus dem letzten Jahrhundert, neue Buzzwords für alte Dinge bringen meistens wenig.

Ich wollte aber das Feature haben, dass sich der Kernel erstens um so wenig Initialisationsarbeit wie nötig kümmern muss und vorallem wollte ich den ganzen Nachgucken-Was-Für-Hardware-Vorhanden-Ist Schmarrn aus dem Kernel raus haben.
Ja - nur warum? Am Ende muss jeder Treiber vorher gucken, ob die Hardware da ist. Bei dir macht das der Bootloader, d.h. du hast unglaubliche Schwierigkeiten, später mal auf was anderes umzustellen und du bist auf das festgenagelt, was dein Bootloader kann. Nix saubere Trennung zwischen Bootloader und Kernel, nix Portabilität und vor allem nix gute Interoperabilität mit anderen Betriebssystemen. Das sind natürlich auch Designziele, aber in jedem Fall sind es Folgen von Designentscheidungen.

Wenn ich es mir recht überlege, wenn erik ne lebend-Geburt machen will, muss er das ja auch schon alles im Bootloader machen (mal davon abgesehen, dass er ja nicht unterschiedliche Hardware unterstützen möchte).
Will er auch. Bei einer einzigen Hardwareplattform ist das durchaus möglich, danach zerbricht das System. Siehe die Diskussion zu ARM.

Zitat von: taljeth
Warum in aller Welt sollte ein Mikrokernel dynamisch ladbare Module brauchen? Das ist vollkommen overengineert.
Warum sollte ein Monolith das brauchen oder Programme?
Der klassische Monolith kann das genausowenig wie das klassische Programm. Es bietet aber Vorteile, es bietet Flexibilität. Bei einem Mikrokernel ist das normalerweise schon per Design unnötig, weil die gleiche Funktionalität an anderer Stelle schon bereitgestellt wird. Zwei Lösungen für fast gleiche Probleme an verschiedenen Stellen ist Overengineering.

Ihr habt also einfach das Überprüfen der Abhängigkeiten in eure Module/Programme verlagert (was es so bei mir nicht geben wird, da dass durch die festgelegte Reihenfolge abgefangen wird).
Treiber in einem Mikrokernel-Design werden auch Abhängigkeiten voneinander haben (z.B. ein USB-Tastaturtreiber vom USB-Stack, der USB-Stack vom USB-Hosttreiber, der von PCI oder alternativ der TCP-Treiber von IP, der IP-Treiber von einer Netzwerkkarte/localhost). Wenn du das an einer Stelle ohnehin implementieren musst, dann kannst du diese Lösung auch allgemein an jeder ähnlichen Stelle nutzen.

Um es mal so zu sagen, ihr wollt mir ja einreden, dass die Möglichkeit, dass der User wählen kann verdammt wichtig ist, aber wieso ist sie das? Wieso muss ich das drin haben?
Weil ich Systeme, die mich zum Idioten erklären, nicht leiden kann. Das ist meine Natur, so bin ich, und das kann ich nicht leiden, weil ich mich nicht für einen Idioten halte.

Ich sags mal so, so wie svenska von Linux und BSD geprägt ist, so bin ich besonders von einem OS geprägt (BeOS) und meines Wissens nach, gibt es dort auch nicht wirklich viele Stellschrauben (wenn überhaupt welche, die mit dem vergleichbar sind, was ihr meint) und es hat trotzdem funktioniert und war mMn seiner Zeit voraus!
War OS/2 auch. Beide Systeme sind tot. Wie gesagt: Nichtmal Windows kann auf Stellschrauben verzichten, sie werden nur besser versteckt. (Und bei den Nicht-Serverversionen werden teilweise auch Stellschrauben schlechter eingestellt.)

Gruß,
Svenska
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 07. November 2011, 17:42
Zitat von: svenska
Hä? Ein UMP-Scheduler sollte nur eine CPU benutzen. Also statt den vorhandenen 4 Kernen wird halt nur einer benutzt. Dann braucht es auch kein SMP-fähiges Locking...
Der Scheduler entscheidet aber nicht auf welcher oder wie vielen CPUs er läuft. Der Timer-IRQ-Handler ruft einfach den Scheduler auf, egal welcher vom Bootloader reingelinkt wurde.

Zitat von: svenska
Und es ist größer, aufwändiger und teurer, weil eine allgemeingültige Plattform immer auch Dinge unterstützen muss, die das reale System dann nicht braucht. Beim PC wäre das beispielsweise ein Tastaturcontroller, der auf einem legacy-freien System umständlich emuliert werden muss. Und darum wollen die Hersteller das nicht.
Das läuft auf etwas ähnliches hinaus wie eine andere Diskussion zu ARM. Wir reden also von einer allgemeinen Plattform, nur wer auf Consumer (in Form von Tablets, Desktops, Notebooks und was weiß ich nicht noch alles) abzielt, implementiert diese Plattform, alle anderen halt nicht. Da sehe ich absolut kein Problem. Zumal ich auch nicht soweit gehen würde zu sagen, es soll wie auf dem PC sein. Reicht es nicht, wenn man eine Schnittstelle zur Firmware hat, wo man bestimmte grundlegende Dinge (z.B. ist nen PCI-Bus vorhanden, wenn ja wo ist der gemappt usw.) nachfragen kann. Da sollte es doch auch keine Probleme mit legacy-freien Systemen geben.

Zitat von: svenska
"Alles, was kein Netbook ist, ist ein Desktop"
Naja, so habe ich es ja nun nicht formuliert. Ich sehe aber z.B. keinen Unterschied zw einem Terminal und einem Desktop. Denn dann wäre ein und der selbe Rechner, einmal ein Terminal, weil Benutzer A Sachen macht die da reinfallen und einmal ein Desktop, weil Benutzer B Sachen macht die da reinfallen und das am besten noch ohne nen Neustart machen zu müssen.
Sind dann nicht eigentlich alle Geräte wo ChromeOS drauf läuft per Definition Terminals?

Zitat von: svenska
Ja - nur warum?
Es macht den Kernel einfacher, genauso wie es den Bootloader komplexer macht. Anders herum ist der Kernel komplexer (und größer) und der Bootloader einfacher. Ist das nicht eigentlich Wumpe?

Zitat von: svenska
m Ende muss jeder Treiber vorher gucken, ob die Hardware da ist. Bei dir macht das der Bootloader, d.h. du hast unglaubliche Schwierigkeiten, später mal auf was anderes umzustellen und du bist auf das festgenagelt, was dein Bootloader kann.
Da hast du was falsch verstanden, ich rede nicht von Treibern! Ich rede nur von Modulen die der Kernel benötigt um überhaupt lauffähig zu sein. Treiber laufen ja in ihrem eigenen Prozess.
Es gibt ja nunmal Geräte die sollten (es geht bestimmt auch anders) einfach im Kernel sein und dazu zählt z.B. der Timer. Ich habe im Moment Module für die CPU (UMP oder SMP), die FPU, den IRQ-Chip, Syscall-Mechanismus, Scheduler und den Timer-Chip.

Zitat von: svenska
Nix saubere Trennung zwischen Bootloader und Kernel, nix Portabilität und vor allem nix gute Interoperabilität mit anderen Betriebssystemen.
Wieso sollte ich eine saubere Trennung zw Bootloader und Kernel wollen? Zwecks Portabilität? Was wenn auf der Ziel-Plattform eh kein Bootloader mit den nötigen Eigenschaften existiert?
Mit dem letzten kann ich gar nix anfangen.

Zitat von: svenska
Es bietet aber Vorteile, es bietet Flexibilität. Bei einem Mikrokernel ist das normalerweise schon per Design unnötig, weil die gleiche Funktionalität an anderer Stelle schon bereitgestellt wird.
Naja, zwecks Vorteile mache ich das doch. Auch bei einem MikroKernel (laut deiner Definition) gibt es Situationen wo dynamisch ladbare Module zur Laufzeit sinnvoll sein kann. Das wären z.B. IRQ-Handler, wenn man die im Kernel haben will oder aber FS-Module, wenn man das VFS im Kernel haben will (was dann mMn kein MikroKernel mehr wäre, aber das ist wieder Geschmackssache).

Zitat von: svenska
Weil ich Systeme, die mich zum Idioten erklären, nicht leiden kann. Das ist meine Natur, so bin ich, und das kann ich nicht leiden, weil ich mich nicht für einen Idioten halte.
Dann hast du wirklich ein Problem ;) Also ich lasse mir von einem System nicht sagen, ob ich ein Idiot bin oder nicht :P

Zitat von: svenska
War OS/2 auch. Beide Systeme sind tot.
Soll heißen?

Zitat von: svenska
Wie gesagt: Nichtmal Windows kann auf Stellschrauben verzichten, sie werden nur besser versteckt.
Ist ja richtig, aber wie viele Benutzer müssen da wirklich ran? Und was sind das dann für Benutzer? Ich bin halt der Meinung, mit der Zielgruppe die ich habe, das sowas unnötig ist und nur mehr Code und Komplexität für Flexibilität bedeutet.

Ich bin mir aber auch darüber im Klaren, dass das auch nicht weit von dem entfernt ist was Apple mit iOS macht, sprich totale Bevormundung des Benutzers. Allerdings bin ich auch der Meinung, dass die meisten Benutzer bevormundet werden sollten, im Sinne aller ;) Wir Freaks machen nunmal den geringeren Teil aus.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 07. November 2011, 18:13
Hallo,


Bei mir laufen (bisher) alle IRQ-Handler mit der selben Real-Time-Priorität.
Ja, genau so hatte ich das auch verstanden. Und genau das ist es was ich kritisiere, Prioritäten gibt es nicht umsonst, eben weil manche Dinge wichtiger sind als andere. Der entscheidende Punkt ist dass das nicht schon zum Compile-Zeitpunkt festgelegt werden kann sondern sich erst dynamisch aus der tatsächlichen Nutzung (durch den User) ergibt.

dass immer die Threads mit höchster Priorität laufen und am Ende ihrer Zeitscheibe wieder hinten an die Ready-Liste gepackt werden, wird der MP3-Player entweder nie oder nur zu sporadisch drankommen.
Wenn Dein MP3 Player verhungert dann musst Du ihm eine höhere Priorität geben. Wenn Du im Vordergrund normal arbeiten möchtest (inklusive längerem Compilerlauf) aber die Hintergrundberieselung trotzdem ohne Aussetzer laufen soll dann braucht die Hintergrundberieselung eben eine höhere Priorität, das ist der Grund warum überhaupt Prioritäten eingeführt wurden. Wenn Dein Scheduler anders arbeitet dann musst Du eben andere Stellschrauben verwenden. Du hast doch bestimmt welche vorgesehen, oder?

Der Treiberprogrammierer weiß schon wie wichtig was ist
Woher?

schließlich legt er auch fest welche Priorität der IRQ bekommt.
Nur weil er das für gewöhnlich tut heißt das nicht das der Treiberprogrammierer im Voraus meinen Workload kennt (den kenne ja noch nicht einmal ich selber im Voraus).

Problem auf Empfangsseite ist, dass du die Priorität des direkten IRQ-Handlers nur einmal festlegen kannst (da wo der Treiber sich registriert) und dann kannst du eigentlich keinen Einfluss mehr darauf nehmen.
Also da ist dann das Design fehlerhaft! Warum sollte man die Priorität von IRQs nicht auch zur Laufzeit ändern können? Ich sehe da keinen echten Hinderungsgrund. Außerdem können viele moderne HW-Geräte mehrere IRQs nutzen und die einzelnen Jobs können individuell einen dieser IRQs bekommen wenn sie fertig sind (bei besseren Ethernet-Controllern kann man z.B. einer bestimmten TCP-Verbindung, exakt definiert über beide Sockets, einen extra IRQ zuweisen). Da geht schon so einiges, man muss es nur wollen.

Denn du weißt ja auch nicht wer die Daten dann haben möchte und wie wichtig die nun sind.
Das ist sicher bei vielen Geräten ersteinmal richtig, aber bei einer RS232-Schnittstelle ist es ziemlich eindeutig wer die Daten bekommt und wie wichtig die sind und auch bei den HW-Geräten die für mehrere Programme parallel arbeiten können (wie z.B. ein Ethernet-Controller oder auch ein AHCI-SATA-Controller) gibt es Mittel und Wege dem nachzuhelfen.

Das mit dem kleinen Prioritäts-Boost für Vordergrund-Prozesse ist zwar nützlich aber nicht das was ich meine, dieser Boost ist auf jeden Fall kleiner als wenn der User seinen Programmen bewusst unterschiedliche Prioritäten gibt eben weil er ganz klar definieren will was ihm wichtig ist.

Das die anderen Treiber nicht verhungern, soll ja durch eine entsprechend "kurze" Zeitscheibe gesichert werden.
Trotzdem kann es dann passieren das wichtige Dinge nicht schnell genug fertig werden weil Sie sich die CPU immer wieder mit weniger wichtigen Dingen teilen müssen, deswegen gibt es ja eigentlich Prioritäten.

So kann es also passieren das der User den Scheduler für Single-CPU Systeme ausgewählt hat, aber auf einem SMP-System funktioniert das nicht und das OS wird crashen. Das finde ich eher nicht so toll.
Du hast also im Bootloader etwas Code der entscheidet welcher Scheduler per Default der richtige ist, dann lege doch jeweils noch eine Liste dazu welche Scheduler in der gegebenen Situation überhaupt möglich sind (klar kann ein Single-CPU-Scheduler nicht auf einem SMP-System arbeiten) und wenn der User eine Vorgabe machen will dann lass den Boot-Loader gegen diese Liste vergleichen und wenn der Wunsch des Users nicht auf der Liste steht dann nimm einfach das Default und gib eine aussagekräftige Fehlermeldung aus. Sorry, aber ich sehe auch da kein Problem.

Zur Laufzeit umstellen will ich mir nicht antun.
Das verlangt doch auch keiner.

Das Du Deinen Kernel erst zur Boot-Zeit zusammenlinken möchtest finde ich gar nicht mal so schlecht (ich bestreite auch nicht das Dein Kernel immer noch ein Micro-Kernel ist weil Du ja nur die wirklich elementaren Dinge in den Kernel linkst und der Rest trotzdem normale User-Mode-Prozesse sind), das spart Dir immerhin indirekte Calls. Ich frage mich nur ob dieser (sicher recht kleine) Performance-Vorteil diesen ganzen Aufwand den Du da treibst wirklich wert ist.


Meinen Kernel linke ich nicht zur Boot-Zeit zusammen, das wäre auch nahezu unmöglich da ich noch auf Assembler-Quell-Text-Ebene linke (mein Linker ist also ne Art Text-Merger) und erst danach der Code-Generator kommt der den tatsächlichen Binär-Code für die CPU generiert und all das möchte ich nicht im Boot-Code haben (schon allein weil ich dort recht enge Platzverhältnisse habe und auch kaum eine gescheite libc anbieten kann, was alles gegen meinen in C++ geschriebenen Code-Generator im Boot-Code spricht). Die Lebendgeburt meines OS bezieht sich darauf das ich in dem Moment wo der Kernel zum Leben erwacht bereits mehrere laufende Prozesse habe, ich lege also noch vom Boot-Code aus die nötigen Datenstrukturen für den Kernel so an das er sieht das er bereits 3 Prozesse mit insgesamt X Threads (der idle-Prozess hat so viele Threads wie CPUs vorhanden sind) hat. Die ersten 3 Prozesse werden also nicht durch den CreateProzess()-Syscall erstellt nachdem der Kernel bereits läuft sondern die sind schon da wenn der Kernel zum Leben erwacht. Dazu muss der Boot-Code natürlich ganz genau wissen wie die Datenstrukturen des Kernels aussehen, ich binde also in den Boot-Code auch einige Teile des Kernel-Quell-Codes ein. Dass das von taljeth eventuell das K-Attribut bekommt nehme ich in Kauf, weil auch ich eine möglichst hohe Performance möchte und mein Kernel keinen Code enthalten soll den er nicht auch zur Laufzeit benötigt. Mein Kernel ist eigentlich nur eine Ansammlung von passiven Exception/IRQ/Syscall-Handlern und hat gar keine aktive main()-Funktion oder etwas vergleichbares, mein Kernel hat nicht mal eine init-Funktion, das muss alles durch den Boot-Code erledigt werden. Das bedeutet zwar das der Kernel und der Boot-Code immer exakt zusammen passen müssen aber da der Kernel eh als Binär-Klumpen mit in den Boot-Code integriert (quasi als rodata-Sektion mit reingelinkt) werden muss ist das auch kein Problem wenn nach dem compilieren des Kernels auch der Boot-Code neu compiliert werden muss. Mein Boot-Code richtet im übrigen keine normalen HW-Geräte ein, er fasst nicht mal den IRQ-Controller an (außer das er dort die physischen Adressen der IRQ-Handler-Funktionen vom Kernel konfiguriert), er richtet nur den physischen RAM ein und aktiviert alle CPUs (und legt auf jede CPU einen der idle-Threads), mein Boot-Code kümmert sich also nur um den absoluten Kernbereich der Plattform. Das Einrichten der restlichen HW-Geräte, also alles was PCI und dahinter ist, wird erst zur Laufzeit durch die normalen User-Mode-Prozesse gemacht.


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 07. November 2011, 19:00
Zitat von: erik
Also da ist dann das Design fehlerhaft! Warum sollte man die Priorität von IRQs nicht auch zur Laufzeit ändern können? Ich sehe da keinen echten Hinderungsgrund.
Also sind meines Wissens nach alle "wichtigen" OS´e vom Design her fehlerhaft? Mir wäre nämlich nicht bewusst, dass die IRQ-Prioritäten zur Laufzeit dynamisch geändert werden. Das ist auch ein "wenig" aufwendig. (Mir dämmert, gleich kommt jemand und sagt mir mit meinem Boottime-linken ist es genauso  :roll:)

Wenn man damit leben kann, dass das nicht immer funktioniert, wäre es mit ein "wenig" Aufwand möglich. Es kann nicht immer funktionieren, weil ich ja bei nem PIC die Prioritäten gar nicht ändern kann und bei nem IO-APIC geht das zwar, aber es kann ja sein, dass ich keinen INT-Vektor mehr für diese Priorität frei habe.
Ansonsten besteht der Aufwand darin, dass ich nen freien INT-Vektor suchen muss und ich muss diesen dann im IO-APIC wechseln. Bei exklusiven Geräte wäre das wahrscheinlich noch in Ordnung, aber bei gesharten Geräten, weiß ich ersten nicht für welchen Prozess die Daten sind (ist doch bei USB so oder?) und der Aufwand für das ständige Umstellen des Vektors dürfte auch nicht so toll sein.

Um dein Bsp. mit der RS232 Schnittstelle zu nehmen. Wir reden da von vllt 16bytes die aus dem FIFO Buffer gelesen werden. Wieso sollte das so schlimm sein?

Zitat von: erik
Außerdem können viele moderne HW-Geräte mehrere IRQs nutzen und die einzelnen Jobs können individuell einen dieser IRQs bekommen wenn sie fertig sind (bei besseren Ethernet-Controllern kann man z.B. einer bestimmten TCP-Verbindung, exakt definiert über beide Sockets, einen extra IRQ zuweisen). Da geht schon so einiges, man muss es nur wollen.
Bzw. wissen, mir war das bisher nicht bekannt.

Zitat von: erik
Trotzdem kann es dann passieren das wichtige Dinge nicht schnell genug fertig werden weil Sie sich die CPU immer wieder mit weniger wichtigen Dingen teilen müssen, deswegen gibt es ja eigentlich Prioritäten.
Da hast du schon recht, aber ich bin bisher immernoch der Meinung, dass ein IRQ-Handler doch nicht wirklich viel macht oder? Wie lange kann sowas schon dauern? (ich weiß es nicht, ob da nicht Sachen dabei sind, die wirklich "lange" dauern)

Zitat von: erik
Du hast also im Bootloader etwas Code der entscheidet welcher Scheduler per Default der richtige ist, dann lege doch jeweils noch eine Liste dazu welche Scheduler in der gegebenen Situation überhaupt möglich sind (klar kann ein Single-CPU-Scheduler nicht auf einem SMP-System arbeiten) und wenn der User eine Vorgabe machen will dann lass den Boot-Loader gegen diese Liste vergleichen und wenn der Wunsch des Users nicht auf der Liste steht dann nimm einfach das Default und gib eine aussagekräftige Fehlermeldung aus. Sorry, aber ich sehe auch da kein Problem.
Das ist die erste Lösung die mir gefällt bzw. auf mein konkretes Problem eingeht ;)

Zitat von: erik
Ich frage mich nur ob dieser (sicher recht kleine) Performance-Vorteil diesen ganzen Aufwand den Du da treibst wirklich wert ist.
Svenska meinte doch das Cache-Misses heutzutage verdammt teuer sind. Also entweder die sind teuer und es lohnt sich diese zu vermeiden oder nicht. Was für nen Aufwand meinst du konkret? Die Überprüfung welche Hardware vorhanden ist und welcher Code genommen wird, den habe ich so oder so. Der "Linker" den müsste ich nicht unbedingt haben, aber ganz ehrlich, das ist gar nicht so viel Code. Ne Symboltabelle erzeuge ich auch so oder so (die benutze ich für Stack-Traces). Was aber stimmt, ist dass das Linken relativ "zeitaufwendig" ist. Allerdings relativert sich diese Zeit mit der Laufzeit des OS.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 07. November 2011, 21:17
Hallo,


Also sind meines Wissens nach alle "wichtigen" OS´e vom Design her fehlerhaft?
Also wenn Du das so siehst dann eindeutig ja. Obwohl ich mir sicher bin das Linux (und andere OSe die MSI(-X) anständig unterstützen) z.B. bei den AHCI-SATA-Controllern mehrere IRQs nutzen können und zumindest theoretisch diesen auch unterschiedliche Prioritäten geben können, üblicherweise wird das aber benutzt um jeden dieser IRQs fest einer bestimmten CPU zuzuordnen damit dann der IRQ-Handler auch möglichst auf der CPU läuft von welcher auch der Job kam dessen Commit der IRQ signalisiert.

Das ist auch ein "wenig" aufwendig.
Wie Aufwändig das ist hängt natürlich vom IRQ-Controller ab aber die modernen APICs der x86-Plattform sollten das eigentlich recht einfach können. Beim IRQ-Controller auf meiner Plattform ist das auch ganz einfach. Beim klassischen PIC geht das natürlich gar nicht aber den ziehe ich ja auch nicht als Referenz heran.

Um dein Bsp. mit der RS232 Schnittstelle zu nehmen. Wir reden da von vllt 16bytes die aus dem FIFO Buffer gelesen werden. Wieso sollte das so schlimm sein?
Das trifft nur auf 16550 kompatible UARTs zu, bei den 16750 sind es schon 64 Bytes und beim 16950 sind es 256 Bytes (beide mit individuell frei konfigurierbaren Schwellen für Senden und Empfangen). Nebst dessen das dort immer nur ein Byte nach dem anderen mit elend langsamen I/O-Port-Zugriffen transferiert werden kann, ich schätze in der Zeit wo auch nur 10 Bytes aus so einem UART (der am Ende hinter 2 PCI-Bussen und dann noch an einem LPC hängt, also wo schon allein der Weg eine lange Latenz hat) geholt werden können könnte ein moderner Core-i? oder Bulldozzer mindestens 100kB Daten im RAM kopieren. Diese I/O-Port-Zugriffe werden nämlich immer rein sequenziell abgearbeitet (könnte ja theoretisch ein A20-Gate bei manipuliert werden) und die CPU verarbeitet auch keine anderen Befehle während dessen, das ist einer der Gründe warum die PCI-Spec fordert das neue/native PCIe-Geräte jede Ressource als Speicher anbieten müssen.

Bzw. wissen, mir war das bisher nicht bekannt.
Naja, ich hätte jetzt schon erwartet das Du Dich als OS-Entwickler mit sowas mal beschäftigst. ;)

Wie lange kann sowas schon dauern? (ich weiß es nicht, ob da nicht Sachen dabei sind, die wirklich "lange" dauern)
Ich weiß auch nicht wie lange ein IRQ-Handler im Worst-Case so brauchen kann aber ich denke dass das OS-Design auch mit wirklich worstigen Worst-Cases anständig umgehen können muss.

Das ist die erste Lösung die mir gefällt bzw. auf mein konkretes Problem eingeht ;)
Gerne, dafür bin ich doch da. Außerdem bin ich der Meinung das Svenska und taljeth nun genug auf Deiner Idee rumgehackt haben. Klar ist das mit dem Linken zur Boot-Zeit ein wenig strange aber eine bessere Lösung gibt es IMHO nicht wenn man keine unnütze Indirektion haben will. Mir persönlich wäre das wohl zu aufwendig aber wenn Du das machen willst dann nur zu. Wie taljeth ja selber geschrieben hat, es ist Dein OS und da darfst Du natürlich auch mal neue Wege ausprobieren. Wie groß der Performance-Gewinn wirklich ist kann ich nur schwer abschätzen, die Funktions-Pointer dürften (zumindest bei den häufig benutzen Kernel-Komponenten, ich benutze extra nicht Module weil es IMHO keine Module sind) auch alle im Cache sein so das der Overhead wohl nicht allzu groß ist. Wer es genau wissen will wird wohl um eine ordentliche Performance-Messung nicht umhin kommen. Ein ähnliches Problem haben ja auch virtuelle Methoden in C++-Klassen, auch dort kostet das etwas Performance aber das ist wohl in den meisten Fällen sehr wenig. Über die Boot-Zeit des OS würde ich mir an Deiner Stelle nicht so arg viele Sorgen machen, da dürfte ein besseres BIOS sicher mehr Potential bieten.


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 07. November 2011, 21:48
Zitat von: erik
Obwohl ich mir sicher bin das Linux (und andere OSe die MSI(-X) anständig unterstützen) z.B. bei den AHCI-SATA-Controllern mehrere IRQs nutzen können und zumindest theoretisch diesen auch unterschiedliche Prioritäten geben können, üblicherweise wird das aber benutzt um jeden dieser IRQs fest einer bestimmten CPU zuzuordnen damit dann der IRQ-Handler auch möglichst auf der CPU läuft von welcher auch der Job kam dessen Commit der IRQ signalisiert.
Es mag ja sein, dass sie den verschiedenen IRQs verschiedene Prioritäten geben, aber ich denke nicht dass sie sie zur Laufzeit dynamisch ändern.

Zitat von: erik
Wie Aufwändig das ist hängt natürlich vom IRQ-Controller ab aber die modernen APICs der x86-Plattform sollten das eigentlich recht einfach können.
Ich habe keine Ahnung an welchem Takt so ein IO-APIC hängt, wären pro IRQ 4 32bit Zugriffe (2x lesen und dann 2x schreiben). Ich denke aber mal fast das aufwendigste wäre sich nen neuen INT-Vektor zu holen und den alten gegebenenfalls (wenn man denn nen neuen bekommen hat) freizugeben.

Zitat von: erik
Das trifft nur auf 16550 kompatible UARTs zu, bei den 16750 sind es schon 64 Bytes und beim 16950 sind es 256 Bytes (beide mit individuell frei konfigurierbaren Schwellen für Senden und Empfangen). Nebst dessen das dort immer nur ein Byte nach dem anderen mit elend langsamen I/O-Port-Zugriffen transferiert werden kann, ich schätze in der Zeit wo auch nur 10 Bytes aus so einem UART (der am Ende hinter 2 PCI-Bussen und dann noch an einem LPC hängt, also wo schon allein der Weg eine lange Latenz hat) geholt werden können könnte ein moderner Core-i? oder Bulldozzer mindestens 100kB Daten im RAM kopieren.
Das ist doch mal nen Wort, hätte nicht gedacht dass das so langsam ist.

Wobei ich denke, dass RS232 vllt nen schlechtes Bsp ist. Denn mir fällt gerade nix ein, wo man das nutzt und dann nicht gerade die Anwendung die wichtigste ist (dir doch bestimmt ;)).

Zitat von: erik
Naja, ich hätte jetzt schon erwartet das Du Dich als OS-Entwickler mit sowas mal beschäftigst.
Ich weiß du siehst das anders, aber ich muss erstmal dahin kommen und um das zu schaffen, muss ich auch motiviert sein und Spaß dabei haben und das ist nicht der Fall wenn ich ganz viele technische Dokumentationen lese, die ich wahrscheinlich eh nicht verstehe, weil mir einfach die Zusammenhänge fehlen. Das kommt dann (leider) erst wenn es soweit ist, dass ich dafür konkreten Code schreibe.

Zitat von: erik
Außerdem bin ich der Meinung das Svenska und taljeth nun genug auf Deiner Idee rumgehackt haben. Klar ist das mit dem Linken zur Boot-Zeit ein wenig strange aber eine bessere Lösung gibt es IMHO nicht wenn man keine unnütze Indirektion haben will.
Mit dem rumhacken habe ich erstmal kein Problem. Es ist halt nur so, dass ich mir gewisse Features in den Kopf gesetzt habe und da hilft es nix, wenn man ständig die Antwort auf ein Problem bekommt, nutz halt was anderes (was du übrigens auch oft mit deinen Segmenten gemacht hast ;)).

Jedes Design hat so seine Schwächen und damit muss man leben, wenn man ganz bestimmte Sachen will/braucht.

Zitat von: erik
Wie groß der Performance-Gewinn wirklich ist kann ich nur schwer abschätzen, die Funktions-Pointer dürften (zumindest bei den häufig benutzen Kernel-Komponenten, ich benutze extra nicht Module weil es IMHO keine Module sind) auch alle im Cache sein so das der Overhead wohl nicht allzu groß ist.
Wenn dem so ist, dann macht es aber auch keinen Sinn, sich über den Cacheinhalt im Zusammenhang mit Threads nen Kopf zu machen. Entweder es ist wichtig oder es ist nicht wichtig. Wie intelligent sind denn die Verdrängungsstrategien? Zumal man einfach nix dagegen machen kann, wenn der User Cache-Trashing betreibt.

Zumal ich finde, so sieht der Code auch schöner aus und ist lesbarer, hat ja auch Auswirkungen auf die Wartbarkeit (leider ;)).

Zitat von: erik
Ein ähnliches Problem haben ja auch virtuelle Methoden in C++-Klassen, auch dort kostet das etwas Performance aber das ist wohl in den meisten Fällen sehr wenig.
Was übrigens ein sehr interessantes Thema ist. Normalerweise wird ja zu jedem Objekt noch nen Pointer mitgeführt über den man rausbekommt was für ein Typ das ist. Da ich aber keine RTTI nutze, müsste der Compiler es relativ gut optimieren können und nur da wo die entsprechende Methode genutzt werden nen Pointer mitgeben. Es gab da auch mal nen Paper zu wie gut bestimmte Compiler sowas optimieren können.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: Svenska am 07. November 2011, 22:20
Das läuft auf etwas ähnliches hinaus wie eine andere Diskussion zu ARM. Wir reden also von einer allgemeinen Plattform, nur wer auf Consumer (in Form von Tablets, Desktops, Notebooks und was weiß ich nicht noch alles) abzielt, implementiert diese Plattform, alle anderen halt nicht.
Dann hast du in dieser Diskussion genau garnichts gewonnen. Du zielst nämlich nicht auf das Problem, sondern auf das Symptom ab.

Reicht es nicht, wenn man eine Schnittstelle zur Firmware hat, wo man bestimmte grundlegende Dinge (z.B. ist nen PCI-Bus vorhanden, wenn ja wo ist der gemappt usw.) nachfragen kann. Da sollte es doch auch keine Probleme mit legacy-freien Systemen geben.
Gibt's schon, nennt sich Flatted Device Tree. Wird nur nicht überall implementiert, insbesondere nicht bei irgendwelchen komischen In-House-Bootloadern und selbstverständlich nicht bei Windows CE. Es handelt sich schließlich um eine Linux-Technologie.

Sind dann nicht eigentlich alle Geräte wo ChromeOS drauf läuft per Definition Terminals?
Wenn man "Webbrowsing" als Tätigkeit auffasst nicht, denn das tust du lokal. Im Gegensatz zu Terminals, die mehr oder weniger dumm sind und wo deine Aktivitäten auf dem Server ausgeführt werden.

Aber ja, die Grenze ist nicht besonders hart, denn es gibt auch Terminals, die einige Anwendungen lokal vorhalten. Bleibt der Unterschied, dass (echte) Terminals oft diskless sind oder nur mit wenig Flash ausgestattet und insbesondere den Anwendungen keinen lokalen Speicher zur Verfügung stellen. Es sei denn, man schließt USB-Sticks o.ä. an.

Es gibt ja nunmal Geräte die sollten (es geht bestimmt auch anders) einfach im Kernel sein und dazu zählt z.B. der Timer.
Ja. Und je nach Mainboard, CPU oder BIOS-Implementation sind manche Timer funktionsfähig oder eben auch nicht. Deswegen halte ich den Kernel für die richtige Stelle, den entsprechenden Timer auszusuchen.

Andere Frage: Du hast ein Modul für den zu verwendenden Syscall-Mechanismus. Du unterstützt also mehrere davon. Gibt es ein Fallback, d.h. funktioniert "int 0x80" immer oder nur, wenn die CPU weder SYSENTER noch SYSCALL kann?

Zitat von: svenska
Nix saubere Trennung zwischen Bootloader und Kernel, nix Portabilität und vor allem nix gute Interoperabilität mit anderen Betriebssystemen.
Wieso sollte ich eine saubere Trennung zw Bootloader und Kernel wollen? Zwecks Portabilität? Was wenn auf der Ziel-Plattform eh kein Bootloader mit den nötigen Eigenschaften existiert?
Mit dem letzten kann ich gar nix anfangen.
Auf jeder Plattform existiert ein Bootloader, der eine (oder mehrere, notfalls als Archiv) Dateien in den Speicher laden und reinspringen kann. Aber ein Bootloader, der speziell dein Konfigurationsdateiformat und deine Modulstrukturen unterstützt, ist eher selten. Besonders, wenn er zusätzlich noch die Hardware initialisieren muss (was RedBoot und U-Boot können, dein Bootloader muss für jedes einzelne Board angepasst werden; außerdem gibt es sog. Hardware-Quirks je nach Board-Revision).

Wenn jedes Betriebssystem auf dem Rechner von z.B. GRUB geladen werden kann, erhöht das die Interoperabilität verschiedener Betriebssysteme auf einem Rechner ungemein. Sieht man vor allem daran, wenn man Windows neu installiert. Alternative Bootloader sind dann weg. Das ist dein Wunschweg?

Auch bei einem MikroKernel (laut deiner Definition) gibt es Situationen wo dynamisch ladbare Module zur Laufzeit sinnvoll sein kann. Das wären z.B. IRQ-Handler, wenn man die im Kernel haben will oder aber FS-Module, wenn man das VFS im Kernel haben will (was dann mMn kein MikroKernel mehr wäre, aber das ist wieder Geschmackssache).
Dynamisch ladbare Module auf einem Mikrokernel halte ich generell für unnötig, aber vielleicht sehen andere das anders. In meinem Konzept ist der IRQ-Handler vollständig im Kernel, wenn ein IRQ auftritt, wird ein Event an den für diesen IRQ registrierten Treiber geschickt und der schnellstmöglich scheduled. Treiber laufen niemals im IRQ-Kontext. Mein VFS kennt das Konzept einer "Datei" und ein Pseudo-Dateisystem (so ne Mischung aus debugfs, procfs, sysfs oder kernfs), was aber nur die interne Struktur des VFS an sich exportiert. Dateisysteme bleiben Module.

Zitat von: svenska
Weil ich Systeme, die mich zum Idioten erklären, nicht leiden kann. Das ist meine Natur, so bin ich, und das kann ich nicht leiden, weil ich mich nicht für einen Idioten halte.
Dann hast du wirklich ein Problem ;) Also ich lasse mir von einem System nicht sagen, ob ich ein Idiot bin oder nicht :P
Du möchtest aber ein System programmieren, was den Benutzer prinzipiell zum Idioten erklärt, denn er kann ja nichts daran ändern. ;-)

Zitat von: svenska
Wie gesagt: Nichtmal Windows kann auf Stellschrauben verzichten, sie werden nur besser versteckt.
Ist ja richtig, aber wie viele Benutzer müssen da wirklich ran?
Ich war da öfter drin. Wenn man mit Settop-Boxen zu tun hat (Firmware-Updates oder Streamen des Fernsehsignals - also normale Benutzung), dann gibt es einige Tweaks, die diese Funktionalität erst ermöglichen, weil der Netzwerkstack in den normalen Einstellungen (und den Endnutzer-Windows-Versionen) für diese Lasten nicht ausreichend performt. Dazu muss man kein Guru sein, da gibt es ausgefeilte Scripte und Programme für, die dann ein paar Registry-Einträge verändern, das kriegt auch jeder Depp hin. (Bei Windows-Server sind das die Standardeinstellungen.) Und danach kann man vom DVB-T-Tuner auf eine Windows-Freigabe aufnehmen. Gäbe es die Stellschraube nicht, ginge es nie.

Und was sind das dann für Benutzer? Ich bin halt der Meinung, mit der Zielgruppe die ich habe, das sowas unnötig ist und nur mehr Code und Komplexität für Flexibilität bedeutet.
Das sind Benutzer, die einen Computer für Dinge einsetzen wollen, die der Betriebssystementwickler nicht vorgesehen hat. Wenn deine Zielgruppe nur aus Kindern und ihren Lernprogrammen sowie den Eltern mit dem Browser und Office-Paket besteht, dann hast du vielleicht Recht. Aber auch Familienväter könnten mal das Fernsehprogramm aufzeichnen wollen...

Ich bin mir aber auch darüber im Klaren, dass das auch nicht weit von dem entfernt ist was Apple mit iOS macht, sprich totale Bevormundung des Benutzers. Allerdings bin ich auch der Meinung, dass die meisten Benutzer bevormundet werden sollten, im Sinne aller ;) Wir Freaks machen nunmal den geringeren Teil aus.
Damit betrachte ich die Diskussion für beendet. Implementiere eine Whitelist für Anwendungen. Ach so, und bitte führe die Diktatur wieder ein. Freiheit ist überbewertet.

Gruß,
Svenska,
der grad echt enttäuscht von dir ist.

[PS: Beim Absenden hab ich grad wieder einen HTTP 503 bekommen.]
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 07. November 2011, 22:49
Zitat von: svenska
Dann hast du in dieser Diskussion genau garnichts gewonnen. Du zielst nämlich nicht auf das Problem, sondern auf das Symptom ab.
Wir waren uns doch aber einig, dass auf den meisten MikroKontrollern eine solche Firmware nix verloren hat. Das ist dann halt ne andere Plattform. Problem wird sein es den Herstellern schmackhaft zu machen und MS ist da mMn der richtige für (ob das nun gut ist oder nicht, ist ja erstmal egal). Die haben einfach genug Marktmacht um sowas auch mal durchzudrücken.

Zitat von: svenska
Ja. Und je nach Mainboard, CPU oder BIOS-Implementation sind manche Timer funktionsfähig oder eben auch nicht. Deswegen halte ich den Kernel für die richtige Stelle, den entsprechenden Timer auszusuchen.
Naja, aber den Code will ich ja gerade nicht im Kernel haben.

Zitat von: svenska
Andere Frage: Du hast ein Modul für den zu verwendenden Syscall-Mechanismus. Du unterstützt also mehrere davon. Gibt es ein Fallback, d.h. funktioniert "int 0x80" immer oder nur, wenn die CPU weder SYSENTER noch SYSCALL kann?
Ich gehe (den deiner Meinung nach falschen) radikalen Weg und es wird dir vorgeschrieben wie du in den Kernel kommst. Es muss immer eine Funktion aufgerufen werden (was hinter Library-Calls versteckt wird) und die hat immer den Code für die optimalste Variante. So ist sicher gestellt das alle Programme auf allen CPUs laufen ohne dass das Programm sowas vorher kontrollieren müsste.

Wenn du trotzdem auf eigene Faust in den Kernel willst, wird es dann vermutlich nach dem Zurückspringen in den UserMode krachen.

Zitat von: svenska
Auf jeder Plattform existiert ein Bootloader, der eine (oder mehrere, notfalls als Archiv) Dateien in den Speicher laden und reinspringen kann. Aber ein Bootloader, der speziell dein Konfigurationsdateiformat und deine Modulstrukturen unterstützt, ist eher selten. Besonders, wenn er zusätzlich noch die Hardware initialisieren muss (was RedBoot und U-Boot können, dein Bootloader muss für jedes einzelne Board angepasst werden; außerdem gibt es sog. Hardware-Quirks je nach Board-Revision).

Wenn jedes Betriebssystem auf dem Rechner von z.B. GRUB geladen werden kann, erhöht das die Interoperabilität verschiedener Betriebssysteme auf einem Rechner ungemein. Sieht man vor allem daran, wenn man Windows neu installiert. Alternative Bootloader sind dann weg. Das ist dein Wunschweg?
GRUB kann doch meinen BootloaderBootsektor laden. Zumal ich ja auf anderen Architekturen (wenn es zu umständlich wird, was ohne eine vernünftige Plattform der Fall ist) einfach den Weg der 3.-Stage gehen kann.
Auch Windows kann von GRUB geladen werden, also da sehe ich kein Problem. Das Windows den vorhandenen Bootloader rauskickt ist nen ganz anderes Thema und durchaus ein Problem.

Zitat von: svenska
In meinem Konzept ist der IRQ-Handler vollständig im Kernel, wenn ein IRQ auftritt, wird ein Event an den für diesen IRQ registrierten Treiber geschickt und der schnellstmöglich scheduled. Treiber laufen niemals im IRQ-Kontext.
Ich möchte meinen das einige den "kurzen" IRQ-Handler direkt ausführen (ist dort ein Modul des Treibers). Den Weg hatte ich auch mal ganz am Anfang geplant, aber ich habe mich dann dazu entschieden jeden zur Laufzeit hinzufügbaren Code rauszuschmeißen.

Zitat von: svenska
Du möchtest aber ein System programmieren, was den Benutzer prinzipiell zum Idioten erklärt, denn er kann ja nichts daran ändern.
Sagen wir mal, am Kernel möchte ich den User nicht rumspielen lassen. Was den UserSpace betrifft (und damit ja eigentlich alle Services), ist das schon wieder was anderes.

Zitat von: svenska
Wenn man mit Settop-Boxen zu tun hat (Firmware-Updates oder Streamen des Fernsehsignals - also normale Benutzung), dann gibt es einige Tweaks, die diese Funktionalität erst ermöglichen, weil der Netzwerkstack in den normalen Einstellungen (und den Endnutzer-Windows-Versionen) für diese Lasten nicht ausreichend performt.
Da sehe ich jetzt wieder nicht so das Problem, da der Netzwerkstack erstmal nicht so wichtig ist um das System zum Reagieren zu bringen.

Machen wir es kurz, auf den Kernel wirst du bei meinem OS keinen Einfluss nehmen können, schon gar nicht zur Laufzeit.

Zitat von: svenska
Damit betrachte ich die Diskussion für beendet. Implementiere eine Whitelist für Anwendungen. Ach so, und bitte führe die Diktatur wieder ein. Freiheit ist überbewertet.
Wow, der Mensch hat sich in seiner ganzen Lebensgeschichte nicht geändert, sieht immernoch nur schwarz oder weiß ;) Ich bin durchaus der Meinung das der Otto-Normal-Benutzer vor sich selbst beschützt werden sollte. Wozu sonst z.B. irgendwelche Sicherheitsaspekte in das System einbauen, ist ja auch ne Art der Bevormundung.

Und nur mal am Rande, Diktatur hat auch so seine Vorteile, auch wenn man sowas gerade in Deutschland nicht hören will.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 08. November 2011, 20:12
Hallo,


Es gibt ja nunmal Geräte die sollten (es geht bestimmt auch anders) einfach im Kernel sein und dazu zählt z.B. der Timer.
Ja. Und je nach Mainboard, CPU oder BIOS-Implementation sind manche Timer funktionsfähig oder eben auch nicht. Deswegen halte ich den Kernel für die richtige Stelle, den entsprechenden Timer auszusuchen.
Ich finde der Kernel ist eben gerade nicht der richtige Ort um den Kernel je nach vorhandener Hardware zu konfigurieren/initialisieren, das darf ruhig ein vorgelagertes Lade-System machen. Natürlich bedeutet dass das dann beide Komponenten zwingenst zusammenpassen müssen (ist bei meinem System ja nicht anders) aber auch darin sehe ich absolut kein Problem. Ich denke FlashBurn's Fehler ist das er aus seiner Kernel-Initialisierung gleich noch einen Boot-Loader gemacht hat, vielleicht wäre es ein besserer Weg wenn er die Boot-Loader-Funktionalität (laden aller Dateien von HDD usw.) von GRUB o.ä. erledigen lässt und sich darauf konzentriert seinen Kernel passend zur vorhandenen Hardware zu initialisieren/zusammenzusetzen.

Andere Frage: Du hast ein Modul für den zu verwendenden Syscall-Mechanismus. Du unterstützt also mehrere davon. Gibt es ein Fallback, d.h. funktioniert "int 0x80" immer oder nur, wenn die CPU weder SYSENTER noch SYSCALL kann?
Wozu ein Fallback wenn er sicherstellen kann das alle normalen Programme wie gewünscht funktionieren? Klar, ich würde vermutlich auch einen Fallback einbauen, aber hat nicht auch FlashBurn recht wenn er dagegen hält dass das unnütze Speicherverschwendung ist?

In meinem Konzept ist der IRQ-Handler vollständig im Kernel, wenn ein IRQ auftritt, wird ein Event an den für diesen IRQ registrierten Treiber geschickt und der schnellstmöglich scheduled. Treiber laufen niemals im IRQ-Kontext.
Da sind wir Drei uns doch eigentlich einig. Der Grund warum FlashBurn dafür verschiedene Module (mit nach außen identischer Funktionalität) haben möchte liegt doch darin begründet das die x86-Plattform ziemlich unterschiedliche IRQ-Controller hat (die sind vor allem so arg unterschiedlich das es nicht reichen würde nur an ein paar Stellen im Quell-Code eine winzige Verzweigung o.ä. einzubauen). FlashBurn hat da genau 2 Möglichkeiten: entweder er untersützt nur eine Sorte IRQ-Controller und verliert dabei erheblich an Funktionalität (beim alten PIC) bzw. kann nur die neuen PCs unterstützen (beim neuen APIC) oder er unterstützt andererseits alle IRQ-Controller. FlashBurn hat sich für letzteres entschieden und ich persönlich finde seine Entscheidung, aber auch seine Lösung für die daraus resultierenden Probleme, gut. Klar mag das auch etwas seinem Naturell, möglich komplexe Lösungen zu bevorzugen, entsprechen aber so ist er nun mal, das wird keiner von uns ändern.

Und was das Thema Stellschrauben angeht: FlashBurn kennt doch jetzt einen Weg wie er seinen Automatismus mit einer Stellschraube versehen kann, lassen wir ihn das doch erst mal implementieren und meckern dann weiter.


Es mag ja sein, dass sie den verschiedenen IRQs verschiedene Prioritäten geben, aber ich denke nicht dass sie sie zur Laufzeit dynamisch ändern.
Vermutlich nicht, aber zum einen könnte man das sicher einbauen und zum anderen kann ein HW-Gerät ja mehrere IRQs haben die dann fest unterschiedliche Prioritäten haben und es wird für jeden Job ein passender IRQ ausgewählt.

Ich habe keine Ahnung an welchem Takt so ein IO-APIC hängt, wären pro IRQ 4 32bit Zugriffe (2x lesen und dann 2x schreiben).
Das Ändern einer IRQ-Priorität sollte bei den APICs auf einen einzigen Schreibzugriff hinaus laufen.

Ich denke aber mal fast das aufwendigste wäre sich nen neuen INT-Vektor zu holen und den alten gegebenenfalls (wenn man denn nen neuen bekommen hat) freizugeben.
Wieso willst Du neue INT-Vektoren zuweisen? Das ist doch Quatsch und sicher auch gar nicht so einfach machbar zur Laufzeit, es müsste ja auch atomar durchgeführt werden und da dürften die üblichen HW-Geräte nicht so einfach mitspielen bzw. man bräuchte entsprechende Unterstützung durch die Treiber.

hätte nicht gedacht dass das so langsam ist.
Doch, I/O-Port sind extrem langsam, im Verhältnis zum Speicher. Deswegen unterstützt ja auch kaum eine andere Plattform I/O-Ports, die meisten kennen nur Speicher.

Wobei ich denke, dass RS232 vllt nen schlechtes Bsp ist. Denn mir fällt gerade nix ein, wo man das nutzt und dann nicht gerade die Anwendung die wichtigste ist (dir doch bestimmt ;)).
Viele ältere ISDN-Anlagen werden per RS232 konfiguriert und das ist meistens nicht so wichtig wie der MP3-Player im Hintergrund (ist doch egal ob die Konfiguration 2,0 Sekunden oder 2,1 Sekunden benötigt). Auch gibt es immer noch recht viele managed Ethernet-Switches mit ner RS232-Konsole (da muss der Admin zwar persönlich und mit Laptop vor Ort sein aber dafür funktioniert die wenigstens immer, selbst wenn das LAN mal komplett tot oder kaputt-konfiguriert ist), dem Admin dürfte der MP3-Player im Hintergrund auch wichtig genug sein das dieser eine höhere Priorität bekommt als das Terminal-Programm (wenn der Admin ne hundertstel Sekunde länger braucht ist das schließlich immer noch bezahlte Arbeitszeit).

und da hilft es nix, wenn man ständig die Antwort auf ein Problem bekommt, nutz halt was anderes (was du übrigens auch oft mit deinen Segmenten gemacht hast ;)).
Ja, ich weiß, meine Antworten sind auch nicht immer sehr hilfreich. ;)
Jeder hat eben so seine Vorstellungen und sieht auch ganz spezielle Vorteile darin, da kommt dann eben auch mal eine Antwort in der Art "is doch gar kein Problem, mach das so wie ich und schon ist alles in Butter".


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 08. November 2011, 20:37
Zitat von: erik
Ich denke FlashBurn's Fehler ist das er aus seiner Kernel-Initialisierung gleich noch einen Boot-Loader gemacht hat, vielleicht wäre es ein besserer Weg wenn er die Boot-Loader-Funktionalität (laden aller Dateien von HDD usw.) von GRUB o.ä. erledigen lässt und sich darauf konzentriert seinen Kernel passend zur vorhandenen Hardware zu initialisieren/zusammenzusetzen.
Das könnte ich auch immernoch machen, aber mich hat es halt auch interessiert wie sowas funktioniert, wie man ein allgemeines Interface anbietet (einmal um eine Datei zu laden und einmal um einen Sektor zu laden) und damit hatte ich dann GRUB ersetzt ;)

Ist es eigentlich möglich GRUB zur Laufzeit (von GRUB) zu konfigurieren? Ich habe bei mir ein Bootmenü mit drin, wo ich im Moment nur die Auflösung für VBE wählen kann, aber da könnte ich auch so Sachen wie den Scheduler auswählen lassen. Was dann wieder gegen GRUB sprechen würde, weil ich nach der Auswahl im Menü z.B. auch ganz andere Module laden würde (wahrscheinlich).

Zitat von: erik
Klar, ich würde vermutlich auch einen Fallback einbauen, aber hat nicht auch FlashBurn recht wenn er dagegen hält dass das unnütze Speicherverschwendung ist?
Warum würdest du einen Fallback einbauen? Mir geht es dabei nicht mal um die "Speicherverschwendung".

Ich sehe es so, der Kernel bietet eine API an und an die hat man sich zu halten (ist doch unter Linux und allen andern OS´en nicht anders) und diese sieht vor, dass man nur in den Kernel kommt, indem man eine bestimmte Funktion aufruft.
Durch diese Abstraktion stelle ich sicher, dass immer die best mögliche Variante genutzt wird, egal wie alt das Programm ist und ohne das sich das Programm darum kümmern muss (ich nehme dem Programmierer Arbeit ab).
Dazu kommt noch, stell dir vor du müsstest Grafikhardware direkt und nicht über DirectX/OpenGL programmieren. Dann müsstest du für jede Graka nen eigenen Treiber schreiben und würdest nie alle Unterstützen können und deine Software würde irgendwann nicht mehr funktionieren. Durch solch ein Interface ist sichergestellt das der Programmierer solche Details nicht kennen muss.
Zumal man eigentlich auch nicht Direkt mit dem Kernel zu tun haben sollte, sondern sowas immer über eine Library abstrahiert sein sollte. Alleine schon, falls sich das Interface zum Kernel mal ändern sollte.
Ich sehe darin keine Nachteile, sondern nur Vorteile.

Zitat von: erik
Vermutlich nicht, aber zum einen könnte man das sicher einbauen und zum anderen kann ein HW-Gerät ja mehrere IRQs haben die dann fest unterschiedliche Prioritäten haben und es wird für jeden Job ein passender IRQ ausgewählt.
Wenn die Hardware in der Lage ist mehrere verschiedene IRQs zu produzieren, dann stimme ich dem zu.

Zitat von: erik
Das Ändern einer IRQ-Priorität sollte bei den APICs auf einen einzigen Schreibzugriff hinaus laufen.
Nope, du liest einmal um den INT-Vektor (und genau der repräsentiert die Priorität/legt sie fest) auszulesen, diesen freizugeben und dir nen neuen zu holen (das wäre das Schreiben).

Zitat von: erik
Wieso willst Du neue INT-Vektoren zuweisen? Das ist doch Quatsch und sicher auch gar nicht so einfach machbar zur Laufzeit, es müsste ja auch atomar durchgeführt werden und da dürften die üblichen HW-Geräte nicht so einfach mitspielen bzw. man bräuchte entsprechende Unterstützung durch die Treiber.
Da der INT-Vektor aber die Priorität festlegt, weißt du jetzt warum das nicht dynamisch gemacht wird ;)

Zitat von: erik
Doch, I/O-Port sind extrem langsam, im Verhältnis zum Speicher. Deswegen unterstützt ja auch kaum eine andere Plattform I/O-Ports, die meisten kennen nur Speicher.
Hieße das auch, dass ein USB-RS232-Adapter viel besser ist als eine native Schnittstelle?

Zitat von: erik
Viele ältere ISDN-Anlagen werden per RS232 konfiguriert und das ist meistens nicht so wichtig wie der MP3-Player im Hintergrund (ist doch egal ob die Konfiguration 2,0 Sekunden oder 2,1 Sekunden benötigt). Auch gibt es immer noch recht viele managed Ethernet-Switches mit ner RS232-Konsole (da muss der Admin zwar persönlich und mit Laptop vor Ort sein aber dafür funktioniert die wenigstens immer, selbst wenn das LAN mal komplett tot oder kaputt-konfiguriert ist), dem Admin dürfte der MP3-Player im Hintergrund auch wichtig genug sein das dieser eine höhere Priorität bekommt als das Terminal-Programm (wenn der Admin ne hundertstel Sekunde länger braucht ist das schließlich immer noch bezahlte Arbeitszeit).
Naja, das man die Priorität nicht dynamisch zur Laufzeit ändern kann, sollte dir inzwischen klar sein ;)

RS232 wird ja auch noch viel in der Industrie eingesetzt. Dass der IRQ-Handler aber immer ne höhere Priorität als der MP3-Player hat, lässt sich wohl nicht so wirklich vermeiden. Ich wollte eigentlich keine ganzen Programme in die Real-Time-Priorität reinlassen, sondern nur die IRQ-Handler oder spricht da was dagegen?

Zitat von: erik
Ja, ich weiß, meine Antworten sind auch nicht immer sehr hilfreich.
Hast dich aber gebessert.

Was ich dazu noch sagen kann, in der Uni bekommt man ganz bestimmte Aufgaben und wenn da mal jemand ein Problem hat und andere immer nur ankommen und sagen, ne das ist doof, nimm doch lieber das und das. Dann hilft dem das gar nicht weiter. Der soll das nehmen und gut ist. Ist ja leider in der reallen Welt genauso (da wo man jemanden über sich hat).
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 09. November 2011, 12:21
Hallo,


Warum würdest du einen Fallback einbauen? Mir geht es dabei nicht mal um die "Speicherverschwendung".
Hm, so nach etwas nachdenken bin ich mir auch nicht mehr so sicher. Das Problem was ich sehe ist das Du mit Deinem Weg statisches Linken (zumindest der libc) grundsätzlich ausschließt und das gefällt mir persönlich gar nicht, damit wird auch LTO eingeschränkt. Ich denke die meisten Hobby-OSe benutzen nur statisch gelinkte Executables, einfach weil man sich damit das fertig-linken im Executable-Loader sparen kann (ist ja doch kein so ganz einfaches Thema und für den ersten Anlauf kann man eigentlich auch gut drauf verzichten). In meinem Projekt wird es wohl zum Anfang auch nur statisch gelinkte Executables geben, für was besseres bräuchte ich auch ein neues Executable-Format und müsste mir auch überlegen wie ich das bei den OpCodes meiner CPU überhaupt hinkriegen kann (ich müsste für die unrelozierten Calls wohl immer die maximale OpCode-Größe benutzen). Prinzipiell sehe ich kein Problem darin wenn der Kernel immer alle Wege anbietet die die CPU auch unterstützt (was nicht geht endet eh in einer Exception). Das Kernel-Syscall-API sollte sich auch möglichst niemals ändern sondern nur um neue Funktionen erweitert werden. Auf der anderen Seite ändert sich das Kernel-Syscall-API bei Windows auch mit jeder Version (oft sogar bei Service-Packs) erheblich, Windows unterstützt wimre auch nur sehr begrenzt fertig gelinkte Executables (solche Executables laufen dann nur unter einer ganz bestimmten Windows-Version).

Zitat von: erik
Das Ändern einer IRQ-Priorität sollte bei den APICs auf einen einzigen Schreibzugriff hinaus laufen.
Nope, du liest einmal um den INT-Vektor (und genau der repräsentiert die Priorität/legt sie fest) auszulesen, diesen freizugeben und dir nen neuen zu holen (das wäre das Schreiben).
Ich will Dir ja nicht widersprechen aber es fällt mir wirklich extrem schwer zu glauben das bei den 24 IRQs die so ein I/O-APIC unterstützt nicht auch jeder IRQ ein individuelles Prioritätsregister hat, ließ da Bitte noch mal in der APIC-Spec genau nach. Ich kann mir einfach nicht vorstellen das die Intel-Ingeniöre solch kapitale Fehler gemacht haben.

Hieße das auch, dass ein USB-RS232-Adapter viel besser ist als eine native Schnittstelle?
Jain, so ein USB-RS232-Adapter erzeugt auf dem PC auf jeden Fall eine deutlich geringere CPU-Last (eben weil der USB-Host-Controller, zumindest seit EHCI, die Daten als Busmaster in den RAM legt bzw. von dort holt) aber man hat auch deutlich längere Latenzen (was u.a. durch das Framing im USB-Protokoll begründet ist). Ich hab mal für eine spezielle Industrie-Anwendung eine Multi-RS232-Karte auf FPGA-Basis entwickelt und mein UART hat auch alles als Busmaster erledigt so das nur alle paar kB mal ein IRQ erforderlich war, damit konnte der Linux-PC ohne spürbare CPU-Last 4 RS232-Verbindungen mit 1,9MBaud voll duplex sättigen, das wäre mit klassischen UARTs nicht möglich gewesen.

Ich wollte eigentlich keine ganzen Programme in die Real-Time-Priorität reinlassen, sondern nur die IRQ-Handler oder spricht da was dagegen?
Es spricht IMHO was dagegen das Du aus der "Real-Time"-Priorität was besonderes machst. Wenn Du einfach X Prioritäten hast dann sollte es grundsätzlich möglich sein das sowohl IRQ-Handler als auch normale Threads jede dieser Prioritäten haben können (wobei die höheren Prioritäten auf jeden Fall ausreichend geschützt werden müssen aber das ist ein anderes Problem).

Hast dich aber gebessert.
Danke, ich gebe mir auch Mühe, zumindest hin und wieder. ;)


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 09. November 2011, 12:45
Zitat von: erik
Das Problem was ich sehe ist das Du mit Deinem Weg statisches Linken (zumindest der libc) grundsätzlich ausschließt und das gefällt mir persönlich gar nicht, damit wird auch LTO eingeschränkt.
Wieso, ich stelle diese Funktionen auch als statische Libraries zur Verfügung.

Zitat von: erik
Ich denke die meisten Hobby-OSe benutzen nur statisch gelinkte Executables, einfach weil man sich damit das fertig-linken im Executable-Loader sparen kann (ist ja doch kein so ganz einfaches Thema und für den ersten Anlauf kann man eigentlich auch gut drauf verzichten).
Also das will ich nicht. Dynamische Libraries sollen bei mir eigentlich Standard sein (vom RT-Linker, VFS-Server und Device-Server mal abgesehen).

Zitat von: erik
Ich will Dir ja nicht widersprechen aber es fällt mir wirklich extrem schwer zu glauben das bei den 24 IRQs die so ein I/O-APIC unterstützt nicht auch jeder IRQ ein individuelles Prioritätsregister hat, ließ da Bitte noch mal in der APIC-Spec genau nach. Ich kann mir einfach nicht vorstellen das die Intel-Ingeniöre solch kapitale Fehler gemacht haben.
Zitat von: Intel IO-APCI Manual"
Unlike IRQ pins of the 8259A, the notion of interrupt priority is completely unrelated to the position of the
physical interrupt input signal on the APIC. Instead, software determines the vector (and therefore the priority)
for each corresponding interrupt input signal.
Sollte doch als "Beweis" reichen oder ;)
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 09. November 2011, 15:58
Hallo,


Wieso, ich stelle diese Funktionen auch als statische Libraries zur Verfügung.
Aber sobald diese Library ins Executable eingelinkt wurde ist das Executable fest. Wenn Du dort die Library-Version einbaust die INT für Syscalls benutzt aber das Executable dann auf einem PC laufen soll der SYSENTER will geht Dein Executable nicht mehr. Das ist irgendwie doof, oder? Vor allem weil man ja durchaus auch mal wegen der Performance statisch linken möchte (damit selbst die libc von LTO profitiert).

Dynamische Libraries sollen bei mir eigentlich Standard sein
Wenn Du das sauber hinkriegst ist das natürlich toll.

..... Sollte doch als "Beweis" reichen oder ;)
Hm, da bin ich echt platt, die meisten IRQ-Controller mit denen ich bis jetzt zu tun hatte haben für jeden IRQ ein individuelles Priority-Register (selbst bei Intel-Produkten wie einem PXA270). Mein IRQ-Controller soll das auch haben, einfach weil ich das als absolut normale Selbstverständlichkeit betrachte, trotz dessen dass das Logik-Ressourcen im FPGA frisst.


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 09. November 2011, 16:57
Zitat von: erik
Aber sobald diese Library ins Executable eingelinkt wurde ist das Executable fest. Wenn Du dort die Library-Version einbaust die INT für Syscalls benutzt aber das Executable dann auf einem PC laufen soll der SYSENTER will geht Dein Executable nicht mehr. Das ist irgendwie doof, oder? Vor allem weil man ja durchaus auch mal wegen der Performance statisch linken möchte (damit selbst die libc von LTO profitiert).
Manchmal denke ich ihr wollt mich nicht verstehen ;)

Hast du dich schonmal mit den Syscall-Methoden unter Linux und Windows beschäftigt?

Also mal ganz genau beschrieben, der Caller packt seine Parameter auf den Stack (ja die werden nicht über die Register weitergereicht, da die dann eh auf den Kernel-Stack gepackt werden müssen und kopieren muss man auch wenn es zu viele sind) und callt die von mir zur Verfügung gestellt Funktion. Diese Funktion macht nix anderes, als die Funktionsnr (vom Kernel-API) in EAX zu packen und einen "jmp" zu einer festen Adresse zu machen und das ist jetzt der Punkt wo es transparent für die Anwendung ist. Denn an dieser festen Adresse steht der Code für nen kleinen Stub der in den Kernel springt und genau hier wird dann die jeweils beste zur Verfügung stehende Methode genutzt.
Dazu braucht man weder dynamische Libraries noch wird in das Programm irgendetwas reingelinkt was mit der eigentlich verwendeten Methode zu tun hat.

Diese Page (bisher ist es nur eine) ist am oberen Ende eines jeden Adressraumes gemappt. Da können dann auch noch mehr Sachen rein (Haiku stellt so z.B. optimierte memcpy und Konsorten zur Verfügung und ich wollte das eigentlich auch noch so machen) und diese Adressen ändern sich nicht. Man muss halt sehen wie viel da rein soll, sprich ob es Sinn macht in der oberesten Page nur Adressen auf Funktionen zu speichern oder (so wie ich bisher) da schon direkt Code reinzuschreiben.

Zitat von: erik
Wenn Du das sauber hinkriegst ist das natürlich toll.
Wenn du das so schreibst klingt das als wenn du schon eine Idee hast was da schief gehen könnte bzw. wo es Probleme geben wird?

Nochmal zum IO-APIC, soweit ich es verstanden habe, sind dem die Prioritäten auch eigentlich egal. Die Lokalen-APICs interessiert das dann erst.

Ich habe mir heute nochmal (versucht) GRUB und auch U-Boot näher anzuschauen und nein, definitiv nicht als einziger Bootloader. Alleine schon durch die Tatsache das ich einige Annahmen mache wo meine Module liegen, macht es die Sache einfacher. Soweit ich es verstanden habe, werden die Module bei GRUB1 direkt hinter den Kernel geladen (ich gehe davon aus, dass das mit 4kb alignt passiert, habe dazu aber nix gefunden) und bei GRUB2 irgendwo in den höheren Speicher. Ich denke fast mal mit GRUB2 werden wohl einige Hobby-OS´s Probleme genau deswegen bekommen.
In der U-Boot Doku habe ich gar nix gefunden wie man andere Systeme oder Kernel allgemein lädt, da ist alles auf Linux bezogen, von Multiboot auch keine Spur. D.h. also wenn man auf eine andere Architektur will und dann nicht mehr GRUB nutzen kann, muss man sich sowieso was einfallen lassen.

Wenn jemand mal nen Link zu einer halbwegs guten Doku (oder wenigstens irgendwelche interessanten Infos) zu fastboot (Android) hat, wäre ich sehr dankbar dafür.

Ich bin immernoch überzeugter eigener-Bootloader-Nutzer ;)
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 09. November 2011, 20:23
Hallo,


Manchmal denke ich ihr wollt mich nicht verstehen ;)
Was Du da beschreibst hast Du in diesem Thread auch noch gar nicht erwähnt und leider kann ich mir nicht zu jedem hier diskutiertem OS alle intimen Details merken. Wobei dieser Weg immer zwei zusätzliche Indirektionen hat, im Gegensatz dazu wenn der richtige Syscall-Befehl direkt in den User-Code geinlined wird (was mit LTO bei kleinen libc-Funktionen ja problemlos möglich wäre). Irgendwie verstehe ich Dich wirklich nicht, da nimmst Du einen riesigen Aufwand mit einem zur Boot-Zeit zusammengelinkten Kernel in kauf nur um eine einzige Indirektion zu sparen und dann baust Du gleich zwei davon in den User-Mode ein. Was soll ich den jetzt noch über Dich denken?

Hast du dich schonmal mit den Syscall-Methoden unter Linux und Windows beschäftigt?
Sicher nicht annähernd so ausgiebig wie Du. Die x86-Plattform ist auch die einzigste bei der das so ein komplexes Thema ist, alle anderen Plattformen haben einen spezifischen Syscall-Befehl, der von der CPU möglichst performant unterstützt wird, und gut.

Wenn du das so schreibst klingt das als wenn du schon eine Idee hast was da schief gehen könnte bzw. wo es Probleme geben wird?
Nö nö, keine Sorge, ich bin bloß der Meinung das gerade das Thema Linken gerade für OS-Dev-Anfänger eher nicht so geeignet ist und daher viele diesen Punkt auf eine spätere Version vertagen. Ich glaube tyndur kann das auch noch nicht, falls ich mich irre so möge man mich Bitte korrigieren.

Ich bin immernoch überzeugter eigener-Bootloader-Nutzer ;)
Ich kann Dich da gut verstehen. Obwohl ich als Ausweg lieber eine Art Init-RAM-Disk nehmen würde in der der eigentliche Kernel mit allen potentiellen Komponenten und auch alle wichtigen Services, die ein Micro-Kernel-OS ja benötigt damit weitere User-Mode-Programme von HDD geladen werden können, drin sind und dazu ein Programm (welches für den Boot-Loader als Kernel behandelt wird) das daraus dann ein lauffähiges System zusammenbaut. Problem dabei wäre natürlich das Du dann von der Vorbereitung des virtuellen Speichers durch den Boot-Loader abhängig bist aber ich denke auch das lässt sich lösen.


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 09. November 2011, 20:45
Zitat von: erik
Was Du da beschreibst hast Du in diesem Thread auch noch gar nicht erwähnt und leider kann ich mir nicht zu jedem hier diskutiertem OS alle intimen Details merken.
Habe ich nicht? Mir war so ;)

Zitat von: erik
Wobei dieser Weg immer zwei zusätzliche Indirektionen hat, im Gegensatz dazu wenn der richtige Syscall-Befehl direkt in den User-Code geinlined wird (was mit LTO bei kleinen libc-Funktionen ja problemlos möglich wäre). Irgendwie verstehe ich Dich wirklich nicht, da nimmst Du einen riesigen Aufwand mit einem zur Boot-Zeit zusammengelinkten Kernel in kauf nur um eine einzige Indirektion zu sparen und dann baust Du gleich zwei davon in den User-Mode ein. Was soll ich den jetzt noch über Dich denken?
Das Inlinen geht ja nicht, dadurch dass es verschiedene Methoden gibt. Als Bsp. was machst du wenn der Code die syscall/sysret Variante nimmt (AMD) und auf einer Intel CPU läuft?
Wieso eigentlich 2 Indirektionen (steh da gerade aufm Schlauch)? Wegen einmal "call" und einmal "jmp"?

Mir wäre es am liebsten wenn man dem Compiler irgendwie sagen könnte, dass er diese bestimmte Adresse callen soll und die Parameter auf den Stack pusht. Also wenn jemandem sowas bekannt ist, immer her damit.

Aber wo ich da gerade so drüber nachdenke, kann man nicht einfach ne Funktion "bool sendMsg(buffer,receiver,flags);" definieren und dann beim Laden des Programms das Symbol auflösen? Damit würde ich mir ja eine Indirektion sparen (wieso bin ich da nicht früher draufgekommen).

Zumal auch das mit dem Inlinen aus einem anderen Grund doof ist, "syscall"/"sysenter" sind nicht so einfach wie ein "int", da muss man erst Register sichern und bestimmte Sachen in diese Register laden oder das passiert automatisch (ich mag die beiden Varianten nicht so, aber sie sind halt schneller als "int"). Deswegen habe ich auch nen Stub dafür (und mache leider noch keine Überprüfungen ob die Rücksprungadresse wirklich ok ist).

Fällt dir denn ne bessere Variante ein? Deswegen habe ich das bisher so gemacht, mir ist keine bessere (kompliziertere ;)) eingefallen.

Zitat von: erik
Die x86-Plattform ist auch die einzigste bei der das so ein komplexes Thema ist, alle anderen Plattformen haben einen spezifischen Syscall-Befehl, der von der CPU möglichst performant unterstützt wird, und gut.
Warum ist das eigentlich bei x86 so kompliziert? Ist das nicht auch wegen den Segmenten so?

Zitat von: erik
Nö nö, keine Sorge, ich bin bloß der Meinung das gerade das Thema Linken gerade für OS-Dev-Anfänger eher nicht so geeignet ist und daher viele diesen Punkt auf eine spätere Version vertagen.
Also object-files in ein executeable-file zu linken ist gar nicht so schwer. Viel schwieriger sind dann schon die shared-libraries, aber auch das sollte (wenn man das vorherige kann) nicht so schwierig sein.

Zitat von: erik
Obwohl ich als Ausweg lieber eine Art Init-RAM-Disk nehmen würde in der der eigentliche Kernel mit allen potentiellen Komponenten und auch alle wichtigen Services, die ein Micro-Kernel-OS ja benötigt damit weitere User-Mode-Programme von HDD geladen werden können, drin sind und dazu ein Programm (welches für den Boot-Loader als Kernel behandelt wird) das daraus dann ein lauffähiges System zusammenbaut. Problem dabei wäre natürlich das Du dann von der Vorbereitung des virtuellen Speichers durch den Boot-Loader abhängig bist aber ich denke auch das lässt sich lösen.
Du musst dann deinen Loader auch an jeden Bootloader anpassen oder im Falle von U-Boot vllt sogar nen Linux-Kernel emitieren (keine Ahnung ob das wirklich nötig oder schwer ist).
Da du dann ja eigentlich auch Dinge machst, die der erste Bootloader schon kann, kannst du den auch gleich selber schreiben. Mit einer Ausnahme, wenn der erste Bootloader die Firmware darstellt. Da macht es dann wenig Sinn dieses neu zu schreiben.

Ich habe da mit nem eigenen Bootloader auf x86 eventuell weniger Arbeit, weil entweder du musst deinen Loader an mehrere Bootloader anpassen oder der User müsste deinen Bootloader nutzen (was bei mir der Fall ist, aber das ist das gleiche als ob du Windows damit laden würdest).

Da gefällt mir der Bootloader vom Raspberry Pi sogar besser als U-Boot oder so. Da wird einfach ne bestimmte Datei an eine festgelegt Adresse geladen und einfach dahin gesprungen. Damit kann ich arbeiten ;)
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: erik.vikinger am 10. November 2011, 14:28
Hallo,


Das Inlinen geht ja nicht, dadurch dass es verschiedene Methoden gibt.
Eben, das ist es ja warum ich der Meinung bin dass das bei x86 deutlich komplexer ist als bei anderen CPU-Architekturen.

Wieso eigentlich 2 Indirektionen?
Na einmal weil Du vom normalen Code in die entsprechende Library springen musst (solange Du nicht statisch linkst und dabei LTO benutzt bleibt das auch) und dann muss die Library noch in Deine Spezial-Page springen (solange Du flexibel alle 3 möglichen Wege von x86 anbieten möchtest bleibt das auch). Ob das per CALL oder per JMP gemacht wird macht erstmal keinen so großen konzeptionellen Unterschied. Bei mir ist der SYSCALL-Befehl direkt in der Library drin und der Compiler hat beim statischen Linken per LTO noch die Möglichkeit auch das direkt in den User-Code zu inlinen.

Mir wäre es am liebsten wenn man dem Compiler irgendwie sagen könnte, dass er diese bestimmte Adresse callen soll und die Parameter auf den Stack pusht. Also wenn jemandem sowas bekannt ist, immer her damit.
Du könntest einen Funktionsprototypen als typedef deklarieren und in Deinem Code eine Konstante (die das Sprungziel darstellt) in einen Funktions-Pointer mit diesem Typ casten und das dann direkt aufrufen, wenn Du das in ein passendes Makro verpackst dürfte das sogar recht hübsch aussehen.

Du musst dann deinen Loader auch an jeden Bootloader anpassen oder im Falle von U-Boot vllt sogar nen Linux-Kernel emitieren
Wenn Du Deinen Loader Multiboot-kompatibel machst sollte das meiste schon mal funktionieren und wenn Dein Loader dann noch so aussieht wie ein Linux-Kernel dürftest Du mit (fast) jedem Open-Source-Boot-Loader auf (fast) allen Plattformen klar kommen.


Grüße
Erik
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 10. November 2011, 15:16
Zitat von: erik
Du könntest einen Funktionsprototypen als typedef deklarieren und in Deinem Code eine Konstante (die das Sprungziel darstellt) in einen Funktions-Pointer mit diesem Typ casten und das dann direkt aufrufen, wenn Du das in ein passendes Makro verpackst dürfte das sogar recht hübsch aussehen.
Hätte ich damit nicht wieder eine Indirektion (durch den Funktionspointer)? Diese Variante hätte natürlich den Vorteil, dass sie keinen Linker erfordert.

Zitat von: erik
Wenn Du Deinen Loader Multiboot-kompatibel machst sollte das meiste schon mal funktionieren und wenn Dein Loader dann noch so aussieht wie ein Linux-Kernel dürftest Du mit (fast) jedem Open-Source-Boot-Loader auf (fast) allen Plattformen klar kommen.
Naja, aber selbst Multiboot-kompatibel ist ja nur die halbe Miete (für den normalen OS-Dever). Wenn man das richtig macht dann sollte das klappen, aber auch das ist schon verdammt aufwendig. Man muss immer vom schlimmsten ausgehen und aufs beste hoffen ;)

Ein anderes Problem lässt sich leider weder durch einen eigenen Bootloader noch durch eine eigenen 3.-Stage lösen. Denn selbst wenn dein Loader aussieht wie ein Linux-Kernel (ich wüsste nichtmal was das heißt), gibt es noch die ganzen Architekturen außerhalb von x86 ;)
Ich kenne es nur vom Toshiba AC100 (ARM Cortex-A9 und von nVidia), aber da müssen sogar ein paar Werte im Kernel (ich vermute mal repräsentiert durch ein Symbol) ganz genau so sein wie es (in dem Fall) fastboot haben möchte. Und diese Werte können sich schon durch ein Update ändern (das war mal nen Problem bzw. ist es für einige immernoch).

Lange Rede kurzer Sinn, außerhalb von x86 hat man sowieso ganz schön Arbeit und kann nicht mal eben eine Datei (was teilweise schon geht) oder eine bestimmte Datei laden lassen.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: Svenska am 10. November 2011, 16:53
Lange Rede kurzer Sinn, außerhalb von x86 hat man sowieso ganz schön Arbeit und kann nicht mal eben eine Datei (was teilweise schon geht) oder eine bestimmte Datei laden lassen.
Das geht sogar ganz wunderbar, wenn du dich auf eine Plattform beschränkst. Du sagst "x86", meinst aber "ibmpc".
Es gibt keine ARM-Betriebssysteme, es gibt auch keine x86-Betriebssysteme.
Es gibt sie aber fürn PC, fürs Galaxy Tab, fürn PNA 200S, fürn Zaurus, fürn Nexus One oder für den WLAN-Chipsatz in deinem Notebook.
Titel: Re: Threads blockieren und wieder aufwecken
Beitrag von: FlashBurn am 10. November 2011, 17:10
Zitat von: svenska
Das geht sogar ganz wunderbar, wenn du dich auf eine Plattform beschränkst. Du sagst "x86", meinst aber "ibmpc".
Jap, da hast du natürlich recht.

Sagen wir einfach außerhalb von "ibmpc" gibt es einfach zu viele Plattformen, die zwar eigentlich alle die gleiche Architektur verwenden, aber das wars dann auch schon. Ist aus Hobby-OS-Dever Sicht nicht so das Wahre.