8 Punkte von GN⁺ 2023-07-24 | 1 Kommentare | Auf WhatsApp teilen
  • Dies ist der Einstieg in ein persönliches Lernprojekt, das vollständig verstehen will, was tatsächlich passiert, wenn auf einem Computer ein Programm ausgeführt wird
  • Die zentrale Frage führt dazu, ob ein Programm direkt auf der CPU ausgeführt wird, wie System Calls funktionieren und wie mehrere Programme gleichzeitig laufen
  • Außerhalb von Universitätsvorlesungen war es schwer, umfassende Systemressourcen zu finden, sodass Informationen aus Quellen unterschiedlicher Qualität und mit widersprüchlichen Aussagen selbst gefiltert werden mussten
  • Nach mehreren Wochen Recherche und fast 40 Seiten Notizen versteht der Autor nun besser, wie ein Computer vom Start bis zur Programmausführung arbeitet
  • Auch Leser, die glauben, das Thema bereits zu kennen, könnten noch etwas Neues lernen; wer wenig Zeit hat, sollte zuerst Kapitel 3 lesen

Der Ausgangspunkt, um Programmausführung zu verstehen

  • Der Ausgangspunkt dieses Textes ist die Frage, was genau passiert, wenn man auf einem Computer ein Programm ausführt
  • Ein gewisses Verständnis von Low-Level-Wissen war bereits vorhanden, aber es war schwierig, die einzelnen Teile zu einem durchgehenden Ablauf zu verbinden
  • Die Frage verzweigt sich in drei Richtungen
    • Ob Programme wirklich direkt auf der CPU ausgeführt werden
    • Was System Calls sind und wie sie in der Praxis funktionieren
    • Wie mehrere Programme gleichzeitig ausgeführt werden

Rechercheprozess und Lesereihenfolge

  • Da es nicht viele umfassende Systemressourcen gab, mussten verschiedene Quellen direkt untersucht werden; ihre Qualität und ihre Inhalte unterschieden sich teils stark
  • Durch mehrere Wochen Recherche und fast 40 Seiten Notizen entstand ein besseres Verständnis dafür, wie ein Computer vom Startvorgang bis zur Programmausführung arbeitet
  • Das Ziel ist, selbst den einen soliden erklärenden Text zu schreiben, den der Autor gern gefunden hätte
  • Lesern, die meinen, den Inhalt bereits zu kennen, oder wenig Zeit haben, wird empfohlen, zuerst Kapitel 3 zu lesen
  • Der nächste Artikel setzt sich mit Kapitel 1: The “Basics” fort

