Forum: Mikrocontroller und Digitale Elektronik Compiler Verständnisfrage


von Samuel J. (capstrovor)


Lesenswert?

Hallo

Ich habe mich heute gefragt, wie der C-Compiler Variablen adressiert 
(vll etwas komisch ausgedrückt).

Ich meine das so:
Wenn ich für den AVR ein Programm in Assembler schreibe, dann muss ich 
mir überlegen, wo ich Variablen im RAM ablege (wenn ich z.B. den Wert 
von R16 an die Adresse xyz speichern will). Da gibt es ja dann auch 
keine Variablen.

Aber in C deklariere ich z.B.
int i;

Das sind ja 32bit. Bei der Deklaration müsste doch eigentlich schon 
Speicher reserviert werden, oder? Aber wo speichere ich die Adresse ab? 
Und wie rufe ich diese wieder auf (...also an welcher Adresse hole ich 
mir die Adresse?! :D).
Wenn ich jetzt diese Variable mit etwas beschrieben will, z.B
i = 10;
dann müsste ja zuerst die Zahl 00000000|00000000|00000000|00001010 in 4 
Arbeitsregister geschrieben werden, und den Inhalt dieser vier Register 
dann an die Adresse des vorher reservierten Speichers geschoben werden.
Also wie setzt der Compiler diesen C-Code in Assembler um?

von Peter II (Gast)


Lesenswert?

Samuel J. schrieb:
> Also wie setzt der Compiler diesen C-Code in Assembler um?

schau es dir doch einfach an, im ASM listing sieht man es.

Ja der Compiler ordnet jede globale Variable einen Speicherplatz zu 
(genauer gesagt macht das er der Linker). Und überall wo die Variable 
verwendet wird, wird dann einfach die Adresse verwendet.

von Kleinender (Gast)


Lesenswert?

Samuel J. schrieb:
> Also wie setzt der Compiler diesen C-Code in Assembler um?

Bei AVR-GCC: Little-Endian

von (prx) A. K. (prx)


Lesenswert?

Samuel J. schrieb:
> (vll etwas komisch ausgedrückt).

Ja.

> Aber in C deklariere ich z.B.
> int i;
> Das sind ja 32bit.

Nein. Es sind mindestens 16 Bits.

> Bei der Deklaration müsste doch eigentlich schon
> Speicher reserviert werden, oder?

Bei Speicherklasse "auto" nicht, also den lokalen Variablen ohne 
expliziter Speicherklasse. Die liegen in aller Regel auf dem Stack und 
werden damit dynamisch reserviert.

> Aber wo speichere ich die Adresse ab?

Bei statischer Adresse (extern/static) im Befehl. Bei "auto" im Befehl 
als Distanz zum Stack- oder Frame-Pointer.

> Und wie rufe ich diese wieder auf (...also an welcher Adresse hole ich
> mir die Adresse?! :D).

Kling nach endloser Rekursion, zumindest in der Frage.

Volgo: Hä?

> Also wie setzt der Compiler diesen C-Code in Assembler um?

Jeder ein wenig anders.

von Peter II (Gast)


Lesenswert?

Samuel J. schrieb:
> Das sind ja 32bit

nein, nur bei einer 32bit CPU. Bei einer 8 oder 16bit CPU sind es nur 
16bit.

von Karl H. (kbuchegg)


Lesenswert?

Samuel J. schrieb:
> Hallo
>
> Ich habe mich heute gefragt, wie der C-Compiler Variablen adressiert
> (vll etwas komisch ausgedrückt).
>
> Ich meine das so:
> Wenn ich für den AVR ein Programm in Assembler schreibe, dann muss ich
> mir überlegen, wo ich Variablen im RAM ablege

bei den meisten Assemblern brauchst du das nicht. Du teilst dem 
Assembler einfach nur mit, dass du zb 2 Bytes unter einem Namen 
reserviert haben willst, und der Assembler macht das. Er merkt sich die 
Adresse in Tabellen während eines Assembler Laufes und setzt die Adresse 
überall dort ein wo du den Namen verwendest. Den exakte Zahlenwert, wo 
genau das Teil im Speicher liegt, brauchst du nie wirklich.

