Wie wird ein Programm auf CPU-Ebene ausgeführt?


14

Ich weiß, dass dies eine sehr häufige Frage ist. Aber ich habe einen anderen Blickwinkel. Ich werde nur versuchen, es hier zu artikulieren.

Soweit ich weiß, ist jede Anweisung, die eine CPU ausführt, in Maschinensprache und jede CPU kann dank ALU und ihrer Transistoren einige Rechenoperationen ausführen (wenn wir auf Hardwareebene vorgehen).

Dies ist jedoch einfacher zu tippen als zu verstehen. Wenn die CPU also nur Addieren, Subtrahieren usw. ausführt, wie wird dann ein Programm, beispielsweise ein JAVA-Programm, das print Hello World sagt, mit diesen arithmetischen Operationen ausgeführt?

Ich meine, wie wird dieses Programm in etwas umgewandelt, das nur eine Ergänzung für die CPU ist?

PS Wenn diese Frage für diese Website nicht zutreffend ist, entschuldige ich mich.

-----Zweiter Teil-----

Okay. Vielen Dank an alle für die schnelle und enthusiastische Beantwortung. Ich dachte, es ist besser, meine Frage ein wenig zu ändern, als alle Antworten zu kommentieren und sie erneut zu stellen.

Hier ist es also.

Erstens haben alle ausdrücklich ein Beispiel für Hallo Welt beantwortet. Das ist meine Schuld. Ich hätte dieses Generikum behalten sollen. Hallo Welt Beispiel bringt in Frage Ausgabegeräte und wie seine Verarbeitung ist nicht nur auf die CPU beschränkt, die zu Recht in Ihren Antworten gebracht wird.

Auch viele von Ihnen haben mir mitgeteilt, dass CPU mehr als nur Addition leistet. Ich stimme dem zu. Ich habe das einfach nicht geschrieben und bin davon ausgegangen. Soweit ich weiß, ist dies der Prozess:

  1. Lesen Sie den Befehl aus dem Speicher (mit Daten- und Adressbussen und Programmzählern)

    1. Speichern Sie die Daten in einem Register in der CPU
    2. Jetzt führt ALU arithmetische Operationen aus, natürlich nachdem der Befehl dekodiert wurde, oder macht einen Sprung, wenn es sich um einen If Like-Befehl handelt
    3. Und dann mit anderen Ressourcen kommunizieren, wenn nötig, wie zum Beispiel mit dem Ausgabegerät und so weiter. Darüber hinausgehende Prozesse sind vorerst trivial.

In Schritt 3, in dem die CPU einen Befehl dekodiert und eine arithmetische Operation ausführt (hier nehmen wir an, dass keine andere Operation ausgeführt werden kann, als die aktuelle Anweisung zu überspringen. Da arithmetische Operationen meistens ausgeführt werden. Wir werden uns also daran halten ) Hier endet meine Visualisierung. Wie eine Anweisung aus meinem Programm nur eine Rechenoperation für die CPU ist. Es führt diese arithmetische Operation aus und diese Anweisung erfüllt ihren Zweck.

Ich hoffe, ich habe mich diesmal klar ausgedrückt.

PS Ich gehe hier von der Annahme aus, dass ALU nicht auf die tatsächliche Rechenoperation beschränkt ist, die wir in unseren Programmen ausführen, sondern alle Befehle, die jetzt in binärer Form vorliegen, durch Addieren oder Subtrahieren usw. ausführt, um das gewünschte Ergebnis zu erzielen nachgeben. Wenn ich hier falsch liege, beantworte ich meine Frage richtig.


Ich verstehe, dass der Compiler das Programm in Maschinensprache konvertiert. Ich kann mir ein Programm einfach nicht als eine arithmetische Operation vorstellen. Wenn es sich bei dem Programm selbst um das Hinzufügen von zwei Zahlen handelt, ist es zwar verständlich, aber ansonsten nicht. :)
user2827893

1
Vielleicht sollten Sie sich den eigentlichen Befehlssatz von CPUs ansehen, zum Beispiel sehr einfache wie MC6502, Z80 ... und dann sehen, dass es Speicherzugriffsanweisungen, Datenverarbeitungsanweisungen, Verzweigungen ... gibt. Sie könnten dann erraten, wie sie sein können kombiniert für die Implementierung eines beliebigen Algorithmus.
TEMLIB,

3
Eine CPU kann definitiv mehr als nur hinzufügen. Es ist wichtig zu erwähnen, dass eine CPU Vergleiche und Sprünge durchführen kann.
Theodoros Chatzigiannakis

