Veraltet und vergessen

Nehmt euch doch auch ein Glas Wein, heute philosophiert Herr Cypax mal ein bisschen vorm Kaminfeuer...
Wireless Toolkit
Vom Aussterben bedroht: alte Programmiertools
Wir haben einen Pkw, der ist 11 Jahre alt. Eine Waschmaschine, die ist 15 Jahre alt. Meine Nähmaschine ist über 30. Und im Keller ist noch ein Bakelittelefon aus den 50ern angeschlossen. All diese technischen Dinge sind immer noch in Betrieb, sehen nicht groß anders aus als ein Neugerät (ok, ja, das Telefon schon etwas) und funktionieren auch anno 2021 nicht grundsätzlich schlechter, als es ein Neugerät würde. Deutlich anders sieht das schon bei den Computern aus. Und extrem auffällig ist der Technikwandel bei Handys. Das ist mir letztens wieder so richtig gewahr geworden, als ich für den neuen Bombenwecker ein Nokia von 2007 wieder in Betrieb genommen habe, um dafür eine Anwendung zu programmieren. 2007 steckten Android und iOS noch in den Babywindeln und es war noch nix groß mit Smartphones, Apps und derlei. Die Handys hatten mickrige Grafikdisplays, wenig Speicher und "Apps" waren im Wesentlichen kleine Java-Programme, die in einer sehr rudimentären Laufzeitumgebung liefen. Und normalerweise findet man im Netz ja zu jedem beliebigen Thema Informationen in unbewältigbarem Überfluss - um aber herauszufinden, wie und womit ich genau dieses Handy programmiere, wo man noch eine Entwicklungsumgebung herbekommt, wo eine Laufzeitumgebung und so weiter, musste ich erstmal richtig ausgiebig graben. Und ja, gefunden habe ich dann letzten Endes schon noch irgendwie alles. Aber es stellte sich dabei auch dieser Eindruck ein, sich auf nahezu völlig ausgestorbenem Gebiet zu bewegen. Fast wie ein Archäologe. Klar, von all den Bibliotheken, Konzepten und Quelltexten aus dieser Zeit ist natürlich auch einiges in Neuentwicklungen eingeflossen und lebt damit bis heute in gewisser Form weiter. Auch heutige Android-Apps beispielsweise basieren letztlich alle auf Java. Und manche IT-Themen überleben sogar dauerhaft in kleinen ökologischen Nischen. So wie manche Bankenanwendung noch in Fortran geschrieben ist oder es immer noch Geldautomaten mit Windows XP gibt. Und folglich gibt es dazu auch noch entsprechende Nischen-Communities, wo auch heute noch zumindest geringe Aktivität besteht. Bei der Handy-Anwendung á la 2007 hingegen stellte sich so ein richtiges Friedhofsgefühl ein. Die Art, wie noch vor wenigen Jahren Handyprogramme entwickelt wurden, all die Forenbeiträge, Diskussionen, Tools und Tutorials sind mausetot und kommen auch nie wieder. Da kommt man schon mal ins Grübeln, was das für eine bizarr kurzlebige Branche ist.

Warum man fremdem Code nicht trauen darf

main():
  1. int a = 0;
  2. printf("Der Wert von Variable 'a' ist %d.\n", a);
  3. BoeseFunktion(4711);
  4. a = 1;
  5. printf("Der Wert von Variable 'a' ist jetzt %d.\n", a);
Was meint ihr, gelingt es mir als Programmierer von BoeseFunktion() dass die Ausgabe in der zweiten Zeile Der Wert von Variable 'a' ist jetzt 0. lautet? Selbst wenn 'a' nur lokal der Main-Funktion bekannt ist? Ich sage ja:
BoeseFunktion():
  1. void BoeseFunktion(int tmp) {
  2.   int* pointer;
  3.   pointer = &tmp - 1;
  4.   *pointer = *pointer + 8;
  5. }
Das funktioniert eigentlich ganz einfach. Mit pointer = &tmp - 1; hole mich mir einen Zeiger auf den ersten Funktionsparameter und reduziere die Adresse um 1. Auf x86-Systemen bin ich nun an der Rücksprungadresse von BoeseFunktion() auf dem Stack. So sieht das im Speicher aus: Stack Und mit *pointer = *pointer + 8; erhöhe ich anschließend die Rücksprungadresse um 8 Byte, was gerade soviel ist um das a = 1; in main() zu überspringen. Solches Gehacke mit Pointer-Arithmetik ist natürlich sowieso pfui. Aber was uns dieses Beispiel eigentlich lehren soll ist, dass man fremden Funktionen nicht trauen soll. Es könnte schließlich sein, dass ich sie geschrieben habe... 😉

Schadensabwehr