> Aber in C deklariere ich z.B.
> int i;
>
> Das sind ja 32bit. Bei der Deklaration müsste doch eigentlich schon
> Speicher reserviert werden, oder?

Auch wenn es technisch etwas komplizierter ist (weil da auch noch ein 
Linker im Spiel ist): Bei der Definition einer Variablen wird Speicher 
zugeteilt und Compiler (bzw. Linker) merken sich, an welcher Adresse das 
ist.

> Aber wo speichere ich die Adresse ab?
Gar nicht.
Die geht dich nichts an.

> Und wie rufe ich diese wieder auf (...also an welcher Adresse hole ich
> mir die Adresse?! :D).

Du kannst immer den Adress of OPerator benutzen, wenn du die Adresse 
haben willst.

> Wenn ich jetzt diese Variable mit etwas beschrieben will, z.B
> i = 10;
> dann müsste ja zuerst die Zahl 00000000|00000000|00000000|00001010 in 4
> Arbeitsregister geschrieben werden, und den Inhalt dieser vier Register
> dann an die Adresse des vorher reservierten Speichers geschoben werden.

Ja.
Aber wieder: das geht dich nicht an. Das erledigen Compiler/Linker für 
dich. Genau dazu hat man ja COmpiler, damit man sich um solche 
"Kleinigkeiten" nicht kümmern muss.

von Samuel J. (capstrovor)


Lesenswert?

Peter schrieb:
> Ja der Compiler ordnet jede globale Variable einen Speicherplatz zu
Ok, bei einem AVR, bei dem immer nur 1 Programm läuft verstehe ich das, 
aber wie geht das bei x86 Prozessoren?
Da ist ja nicht immer die gleiche Speicheradresse frei?

von Ulrich H. (lurchi)


Lesenswert?

Der Datentyp int hat üblicherweise, insbesondere beim 8 Bit AVR nur 16 
Bit Größe.

Wie der Compiler die variablen im Speicher ablegt hängt vom Compiler und 
der Art der Variable (Global, lokal, static). Die Adresse zu der 
variable muss vor allem der Compiler kennen und beim Zugriff einsetzten. 
Im µC speichern muss er das oft nicht.

Was der Compiler daraus macht, schaut man sich am besten direkt an, etwa 
bei GCC im .lst File.

von (prx) A. K. (prx)


Lesenswert?

Samuel J. schrieb:
> Da ist ja nicht immer die gleiche Speicheradresse frei?

Aus Sicht von Compiler, Linker und Programm schon. Aus Sicht des 
Betriebssystems und der Speicherchips nicht. Den Unterschied dazwischen 
nennt man Speicherverwaltungseinheit, oder MMU.
https://de.wikipedia.org/wiki/Memory_Management_Unit

von Peter II (Gast)


Lesenswert?

Samuel J. schrieb:
> Ok, bei einem AVR, bei dem immer nur 1 Programm läuft verstehe ich das,
> aber wie geht das bei x86 Prozessoren?
> Da ist ja nicht immer die gleiche Speicheradresse frei?

geht dort genauso, jeder Prozess hat seinen eigenen Virtuellen Speicher.

Bei Systemen wo das nicht so ist, kann das Betriebssystem beim laden 
einfach einen Offset vorgeben.

von Samuel J. (capstrovor)


Lesenswert?

Ok ich verstehe.

Also wenn ich mal eure 2 Antworten zusammenfasse, wäre das dann so 
gemeint:
Jedes Programm hat einen Speicherbereich im RAM zugewiesen (kurze 
Zwischenfrage: statisch oder dynamisch?). Sagen wir mal jedes Programm 
hat 1MB platz. Wenn jetzt das Programm auf die Adresse 0x01 zugreifen 
will, addiert die MMU den Offset, also die Anfangsadresse des Programms, 
um auf die tatsächliche Adresse des Chips zu kommen.
Ist das so richtig?

von Peter II (Gast)


Lesenswert?

Samuel J. schrieb:
> Wenn jetzt das Programm auf die Adresse 0x01 zugreifen
> will, addiert die MMU den Offset, also die Anfangsadresse des Programms,
> um auf die tatsächliche Adresse des Chips zu kommen.
> Ist das so richtig?

