Unix-Philosophie

aus Wikipedia, der freien Enzyklopädie

Die Unix-Philosophie ist eine Menge von Regeln und Herangehensweisen bei der Software, die auf den Erfahrungen der führenden Unix-Programmierer basieren.

McIlroy: A Quarter Century of Unix

Douglas McIlroy, der Erfinder der Unixpipes, fasste die Philosophie folgendermaßen zusammen:

  • Schreibe Computerprogramme so, dass sie nur eine Aufgabe erledigen und diese gut machen.
  • Schreibe Programme so, dass sie zusammenarbeiten.
  • Schreibe Programme so, dass sie Textströme verarbeiten, denn das ist eine universelle Schnittstelle.

Gewöhnlich wird das verkürzt zu: „Mache nur eine Sache und mache sie gut.“

Von diesen drei Lehrsätzen sind vor allem die ersten beiden nicht auf Unix beschränkt, jedoch betonen Unix-Programmierer alle drei Lehrsätze stärker als andere Programmierer.

Pike: Notes on Programming in C

Rob Pike schlägt die folgenden Regeln als Grundsätze des Programmierens vor,[1] jedoch kann man sie genauso als Kerngedanken der Unix-Philosophie sehen:

  • Regel 1: Man kann nicht sagen, welcher Programmteil den Hauptteil der Leistung fressen wird. Da Engpässe oft an überraschenden Stellen auftauchen, soll man nichts zur Erhöhung der Geschwindigkeit einbauen, bevor man gezeigt hat, wo der Engpass sitzt.
  • Regel 2: Miss die Programmlaufzeit. Feile erst an der Geschwindigkeit, wenn du sie gemessen hast und selbst dann erst, wenn der betrachtete Teil einen dominierenden Anteil der Rechenzeit frisst.
  • Regel 3: Hochgezüchtete Algorithmen sind langsam, wenn die Eingabedatenmenge (siehe Komplexitätstheorie) klein ist und das ist der Normalfall. Hochgezüchtete Algorithmen haben große Fixkosten. Solange man nicht weiß, dass häufig große Werte annehmen wird, sollte man auf hochgezüchtete Algorithmen verzichten. (Und selbst, wenn groß wird, gilt zuerst Regel 2.)
  • Regel 4: Hochgezüchtete Algorithmen sind fehleranfälliger als einfache und viel schwieriger zu implementieren. Benutze sowohl einfache Algorithmen als auch einfache Datenstrukturen.
  • Regel 5: Daten sind wichtiger. Wenn du die richtigen Datenstrukturen gewählt hast und alles gut gestaltet ist, werden sich die Algorithmen fast immer von alleine ergeben. Datenstrukturen sind das zentrale Thema des Programmierens, nicht Algorithmen.
  • Regel 6: Es gibt keine Regel 6.

Die Regeln 1 und 2 wiederholen den berühmten Grundsatz von Donald E. Knuth: „Zu frühe Optimierung ist die Wurzel allen Übels.“ Ken Thompson formulierte die Regeln 3 und 4 als „Verwende im Zweifelsfall rohe Gewalt.“; diese Regeln sind Beispiele für das KISS-Prinzip. Regel 5 stammt von Fred Brooks, der sie im Buch Vom Mythos des Mann-Monats erwähnt hat und wird oft als „Schreibe dummen Code, der schlaue Daten verwendet.“ formuliert. Die Regel 6 ist dem Sketch von Bruces aus Monty Python’s Flying Circus (Folge 22) entlehnt.

Mike Gancarz: The UNIX Philosophy

1994 veröffentlichte Mike Gancarz (er gehörte zu den Entwicklern des X Window Systems) seine Erfahrungen mit Unix in Form des Buches The Unix Philosophy, das sich auch auf Anekdoten aus Diskussionen mit Kollegen stützt sowie mit Leuten aus anderen Gebieten, die selbst auf Unix angewiesen waren. Darin werden neun Hauptforderungen genannt:

  1. Klein ist schön.
  2. Gestalte jedes Programm so, dass es eine Aufgabe gut erledigt.
  3. Erzeuge so bald wie möglich einen funktionierenden Prototypen.
  4. Bevorzuge Portierbarkeit vor Effizienz.
  5. Speichere Daten in einfachen Textdateien.
  6. Nutze die Hebelwirkung der Software zu deinem Vorteil.
  7. Verwende Shellskripte, um die Hebelwirkung und die Portierbarkeit zu verbessern.
  8. Vermeide Benutzeroberflächen, die den Benutzer fesseln.
  9. Mache jedes Programm zu einem Filter.