1
Sie (scheinen) weigern sich immer noch, IF (Entscheidung treffen) und MOVE (Daten lesen und speichern) zu sehen. Die Programmierung erfolgt zu 99%, IF und MOVE. Arithmetik ist vernachlässigbar. Dein erstes Beispiel (Hallo Welt) hat überhaupt keine Arithmetik.
Edc65

1
1. Ich denke, Sie werden mit größerer Wahrscheinlichkeit gute Antworten erhalten, wenn Sie eine neue Frage mit Ihrer neuen Verwirrung stellen, anstatt diese Frage zu bearbeiten, um Ihre Fragen zu ändern. Sie haben gute Antworten auf Ihre ursprüngliche Frage erhalten, und Ihre ursprüngliche Frage scheint für sich allein zu stehen. Warum also nicht die Bearbeitung entfernen und eine neue Frage stellen? 2. Das heißt, ich kann den neuen Teil nicht verstehen. Was genau ist deine Frage zum neuen Teil? Wenn Sie sagen "Hier endet meine Visualisierung", verstehen Sie dann Schritt 3 oder nicht Schritt 3? Wenn ja, was verstehst du dann nicht?
DW

Antworten:


7

Sie können versuchen, ein einfaches Programm zu nehmen und es zu nativem Maschinencode zu kompilieren. (Java wird normalerweise in JVM-Code kompiliert, aber Andrew Tennenbaum hat ein Buch, in dem er beschreibt, wie man eine CPU entwirft, die so nativ läuft.) Auf GCC geben Sie beispielsweise dem Compiler den -SSchalter.

Dies zeigt Ihnen, dass alles, was schwierig ist, wie E / A, durch Aufrufen des Betriebssystems implementiert wird. Während Sie den Quellcode in den Linux-Kernel herunterladen und das Gleiche tun könnten, geschieht unter der Haube Folgendes: Alles manipuliert den Zustand des Computerspeichers, zum Beispiel die Liste der ausgeführten Prozesse, oder es wird mit der Hardware gesprochen Spezielle Speicheradressen, die sie steuern oder spezielle CPU-Anweisungen wie inund outauf dem x86 verwenden. Im Allgemeinen sprechen jedoch nur spezielle Programme, die als Gerätetreiber bezeichnet werden, mit bestimmter Hardware, und das Betriebssystem sendet Anforderungen zur Verwendung von Hardware an den richtigen Treiber.

Insbesondere, wenn Sie drucken, "Hallo, Welt!" Ihr Compiler wandelt dies in einen Befehlssatz um, der den String an einer bestimmten Stelle lädt (z. B. %rdiindem er die Adresse des Strings im Speicher in das Register lädt ) und eine Bibliotheksfunktion mit dem callBefehl aufruft . Diese Bibliotheksfunktion ermittelt möglicherweise die Länge der Zeichenfolge mit einer Schleife und ruft dann den Systemaufruf aufwrite()Schreiben dieser Anzahl von Bytes aus der Zeichenfolge in den Dateideskriptor Nr. 1, der die Standardausgabe darstellt. Zu diesem Zeitpunkt sucht das Betriebssystem nach der Dateinummer 1 dieses Prozesses und entscheidet, was das Schreiben darauf bedeutet. Wenn Schreibvorgänge für die Standardausgabe auf Ihrem Bildschirm gedruckt werden, werden die Bytes in einen Puffer kopiert, der dann von Ihrem Terminalprogramm gelesen wird und dem Fenstersystem mitteilt, welche Buchstaben wo in welcher Schriftart abgelegt werden sollen. Das Fenstersystem legt genau fest, wie das aussehen soll, und weist einen Gerätetreiber an, die Pixel auf dem Bildschirm zu platzieren, indem der Videospeicher geändert wird.


Vielen Dank @Lorehead. Diese Erklärung sieht gut aus für Hallo Welt Beispiel.
user2827893

5

Ihre CPU an sich ist dumm, wie Sie herausgefunden haben. Aber es gibt einen Mikrokosmos von Hardware-Chips. Sie haben eine Anweisung, mit der Sie eine Zeile der CPU auf einen hohen Pegel setzen können, der mit einem anderen Chip verbunden ist. Dieser Hardware-Chip überwacht die Leitung und sagt: "Hey, wenn diese Leitung hoch ist, mache ich etwas mit einigen anderen Leitungen."

Um dies zu vereinfachen, sind diese Zeilen zu Gruppen zusammengefasst. Einige werden verwendet, um Geräte zu adressieren, einige werden verwendet, um Daten für diese Adressen zu übertragen, und andere sind nur "Alter, in meinen Chip-Zeilen ist etwas Wichtiges los".