in etwa. Es wird eine Tabelle sein. Also eine Übersetzung von 
Virtueller-Programm Adresse zu Physikalischer-Ram Adresse.

von (prx) A. K. (prx)


Lesenswert?

Samuel J. schrieb:
> Jedes Programm hat einen Speicherbereich im RAM zugewiesen (kurze
> Zwischenfrage: statisch oder dynamisch?).

Windows/Linux: dynamisch. Bare Metal, wie AVR: statisch.

> Ist das so richtig?

So ungefähr. Nur verwendet man jenseits 8086/286 keine Addition, sondern 
macht das seitenweise (paging). Weshalb es virtuellen Programmspeicher 
geben kann, für den kein physikalischer Speicher reserviert ist. Das 
scheppert dann beim Zugriff und der Speicher wird beschafft (oder das 
Programm fliegt weg).

von Peter II (Gast)


Lesenswert?

Samuel J. schrieb:
> Jedes Programm hat einen Speicherbereich im RAM zugewiesen (kurze
> Zwischenfrage: statisch oder dynamisch?).

wie soll es dann statisch sein, wenn man das Programm jederzeit starten 
und beenden kann?

Du musst schon etwas unterscheiden, zwischen eine Programm was innerhalb 
einen Betriebssysteme läuft und einen Programm was direkt von einer CPU 
ausgeführt wird.

von Samuel J. (capstrovor)


Lesenswert?

Peter schrieb:
> wie soll es dann statisch sein, wenn man das Programm jederzeit starten
> und beenden kann?

Ich meinte damit ob der zugewiesene Speicher immer gleich groß ist und 
ein Programm, wenn es mehr Speicher braucht, mehrere davon bekommt, oder 
ob es gleich dynamisch angepasst wird.

> Du musst schon etwas unterscheiden, zwischen eine Programm was innerhalb
> einen Betriebssysteme läuft und einen Programm was direkt von einer CPU
> ausgeführt wird.

Das verstehe ich jetzt nicht so ganz. Ein Programm wird doch im 
Endeffekt immer von der CPU ausgeführt. Und ein OS ist doch eigentlich 
auch nur eine Lib mit Syscalls. So Sachen wie Shell oder Grafische 
Oberfläche sind doch auch nur Programme in einem anderen Systemmode. 
Oder täusche ich mich da?

von (prx) A. K. (prx)


Lesenswert?

Samuel J. schrieb:
> Ich meinte damit ob der zugewiesene Speicher immer gleich groß ist

Nein. Auf den realen Speicher bezogen.

> oder ob es gleich dynamisch angepasst wird.

Ja.

> Das verstehe ich jetzt nicht so ganz. Ein Programm wird doch im
> Endeffekt immer von der CPU ausgeführt.

Es können auch mehrere CPUs sein. Und es kann auch eine einzelne CPU 
gleichzeitig mit mehreren Programmen beschäftigt sein, auch mehrfach dem 
gleichen.

> Und ein OS ist doch eigentlich auch nur eine Lib mit Syscalls.

Nein. Das hat ein Eigenleben.

> So Sachen wie Shell oder Grafische
> Oberfläche sind doch auch nur Programme in einem anderen Systemmode.

Shell ja. Bei der GUI hängt das vom System ab.

Filesysteme können beispielsweise eine Kombination aus Kernel-Code und 
Hintergrundprozessen sein.

von Karl H. (kbuchegg)


Lesenswert?

Samuel J. schrieb:
> Peter schrieb:
>> wie soll es dann statisch sein, wenn man das Programm jederzeit starten
>> und beenden kann?
>
> Ich meinte damit ob der zugewiesene Speicher immer gleich groß ist und
> ein Programm, wenn es mehr Speicher braucht, mehrere davon bekommt, oder
> ob es gleich dynamisch angepasst wird.