Die folgenden zehn weniger strengen Forderungen werden nicht allgemein als Teil der Unixphilosophie akzeptiert und führten zum Teil auch zu heftigen Debatten (beispielsweise, ob ein monolithischer Kernel oder ein Mikrokernel bevorzugt werden solle):

  1. Lasse den Benutzer die Umgebung nach seinen Bedürfnissen festlegen.
  2. Mache Betriebssystemkerne klein und leichtgewichtig.
  3. Schreib klein und halte Befehlsnamen kurz.
  4. Verschone Bäume.
  5. Schweigen ist Gold.
  6. Denke parallel.
  7. Die Summe der Teile ist mehr als das Ganze.
  8. Suche die 90/10-Lösung.
  9. Schlechter ist besser.
  10. Denke hierarchisch.

Schlechter ist besser

Richard P. Gabriel behauptet, ein grundlegender Vorteil von Unix komme von einer Designphilosophie, die er als schlechter ist besser bezeichnet. Nach dieser Philosophie ist die Einfachheit sowohl der Benutzerschnittstelle als auch der Umsetzung im Programm viel wichtiger als jede andere Eigenschaft des Systems – inklusive Eigenschaften wie Fehlerfreiheit, Konsistenz und Vollständigkeit. Gabriel argumentiert, dass diese Vorgehensweise grundlegende Vorteile bei der Weiterentwicklung der Software biete, allerdings zweifelt er auch an der Qualität der Programme bei so mancher Umsetzung dieser Vorgehensweise.

Beispielsweise besaßen die ersten Unixsysteme einen rein monolithischen Kernel; Benutzerprozesse, die Kernelfunktionsaufrufe durchführten, verwendeten für diese den Userstack. Wenn nun ein Signal an einen Prozess geschickt werden sollte, während dieser durch einen länger andauernden Kernelfunktionsaufruf blockiert war, konnte der Signalhandler nicht ausgeführt werden – denn es befanden sich möglicherweise kritische Daten für den Kernel auf dem Stack. Was sollte getan werden? Eine Möglichkeit wäre, mit dem Signal zu warten, bis der Kernelaufruf beendet ist – das kann jedoch sehr lange dauern, manchmal zu lange. Eine weitere Möglichkeit wäre, den Kernelaufruf unter der Voraussetzung, dass beim Signalhandler alles glattläuft, zwischenzuspeichern, um ihn später fortsetzen zu können.

In solchen Fällen bevorzugten Ken Thompson und Dennis Ritchie Einfachheit vor Perfektion. Wenn ein solcher Fall eintritt, beendet der Kernel den Funktionsaufruf mit einer Fehlermeldung, die besagt, dass der Funktionsaufruf nicht ausgeführt wurde (dies ist der Interrupted System Call mit der Fehlernummer 4 = EINTR; diese Unterbrechung kam natürlich vom Signalhandler). Das kommt nur bei einer Handvoll lang andauernder Systemaufrufe wie z. B. read(), write(), open(), select() usw. vor. Diese Vorgehensweise hat den Vorteil, dass sie das I/O-System wesentlich einfacher macht (da Sonderfälle nicht berücksichtigt werden müssen). Die meisten Programme stört das nicht, weil sie sowieso keine Signale verwenden bzw. sich bei einem SIGINT beenden. Die paar wenigen Programme, die Signale verwenden, können auf dieses Problem reagieren, indem sie die Kernelfunktionsaufrufe mit einem Wrapper umgeben, der bei einem aufgetretenen EINTR den Aufruf gleich noch einmal wiederholt. Damit ist das Problem auf eine einfache Art gelöst.

Aus diesen Gründen war Unix in seiner Anfangszeit das Betriebssystem, das am häufigsten abstürzte (mehrmals pro Tag), allerdings auch den schnellsten Neustart hatte. Wegen seiner Einfachheit wurde innerhalb von zehn Jahren aus Unix das stabilste System, das mit fehlerfreien Laufzeiten im Bereich von Monaten und Jahren statt Stunden aufwarten konnte.

Raymond: The Art of Unix Programming

