Warum gibt es eine Linux-Kernelrichtlinie, die den Benutzerraum niemals aufteilt?

38

Ich fing an, über dieses Problem im Kontext der Etikette auf der Linux-Kernel-Mailingliste nachzudenken. Der Linux-Kernel ist das bekannteste und wohl erfolgreichste und wichtigste freie Software-Projekt der Welt. Und der Projektgründer und Leiter, Linus Torvalds, braucht hier eindeutig keine Einführung.

Linus zieht gelegentlich Kontroversen mit seinen Flammen auf dem LKML an. Diese Flammen haben, wie er selbst zugibt, häufig damit zu tun, den Benutzerraum zu durchbrechen. Was mich zu meiner Frage bringt.

Kann ich eine historische Perspektive haben, warum es so schlimm ist, den Benutzerraum zu knacken? Soweit ich weiß, sind für die Aufteilung des Benutzerraums Korrekturen auf Anwendungsebene erforderlich. Ist dies jedoch eine schlechte Sache, wenn der Kernelcode verbessert wird?

So wie ich es verstehe, besagt Linus 'erklärte Richtlinie, dass das Nicht-Brechen des Benutzerraums alles andere übertrumpft, einschließlich der Codequalität. Warum ist das so wichtig und was sind die Vor- und Nachteile einer solchen Politik?

(Eine solche, konsequent angewandte Politik hat eindeutig einige Nachteile, da Linus gelegentlich "Meinungsverschiedenheiten" mit seinen Top-Leutnants im LKML zu genau diesem Thema hat. Soweit ich das beurteilen kann, ist er in dieser Angelegenheit immer auf dem richtigen Weg.)

Faheem Mitha
quelle
1
Sie haben den Namen von Linus in der Einleitung falsch geschrieben.
Ismael Miguel
Ich war mir nicht sicher, aber ich vergaß zu stimmen und gab jetzt meine Stimme ab.
Ismael Miguel
1
Siehe auch
Faheem Mitha

Antworten:

38

Der Grund ist kein historischer, sondern ein praktischer. Es gibt viele, viele Programme, die auf dem Linux-Kernel laufen. Wenn eine Kernel-Schnittstelle diese Programme beschädigt, müsste jeder diese Programme aktualisieren.

Nun ist es wahr, dass die meisten Programme tatsächlich nicht direkt von Kernel-Interfaces (den Systemaufrufen ) abhängen , sondern nur von Interfaces der C-Standardbibliothek (C- Wrapper um die Systemaufrufe). Oh, aber welche Standardbibliothek? Glibc? uClibC? Dietlibc? Bionic? Musl? etc.

Es gibt aber auch viele Programme, die betriebssystemspezifische Dienste implementieren und von Kernel-Schnittstellen abhängen, die von der Standardbibliothek nicht verfügbar gemacht werden. (Unter Linux werden viele davon über /procund angeboten /sys.)

Und dann gibt es statisch kompilierte Binärdateien. Wenn ein Kernel-Upgrade einen dieser Fehler verursacht, besteht die einzige Lösung darin, sie erneut zu kompilieren. Wenn Sie die Quelle haben: Linux unterstützt auch proprietäre Software.

Selbst wenn die Quelle verfügbar ist, kann es schmerzhaft sein, alles zu sammeln. Vor allem, wenn Sie Ihren Kernel aktualisieren, um einen Fehler mit Ihrer Hardware zu beheben. Leute aktualisieren ihren Kernel oft unabhängig vom Rest ihres Systems, weil sie die Hardwareunterstützung benötigen. Mit den Worten von Linus Torvalds :

Das Brechen von Benutzerprogrammen ist einfach nicht akzeptabel. (…) Wir wissen, dass die Leute jahrelang alte Binaries verwenden und dass ein neues Release nicht bedeutet, dass man das einfach rausschmeißen kann. Sie können uns vertrauen.

Er erklärt auch, dass ein Grund, dies zu einer strengen Regel zu machen, darin besteht, die Abhängigkeitshölle zu vermeiden, bei der Sie nicht nur ein anderes Programm aktualisieren müssen, um einen neueren Kernel zum Laufen zu bringen, sondern auch noch ein weiteres und ein weiteres Programm aktualisieren müssen , weil alles von einer bestimmten Version von allem abhängt.

