Wie funktioniert eventbasierte Programmierung?

2 Antworten

Um bei Windows basierten Systemen zu bleiben.. intern werden Nachrichten verschickt. jedes Fenster jeder Button und jede Mausbewegung ist eine Message in einer Warteschlange. Windows reicht die Nachrichten durch bis zur gewünschten stelle.

Woher ich das weiß:Berufserfahrung – Softwareentewickler / Unternehmensberater bei CSDIT iR

verreisterNutzer  02.04.2018, 18:02

Ich habe leider den Eindruck, du hast dich noch nie mit diesem Sachverhalt events wirklich beschäftigt -- baue einfach mal dein Eventsystem, wird von allen gängigen Programmiersprachen (direkt, d.h. über eventhandler) unterstützt ...

0
R4c1ngCube 
Beitragsersteller
 02.04.2018, 20:05
@verreisterNutzer

Es stimmt tatsächlich, dass ich mich noch nicht viel damit beschäftigt habe. Aber es ist auch nicht so, dass ich es noch nie verwendet habe.
Zum einem habe ich schon verschiedene c# windows-forms geschrieben, wo events unumgänglich sind und in c++ habe ich mit Hilfe der Windows-api message loops geschrieben, was ja im Prinzip das Eventsystem von Windows ist.

Allerdings fängt hier mein Problem an: ein message LOOP, mir wurde in vorausgegangenen Fragen in verschiedenen Foren gesagt, dass in Eventsystemen kein Polling vorkommt oder ich sollte besser sagen, ich wurde zurechtgewiesen :)
Ich hatte nämlich versucht mir mit einem simulierten os (bestehend nur aus einer Schleife in der ich verschiedene Tastenzustände polle) ein eigenes Eventsystem zu schreiben und dieses war bis auf die zentrale Schleife tatsächlich rein reaktiv und hat auch funktioniert.

Und ich konnte mir keinen Weg vorstellen, wie es irgendwie möglich sein soll etwas rein reaktiv zu machen, ganz ohne polling spätestens auf hardware-ebene. Daher die Frage wie es tatsächlich funktioniert, weil mir wiederholt mitgeteilt wurde, dass es eben geht.

Aus anderen Quellen habe ich mittlerweile aber erfahren, dass wohl (angeblich) nichtmal die interrupts bei Prozessoren ohne polling ablaufen (Aber noch nicht zum Zeitpunkt zu dem ich die Frage gestellt hatte)

0
R4c1ngCube 
Beitragsersteller
 02.04.2018, 16:47

Das ist mir noch zu unkonkret :)

Es werden Nachrichten verschickt. Was heißt das? Woraus bestehen Nachrichten?

Wie werden die Nachrichten empfangen / erkannt?
Wenn Nachrichten zB. Werte in einem gemeinsamen Speicher sind, müsste ja wieder gepollt werden.
Wenn Nachrichten eine Art "Aufruf" sind, der Aufruf von was?

Wenn Windows die Nachrichten "verschickt", wie stellt Windows fest, ob zB. ein Button gedrückt ist?

Es geht mir weniger darum, was das Konzept davon ist, als darum wie es technisch umgesetzt ist, bzw, wie ich mir ein eigenes in sich geschlossenes Eventsystem "aufbauen" könnte

0
geri3d  02.04.2018, 17:22
@R4c1ngCube

Versuch ma die winproc auszulesen bei deinem fenster dann siehst du welche Messages gesendet weren

0

Events werden vom System an den Prozess geleitet. Dazu wird ein Eventhandler geschrieben, der a priori dem System bekannt gemacht wird.

Sodenn ein Event eintritt, schaut das System nach den zu bedienenden Handlern und schickt das Event an den Handler, indem es diesen asynchron aufruft.

Der eigentliche Hauptprozess legt sich dann üblicherweise schlafen, das System weckt diesen, sobald der Eventhandler aufgerufen wurde. (Wobei das nicht exakt zugesichert wird, ohne korrekte Synchronisierung etc. würde es ggf. zu einem Race kommen).