Am Ende weist Ihre CPU lediglich einen anderen Chip an, das Signal auf dem Monitor so zu ändern, dass es wie "Hello World" aussieht.

Google zeichnet eine 7-Segment-Anzeige. Es hat Drähte, die ein Segment zum Leuchten bringen, wenn Spannung angelegt wird. Wenn Sie nun eine Ausgangsleitung Ihrer CPU mit einer Zeile der 7-Segment-Anzeige verbinden, leuchtet die Anzeige auf. Es ist nicht die CPU, die die LED zum Leuchten bringt, sondern sie legt nur Spannung an die Leitungen an.

Wenn Ihre CPU jetzt alle Zeilen für das H auf Hoch setzt, zeigt das 7-Segment H an, obwohl H keine Zahl ist, zu der eine CPU addieren oder von der sie subtrahieren würde.

Wenn nun alle Ebenen übereinstimmen, was für die 7-Segment-Anzeige H erforderlich ist (setzen Sie 5 bestimmte Zeilen auf Hoch), kann der Java-Compiler Code für die Anzeige H erstellen zu abstrahieren. Die unterste Ebene beginnt mit: "Yo, es gibt wie 26 Buchstaben, lassen Sie uns jedem Buchstaben eine Nummer zuweisen - wie wäre es, wenn wir dem Buchstaben 'H' die Nummer '72' geben? Dann können Sie mir einfach sagen" Display letter 72 ", Anstelle von "Setze Zeile 309 hoch, setze Zeile 310 hoch, setze Zeile 498 hoch, setze Zeile 549 hoch, setze Zeile 3 hoch." sich um sie zu kümmern.

Also ja, es summiert sich zu einer riesigen Zuordnung von Zahlen oder Bits, die die CPU tatsächlich verarbeiten kann, zu Bedeutungen, auf die sich alle in der Kette geeinigt haben.


3

Als Teil eines CS-Studiengangs am College habe ich ein erweitertes Beispiel für die Registerübertragungssprache studiert , die eine CPU definiert. Ich war inspiriert, es anders zu interpretieren und einen Simulator zu schreiben, der eine solche Notation als Definition akzeptiert, und veröffentlichte dies in Embedded Systems Programming (Ausgabe März 1989), um die gleiche Art von Frage zu beantworten, die Sie gestellt haben und die es den Menschen ermöglichte Bauen Sie ihr intuitives Verständnis für solche Dinge auf.

In der Klasse fingen wir an, diese Resister-Transfer-Notation in tatsächliche Logikgatter auf den Registern zu destillieren! Es schreibt sich selbst: Sehen Sie sich alles an, was das Register 'A' als Ziel hat und den Code A = (case1) oder (case2) ... und der als Summe von Produkten oder Produkt von Summen in normalisierter Form ausgedrückt wird.

Erst am Ende des Kurses habe ich erfahren, dass dies eine echte CPU ist: der PDP-8, wenn ich mich richtig erinnere.

Heute könnten Sie das Gate-Diagramm in einen programmierbaren Logik-Array-Chip einspeisen.

Das ist der Kern der Sache: Ein Register wird mit dem Ergebnis von UND- und ODER-Gattern gesetzt, die zu anderen Registern zurückführen. Einer der einzuschließenden Werte ist der Opcode-Wert.

Stellen Sie sich vor: A: = (Opcode == 17 & X + Y) | (Opcode == 18 & X + Z) | ...

Moderne CPUs sind komplizierter, mit Pipelines und Bussen, aber einzelne Untereinheiten wie eine einzelne ALU funktionieren auf diese Weise.


2

Sie denken hier über die CPU nach, aber beim Ausführen von 'Hello World' ist noch eine andere Komponente beteiligt: ​​das Display!

Für die CPU ist ein Wert im Speicher nur eine Zahl, die als gegebene Anzahl von Bits (Nullen und Einsen) dargestellt wird.

Wie es auf dem Bildschirm zu Buchstaben wird, ist eine andere Geschichte: Das Display hat auch Speicher. Dieser Speicher (Grafikspeicher) wird auf 'Pixel' auf dem Bildschirm abgebildet. Jedes Pixel ist mit einem Wert codiert: Wenn es sich um eine sehr einfache monochrome Anzeige handelt, ist der Wert nur die Intensität. Bei Farbanzeigen ist der Wert eine Kombination aus Rot, Grün und Blau (RGB), die auf viele verschiedene Arten codiert werden kann.