Aber wie bekommt man nun das Problem in den Griff, dass Fremdcode, den man in so gut wie jedem Projekt findet, unbeabsichtigt oder beabsichtigt Unfug anstellt und Daten oder die Programmausführung beeinträchtigt? Da gibt es mehrere Möglichkeiten. Unter anderem:
  • Logische Programmablaufkontrolle Man stelle sich vor, jede eigene Funktion meldet sich bei ihrem Aufruf an einer zentralen Instanz mit einem eindeutigen Key. Die zentrale Instanz überprüft dann anhand einer Tabelle ob es in Ordnung ist, dass Funktion3 nun auf Funktion2 folgt oder nicht. Bringt fremder Code - zum Beispiel eine defekte DLL - den Ablauf durcheinander so wird dies schnell aufgedeckt. Voraussetzung für ein solches System ist natürlich eine deterministische Reihenfolge der Funktionsaufrufe.
  • Datenredundanz Zuerst einmal sollten alle Daten, die irgendwie wichtig sind, redundant vorliegen; am besten redundant-invers. Das heißt, es gibt nicht eine Variable 'a', sondern z.B. 'unsigned char u8VentilSollwert' und 'unsigned char u8VentilSollwertInvers'. Das hat nicht nur einen eingängigeren Namen als 'a' sondern informiert uns auch gleich noch über das Präfix u8, dass es eine unsigned 8-Bit-Variable ist mit Wertebereich von 0..255. In der einen steht dann z.B. 1 (binär 0000 0001) und in der anderen das Gegenteil, also 254 (1111 1110). Vor Verwendung wird jedes Mal geprüft, ob die beiden Variablen noch Bit-invers sind und falls nicht mit Fehlermeldung abgebrochen.
  • Datensicherung Wenn die Daten umfangreicher sind, gibt es die Möglichkeit sie mit einer Checksumme zu sichern. Wird die Checksumme oder die Daten beschädigt, so fällt dies auf, sobald man die Checksumme nachrechnet und mit dem Prüfwert vergleicht.

PC-Lüfter: Tachosignal mit Kondensator

PC-Lüfter machen umso mehr Lärm, umso schneller sie laufen. Wenn man sie aber nur mit reduzierter Drehzahl laufen lässt, dann kühlen sie natürlich leider auch entsprechend weniger. Für meinen Desktop-PC habe ich mir darum einen 220mm-Lüfter besorgt, mit dem Gedanken, dass dieser auch bei halber Drehzahl noch soviel Luftstrom produziert wie zwei laute 80mm-Lüfter. Allerdings habe ich beim Kauf nicht darauf geachtet, dass er nur einen 4-pin-Molexstecker hat und mit diesem konstant auf 12V läuft. Darum habe ich einen alten 60mm-CPU-Lüfter um dessen Kabel erleichtert und an den neuen Fan gelötet:
camper mover
Hat sogar einen Signalausgang (an gelbem Kabel) und einen Hall-Sensor - leider fehlt ein Teil der Elektronik

So sollte er sich an die Lüfterregelung anschließen und regeln lassen. Hm, geht aber nicht, das Tachosignal fehlt. Und warum? Weil der Hersteller bei diesem Modell (AK-F2230SM-CB) 5 Cent gespart hat indem er einen Transistor und zwei Widerstände weggelassen hat. Toll. Den Hall-Sensor hingegen haben sie eingebaut. Na gut, was tut man dann als schnellen Workaround? → man lötet einen Kondensator zwischen Plus und Signal, dann ist auch die Lüfterregelung wieder glücklich:
camper mover
Mit dem Dremel etwas Platz gemacht, damit der Aufkleber danach wieder glatt aufliegt

Ich hatte einen 100nF-Kondensator zu Hand; ein etwas anderer Wert ist aber vermutlich auch nicht kritisch.

Qt: bloß keine Waisenkinder instantiieren

Wenn man in Qt eine Klasse implementiert, sollte man tunlichst darauf achten, dass alle instantiierten Attribute this als parent haben. Andernfalls kann es nämlich sein, dass irgendwann der Garbage-Kollektor auf das offenbar elternlose Kind-Objekt stößt und es im Sinne von "ist das Kunst oder kann das weg" in die Tonne wirft. Und dann hat man den Salat. Ist mir hiermit passiert:
  1. class Measurement : public Object
  2. {
  3. Q_OBJECT
  4. public:
  5.   Measurement(QObject* parent = 0);
  6.   ~Measurement();
  7. };
  8. class XYPlot : public QQuickPaintedItem
  9. {
  10. Q_OBJECT
  11. public:
  12.   XYPlot(QQuickPaintedItem* parent = 0);
  13. private:
  14.   Measurement m_Measurement;
  15. };
Das Elternelement hier ist XYPlot, welches ein Attribut der Klasse Measurement besitzt. Bei mir gab es unreproduzierbare SIGSEGV segmentation faults, was immer auf irgendein Problem mit ungültigem Pointer hinweist. Beim Debuggen habe ich dann festgestellt, dass kurz zuvor der Dekonstruktor von Measurement aufgerufen wurde - obwohl das zugehörige XYPlot-Objekt noch existierte. Woran lag es? ⇒ Ich hatte vergessen, dem Kind-Objekt zu sagen wer sein parent ist. Im Konstruktor der Elternklasse XYPlot also m_Measurement mit this instantiieren:
  1. XYPlot::XYPlot( QQuickPaintedItem* parent) : QQuickPaintedItem(parent), m_Measurement(this)
  2. {
  3. }
Also besser gut aufpassen bei so was, sonst kann das Debugging richtig ätzend werden.