Es ist einigermaßen in Ordnung, eine wohldefinierte Einwegabhängigkeit zu haben. Es ist traurig, aber manchmal unvermeidlich. (…) Was NICHT in Ordnung ist, ist eine wechselseitige Abhängigkeit. Wenn User-Space-HAL-Code von einem neuen Kernel abhängt, ist das in Ordnung, obwohl ich vermute, dass Benutzer hoffen würden, dass es nicht der "Kernel der Woche", sondern eher der "Kernel der letzten Monate" ist.

Aber wenn Sie eine ZWEI-WEG-Abhängigkeit haben, sind Sie geschraubt. Das bedeutet, dass Sie im Sperrschritt upgraden müssen, und das ist einfach NICHT AKZEPTIERBAR. Es ist für den Benutzer schrecklich, aber was noch wichtiger ist, es ist für Entwickler schrecklich, weil es bedeutet, dass Sie nicht "ein Fehler ist passiert" sagen und Dinge wie versuchen können, ihn mit Halbierung oder ähnlichem einzugrenzen.

Im Benutzerbereich werden diese gegenseitigen Abhängigkeiten normalerweise durch Beibehalten verschiedener Bibliotheksversionen aufgelöst. Sie dürfen jedoch nur einen Kernel ausführen, daher muss dieser alles unterstützen, was die Leute damit tun möchten.

offiziell ,

Die Abwärtskompatibilität für [Systemaufrufe für stabil erklärt] wird für mindestens 2 Jahre garantiert.

In der Praxis jedoch

Die meisten Schnittstellen (wie Syscalls) sollten sich niemals ändern und immer verfügbar sein.

Was sich jedoch häufiger ändert, sind Schnittstellen, die nur für hardwarebezogene Programme vorgesehen sind /sys. ( /procAuf der anderen Seite, die seit der Einführung von /sysnicht hardwarebezogenen Diensten so gut wie nie auf inkompatible Weise unterbrochen wurde.)

Zusammenfassend,

Das Aufteilen des Benutzerraums würde Korrekturen auf Anwendungsebene erfordern

und das ist schlecht, weil es nur einen Kernel gibt, den die Leute unabhängig vom Rest ihres Systems aktualisieren möchten, aber es gibt viele verschiedene Anwendungen mit komplexen Abhängigkeiten. Es ist einfacher, den Kernel stabil zu halten, als Tausende von Anwendungen in Millionen verschiedener Setups auf dem neuesten Stand zu halten.

Gilles 'SO - hör auf böse zu sein'
quelle
1
Danke für die Antwort. Die als stabil deklarierten Schnittstellen sind also eine Obermenge der POSIX-Systemaufrufe. Meine Frage zur Geschichte ist, wie sich diese Praxis entwickelt hat. Vermutlich haben sich die Originalversionen des Linux-Kernels anfangs keine Gedanken über Speicherplatzprobleme gemacht.
Faheem Mitha
3
@FaheemMitha Ja, seit 1991 . Ich glaube nicht, dass sich der Ansatz von Linus weiterentwickelt hat. Es waren immer "Schnittstellen für normale Anwendungen ändern sich nicht, Schnittstellen für Software, die sehr stark an den Kernel gebunden sind, ändern sich sehr sehr selten".
Gilles 'SO - hör auf, böse zu sein'
24

In jedem voneinander abhängigen System gibt es grundsätzlich zwei Möglichkeiten. Abstraktion und Integration. (Ich verwende absichtlich keine Fachbegriffe). Mit Abstraction sagen Sie, dass beim Aufrufen einer API der Code hinter der API zwar geändert werden kann, das Ergebnis jedoch immer gleich ist. Wenn wir beispielsweise anrufen, ist fs.open()es uns egal, ob es sich um ein Netzwerklaufwerk, eine SSD oder eine Festplatte handelt, wir erhalten immer einen offenen Dateideskriptor, mit dem wir etwas anfangen können. Ziel von "Integration" ist es, die "beste" Möglichkeit zu bieten, etwas zu tun, auch wenn sich die Art und Weise ändert. Das Öffnen einer Datei kann beispielsweise für eine Netzwerkfreigabe anders sein als für eine Datei auf der Festplatte. Beide Möglichkeiten werden im modernen Linux-Desktop ziemlich häufig verwendet.

