Nicht gut gealtert

Posted on 08.09.2024

Ich pflege ja nun seit geraumer Zeit den Code einiger uralter freier Softwareprojekte, deren Entwickler teilweise sogar schon verstorben sind.

Dabei geht es zur Zeit nur darum, die Programme funktionsfähig zu halten, auch wenn sich die Umgebung verändert, in der sie arbeiten.

Ich will eigentlich mal zusammenschreiben, warum ich das mache, aber darum geht es mir heute Abend nicht. Dieser Post beschreibt, wie sich die Umgebung verändert, anhand zweier Beispiele.

Striktere Compiler

Der Code ist zum guten Teil 20 Jahre, einiger sogar über 30 Jahre alt. Es ist C-Code, und was seine Semantik betrifft, hat sich seitdem kaum etwas geändert. Die gängigen Compiler, gcc und clang, sind hingegen ein ganzes Stück empfindlicher geworden: “Altes C” erlaubte die Nutzung undeklarierter Funktionen und hat den Stack einfach so zusammengebaut, wie es die übergebenen Argumente erlaubten. Der Compiler hat gegebenenfalls eine Warnung ausgespuckt, es aber ansonsten toleriert.

Ich hatte tla vor etwa einem Jahr das letzte Mal angefasst und da passte der Code durch den Compiler. Dieses Jahre brauchte es einige zusätzliche includes, damit der Compiler glücklich war.

Auf der Habenseite kann der Compiler dadurch besser kontrollieren, dass auch alles so aufgerufen wird, wie es sein soll, es werden also Fehlerquellen minimiert.

Es gibt einige weitere Aspekte, die in letzter Zeit verstärkt moniert werden, die werde ich wohl auch noch aufräumen müssen. Das sind zum einen Inkompatibilitäten zwischen signed und unsigned, zum anderen const und nicht-const. Das sind zur Zeit nur Warnungen, aber ich würde mich nicht wundern, wenn das (über einen längeren Zeitraum) strikter gehandhabt wird.

Praktisch unbegrenzte Namensräume

Es gibt Code in tla (beziehungsweise hackerlib), der von einer relativ kleinen Anzahl möglicher Dateideskriptoren pro Prozess ausgeht. Für “virtuelle” Deskriptoren werden Zahlen jenseits dieses Limits genutzt, da diese garantiert nie vom Betriebssystem vergeben werden. Die Bibliothek hält ein Array vor, in dem für jeden Deskriptor, real oder virtuell, ein Pointer auf eine Datenstruktur vorgehalten werden kann.

Das ist trivial, wenn es maximal 1024 solcher offenen Deskriptoren geben kann. Es ist erträglich, wenn es 1024×1024 gibt (bei 8 Bytes pro Pointer werden damit 8MB verschwendet). Eine weitere Erhöhung um den Faktor 1024 wird hingegen schon wirklich schmerzhaft, und tla reagierte mit einer “Panic”, weil das Betriebssystem soviel Speicher nicht zur Verfügung stellen will.

Fürs Erste habe ich in meiner Umgebung die Anzahl per ulimit -n reduziert, aber mittelfristig muss ich die Datenstruktur wohl darauf einrichten, nur so viele Pointer speichern zu wollen, wie auch tatsächlich gebraucht werden.