Wo liegt denn nun Dein Verständnisproblem?

Achso, Je nach OS und Umsetzung werden Events ggf. als Nachrichten via Messagepassing im Kernel realisiert, insbesondere bei Microkernels ist das unumgänglich. Das ändert aber nichts grundlegendes am Ablauf.


R4c1ngCube 
Beitragsersteller
 02.04.2018, 21:03

Verständnis heißt für mich, alle warums und wies beantworten zu können. Oder alternativ das Verstandene bis auf die Grundfesten des eigenen Wissens zurück verknüpft zu haben.

Auf dieser Basis versuche ich also alles möglichst weitgehend zu verstehen.

Und um zB. zu verstehen, dass das os Nachrichten an Prozesse schickt, muss ich nach meiner Definition wissen, aus was so eine Nachricht besteht, wie die codiert sind, in was für Speichertypen die evtl gespeichert werden, an welcher Stelle evtl im os ein Polling passiert oder eben nicht, wie ein eventhandler ggf sogar im maschinencode aussieht oder sogar darunter :)

Ich will nicht nur wissen was es macht und wie ich es verwende, sondern wie es das macht, mit welchem Hintergrundgedanken man dieses Konzept entworfen hat usw.

Natürlich ist dieses Verständnis von Atomebene bis hin zu Konzepten die in höheren Programmiersprachen entworfen werden ein eher unerreichbares Ziel, aber bildliche Darstellungen (nicht auf dich bezogen) sind eher ein Schritt in die entgegengesetzte Richtung und helfen mir wenig weiter, denn das Grundkonzept ist bildlich ja eigentlich ziehmlich simpel.

Ein Beispiel vielleicht:
Jemand möchte Schleifen verstehen. Den meisten würde es reichen, wenn man ihm sagt, dass der Prozessor alle Befehle in der Schleife ausführt, solange die Bedingung erfüllt ist, aber das ist für mich kein Verständnis, das bezeichne ich als Wissen und wenn man damit umgehen kann, die Fähigkeit es anzuwenden. Verständnis heißt für mich die ebene darunter anzuschauen, zB. in Assembler, damit man mal die tatsächlichen Sprungbefehle sieht, man sich dann anschaut, wie Befehle tatsächlich im Ram stehn und wie der Prozessor letztendlich die Befehle abhandelt.

Ich habe dieses Verständnis nicht, aber deswegen stelle ich ja Fragen, um es verstehen zu können :)

Natürlich erwarte ich nicht, dass es mir hier bis zur Prozessor-ebene erklärt wird. Mir geht es vor allem um die 2. Frage, ist es rein reaktiv oder ist irgendwo ein polling nötig. Ich bin, wie oben dargelegt, zu dem Schluss gekommen, dass es nicht ohne polling möglich ist, mir wurde allerdings anderes gesagt und ich habe keine Idee wie es umgesetzt werden könnte.

Ich habe oben ja geschrieben wie ich glaube, dass es umgesetzt werden könnte und ab welchem Punkt ich nicht mehr wüsste wie man das lösen könnte und wollte wissen, ob es so gelöst wird (Verweise auf die handler werden an das os gegeben, dieses pausiert den Prozess bis das entsprechende Event eintritt und setzt dann den Prozess fort [hierzu wollte ich eben noch wissen, wie das fortsetzen aussieht]) und wollte dann wissen, wie es das os schafft ohne polling festzustellen, ob externe hardware gerade bestimmte Signale sendet, die Antwort ist allerdings, wie ich inzwischen erfahren habe schlicht: garnicht :D Was meine Frage natürlich ein bisschen unnötig macht.
Und um meine erste Frage ein wenig anders zu formulieren - wie hat zB. Windows Events implementiert, wie könnte der source-code aussehen. Ich gebe zu, die Frage kann man eigentlich nicht beantworten oder wird es nicht selbst wenn man es kann. Mir ist leider selbst erst im Nachhinein aufgefallen, dass man darüber nur schwer Aussagen treffen kann, ich hatte Events als allgemein gleich verwendetes Konzept betrachtet, so wie auch Schleifen überall wenigstens nahezu gleich implementiert sind und hatte außer Acht gelassen, dass es vermutlich verschiedene Implementierungen gibt.