Aus Entwicklersicht handelt es sich um "Arbeitet mit jeder Version" oder "Arbeitet mit einer bestimmten Version". Ein gutes Beispiel dafür ist OpenGL. Die meisten Spiele funktionieren mit einer bestimmten Version von OpenGL. Es spielt keine Rolle, ob Sie aus dem Quellcode kompilieren. Wenn das Spiel für OpenGL 1.1 geschrieben wurde und Sie versuchen, es auf 3.x laufen zu lassen, werden Sie keine gute Zeit haben. Am anderen Ende des Spektrums wird erwartet, dass einige Anrufe funktionieren, egal was passiert. Zum Beispiel möchte ich anrufen, ich möchte fs.open()mich nicht darum kümmern, auf welcher Kernel-Version ich bin. Ich möchte nur einen Dateideskriptor.

Für jeden Weg gibt es Vorteile. Die Integration bietet "neuere" Funktionen auf Kosten der Abwärtskompatibilität. Während Abstraktion Stabilität gegenüber "neueren" Aufrufen bietet. Es ist wichtig zu wissen, dass dies Priorität hat und nicht die Möglichkeit.

Aus kommunaler Sicht ist Abstraktion in einem komplexen System ohne wirklich guten Grund immer besser. Stellen Sie sich zum Beispiel vor, Sie würden fs.open()je nach Kernel-Version unterschiedlich arbeiten. Dann müsste eine einfache Interaktionsbibliothek für das Dateisystem mehrere hundert verschiedene Methoden zum Öffnen von Dateien (oder wahrscheinlich Blöcke) verwalten. Wenn eine neue Kernel-Version herauskommt, können Sie nicht "upgraden", sondern müssen jede einzelne von Ihnen verwendete Software testen. Kernel 6.2.2 (Fake) kann Ihren Texteditor beschädigen.

Für einige Beispiele aus der Praxis ist es OSX normalerweise egal, wie viel Speicherplatz für Benutzer zur Verfügung steht. Sie zielen häufiger auf "Integration" als auf "Abstraktion". Und bei jedem größeren Betriebssystem-Update geht es schief. Das heißt nicht, dass ein Weg besser ist als der andere. Es ist eine Wahl und Designentscheidung.

Am wichtigsten ist, dass das Linux-Ökosystem mit großartigen Open Source-Projekten gefüllt ist, in denen Menschen oder Gruppen in ihrer Freizeit an dem Projekt arbeiten oder weil das Tool nützlich ist. In diesem Sinne werden diese Entwickler in dem Moment, in dem es keinen Spaß mehr macht und anfängt, PIA zu werden, woanders hingehen.

Ich habe zum Beispiel einen Patch an gesendet BuildNotify.py. Nicht weil ich altruistisch bin, sondern weil ich das Tool benutze und eine Funktion wollte. Es war einfach, also hier einen Patch. Wenn es kompliziert oder umständlich wäre, würde ich es nicht benutzen BuildNotify.pyund ich würde etwas anderes finden. Wenn mein Texteditor bei jedem Kernel-Update kaputt gehen sollte, würde ich einfach ein anderes Betriebssystem verwenden. Meine Beiträge zur Community (so klein sie auch sein mögen) würden nicht weiter bestehen oder existieren und so weiter.

Also wurde die Designentscheidung getroffen, um Systemaufrufe zu abstrahieren, so dass fs.open()es funktioniert , wenn ich es tue . Das bedeutet, fs.openlange nach fs.open2()der Popularität zu bleiben.

Historisch ist dies das Ziel von POSIX-Systemen im Allgemeinen. "Hier finden Sie eine Reihe von Aufrufen und erwarteten Rückgabewerten, die Sie in der Mitte herausfinden." Wieder aus Gründen der Portabilität. Warum sich Linus für diese Methode entscheidet, hängt von seinem Gehirn ab, und Sie müssten ihn fragen, warum. Wenn ich es jedoch wäre, würde ich die Abstraktion der Integration in einem komplexen System vorziehen.