Das spielen jetzt mehrere Komponenten zusammen.
Wenn eine MMU im Spiel ist, die mit Paging funktioniert, dann muss nicht 
zu jedem Zeitpunkt jede vrituelle Adresse auch auf tatsächlich physisch 
vorhandenem Speicher abgebildet sein.
Wenn ein Programm in so einem System loslegt, dann spricht es den 
Speicher (über die virtuelle Adresse ) einfach an. Die MMU kriegt diese 
Adresse und sieht nach, zu welchem physikalisch vorhandenem Speicher 
diese Adresse gehört. Gibt es physikalischen Speicher dafür, dann geht 
die Adresse an den Adressbus raus und der zuständige Speicherbaustein 
liefert die Daten (oder verinnahmt sich die Daten vom Datenbus). Ist 
aber der virtuellen Adresse kein physischer Speicher zugeordnet, dann 
löst die MMU eine Ausnahmebehandlung aus. Der Ball geht zum 
betriebssystem, welches dafür zuständig ist, einen entsprechenden 
Speicherbereich freizuschaufeln (falls gerade nichts frei ist) und in 
der MMU eine Verknüpfung von der virtuellen Adresse in diesen (ev. erst 
freigeräumten) Speicher herzustellen.
NOrmalerweise geschehen derartige Reservierungen in Form von 
Speicherblöcken, die man auch Pages nennt. Denn wenn man das für jede 
Adresse einzeln machen müsste, würde das ausufern. Die MMU verwaltet zb 
jeweils 4K grosse Speicherblöcke. Aus der virtuellen Adresse wird die 
Blocknummer ermittelt, in der diese Adresse liegt und mit der wird dann 
in den Tabellen nachgesehen, welcher physikalisch tatsächlich vorhandene 
Speicher (der dann ebenfalls in 4K großen Blöcken eingeteilt ist) da 
dazugehört.

Das heißt aber auch: Das Programm kann beim Starten über den kompletten 
Adressraum verfügen. Verfügen in dem Sinne, das es einfach den Speicher 
(aus seiner Sicht) anspricht, obwohl ihm dafür noch gar kein physischer 
Speicher zugeordnet wurde. Das geschieht erst nach und nach, je nachdem 
wie und wo das Programm mittels seiner virtuellen Adressen zugreift.

>> Du musst schon etwas unterscheiden, zwischen eine Programm was innerhalb
>> einen Betriebssysteme läuft und einen Programm was direkt von einer CPU
>> ausgeführt wird.
>
> Das verstehe ich jetzt nicht so ganz. Ein Programm wird doch im
> Endeffekt immer von der CPU ausgeführt. Und ein OS ist doch eigentlich
> auch nur eine Lib mit Syscalls.

So einfach ist die Sache nicht. Ein Betriebssytem hat sich auch um die 
Verwaltung der Resourcen zu kümmern. Zum Beispiel um die Zuteilung von 
real existierendem Speicher zu virtuellen Adressen.

von Samuel J. (capstrovor)


Lesenswert?

Ok danke für eure Antworten! :)

A.K
>Nein. Das hat ein Eigenleben.

Was meinst du genau mit Eigenleben?
Meinst du damit so Sachen wie TCP/IP Stack und solche Anwendungen?

von Peter II (Gast)


Lesenswert?

Samuel J. schrieb:
>>Nein. Das hat ein Eigenleben.
>
> Was meinst du genau mit Eigenleben?

es kann z.b. deim Programm einfach keine CPU zeit mehr zuweisen, es kann 
dein Programm komplett aus dem Speicher löschen. Es kann halt dinge 
mache die das Programm nicht kontrollieren kann.

von Karl H. (kbuchegg)


Lesenswert?

Samuel J. schrieb:
> Ok danke für eure Antworten! :)
>
> A.K
>>Nein. Das hat ein Eigenleben.
>
> Was meinst du genau mit Eigenleben?

Du hast anscheinend noch nie erlebt, wenn Windows zwischendurch mal den 
Speicher aufräumt. Plötzlich fängt deine Festplatte an zu laufen, weil 
Windows anfängt zu voll gewordenen Speicher auf die Festplatte 
auszulagern bzw. frei gewordenen Speicher wieder aus der Festplatte mit 
den Bytes zu beschreiben.

Auf heutigen Systemen ist das Betriebssystem nicht einfach nur eine Lib. 
Genau anders rum. Das Betriebssystem ist das Hauptprogramm das die 
Kontrolle über alles hat und es teilt deinem Programm Dinge zur 
Benutzung zu. Das BS ist der Boss!

von Samuel J. (capstrovor)


Lesenswert?