Zusammenfassend also:
Mein Verständnisproblem: kein Zugriff auf den source-code von Microsoft :D
Das wird hier aber niemand lösen können.

Und die Frage bezüglich des pollings hat mir bereits jmd anderes beantwortet, damit ist meine Frage inzwischen vermutlich geklärt :)

Dennoch danke für die Antwort, du hast trotzdem einige Details genannt die ich noch nicht wusste.

evtl noch kurz eine Erklärung wie es zu der Frage kam. Ich lerne aktuell c++ und bin eben inzwischen was die Grundfunktionen angeht eigentlich am Ende, zumindest mit denen von c und mache jetzt eben mit "höheren Konzepten" weiter (eben Dingen die in c/c++ code geschrieben sind und nicht dem code selbst). Jetzt hatte ich vor längerer Zeit von Events gehört und davon, dass es schlicht darum geht, dass auf ein vorausgegangenes Signal eine Routine ausgeführt wird.
Ich habe dann (bei threads hatte ich das auch versucht oder zumindest etwas, was das ersetzen kann) versucht, mir ein Eventsystem zu basteln, ohne wirklich zu wissen, wie sowas abläuft. Und dabei ist die oben stehende Idee entstanden (mittlerweile habe ich aber natürlich auch richtige Events verwendet).

Das habe ich dann in tatsächlichem c++ code gepostet woraufhin mir allerdings gesagt wurde, dass events rein reaktiv sind weswegen ich denn etwas polle. Von da an habe ich mich bis heute immer gefragt, wie man das denn bitte ohne umsetzen soll (bis mir vorhin in nem andren Forum jemand geschrieben hat, dass es schlicht nicht ohne geht).

Und deswegen auch immer die Frage - wie ist es wirklich? :)

0
KarlRanseierIII  02.04.2018, 21:37
@R4c1ngCube

Okay, verstehe, Du möchtest am liebsten die genaue Struktur etc. - Das variiert natürlich mit dem jeweiligen Betriebssystem und mit der Hardware.

Auf unterster Eben (Hardware) gibt es sowohl Polling, als auch 'Events'. Üblicherweise sendet heutzutage ein Gerät einen IRQ an die CPU (Okay, das könnte jetzt mit allen Varianten langatmig werden), sagen wir vereinfacht: Der externe Interruptcontroller zieht einen PIN der CPU auf HIGH, woran die CPU erkennt, das sie die normale Abarbeitung unterbrechen soll. (Ggf. sind es auch mehrere Leitungen für verschiedene Typen, oder Prioritäten, oder man nutzt eien einzelne Line für den IRQ und weitere um z.B. einen Zahlenwert als Zusatzinformation anzulegen).

Das Steuerwerk erkennt nun den geänderten Eingang und sorgt dafür, daß der normale Ablauf unterbrochen wird - natürlich steht es der Implementierung frei, ob das Rechenwerk den Status in jedem Zyklus berücksichtigt und ggf. erstmal die Pipeline leer laufen lässt, oder ob es sich um einen NMI handelt, der quasi unabwendbar ist. NMI=Non maskable Interrupt - Diese sind meistens sepziellen schweren Fehlern vorbehalten, nicht einfachem IO.

Sobald die CPU in einem geeigneten Zustand ist, sichert sie ihren Zustand (Registerinhalte etc. pp.), springt die ISR (Interrupt Service Routine) im Kernel an udn führt diese aus.

Die ISR entscheidet, wie auf den Interrupt zu reagieren ist, bei einem Tastendruck könnte sie zum Beispiel entscheiden diesen erstmal in einem Puffer im Tastaturtreiber abzulegen und ein Flag im Kernel zu setzen, daß der Treiber anzuspringen ist, weil Daten verarbeitet werden sollen.