coteyr
quelle
1
Die API für den Benutzerbereich, die "Syscall" -API, ist genau definiert (insbesondere die POSIX-Teilmenge) und stabil, da das Entfernen eines Teils davon die Software beschädigt, die möglicherweise von Benutzern installiert wurde. Was es nicht hat, ist eine stabile Treiber- API.
pjc50
4
@FaheemMitha, es ist umgekehrt. Es steht den Kernel-Entwicklern frei, die Treiber-API zu unterbrechen, wann immer sie dies wünschen, solange sie alle kernelinternen Treiber vor der nächsten Version reparieren. Es geht darum, die Userspace-API zu brechen oder sogar Nicht-API-Dinge zu tun, die den Userspace brechen könnten und epische Reaktionen von Linus hervorrufen.
Mark
4
Zum Beispiel, wenn jemand beschließt, es zu ändern, indem er unter bestimmten Umständen einen anderen Fehlercode von ioctl () zurückgibt : lkml.org/lkml/2012/12/23/75 (enthält Flüche und persönliche Angriffe auf den verantwortlichen Entwickler). Dieser Patch wurde abgelehnt, weil er PulseAudio und damit alle Audiodaten auf GNOME-Systemen beschädigt hätte.
pjc50
1
@FaheemMitha, im Grunde def add (a, b); return a + b; end --- def add (a, b); c = a + b; return c; end --- def add (a, b); c = a + b + 10; return c - 10; Ende - sind alle die "gleiche" Implementierung von add. Was ihn so aufregt, ist, wenn die Leute auf jeden Fall hinzufügen (a, b); return (a + b) * -1; end Grundsätzlich ist es in Ordnung zu ändern, wie "interne" Dinge im Kernel funktionieren. Ändern, was zu einem definierten und "öffentlichen" API-Aufruf zurückgegeben wird, ist nicht. Es gibt zwei Arten von API-Aufrufen "privat" und "öffentlich". Er ist der Ansicht, dass öffentliche API-Aufrufe niemals ohne guten Grund geändert werden sollten.
Coteyr
3
Ein Beispiel ohne Code; Wenn Sie in den Laden gehen, kaufen Sie 87 Oktangas. Als Verbraucher ist es Ihnen "egal", woher das Gas stammt oder wie es verarbeitet wurde. Es ist dir einfach wichtig, dass du Benzin bekommst. Wenn das Gas einen anderen Raffinierungsprozess durchlaufen hat, ist es Ihnen egal. Sicher kann sich der Veredelungsprozess ändern. Es gibt sogar verschiedene Ölquellen. Aber was Sie interessiert, ist 87 Oktangas zu bekommen. Seine Position ist also, Quellen zu wechseln, Raffinerien zu wechseln, was auch immer, solange an der Pumpe 87 Oktangas austritt. All das Zeug "hinter den Kulissen" spielt keine Rolle. Solange es 87 Oktangas gibt.
Coteyr
8

Es ist eine Designentscheidung und -wahl. Linus möchte Anwenderbereichsentwicklern garantieren können, dass Änderungen im Kernel, außer in äußerst seltenen und außergewöhnlichen (z. B. sicherheitsrelevanten) Fällen, ihre Anwendungen nicht beschädigen.

Die Profis sind, dass Userpace-Entwickler nicht finden werden, dass ihr Code aus willkürlichen und launischen Gründen plötzlich auf neuen Kernels bricht.

Die Nachteile sind, dass der Kernel alten Code und alte Systemaufrufe usw. für immer aufbewahren muss (oder zumindest längst über ihre Verfallsdaten hinaus).

cas
quelle
Danke für Ihre Antwort. Ist Ihnen bekannt, wie sich diese Entscheidung entwickelt hat? Mir sind Projekte bekannt, die eine etwas andere Perspektive einnehmen. Zum Beispiel hat das Mercurial-Projekt keine feste API und kann und bricht Code, der darauf beruht.
Faheem Mitha
Nein, sorry, ich kann mich nicht erinnern, wie es dazu gekommen ist. Sie könnten Linus oder LKML mailen und ihn fragen.
cas
2
Mercurial ist kein Betriebssystem. Der springende Punkt eines Betriebssystems ist es, das Ausführen anderer Software darauf zu ermöglichen, und zu verhindern, dass andere Software sehr unbeliebt ist. Zum Vergleich: Windows hat auch sehr lange die Abwärtskompatibilität bewahrt. 16-Bit-Windows-Code wurde erst kürzlich veraltet.
pjc50
@ pjc50 Es ist wahr, dass Mercurial kein Betriebssystem ist, aber es gibt trotzdem andere Software, auch wenn nur Skripte, die davon abhängen. Und kann möglicherweise durch Änderungen gebrochen werden.
Faheem Mitha