1 Kommentare

 
GN⁺ 2023-07-24
Hacker-News-Meinungen
  • Ich bin die Person, die das gemacht hat. Danke fürs Lesen und dafür, dass ihr mir ganz Hacker-News-typisch viele Korrekturen mitgeteilt habt. Putting the "You" in CPU ist noch mitten in der Arbeit; ursprünglich wollte ich es erst weiter ausfeilen, Inhalte ergänzen und dann etwa nächste Woche auf HN posten.
    Ich bin 17, habe vor einem Jahr die Highschool abgebrochen und angefangen, Vollzeit bei Hack Club (https://hackclub.com/) zu arbeiten. Ich programmiere, solange ich zurückdenken kann, und vor etwa sechs Jahren habe ich mit Homeschooling angefangen, um mich stärker auf Programmierung und andere Interessen konzentrieren zu können.
    Ich habe mir alles selbst beigebracht und nie einen Systems-Kurs an der Uni besucht; meine eigene Antwort auf „Was passiert, wenn man etwas ausführt?“ gefiel mir nicht. Also habe ich sehr viel Zeit investiert, um es so tief wie möglich zu verstehen, und dabei festgestellt: Betriebssysteme und Hardware machen wirklich Spaß zu lernen, aber die Online-Ressourcen zu diesem Thema sind oft miserabel.
    Bei der Recherche landet man häufig bei Vorlesungsfolien-PDFs von 2014 oder bei StackOverflow-Antworten, die in Wirklichkeit falsch oder übermäßig vereinfacht sind. Deshalb habe ich Putting the "You" in CPU geschrieben, in der Hoffnung, dass es für Leute, die sich dieses Gebiet selbst beibringen wollen, eine bessere Ressource ist. Es ist noch nicht perfekt, weil ich noch ein paar Absätze zu SMP schreiben muss, aber ich halte es für deutlich besser als die meisten Materialien, die ich gesehen habe. Diagramme und Schaubilder habe ich zum ersten Mal erstellt; je weiter die Kapitel fortschreiten, desto besser wurden sie, und auf ein paar der späteren Bilder bin ich ziemlich stolz.
    Das Ganze ist als Open Source auf GitHub verfügbar: https://github.com/hackclub/putting-the-you-in-cpu

    • In letzter Zeit denke ich auch oft: „Solche Online-Ressourcen sind einfach zu schlecht.“ Besonders, wenn ich nach detaillierten Blockdiagrammen des Linux-Kernels, der Funktionsweise von Page Tables, Vergleichen von funktionsaufrufbezogenen ABIs je Plattform, Details zu Low-Level-Netzwerkprotokollen oder reservierten Linux-PID-Nummern suche.
      Manchmal finde ich Antworten in Reddit-Threads, aber meistens ist viel Rauschen dabei, und ich zweifle auch daran, ob Reddit ein verlässlicher Ort zur Wissensspeicherung ist. StackOverflow ist besonders wenig hilfreich, weil es vor allem um Fragen zur Lösung konkreter Aufgaben geht und die Antworten zwangsläufig kurz sind.
      Für Allgemeinwissen überspringe ich inzwischen Googles kaputte Suche und gehe direkt zu Wikipedia, aber auch Wikipedia hat Grenzen, wie tief es technische Themen abdecken kann. Vielleicht brauchen wir ein Wiki, das die Details der Computer-Innenarchitektur umfassend sammelt. Es gibt viele verstreute Ressourcen, aber es wäre großartig, sie an einem Ort zu haben.
    • Ich hatte in etwa diesem Alter ähnliche Fragen und habe viele interessante Antworten im Linux-Quellcode gefunden. Natürlich war der damals viel einfacher. Besonders die osdev-Community und Intels Dokumentation zur Systemarchitektur waren ein großer Anstoß; soweit ich mich erinnere, steckte in Teil 3a oder 3b besonders viel Wertvolles.
      Diese Dokumente erzählen einem viel mehr darüber, wie die Welt sein könnte, als darüber, wie sie heute tatsächlich ist. Wenn man OSDEV macht, lernt man auch viele interessante Dinge: etwa wie 32-Bit-Prozessoren mit PAE 36-Bit-Adressierung machten oder wie 64-Bit-Prozessoren 52-Bit-Adressierung nutzten, um noch mehr Geld herauszuholen.
      Man versteht auch, warum ein Computer, der im 16-Bit-Modus startet und bis in den long mode kommen soll, systemnahe Assembly-Akrobatik braucht, und warum der 8-Bit-Modus noch übrig ist. Wenn dir das Lernen über das Laden von Binaries gefallen hat, gibt es immer noch vieles, das dich überraschen kann. Zum Beispiel existiert Memory Segmentation auch im 64-Bit-Modus noch, wird im long mode aber auf ein flaches Mapping gezwungen.
    • Hervorragende Arbeit. Das ist ein gutes Material für den Einstieg in die Betriebssystementwicklung; es enthält viele Details, ist aber gut verdaulich. Das Ende von Fußnote #2 in Kapitel 2 könnte leicht verbessert werden. Es sollte widerspiegeln, dass das asynchrone Paradigma mancher Programmiersprachen kooperatives Multitasking ist. Innerhalb eines Prozesses gilt die Verwendung von kooperativem Multitasking normalerweise als sicher, ob sie wünschenswert ist, ist eine andere Frage. Soweit ich weiß, setzt Erlang der Robustheit wegen weiterhin auf präemptives Multitasking.
  • Ich mag solche Materialien. Ich steige gerade in die RISC-V-Welt ein, und es scheint klar: Wer freie und Open-Source-Software voranbringen will, braucht dieses Wissen, damit Software auf den vielen neuen Single-Board-Computern gut läuft.

    • Zur Einordnung: Die RISC-V-Instruktionscodierung gehört, vielleicht abgesehen von Thumb-2, zu den ziemlich seltsamen Dingen, die man erforschen kann. Es gab gute Gründe, sie so zu gestalten, aber wenn du Hex-Dumps lesen willst, wird RISC-V es dir nicht leicht machen. Auch wenn es aus den meisten anderen Blickwinkeln einfach ist.
    • Wenn man mindestens einen Bachelor gemacht hat, würde ich erwarten, dass Programmierer so etwas im Großen und Ganzen schon wissen. Zumindest, wenn man die Kurse zu Computerarchitektur und Betriebssystemen nicht übersprungen hat.
      In meinem Fall gehörte es beim Lernen, wie Computer funktionieren, ganz normal dazu, eine Instruction Set Architecture zu definieren, einen Simulator und Assembler zu implementieren und selbst Assembly zu schreiben. Wer engagierter war, hat sie auch auf einem FPGA implementiert oder ein LLVM-Backend geschrieben.
      Bei Betriebssystemen ist es ähnlich. Einen Kernel selbst zu implementieren, um daran zu lernen, oder zumindest einen bestehenden Kernel zu verändern, ist ein ziemlich üblicher Weg.
  • Wenn du mehr darüber wissen willst, wie Computer auf sehr grundlegender Ebene funktionieren, empfehle ich wirklich Charles Petzolds Buch Code. Es beginnt bei den ersten Prinzipien und arbeitet sich bis auf ein ziemlich hohes Niveau vor.

  • Eine Sache, die dieses Material gut macht, ist, dass es beim Sammeln der Informationen die Erwartungen und die tatsächlichen Lernergebnisse aufgreift. Etwa nach dem Muster: „Ich hatte erwartet, dass es wegen y x ist, tatsächlich war es aber wegen b a!“
    Missverständnisse auszuräumen ist oft ein zentraler Schritt beim Aufbau neuen Wissens, und es hilft, wenn der Autor anerkennt, dass Leser beim Verarbeiten der Informationen ihre eigenen Missverständnisse überwinden müssen. Das ist viel leserfreundlicher als eine Haltung wie: „Das hier ist die richtige Antwort, und wer anders dachte, ist dumm.“

  • In Kapitel 6 ist die Erklärung des Rückgabewerts von fork() falsch. Entgegen dem, was dort steht, sollte im Elternprozess die PID des Kindprozesses zurückgegeben werden und im Kindprozess 0. Ich habe dazu einen Pull Request erstellt.

    • Vielen Dank, wirklich mein Fehler. Ich habe das Codebeispiel korrigiert und gemergt.
  • In der Erklärung, dass add eax, 512 zu 05 00 02 00 00 übersetzt wird, heißt es, das erste Byte 05 sei der Opcode zum Addieren einer 16-Bit-Zahl zum EAX-Register, und die übrigen Bytes seien 512 (0x200) in Little Endian.
    Diese Erklärung beschreibt aber nur die ersten 3 Bytes der 5-Byte-Instruktion. Ich frage mich, was die verbleibenden 00 sind. Wahrscheinlich sollte dort stehen, dass der Opcode eine 32-Bit-Zahl zu EAX addiert?

    • Nicht „he“, sondern „she“, und ja. An der Stelle hätte wohl 32 Bit stehen sollen. Gut aufgepasst.
    • Wenn es um x86 geht, stimmt das. Das eax-Register ist 32 Bit breit; die unteren 16 Bit desselben Registers heißen ax.
  • Guter Artikel. Als autodidaktischer Webentwickler finde ich solche Materialien sehr wertvoll. Sie helfen dabei, sich von mehreren Abstraktionsebenen zu lösen und konkreter zu verstehen, wie die Dinge tatsächlich funktionieren.

  • Sieht nach gutem Material aus, kommt ziemlich schnell zum Kern und ist auch ein bisschen unterhaltsam. Es wirkt weniger wie ein „Monad-Tutorial“, als ich erwartet hatte. Ich werde weiter darin lesen.

    • Freut mich, dass es dir gefällt. Wenn du später Feedback hast, lass es mich gern wissen.
  • Gutes Material. Eine Frage zum Kernel bleibt mir allerdings. In [0] wird erklärt, dass die Shebang vom Kernel verarbeitet und aus buf entnommen wird, ohne die gesamte Datei zu laden, weshalb sie immer auf die Länge von buf abgeschnitten wird.
    Vor vier Jahren war jemand genervt davon, dass der Kernel Pfade mit mehr als 128 Zeichen abschneidet, und hat die Puffergröße verdoppelt, wodurch sich auch die Abschneidestelle verdoppelte. Deshalb verschwindet heute unter Linux alles nach 256 Zeichen in einer Shebang-Zeile vollständig.
    Ich verstehe nicht, warum der Dateipfad von jemandem stillschweigend abgeschnitten wird. Ausgerechnet der Kernel sollte so etwas nicht tun. Ein 256-Byte-Dateipfad mag unsinnig sein, aber eine 256-Byte-Zeichenkette aus Pfad plus Argumenten wird irgendwann fast sicher vorkommen. Skripte stillschweigend kaputtzumachen ist falsch.
    [0]: https://cpu.land/how-to-run-a-program

    • Als Autorin stimme ich dem nachdrücklich zu. Man sollte nicht die Ausführungslogik des Kernels kennen müssen, um die Grenzen der Shebang zu verstehen. Das ist ein törichtes Design, und ich hätte diesen Punkt vermutlich stärker betonen sollen.
      Vor einigen Jahren hat jemand versucht, den Kernel so zu patchen, dass eine zu lange Shebang wenigstens nicht stillschweigend, sondern eindeutig fehlschlägt. Das verursachte jedoch Probleme bei NixOS. Ganz Nix-typisch wurden dort bereits überlange Shebangs mit langen nix-store-Pfaden verwendet. Vorher wurden sie stillschweigend abgeschnitten, funktionierten aber trotzdem; plötzlich schlugen all diese Skripte fehl, wodurch die Abwärtskompatibilität brach. Deshalb musste der Patch zurückgenommen werden, und seitdem gab es keine ähnliche Änderung mehr.
      Details siehe https://lwn.net/Articles/779997/.
    • Man sollte keine komplexen Argumente in eine Shebang stecken. Die Shebang ist dazu da, die Position des Interpreters für die aktuelle Datei anzugeben.
      Shebangs können von sich aus nicht einmal mehr als ein Argument korrekt verarbeiten. Wenn man #!/bin/program -args somescript schreibt, wird daraus ['/bin/program','-args somescript'], was fast nie das gewünschte Ergebnis ist. Das gilt speziell für Linux, und Shebangs sind auch nicht auf allen Unix-Systemen allgemein portierbar.
      Wenn man etwas Komplexeres tun will, sollte man #!/bin/sh verwenden und aus der aktuellen Datei mit den nötigen Argumenten per exec starten.
      Ein Punkt, der im Link fehlt: Mit env kann man den Interpreter im aktuellen Pfad suchen lassen. Zum Beispiel findet #!/usr/bin/env python3 das vom Nutzer verwendete python3, ohne dessen Ort zu kennen.
      Das kann für Skripte nützlich sein, die in virtuellen Umgebungen laufen sollen, die funktionieren, indem sie den PATH für den Python-Interpreter überschreiben. Für installierte Programme sollte man das allerdings nicht tun. Installierte Programme sollten nicht dem Nutzer überlassen, welchen Interpreter sie verwenden; dafür sollte ein absoluter Pfad genutzt werden.
      env hat eine Option -S, mit der sich zur Umgehung dieser Grenze eine Befehlszeile aus einer einzelnen Zeichenkette parsen lässt. Aus Sicht von jemandem, der seit Jahren Tricks in Shebang-Zeilen vermeidet, wirkt das trotzdem bestenfalls wie eine fragwürdige Praxis.
    • Noch überraschender ist, dass der Puffer nicht auf die Blockgröße der Festplatte gesetzt wurde. Normalerweise sind das 512 Byte, aber eben nicht immer.