Wenn also nun der Kernel wieder normal gescheduled wird, wird sich dieser Aufgabe angenommen, er schaut wohin die Eingabe muß (welcher Prozess) und signalisiert diesem wiederum, daß er etwas zu tun hat. Dies kann zum Beispiel passieren, indem wie zuvor beim Interrupt und der ISR einfach der Signalhandler des Prozesses aus dem Kernel heraus asynchron angesprungen wird (ignorieren wir gerade mal Multicore, um es einfach zu halten). Da also der Kernel in Ausführung ist, ruht alles andere. Aber es könnten natürlich Interrupts dazwischenfunken, deswegen werden diese temporär maskiert und der IR-Controller sammelt auflaufende Interrupts in dieser Phase.

Ein typischer Signalhandler, den Du selbst implementierst, wird also Beispielsweise eine globale Variable ändern und sobald Dein Prozess wieder läuft wird er dieses Flag überprüfen und darauf reagieren. Die möglichen Aktionen und Systemaufrufe in Handlern sind stark beschränkt, gute Handler sind sehr Minimalistisch.

Sollte der Kernel stattdessen Message-Passing nutzen, ist der Ablauf im Prinzip ähnlich: Für jeden Prozess gibt es eine Eingangsbox, durchaus mit assoziierter Queue, in diese wird die Nachricht über das Ereignis injeziert. Diese Nachricht ist natürlich strukturiert und besitzt zum Beispiel ein Feld für den Nachrichtentyp, dann eines für den speziellen Fall innerhalb der Gruppe usw. usf. . Diese Eingangsbox kann z.B. auch an einer bestimmten Adresse im Datensegment des Prozessbildes liegen.

---- snipp ----

Wir haben jetzt betrachtet, wie ein Event oder Signal bis zum Prozess gelangt, daß entweder dem Prozess etwas in den Eingangskorb gelegt wird, oder der Prozess einen speziellen codeabschnitt hat, der Auf Signale reagiert und den internen Zustand des Prozesses ändert, wie sieht das aber auf Seite des Prozesses aus?

Irgendwann im normalen Ablauf gelangt der Prozess an einen Punkt, wo er auf Ereignisse bzw. Signale zu warten hat. Er macht dann genau das, er ruft einen Syscall auf und sagt: Ich hab nix mehr zu tun, Du darfst mich im Scheduler verschieben und andere Prozesse können an die Reihe kommen, aber Du weißt ja, wenn dieses oder jenes passiert, dann wecke mich gefälligst auf.

In einer Implementierung (ich nehem Signale) ist das dann so: Prozess führt also einen Syscall aus und ruht. Sobald ein Signal eingeht wird der eventuell vorhandene Signalhandler des Prozesses asynchron ausgeführt. Ebenso wird der Prozess vom Scheduler wieder zeitnah in den Vordegrund geholt. Der Rückgabewert des zuvor aufgerufenen Syscalls enthält eine Information darüber was passiert ist. Dein Code schaut sich also den Rückgabewert an und prüft ihn, es gab ein Signal X -> Okay, prüfe die Datenobjekte, die der HAndler setzt. Der Rückgabewert enthält auch die Information, daß IO-Puffer verändert sind und Daten aus diesen zu holen sind usw. usf. .

Ebenso bei Messages, Du legst Dich z.B. an einer Semaphore schlafen, bei Eingang einer Nachricht (Event) wie die Semaphore freigegeben und Du liest die eingegangenen Nachrichten aus.

--- snapp ---

Wenn Du es noch genauer brauchst, inklusive der Structures usw., dann empfehle ich Dir die im Verhältnis einfachen POSIX-Signale. SYSV-IPC um Dinge wie Messagequeues für IPC zu verstehen. Vor allem, Du kannst dir auch jederzeit den Kernel Source anschauen.

0