>deim Programm einfach keine CPU zeit mehr zuweisen

Das wäre ja dann der Scheduler, also auch ein Programm im Kernelmode, 
oder?

von Peter II (Gast)


Lesenswert?

Samuel J. schrieb:
> Das wäre ja dann der Scheduler, also auch ein Programm im Kernelmode,
> oder?

Es ist ein Teil von BS, ob das ganze im Kernelmode läuft ist von der CPU 
anhängig, nicht jede CPU hat so etwas.

von (prx) A. K. (prx)


Lesenswert?

Samuel J. schrieb:
> Was meinst du genau mit Eigenleben?
> Meinst du damit so Sachen wie TCP/IP Stack und solche Anwendungen?

Auch. Aber wie erwähnt kann beispielsweise die Implenmentierung eines 
Filesystems sowohl aus Kernelkomponenten im Sinne deiner Library 
bestehen, als auch aus Hintergrundprozessen für weitergehende 
Verwaltung.

Das kann auch bei Speicherverwaltung so sein. Der Kernel reagiert auf 
die Speicheranforderung, sei es per Syscall oder per Paging-Exception 
wie von KHB beschrieben. Und ein Verwaltungsprozess/thread sorgt 
vielleicht für ausgewogene Zuordnung und passendem Freiraum.

von Samuel J. (capstrovor)


Lesenswert?

> Es ist ein Teil von BS
OK

> ob das ganze im Kernelmode läuft ist von der CPU
> anhängig, nicht jede CPU hat so etwas.
Ok, dann meinte ich etwas anderes. Aber mir ist klar auf was ihr hinaus 
wollt. :)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Samuel J. schrieb:
> Wenn ich für den AVR ein Programm in Assembler schreibe, dann muss ich
> mir überlegen, wo ich Variablen im RAM ablege (wenn ich z.B. den Wert
> von R16 an die Adresse xyz speichern will). Da gibt es ja dann auch
> keine Variablen.

In Assembler kann man es aber auch fast so machen wie der Compiler -- 
zumindest mit dem GNU-Assembler, etwa:
1
;; Program Code
2
.text
3
4
;; The Startup-Code from crt*.o calls main
5
.global main
6
.type main,@function  ;; just for debugger, objdump etc.
7
8
main:
9
    ;; Load address of var to R20/R21
10
    ldi r20, lo8(var)
11
    ldi r21, hi8(var)
12
    ;; Store &var in var
13
    sts var, r20
14
    sts var+1, r21
15
    ;; Epilogue
16
    ret
17
.size main, .-main ;; Just for book keeping
18
19
;; .data Section: Static data initialized at Load-Time
20
.data
21
22
.global var
23
.type var,@object ;; just for debugger, objdump etc.
24
var:
25
    .word 1000
26
27
;; Section .data must be initialized at Load-Time.
28
;; Refer to the respective Bit of Startup-Code to drag it in
29
.global __do_copy_data
30
31
;; Similar for Data in .bss, i.e. initialized to 0 at Load-Time
32
; .global __do_clear_bss

"var" liegt hier im Static Storage, d.h. der Platz für die Variable wird 
vom Linker/Lokator vergeben.  Das Symbol "var" wird dann, wenn lokatiert 
wird, durch die konkrete Adresse ersetzt; bis dahin ist es lediglich 
sowas wie ein eindeutig vergebener Platzhalter.

Das Programm kann übersetzt werden mit
1
$ avr-gcc -mmcu=atmega8 bar.sx -save-temps -o bar.elf -Wa,-alhd=bar.lss -Wl,-Map,bar.map -v -Wl,-v
2
$ avr-objdump -dr bar.o > bar.lso
3
$ avr-objdump -D -j .text -j .data bar.elf > bar.lst

Das liefert erstma die Dröhnung...  Was passiert ist folgendes

1) Der Compiler-Treiber (avr-gcc) erkennt an der Endung .sx, daß es sich 
um eine Assembler-Quelle handelt, die durch den C-Präprozessor soll.

2) Der Treiber ruft den Präprozessor auf und speichert das Ergebnis als 
bar.s, eine reine Assemler-Datei (hier kein Unterschied).