Wenn die CPU einen bestimmten Wert in den Anzeigespeicher schreibt, leuchten die Pixel auf. Um tatsächlich Buchstaben zu schreiben, muss man viele Pixel aufleuchten lassen. In der Regel hat ein Computer einen Zeichensatz (tatsächlich mehrere), der in seinem Betriebssystem definiert ist. (Abstraktion der 'Schriften' selbst, die einer Definition des Aussehens jedes Buchstabens auf dem Bildschirm entspricht)

Wenn der Code kompiliert wird, enthält er alle möglichen Elemente, die aus den Betriebssystembibliotheken stammen, einschließlich dieser Schriftarten / Zeichensätze usw., mit denen die CPU weiß, was an welcher Stelle im Grafikspeicher zu schreiben ist. (Es ist ziemlich komplex, aber das ist die allgemeine Idee: Der Compiler enthält über importierte Bibliotheken viel mehr Code, als nur in Ihrem 'Hallo Welt'-Code enthalten ist.)

Letztendlich ist eine Menge passiert, wie Sie vermuten, aber Sie mussten nicht den ganzen Code schreiben.


1

Hier ist ein formaler Ansatz für Ihre Frage aus dem Bereich der theoretischen Informatik.

Grundsätzlich können wir ein Mapping zwischen dem Rechenmodell einer CPU und einer Turing-Maschine definieren. Es gibt theoretische Beweise dafür, dass die Menge aller vorstellbaren Drehmaschinenprogramme (und damit aller vorstellbaren Programme, die auf einer CPU ausführbar sind) unendlich zählbar ist. Dies bedeutet, dass wir jedes Programm mit einer eindeutigen natürlichen Nummer identifizieren können, einschließlich des Programms, das die natürlichen Nummern auf Turingmaschinen ausdehnen würde .

Da Sie bereits wissen, dass fast alles, was CPUs tun, Berechnungen mit natürlichen Zahlen in binärer Darstellung sind, können Sie davon ausgehen, dass CPUs jedes erdenkliche Programm ausführen können.

Hinweis: Dies ist zu stark vereinfacht, gibt aber meiner Meinung nach eine schöne Intuition.


1

Was helfen kann, ist, Ihr Denken von "Rechnen" abzuwenden. Wenn Sie wirklich versuchen, herauszufinden, was Computer unter der Haube tun, um "Hello World" zu drucken, ist es am besten, eine Stufe tiefer zu denken. Der "Zustand" des Computers kann als ein Satz von Bits beschrieben werden, die von Transistorschaltern gespeichert werden, die entweder ein- oder ausgeschaltet sind (oder Kondensatoren, die entweder geladen oder ungeladen sind). Der Computer manipuliert diese Bits nach Regeln. Die Art und Weise, wie der Computer diese Bits manipulieren darf, wird in Form von Transistoren auf die CPU geschrieben, die das Ändern der Bits von 0 auf 1 oder von 1 auf 0 bewirken.

Wenn eine ALU "arithmetisch arbeitet", bedeutet dies in Wirklichkeit, dass sie den Zustand des Computers auf eine Weise ändert, die unseren arithmetischen Regeln entspricht. Alles was es getan hat, war ein paar Kleinigkeiten zu ändern. Es ist die Bedeutung hinter der Software, die erklärt, warum wir sie als Addition oder Subtraktion betrachten sollten. Die CPU "weiß" nicht, was sie tut. Es ändert sich nur von Staat zu Staat, und das ist alles (zumindest bis Skynet übernimmt).

Wenn Sie so denken, sind kompliziertere Anweisungen wie eine "Sprung" -Anweisung nicht anders. Alles, was es tut, ist einige Bits zu ändern. In diesem Fall ändern sich die Bits, von denen wir wissen, dass sie den Ort des nächsten auszuführenden Befehls bedeuten . Die CPU "weiß" das nicht, aber wir tun es. Also benutzen wir den Befehl, der diese Bits ändert, um von Ort zu Ort in unserem Code zu "springen".

IO ist auch nicht anders, es wechselt nur die Bits. Der einzige kleine Unterschied besteht darin, dass diese Bits mit Transistoren verbunden sind, die schließlich dazu führen, dass Zeichen auf Ihrem Bildschirm aufleuchten. Wenn ich auf ein paar Jahrzehnte zurückblicken darf, als "Hello World" eigentlich einfach war, gab es einen Speicherplatz, in dem, wenn Sie Bits entsprechend den ASCII-Zeichen für "Hello World" darauf schrieben, diese Zeichen direkt in das gerendert wurden Bildschirm. Heutzutage ist es etwas komplizierter, weil wir Grafikkarten und Betriebssysteme haben, die sich damit herumschlagen, aber die Grundidee ist dieselbe. Sie haben eine Reihe von Transistoren, die entweder ein- oder ausgeschaltet sind und mit Schaltkreisen verbunden sind, um ein Pixel auf dem Bildschirm anzuzeigen. Wir stellen die richtigen ein und es sieht so aus, als würde "Hello World" auf dem Bildschirm erscheinen.

