Wie ist das noch gleich mit den Computern? *g*
Someone
0 CK10 Tom0 Michael Schröpl
Hallo erstmal.
Ich sitz da grad mit wem und wir sehen uns nen FIlm den wir runtergeladen haben an (Hackers).
Wie dam auch sei, wir beginnehn zu quatschen und wieder mal stellt sich die Frage:
Wie funktioniert das mit den PCs genau?
Wenn man beispielsweise ein C-Programm compiliert dann wird dass doch in Assembler Code umgewandelt, aber manb kann keinen klaren Code lsen. Wenn man Assembler schreibt muss man das ganze ja eigentlich auch wieder durch ein spezielles Assembler Programm laufen lassen, soweit ich weiß. (in was wird sowas eigentlich geschrieben!?)
Alsop Leute ich denk mal ihr versteht meine Frage jetz mmal so halbwegs und hoffe jemand kann das alles mal verständlich erklären *g*
Someone
Hi,
Wie funktioniert das mit den PCs genau?
Wenn man beispielsweise ein C-Programm compiliert dann wird dass
doch in Assembler Code umgewandelt, aber manb kann keinen klaren
Code lesen.
Nein, wenn man ein C-Programm compiliert, wird das umgewandelt in
Maschienensprache ,) Und klar kann man klaren Code lesen, nur du
und ich vielleicht nicht *g* aber frueher (70ger? weiss net mehr ,))
war es grosse Mode, direkt in Maschinencode zu schreiben, z. B. mit
dem DOS-Debugger.
Der Maschinencode wird vom Betriebssystem in Prozessorbefehle
umgewandelt und verarbeitet ,)
Wenn man Assembler schreibt muss man das ganze ja
eigentlich auch wieder durch ein spezielles Assembler Programm
laufen lassen, soweit ich weiß. (in was wird sowas eigentlich
geschrieben!?)
Klar, Assembler ist zwar schon sehr nahe an Maschinencode, aber
es ist eben noch nicht ganz Maschinencode.
Der Assembler-Compiler wird wahrscheinlich wohl direkt in
Maschinencode geschrieben sein ,)
mfg
CK1
Nein, wenn man ein C-Programm compiliert, wird das umgewandelt in
Maschienensprache ,)
Das ist unterschiedlich. Es ist nicht unueblich, dass ein Compiler den C-Source in ein Assembler-Programm uebersetzt, das dann vom Assembler zu Maschinencode uebersetzt wird. Manche Compiler bauen aber auch direkt den Maschinencode zusammen.
Der Maschinencode wird vom Betriebssystem in Prozessorbefehle
umgewandelt und verarbeitet ,)
Haeh?! Das ist jetzt wirklich Quatsch!
Der Maschinencode wird vom Prozessor ausgefuehrt, Punkt. Da pfuscht kein Betriebssystem oder sonstwas drin rum. Das BS selbst ist ja schliesslich auch nur Maschinencode, nichts anderes als ein stinknormales Programm.
Klar, Assembler ist zwar schon sehr nahe an Maschinencode, aber
es ist eben noch nicht ganz Maschinencode.
Es ist eine Abbildung der Zahlen, die der Prozessor als Instruktionen interpretieren kann, in eine etwas lesbarere Form. Z.B. entspricht auf einem Intel-Prozessor (die normalen in PC eingesetzten meine ich) die Byte-Sequenz
B8 34 12
dem Assemblerbefehl mov ax, 1234h
der die Konstante 1234h (hexadezimal) in das AX-Register laedt.
Da tendenziell jeder Prozessor andere Instruktionen versteht, ist auch der Assembler fuer jeden Prozessor ein anderer.
Der Assembler-Compiler wird wahrscheinlich wohl direkt in
Maschinencode geschrieben sein ,)
Der Assembler-Compiler heisst einfach Assembler, und wird heutzutage sicher nicht mehr in Maschinensprache geschrieben, sondern in einer hoeheren Programmiersprache, fuer die es schon einen Compiler gibt. Tja wie war das mit der Henne und dem Ei, irgendwann musste da wohl mal ein erster Assembler sein. Keine Ahnung, wo der herkam. ;-)
So lange
Hi,
Das ist unterschiedlich. Es ist nicht unueblich, dass ein
Compiler den C-Source in ein Assembler-Programm uebersetzt, das
dann vom Assembler zu Maschinencode uebersetzt wird. Manche
Compiler bauen aber auch direkt den Maschinencode zusammen.
Sorry, ich hab bis jetzt nur gehoert, dass die Compiler den direkt in
Maschinencode uebersetzen.
Haeh?! Das ist jetzt wirklich Quatsch!
Der Maschinencode wird vom Prozessor ausgefuehrt, Punkt. Da
pfuscht kein Betriebssystem oder sonstwas drin rum. Das BS selbst
ist ja schliesslich auch nur Maschinencode, nichts anderes als
ein stinknormales Programm.
hm, tut mir leid, ich hab das anders gelernt... oder ich werf da was
durcheinander *g*
Der Assembler-Compiler heisst einfach Assembler, und wird
heutzutage sicher nicht mehr in Maschinensprache geschrieben,
sondern in einer hoeheren Programmiersprache, fuer die es schon
einen Compiler gibt. Tja wie war das mit der Henne und dem Ei,
irgendwann musste da wohl mal ein erster Assembler sein. Keine
Ahnung, wo der herkam. ;-)
Von dem ersten Compiler hab ich geredet.. der wird wohl in
Maschinencode geschrieben worden sein, zumindest hoert sich das
logisch fuer mich an *g*
mfg
CK1
hi!
Haeh?! Das ist jetzt wirklich Quatsch!
Der Maschinencode wird vom Prozessor ausgefuehrt, Punkt. Da
pfuscht kein Betriebssystem oder sonstwas drin rum. Das BS selbst
ist ja schliesslich auch nur Maschinencode, nichts anderes als
ein stinknormales Programm.
hm, tut mir leid, ich hab das anders gelernt... oder ich werf da was
durcheinander *g*
In welcher Hinsicht? Die ausführbare Datei eines Programs enthält reinen Maschinencode, der direkt vom
Prozessor ausgeführt wird. Gib zb. mal "debug programm.exe" ein und dann "u".
=== cut ===
0CB3:0100 50 PUSH AX
0CB3:0101 4B DEC BX
0CB3:0102 0304 ADD AX,[SI]
0CB3:0104 1400 ADC AL,00
0CB3:0106 0000 ADD [BX+SI],AL
0CB3:0108 0800 OR [BX+SI],AL
0CB3:010A 046A ADD AL,6A
0CB3:010C 7B26 JPO 0134
0CB3:010E F718 NEG WORD PTR [BX+SI]
0CB3:0110 D98C49020000 ESC 09,[SI+0249][SI+0000]
0CB3:0116 1E PUSH DS
0CB3:0117 0400 ADD AL,00
0CB3:0119 000A ADD [BP+SI],CL
0CB3:011B 0000 ADD [BX+SI],AL
0CB3:011D 005265 ADD [BP+SI+65],DL
=== cut ===
Vornedran steht die Adresse der jeweiligen Stelle im Programm (xxxx:0100 ist normalerweise die erste
Anweisung), danach kommt der Maschinencode in Hex-Form, danach des disassemblierte Sourcecode
(Disassemblierung des Sourcecodes ist jederzeit möglich, da - wie Calocybe schon schrieb - jeder
ASM-Befehl einen festen Maschinencode hat).
Um eine EXE-Datei auszuführen (bzw. eigentlich jedes Programm unter irgendeinem OS) lädt das
Betriebssystem, also der Kernel, das Programm in den Speicher und übergibt dem Prozessor die
Speicheradresse der ersten Maschinencode-Anweisung. Der Prozessor beginnt dann an dieser Stelle
und arbeitet die Maschinencodes nach und nach ab, bis er auf eine Anweisung trifft, die diese Abarbeitung
beendet (hm, ein einfacher Interrupt schätze ich, kann mich aber auch irren).
Das Betriebssystem tut also überhaupt nichts weiteres, um dem Prozessor irgendwie den Maschinencode
zur Verfügung zu stellen oder nochmal zu interpretieren und dann entsprechende Prozessor-Befehle
auszuführen.
bye, Frank!
ps. Das ist alles schon relativ lange her. Schlagt mich nicht, wenn ich jetzt teilweise Müll erzählt habe ;)
hi!
Vielleicht noch ein paar Ergänzungen :)
Ein paar ASM-befehle dürfte ich vom Sinn her noch hinkriegen ;)
0CB3:0100 50 PUSH AX
Das stellt woh lden Wert des Registers AX wieder her, der vorher auf dem Stack gespeichert wurde.
0CB3:0101 4B DEC BX
BX um 1 verringern.
0CB3:0102 0304 ADD AX,[SI]
SI zu AX addieren.
Den Rest kenne ich dann eher nicht ;) Falls sich jemand dafür interessiert, hier gibt es eine Liste für die
x86-Prozessoren von Intel: http://www.penguin.cz/~literakl/intel/intel.html
So gut kenne ich mich mit ASM dann aber doch nicht aus. Das einzige, was ich zur Zeit wohl noch
hinkriegen dürfte, sind ein paar Interrupts mit den passenden Registerwerten. Unter Windows und den
meisten neueren Betriebssystemen dürfte das aber größtenteils relativ sinnlos sein :)
bye, Frank!
ps. Das ist alles schon relativ lange her. Schlagt mich nicht, wenn ich jetzt teilweise Müll erzählt habe ;)
Hi Frank,
0CB3:0102 0304 ADD AX,[SI]
SI zu AX addieren.
Sorry, aber das bedeutet den Inhalt der Adresse, auf die SI zeigt, zu AX addieren. Ich glaube da wird implizit DS benutzt, aber sicher bin ich mir nicht. Bei den ganzen Adressierungsarten der Intel-Prozessoren bin ich nie komplett durchgestiegen.
Wie du geschrieben hast, ist lange her. Irgendwo liegt auch noch mein altes Berichtsheft rum, da ist eine Assembler-Routine drin, die in QuickBASIC eingebunden werden kann, um eine Linie auf einer EGA-Karte im "hochauflösenden" Grafik-Modus (640x350) zu malen.
Gruß,
Martin
Hi Frank,
na dann will ich mal für noch mehr Verwirrung sorgen <g>
Um eine EXE-Datei auszuführen (bzw. eigentlich jedes Programm unter irgendeinem OS) lädt das
Betriebssystem, also der Kernel,
dieser Teil vom kernel wird als Loader bezeichnet.
das Programm in den Speicher und übergibt dem Prozessor die
Speicheradresse der ersten Maschinencode-Anweisung.
Bei modernen Betriebssystemen nicht ganz. Zuerst werden die externen Referenzen (Sprungziele in DLLs) aufgelöst, das Code-Segment auf execute-only gesetzt, und ein paar Sachen mehr, mit denen ich mich nie beschäftigen musste. Ältere Betriebssysteme hatten einfach einen Jump oder Call auf die Einsprungadresse.
Der Prozessor beginnt dann an dieser Stelle
und arbeitet die Maschinencodes nach und nach ab, bis er auf eine Anweisung trifft, die diese Abarbeitung
beendet (hm, ein einfacher Interrupt schätze ich, kann mich aber auch irren).
Das müsste ich mal debuggen um genau zu sein, aber ich denke, dem Betriebssystem wird einfach mitgeteilt dass das Programm jetzt fertig ist, durch einen Betriebssystemaufruf.
Das Betriebssystem tut also überhaupt nichts weiteres, um dem Prozessor irgendwie den Maschinencode
zur Verfügung zu stellen oder nochmal zu interpretieren und dann entsprechende Prozessor-Befehle
auszuführen.
ps. Das ist alles schon relativ lange her. Schlagt mich nicht, wenn ich jetzt teilweise Müll erzählt habe ;)
Nicht nur bei dir. Fehlt zur ursprünglichen Frage nur noch dass ein C/C++-Compiler heutzutage auch in C geschrieben ist.
Gruß,
Martin
hi!
das Programm in den Speicher und übergibt dem Prozessor die
Speicheradresse der ersten Maschinencode-Anweisung.
Bei modernen Betriebssystemen nicht ganz. Zuerst werden die externen Referenzen (Sprungziele in
DLLs) aufgelöst, das Code-Segment auf execute-only gesetzt, und ein paar Sachen mehr, mit denen
ich mich nie beschäftigen musste.
Ok, kann sein. Das letzte mal, dass ich was mit ASM zu tun hatte, war unter MS-DOS ;)
Ältere Betriebssysteme hatten einfach einen Jump oder Call auf die Einsprungadresse.
So konnte man übrigens in QuickBasic INLINE-Code ausführen: den Code in eine Variable schreiben und
die Adresse der Variablen anspringen :) Habe ich zwar nie selbst ausprobiert, soll aber angeblich
funktionieren.
Der Prozessor beginnt dann an dieser Stelle
und arbeitet die Maschinencodes nach und nach ab, bis er auf eine Anweisung trifft, die diese Abarbeitung
beendet (hm, ein einfacher Interrupt schätze ich, kann mich aber auch irren).
Das müsste ich mal debuggen um genau zu sein, aber ich denke, dem Betriebssystem wird einfach
mitgeteilt dass das Programm jetzt fertig ist, durch einen Betriebssystemaufruf.
Schau dir mal eine der kleinen .com-Dateien im Windows-Verzeichnis an (zb. chcp.com oder loadfix.com
unter W2K). Die sind überblickbar. Kann es sein, dass man unter DOS eine INT21-Funktion aufrufen
musste? Das ist alles schon so lange her... ;)
bye, Frank!
Hi Frank,
Ok, kann sein. Das letzte mal, dass ich was mit ASM zu tun hatte, war unter MS-DOS ;)
ich auch ;-) Aber man liest sich durch so Einiges.
So konnte man übrigens in QuickBasic INLINE-Code ausführen: den Code in eine Variable schreiben und
die Adresse der Variablen anspringen :) Habe ich zwar nie selbst ausprobiert, soll aber angeblich
funktionieren.
Stimmt, das geht. Hab' ich zwar nie in QuickBASIC gemacht, aber in Turbo Pascal 4/5/6. Ich hab da in Assembler selbstgeschriebene Hardcopyroutinen geladen, die zum Auslesen des Bildschirminhaltes in die Turbo Pascal-Runtime gesprungen sind. Man hat halt so einige Sauereien gemacht <g>
Kann es sein, dass man unter DOS eine INT21-Funktion aufrufen
musste? Das ist alles schon so lange her... ;)
<nachguck> Entweder 0x00 in AH (DOS vor V2) oder 0x4c und in AL den Exitcode. Braucht jemand noch ein Buch "DOS 5 für Programmierer"?
Irgendwie fühl ich mich auf einmal so alt.
Gruß,
Martin
Moin,
Sorry, ich hab bis jetzt nur gehoert, dass die Compiler den direkt in
Maschinencode uebersetzen.
Eines der prominentesten Gegenbeispiele ist wohl
gcc bzw. g++.
Es gibt da noch ganz andere Variationen, z.B. erzeugt "g77" aus
einem FORTRAN Programm C-Code, der dann wiederum an
gcc übergeben wird, um schließlich als Assembler-Source von
"as" in ein Objekt-File umgewandelt zu werden.
Haeh?! Das ist jetzt wirklich Quatsch!
Der Maschinencode wird vom Prozessor ausgefuehrt, Punkt. Da
pfuscht kein Betriebssystem oder sonstwas drin rum. Das BS selbst
ist ja schliesslich auch nur Maschinencode, nichts anderes als
ein stinknormales Programm.hm, tut mir leid, ich hab das anders gelernt... oder ich werf da was
durcheinander *g*
Zu ergänzen ist an dieser Stelle vielleicht noch, daß in einem
Maschinenprogramm absolute Sprungadressen auftreten können.
Da der Speicherbereich des Programms bei jedem Aufruf
verschieden sein kann, müssen diese Sprungadressen
entsprechend angepaßt werden, was in der Tat eine Aufgabe
des Betriebssystems ist (man nennt diesen Vorgang
dann imho 'relozieren').
Die Art, wie und wo diese Sprungadressen im Binärfile
abgelegt sind usw. unterscheidet sich je nach Betriebssystem.
Von dem ersten Compiler hab ich geredet.. der wird wohl in
Maschinencode geschrieben worden sein, zumindest hoert sich das
logisch fuer mich an *g*
Wird wohl sein - ich denke jedenfalls nicht, daß der erste
C-Compiler in Basic geschrieben wurde ;-)
Danach geht es munter die Abstraktionsleiter aufwärts.
So ist z.B. sowohl gcc als auch g++ selbst in C geschrieben.
gcc benötigt also erstmal einen "anderen" C-Compiler, um
selber zu funktionieren. Dafür kann gcc sich dann im zweiten
Durchgang selbst optimieren...
Bis dannundwann
Andreas
Hallo irgendwer ;-)
Wenn man beispielsweise ein C-Programm compiliert dann wird dass doch in Assembler Code umgewandelt, aber manb kann keinen klaren Code lsen. Wenn man Assembler schreibt muss man das ganze ja eigentlich auch wieder durch ein spezielles Assembler Programm laufen lassen, soweit ich weiß. (in was wird sowas eigentlich geschrieben!?)
Alsop Leute ich denk mal ihr versteht meine Frage jetz mmal so halbwegs und hoffe jemand kann das alles mal verständlich erklären *g*
Mit dem C-Compiler wird ein C-Programm direkt in Maschinencode umgewandelt. Dieser ist eine Bytefolge, die den Instruktionen für den Prozessor entsprechen.
Ein Assembler-Code hingegen ist ein Programm, dass aus kurzen, den Prozessorinstruktionen direkt enstprechenden Befehlen, besteht.
Weiter beziehtsich das Assembler-Programm mit Registerzugriffen direkt auf die Hardware-Gegebenheiten der spezifischen Maschine.
Darum werden Assembler-Programme auch gerne als "maschinennah" bezeichnet.
Ich hoffe, das klärt Dir den Sachverhalt etwas auf.
Grüsse
Tom
Wenn man beispielsweise ein C-Programm compiliert
dann wird dass doch in Assembler Code umgewandelt,
aber manb kann keinen klaren Code lsen. Wenn man
Assembler schreibt muss man das ganze ja eigentlich
auch wieder durch ein spezielles Assembler Programm
laufen lassen, soweit ich weiß. (in was wird sowas
eigentlich geschrieben!?)
Nachdem Calocybe und Frank Schönmann die wesentlichen
Antworten bereits gegeben haben, möchte ich noch etwas
nachtragen zum Thema Assembler und Maschinensprache.
Beide sind für mich nämlich wesentlich weiter voneinan-
der entfernt, als die bisherigen Beiträge dies vermuten
lassen. (Das hängt nicht zuletzt mit der Qualität der
verschiedenen Assembler zusammen.)
Maschinensprache besteht also aus Bytefolgen.
Üblicherweise beginnt eine solche Bytefolge mit einer
Kennzeichnung des Befehls (Beispiele siehe bei Frank);
aus dieser Kennzeichnung ist dann auch ableitbar, wie
viele Bytes der Befehl enthält (das ist nämlich sehr
unterschiedlich, weil es vom Befehl abhängt, welche und
wie viele Operanden er verarbeiten soll und in welchem
Format die anzugeben sind: Will man den Inhalt zweier
Register addieren, dann reichen eine ADD-Markierung und
die Registernummern; will man zwei Speicherwerte addieren
Assembler ist eine Sprache, die versucht, diese Maschi-
nensprache durch abstrakte Features handhabbarer zu
machen. Beispielsweise will man eben möglichst *nicht*
wissen müssen, wie lang ein Befehl ist - denn versuche
mal, eine Schleife zu programmieren, wenn Du die Sprung-
adressen in realen Speicherpositionen berechnen mußt!
Füge nur einen einzigen Befehl in Dein Programm ein,
und alle Adressen sind falsch, nämlich um die Länge
dieses Befehls verschoben.
Also versteht Assembler als allererstes schon mal Varia-
blen. So, wie es Befehle in Assembler gibt, mit denen
man Maschinenoperationen notiert, gibt es auch Befehle,
mit denen man Variablen deklariert - ganz ähnlich wie
in JavaScript, bloß beschreibt die Anweisungsfolge (im
Wesentlichen) immer noch sequentiell eine Speicherbele-
gung. Wenn man also mitten zwischen seinen Befehlen
eine Variable deklariert, ist das wahrscheinlich keine
so brilliante Idee - aber ausschließen kann man das
auch nicht.
Und wenn man Speicherstellen einen Variablennamen geben
kann, dann natürlich auch Programmstellen einen Sprung-
markennamen. Der Assembler macht für den Entwickler die
gesamte Adreßrechnerei und generiert am Ende (fast schon
Maschinenbefehle mit den entsprechenden Operandenwerten
(Ich überspringe mal den Aspekt der Relozierbarkeit,
Linker usw. - das fällt dann ins Kapitel Betriebssystem-
architektur.)
Das nächste, was man auch dringend braucht, ist die
Möglichkeit, Code innerhalb eines Programms wiederzu-
verwenden. Also das, wofür man heute Funktionen schreibt.
Dafür gibt es in Assembler zwei Möglichkeiten:
Zudem kann man natürlich beliebige Assembler-Module
separat schreiben und übersetzen, und erst später zu
einem Programm zusammenbinden lassen. Modularität ist
keine Erfindung der neueren Informatikgeschichte.
Die Vielzahl weiterer features eines guten Assemblers
(Dummy-Sections und Basisregister, für etwas Ähnliches
wie Objektadressierung) aufzuzählen würde zu weit führen.
Was ich damit letztlich nur sagen wollte, ist: Auch
wenn heute niemand mehr maschinenabhängig programmiert,
so finden sich doch fast *alle* wesentlichen features
von 3GL-Programmiersprachen bereits in Assemblersprachen
der 70er Jahre wieder.
Die uralte IBM370-Architektur, also der gute alte Groß-
rechner, hat einen (!) Maschinenbefehl, der
1. den Inhalt einer Menge (!) von Registern an eine
Speicherstelle schreibt, deren Adresse in einem als
Operand anzugebenden Register steht,
2. die aktuelle Adresse plus seine Befehlslänge in ein
Register schreibt und
3. an die in einem anderen Register oder als Operand im
Speicher stehende Adresse springt.
Allein mit diesen einzigen Befehl hat man schon eine
wunderbare Prozeduraufrufschnittstelle, mehr braucht
man nicht. (Rückladen einer Registermenge gibt es
natürlich auch, das macht dann die aufgerufene Funktion
direkt vor dem Rücksprung.)
Und die Zieladresse kann man dann auch noch mit symbo-
lischen Adressen (quasi dem "Funktionsnamen") angeben.
Wenn man solche Speicherbereiche im Speicher "stapelt",
dann hat man auch (fast) alle Voraussetzungen für ge-
schachtelte, insbesondere rekursive Funktionsaufrufe.
Ein gutes, strukturiert geschriebenes Assemblerprogramm
ist genauso lesbar wie ein Programm in einer beliebigen
"Hochsprache". Es ist nur länger, weil man Sprachelemente
wie Schleifen oder Case-Abfragen eben selbst programmieren
muß. Das aber wiederum ist eine Eigenschaft der zugrunde-
liegenden Maschinensprache - nicht des Assemblers ...
Und die Maschinensprachen der Großrechner konnten halt
schon vor 30 Jahren Dinge, die ein Pentium heute noch
nicht beherrscht (aber heute auch nicht mehr beherrschen
muß, weil es eben Hochsprachen gibt). Beispielsweise
mit *einem* Maschinenbefehl zwei 16 Millionen Zeichen
lange Strings miteinander zu vergleichen und die Position
der ersten Abweichung in einem Register hinterlegen ...