3) Der Treiber ruft den Assembler auf, dieser assembliert bar.s zu bar.o 
und dumpt seine Arbeit als bar.lss. bar.o enthält Platzhalter für "var", 
die als 0 disassembliert werden weil dem Symbol noch kein Wert 
zugewiesen ist.  Mehr sieht man an dem von objdump erzeugten .lso: Hier 
sieht man die Relocs und deren Adressen.  Für den High-Teil von "var" 
wird ein anderer Reloc benötogt als für den low-Teil, und wieder ein 
anderer für die 16-Bit Adresse des low-Teils bzw. 16-Bit Adresse des 
high-Teils von "var".

4) Der Treiber ruft collect2 und den Linker mit zahlreichen Optionen und 
Dateien auf, darunter alle Objekte und Bibliotheken, die zum fertigen 
Programm benötogt werden: crt*.o (Startup-Code), bar.o (Hauptprogramm), 
libgcc.a (statische Bibliothek mit Basis-Arithmetik und weiteren Teilen 
des Startup-Codes), etc.

5) Der Linker durchsucht die Symboltabellen nach undefinierten Symbolen, 
z.B. "var". Wenn er keine Definition finden kann dann gibt er ein Fehler 
aus.  Ansonsten zieht sich der Linker benötigte Sections aus den 
Bibliotheken (hier z.B. __do_copy_data aus der libgcc.a) um Symbole 
aufzulösen, und der Locator ordnet dann den Symbolen Werte zu. Diese 
Zuordnung passiert anhand des Symboltyps, seiner Größe, seinem 
Alignment, seinen Flags und seiner Section (.data für "bar", .text für 
"main", .init4 für "__do_copy_data").  Wie und wo die einzelnen 
Input-Sections, Objects und Objekte abzulegen und auf Outpt-Sections 
abzubilden sind, erfährt der Linker aus der Linker-Description 
(avr/lib/ldscripts) und Kommandzeilen-Optionen.  Was wohin lokatiert 
wurde siehst du in der Memory-Map bar.map sowie im Dump bar.lst des 
fertigen bar.elf:
1
0000004e <main>:
2
  4e:  40 e6         ldi  r20, 0x60  ; 96
3
  50:  50 e0         ldi  r21, 0x00  ; 0
4
  52:  40 93 60 00   sts  0x0060, r20
5
  56:  50 93 61 00   sts  0x0061, r21
6
  5a:  08 95         ret

var liegt also an Adresse 0x60, dem Start des RAMs beim ATmega8.

> Aber in C deklariere ich z.B.
> int i;

Der Compiler erzeugt den Assembler-Code nach den gleichen Prinzipien wie 
oben.  Das Beispiel entspricht ca.
1
void *var;
2
3
void main (void)
4
{
5
    var = &var;
6
}
wenn im Compileraufruf bar.sx durch bar.c ersetzt wird.  Zusätzlich mit 
-Os -fno-common -fverbose-asm kommt bar.i als Dump-Datei hinzu 
(präprozessierte C-Quelle), und bar.s wird zu:
1
  .file  "bar.c"
2
__SP_H__ = 0x3e
3
__SP_L__ = 0x3d
4
__SREG__ = 0x3f
5
__tmp_reg__ = 0
6
__zero_reg__ = 1
7
8
...
9
  .section  .text.startup,"ax",@progbits
10
.global  main
11
  .type  main, @function
12
main:
13
/* prologue: function */
14
/* frame size = 0 */
15
/* stack size = 0 */
16
.L__stack_usage = 0
17
  ldi r24,lo8(var)   ;  tmp42,
18
  ldi r25,hi8(var)   ; ,
19
  sts var+1,r25   ;  var, tmp42
20
  sts var,r24   ;  var, tmp42
21
  ret
22
  .size  main, .-main
23
.global  var
24
  .section .bss
25
  .type  var, @object
26
  .size  var, 2
27
var:
28
  .zero  2
29
  .ident  "GCC: (GNU) 5.0.0 20140907 (experimental)"
30
.global __do_clear_bss

> Das sind ja 32bit. Bei der Deklaration müsste doch eigentlich schon
> Speicher reserviert werden, oder?

Nö, eine Deklaration legt kein Speicher an, sondern eine Definition. 
Was oben steht ist eine Definition.