Eric S. Raymond fasst in seinem Buch The Art of Unix Programming die Unixphilosophie mit der allseits bekannten Ingenieursweisheit Keep it Simple, Stupid (KISS) zusammen. Anschließend beschreibt er, wie diese Grundhaltung seiner Meinung nach in der Praxis der Unixkultur umgesetzt wird (wobei in der Praxis natürlich gelegentlich sehr deutlich gegen diese Regeln verstoßen wird):

  • Regel der Modularität: Schreibe einfache Bestandteile, die durch saubere Schnittstellen verbunden werden.
  • Regel der Klarheit: Klarheit ist besser als Gerissenheit.
  • Regel des Zusammenbaus: Entwirf Programme so, dass sie mit anderen Programmen verknüpft werden können.
  • Regel der Trennung: Trenne den Grundgedanken von der Umsetzung, trenne die Schnittstellen von der Verarbeitungslogik.
  • Regel der Einfachheit: Entwirf mit dem Ziel der Einfachheit; füge Komplexität nur hinzu, wenn es unbedingt sein muss.
  • Regel der Sparsamkeit: Schreibe nur dann ein großes Programm, wenn sich klar zeigen lässt, dass es anders nicht geht.
  • Regel der Transparenz: Entwirf mit dem Ziel der Durchschaubarkeit, um die Fehlersuche zu vereinfachen.
  • Regel der Robustheit: Robustheit ist das Kind von Transparenz und Einfachheit.
  • Regel der Darstellung: Stecke das Wissen in die Datenstrukturen, so dass die Programmlogik dumm und robust sein kann.
  • Regel der geringsten Überraschung: Mache beim Entwurf der Schnittstellen immer das Nächstliegende, welches für die wenigsten Überraschungen beim Benutzer sorgt.
  • Regel der Stille: Wenn ein Programm nichts Überraschendes zu sagen hat, soll es schweigen.
  • Regel des Reparierens: Wenn das Programm scheitert, soll es das lautstark und so früh wie möglich tun.
  • Regel der Wirtschaftlichkeit: Die Arbeitszeit von Programmierern ist teuer; spare sie auf Kosten der Rechenzeit.
  • Regel der Code-Generierung: Vermeide Handarbeit; schreibe Programme, die Programme schreiben, falls möglich.
  • Regel der Optimierung: Erstelle Prototypen, bevor du dich an den Feinschliff machst. Mache es lauffähig, bevor du es optimierst.
  • Regel der Vielseitigkeit: Misstraue allen Ansprüchen auf „den einzig wahren Weg“.
  • Regel der Erweiterbarkeit: Entwirf für die Zukunft, denn sie wird schneller kommen als du denkst.

Viele dieser Normen werden auch außerhalb der Unix-Gemeinde anerkannt[2] – wenn sie nicht zuerst bei Unix verwendet wurden, wurden sie bald übernommen. Trotzdem betrachten Unix-Experten eine Kombination dieser Regeln als die Grundlage des Unix-Stils.

Rolle des Betriebssystems

Obige Aussagen beschreiben, welche Eigenschaften Programme haben, die Unix zu dem machen, was es ist. Ein weiterer Aspekt der Unix-Philosophie betrifft jedoch auch das Betriebssystem selbst: Damit Programme möglichst einfach, klar und modular gehalten werden können, damit sie gut zusammenarbeiten können und damit sie gut portierbar sein können, muss das Betriebssystem die entsprechenden Voraussetzungen in Form von klaren Schnittstellen und hoher Abstraktion schaffen. In der Praxis:

Alles ist eine Datei

  • Der Zugriff sowohl auf lokale Laufwerke wie auch Netzlaufwerke erfolgt über dieselbe Verzeichnisstruktur; es gibt nicht verschiedene Laufwerke, sondern alles sind Verzeichnisse und Dateien in derselben Baumstruktur.
  • Virtuelle Laufwerke können ebenfalls problemlos realisiert werden, denn sie erscheinen ebenfalls nur als Verzeichnis. Jede Image-Datei an jedem Ort kann durch mounten in den Verzeichnisbaum an jeder Stelle eingebunden werden.
  • Auch der Zugriff auf Geräte erfolgt über das Dateisystem. Einem Gerätetreiber wird eine Gerätedatei im Verzeichnis /dev zugeordnet; durch Lesen und Schreiben dieser Datei kann ein Programm mit dem Gerätetreiber kommunizieren.
  • Auf Kernel-Daten kann ebenfalls über die Verzeichnisstruktur zugegriffen werden, und zwar über das Verzeichnis /proc.

Client-Server-Modell

Kommunikation erfolgt grundsätzlich über Netzwerk-Verbindungen. Auch die interne Kommunikation zwischen beispielsweise Client-Programmen und Daemons wird über Netzwerkschnittstellen geführt, so dass die Programmierung einheitlich ist und die Programme auch wahlweise über das Netzwerk verwendet werden können.

Aus diesem Grund gibt es bei Unix nicht für jedes Anwendungsgebiet eine spezialisierte Programmierschnittstelle, sondern auch vergleichsweise exotische Anwendungen werden auf Dateien oder Netzwerkverbindungen abgebildet.

Zitate

  • „Unix ist einfach. Es erfordert lediglich ein Genie, um seine Einfachheit zu verstehen.“ (Dennis Ritchie)
  • „Unix wurde nicht entwickelt, um seine Benutzer daran zu hindern, dumme Dinge zu tun, denn das würde diese auch davon abhalten, schlaue Dinge zu tun.“ (Doug Gwyn)
  • „Unix sagt niemals ›bitte‹.“ (Rob Pike)

Siehe auch

Weblinks

Einzelnachweise

  1. Notes on Programming in C
  2. Etwa Allen I. Holub: Enough Rope to Shoot Yourself in the Foot: Rules for C and C++ Programming. McGraw-Hill 1995.