Die Verwirrung ist einfach eine Frage der Syntax vs. Semantik. Das Verhalten einer "halb hinzugefügten" oder einer "voll hinzugefügten" in einer ALU ist die Syntax. Es definiert, welche Bits herauskommen, wenn Sie Bits eingeben. Die Semantik davon ist das Konzept der Fähigkeit, Additionen durchzuführen. Sie und ich sind uns bewusst, dass die ALU "Addition machen" kann, aber um wirklich zu verstehen, was darunter passiert, müssen Sie sich daran erinnern, dass eine ALU nur die Bits und Bytes der Syntax manipuliert.


0

CPUs arbeiten so:

  • Aktuellen Befehl holen, Zeiger "Aktueller Befehl" inkrementieren.

  • dekodieren (z. B. herausfinden, wozu diese Anweisung die CPU auffordert)

  • führe es aus (mach was der Befehl sagt) - der aktuelle Befehlszeiger kann geändert werden, wenn der Befehl so etwas wie ein "Sprung" ist.

  • Für immer wiederholen

Moderne CPUs sind komplexer und versuchen, Teile dieses Prozesses stark zu überlappen und sogar vorherzusagen (z. B. mit der Ausführung zu beginnen, während 10 andere Befehle dekodiert werden, während die CPU weit vor dem Zeiger "Aktueller Befehl" abruft, um "Pipelines" voll zu halten) Der wesentliche Prozess ist wirklich der gleiche.

Es gibt viele Arten von Anweisungen, ein Beispiel für die meisten von ihnen ist:

  • Anweisungen zum Verschieben. Diese können X auf ein anderes X kopieren, wobei X Speicher (RAM), ein Register oder eine Adresse im E / A-Bereich ist, wenn die CPU einen solchen Begriff unterstützt.

  • Stack-Manipulationsanweisungen, einschließlich Pop-In-Register, Push-Register auf Stack usw. Hierbei handelt es sich um einen Sonderfall von "Move" -Anweisungen, die ein "Stack-Pointer" -Register verwenden und aktualisieren.

  • Anweisungen, die mathematische Operationen ausführen, entweder zwischen zwei Registern oder zwischen dem Speicher und einem Register. Diese Anweisungen wirken sich automatisch auf ein Merkerregister aus. Ein solches Flag ist das Flag "Null", das gesetzt wird, wenn das Ergebnis Null ist, ein anderes Flag ist das Flag "Negativ", das gesetzt wird, wenn das höchstwertige Bit des Ergebnisses gesetzt ist. Abhängig von der CPU können andere vorhanden sein.

  • Ein Sonderfall für mathematische Operationen sind Vergleichsanweisungen, die einer Subtraktion entsprechen, deren Ergebnis jedoch nicht beibehalten wird. Flags sind weiterhin betroffen.

  • Es gibt Verzweigungsbefehle, die zu einer Speicheradresse springen, wenn bestimmte Flags gesetzt sind. Erinnern Sie sich an das oben erwähnte "Null" -Flag? Es wird auch als "if equal" -Flag verwendet, sodass Anweisungen wie BEQbei vielen CPUs angezeigt werden, die tatsächlich verzweigen, wenn das "zero" -Flag gesetzt ist.

  • Anweisungen, die logische Operationen (AND, OR, NOT), Schiebebits und Testbits ausführen. Sie können abhängig von der CPU Flags wie mathematische Anweisungen beeinflussen.

  • Anweisungen, die bedingungslos springen.

  • Anweisungen, die die Rücksprungadresse auf dem Stapel speichern (ein "Aufruf"), und andere Anweisungen, die eine Adresse vom Stapel entfernen (ein "Rücksprung").

  • Spezielle Anweisungen wie die, die die CPU anhalten, die CPU identifizieren oder Interrupt-Handler aufrufen.

  • "No Operation" - Fast alle CPUs haben einen "No-Op" -Befehl, der nur Zyklen verbraucht und weitergeht.

Dies ist wirklich nur ein Beispiel, es gibt CPUs mit weniger Anweisungstypen und CPUs mit mehr Anweisungen.

Der Punkt ist zu veranschaulichen, dass es in einer CPU neben mathematischen Befehlen viele Arten von Befehlen gibt. Alles in einer höheren Sprache ist in die oben genannten Operationstypen unterteilt, und nur einige davon sind mathematische oder ALU-artige Anweisungen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.