> Aber wo speichere ich die Adresse ab?
> Und wie rufe ich diese wieder auf (...also an welcher Adresse hole ich
> mir die Adresse?! :D).

I.d.R brauchst du die Adresse nicht, zumindest nicht den exakten 
Zahlenwert.  Verfolge einfach mal, was aus folgenden Code wird und 
verstehe die Fehlermeldung, die er erzeugt und wie man sie behebt:
1
extern int i;
2
3
int __attribute__((noinline,noclone))
4
get_int (int *address)
5
{
6
    return *address;
7
}
8
9
int main (void)
10
{
11
    return get_int (&i);
12
}
Dat Attribut-Gedöns dient nur dazu, daß der Code nicht bis zur 
Unkenntlichkeit optimiert wird.

> Wenn ich jetzt diese Variable mit etwas beschrieben will, z.B
> i = 10;
> dann müsste ja zuerst die Zahl 00000000|00000000|00000000|00001010 in 4
> Arbeitsregister geschrieben werden, und den Inhalt dieser vier Register
> dann an die Adresse des vorher reservierten Speichers geschoben werden.

Ja. Es sei denn, es kann wegoptimiert werden (z.B. weil der Wert nicht 
verwendet wird).

> Also wie setzt der Compiler diesen C-Code in Assembler um?

-save-temps -fverbose-asm -g0 ist hier dein Freund.

von Axel S. (a-za-z0-9)


Lesenswert?

Samuel J. schrieb:
>> Du musst schon etwas unterscheiden, zwischen eine Programm was innerhalb
>> einen Betriebssysteme läuft und einen Programm was direkt von einer CPU
>> ausgeführt wird.
>
> Das verstehe ich jetzt nicht so ganz. Ein Programm wird doch im
> Endeffekt immer von der CPU ausgeführt. Und ein OS ist doch eigentlich
> auch nur eine Lib mit Syscalls.

So war das bis (MS-)DOS.

Seit dem hat sich eine Menge getan. PC haben seit dem 386 eine MMU und 
damit die Möglichkeit, mehrere (viele) Tasks mit jeweils ihrem eigenen 
virtuellen Adreßraum zu haben. Das Betriebssystem ist (wenn es diese 
Funktion nutzen will) dann u.A. beauftragt, beim Wechsel zwischen Taks 
nicht nur den CPU-Kontext (Registerbelegung) sondern auch den 
MMU-Kontext (Mapping von virtuellem zu physischem Speicher) 
umzuschalten. Die Speicherwerwaltung ist bei allen nennenswerten 
Betriebssystemen komplett dynamisch. Das geht so weit, daß Teile des 
Maschinencodes eines Programms, die (noch) nicht ausgeführt wurden, auch 
gar nicht im Speicher liegen. Das Mapping hat dann an dieser Stelle ein 
"Loch", das erst dann gefüllt wird, wenn der entsprechende Code auch mal 
zur Ausführung kommt. Genauso kann Speicher der in den Adreßraum eines 
gerade nicht ausgeführten Programms gemappt ist, freigemacht werden 
(ausgelagert, ausgeswapt) und kurzfristig einem anderen Programm gegeben 
werden.

Ja, das ist faszinierend. Eben deswegen hat ein finnischer Student 
namens Linus Torvalds sich auch kurz nachdem er seinen ersten 386er 
hatte, mal eine Polarnacht lang hingesetzt und ein Betriebssystem 
gehackt das die Möglichkeiten des neuen Prozessors auch ausnutzte. Das 
war vor 23 Jahren. Der Rest ist Geschichte.

Die Konzepte hat Linus natürlich nicht neu entwickelt. Die waren in der 
Welt der Mini- und Großrechner schon damals etabliert. Ein wichtiger 
Begriff in diesem Zusammenhang wäre Paging.


XL

von Jens G. (jensig)


Lesenswert?

>Axel Schwenke (a-za-z0-9)

>> Das verstehe ich jetzt nicht so ganz. Ein Programm wird doch im
>> Endeffekt immer von der CPU ausgeführt. Und ein OS ist doch eigentlich
>> auch nur eine Lib mit Syscalls.

>So war das bis (MS-)DOS.

