Die globale Welt von 1970
« | 21 Oct 2018 | »Es ist schon klar, dass eigentlich alles “aus der Historie” entstanden ist. Kein System, das älter als 5 Jahre ist, leidet nicht an seinen Design-Entscheidungen aus der Vergangenheit.
Wie auch immer … manche Dinge sind schon ein bisschen unverzeihlich, wenn wir im 21. Jahrhundert immer noch mit “globalen” Zuständen und Variablen konfrontiert sind, nur weil um 1970 noch keiner objektorientiert nachgedacht hat.
Als Unix “Prozesse” als logische Einheit einführte, muss das wohl der heilige Gral und etwas überaus Fortschrittliches gewesen sein.
Und so wie in der Physik Atome noch vor 200 Jahren wirklich unteilbar waren, so dachte niemand daran, dass Prozesse einmal logische Untereinheiten - heute als Threads bekannt - haben würden.
Und so wurde die dunkle Saat in Form von globalen Variablen in alle APIs
eingesetzt. Globale Signalhandler,
globale Directory/File-Creation-Masks
, und was
fork()
in einem Threading-Kontext alles verbrechen kann, will ich gar nicht ausführen …
Windows NT aus den 1990ern hätte hier ein Messias sein können und tatsächlich, Threads und ganz allgemein HANDLEs und TOKEN lassen uns Zustände schön objektorientiert zuordnet.
… alle Zustände? Nein! Eine kleine Gruppe von Schnittstellen wiedersetzt sich hartnäckig dem guten Konzept und wirft letztendlich wieder alles um.
Privilegien, die nur prozessweit aktivierbar sind, APIs die den Thread-Token ignorieren und sich auf den originalen Prozess-Token fixieren …
Tand, Tand ist das Gebilde von Menschenhand
Eben so schlimm sind Eigenschaften, die sich ungefragt vererben und nicht
“atomar” ein bzw. ausgeschaltet werden können.
Mein liebstes Beispiel sind hier
SOCKETs, die mal
grundsätzlich an Kindprozesse vererbt werden. Wenn ein Thread Sockets
erzeugt, und ein anderer Thread Kindprozesse startet, können wir den
Weihnachtsmann fragen, wer welche Geschenke erhalten hat, die er gar nicht
haben wollte.
In Linux müssten wir nach dem fork()
im Kind “alle” möglichen und
unmöglichen Deskriptoren schließen, bevor wir zu exec()
kommen dürfen.
Und Windows gibt uns zwar SetHandleInformation()
, was aber erst nach
der Erzeugung eines Sockets aufgerufen werden kann … und da ist es
vielleicht auch schon zu spät.
Fazit
Bitte liebe OS-Architekten, bitte unterlasst globale Seiteneffekte.
Und ja, ich weiß dass man Cleanup-Callbacks vor fork() einsetzen kann, und dass es WSA* Routinen gibt, die ein bisschen mehr können. Leider ist trotzdem nicht alles abgedeckt und mal ehrlich … soll sich jetzt jede Ressource in einer “globalen” Liste registrieren und de-registrieren, damit im Falle eines notwendigen Zwischen-Cleanups alles durchgearbeitet werden kann?
Nö! Die Lösung ist alle Seiteneffekte im Vorfeld zu deaktivieren und erst bei Bedarf einschaltbar zu machen … UND NICHT UMGEKEHRT!