So ist das auch jetzt noch.  Oder was sonst führt denn sonst das Program 
aus.
Der große Unterschieht ist nur, daß unter DOS das Programm alles machen 
konnte, was es so wollte (hatte volle Kontrolle auf die Hardware (DOS 
selbst war eigentlich eher nur ein Programmstarter mit ein paar 
nützlichen Routinen), während unter moderneren OS das Programm eben 
nicht mehr alles mit der HW machen darf, was es will.
Dabei ist es eigentlich noch nichtmal das OS, welches dies ermöglicht, 
sondern der Prozessor, der die entprechende Architektur dafür bietet. 
Z.B. unterschiedliche Privilegienlevel bei Intel (Ring 0 - 3).
Das OS (dessen Kernel) läuft auf Ring 0 (höchste Privilegien - kann also 
alles machen), und Programme laufen auf 3 (niedrigste Privilegstufe - 
darf z.B. keine direkten HW-Zugriffe machen).
Damit der Prozessor weis, was er wie machen soll, muß das OS ihn 
sozusagen konfigurieren (Prozessormodus, Pagetables, ...)
Für alles, was das Priogram richting HW (CPU Settings (Register), 
Peripherie) machen will, muß es das OS fragen, indem es die  Syscalls 
benutzt. Damit bleiben sämtliche Zugriffe anch ausen wie auch 
Memorymanagment (mit hilfe des virt. Memorykonzepts) unter Kontoll des 
OS.

von Rolf M. (rmagnus)


Lesenswert?

Peter II schrieb:
> Samuel J. schrieb:
>> Das wäre ja dann der Scheduler, also auch ein Programm im Kernelmode,
>> oder?
>
> Es ist ein Teil von BS, ob das ganze im Kernelmode läuft ist von der CPU
> anhängig, nicht jede CPU hat so etwas.

Der CPU ist es eigentlich ziemlich wurscht, ob der Scheduler im Kernel 
läuft oder nicht. Es ist vielmehr vom Betriebssystem abhängig.

Axel Schwenke schrieb:
> PC haben seit dem 386 eine MMU und damit die Möglichkeit, mehrere (viele)
> Tasks mit jeweils ihrem eigenen virtuellen Adreßraum zu haben.

Eigentlich schon seit dem 286. Da wurde das nur noch nicht so intensiv 
benutzt, und es gab noch kein Paging.

> Die Speicherwerwaltung ist bei allen nennenswerten Betriebssystemen
> komplett dynamisch. Das geht so weit, daß Teile des Maschinencodes eines
> Programms, die (noch) nicht ausgeführt wurden, auch gar nicht im Speicher
> liegen. Das Mapping hat dann an dieser Stelle ein "Loch", das erst dann
> gefüllt wird, wenn der entsprechende Code auch mal zur Ausführung kommt.

Oder z.B. wie beim Unix-Systemcall fork(), mit dem sich ein Prozess 
duplizieren kann. Tatsächlich wird erstmal vom Speicher her nur die 
Seitentabelle kopiert. Der eigentliche Programmspeicher ist bei beiden 
physikalisch der selbe. Erst wenn eine der beiden Programm-Instanzen 
einen Schreibzugriff im Speicher macht, wird die entsprechende 
Speicherseite (und nur die) kopiert, damit die Programme unabhängig von 
einander weiterlaufen können.

> Genauso kann Speicher > der in den Adreßraum eines gerade nicht
> ausgeführten Programms gemappt ist, freigemacht werden (ausgelagert,
> ausgeswapt) und kurzfristig einem anderen Programm gegeben werden.

Und später, wenn der ausgelagerte Speicher wieder zurückgelesen wird, 
muß das im realen Speicher nicht mehr an der selben Stelle sein, wie 
vorher. Für das Progamm ist es aber an der selben Adresse. Das hat nicht 
mal mitbekommen, daß ein Teil seines Speichers zwischendurch ausgelagert 
wurde.

von Samuel J. (capstrovor)


Lesenswert?

Danke an Alle für die hilfreichen Antworten!
Ich habe mir jetzt ein Buch über Betriebssysteme gekauft, da ich mich 
jetzt intensiver damit beschäftigen will.
Aber wenn es wieder Fragen gubt wende ich mich wieder an euch :)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.