Forum: Compiler & IDEs C++ auf default Red Hat Enterprise Linux 5


von Porter (Gast)


Lesenswert?

Hallo Leute,

ich möchte gerne C++11 Code auf einer Default Red Hat Enterprise Linux 5 
Installation ausführen. D.h. ich darf z.B. nicht das Red Hat Developer 
Toolset installieren.
In der Default installation hat RHEL5 leider keinen C++11 fähigen 
Compiler...

Ist es möglich, auf einer anderen Maschine (meinetwegen in einer VM) zu 
Kompilieren und dann einfach auf RHEL5 auszuführen? Wo sind dabei die 
Probleme?


Danke schonmal im Voraus!

(p.s. ich kenne mich mit Linux noch sehr wenig aus)

von Peter II (Gast)


Lesenswert?

Porter schrieb:
> Wo sind dabei die
> Probleme?

wenn du alles statisch links muss nur die gleiche Plattform vorhanden 
sein ( x68, 64bit, ARM ... ).

Wenn du dynamisch links, müssen die die passenden Libs (z.b. glibc) auf 
dem System vorhanden sein (auch mit möglichst der gleichen Version).

Das ganze ist aber kein Linux Problem. Das gleiche Problem hat man auch 
unter Windows.

von Sebastian V. (sebi_s)


Lesenswert?

Du kannst dir auch eine aktuelle Version von GCC oder Clang selbst 
compilieren. Habe ich hier auch gemacht (allerdings auf RHEL6).

Edit: Oder was vermutlich auch geht ist einen PC/VM mit RHEL5 und dem 
Developer Toolset einzurichten und darauf compilieren. Nach meinem 
Wissen ist das Developer Toolset so konfiguriert das die compilierten 
Programme auch auf PCs ohne dieses Toolset laufen (indem die 
Standardlibrary statisch gelinkt wird etc.)

: Bearbeitet durch User
von Porter (Gast)


Lesenswert?

Peter II schrieb:
> Wenn du dynamisch links, müssen die die passenden Libs (z.b. glibc) auf
> dem System vorhanden sein (auch mit möglichst der gleichen Version).

Ich möchte gerne dynamisch linken, da ich sehr viele Threads erzeuge und 
Performance sehr wichtig ist.

Sebastian V. schrieb:
> Du kannst dir auch eine aktuelle Version von GCC oder Clang selbst
> compilieren.

Du meinst also, dass ich das aktuelle GCC mitliefere und dann zuerst GCC 
kompiliere und anschließend mit dem neuen GCC mein Programm?

Sebastian V. schrieb:
> Edit: Oder was vermutlich auch geht ist einen PC/VM mit RHEL5 und dem
> Developer Toolset einzurichten und darauf compilieren. Nach meinem
> Wissen ist das Developer Toolset so konfiguriert das die compilierten
> Programme auch auf PCs ohne dieses Toolset laufen (indem die
> Standardlibrary statisch gelinkt wird etc.)

Das werde ich prüfen, hört sich interessant an!

von Sebastian V. (sebi_s)


Lesenswert?

Porter schrieb:
> Sebastian V. schrieb:
>> Du kannst dir auch eine aktuelle Version von GCC oder Clang selbst
>> compilieren.
>
> Du meinst also, dass ich das aktuelle GCC mitliefere und dann zuerst GCC
> kompiliere und anschließend mit dem neuen GCC mein Programm?

Ne nicht mitliefern. Ich glaube ich hatte dein Problem anfangs falsch 
verstanden. Wenn du keine root Rechte hast und dir der Admin auch kein 
Developer Toolkit installieren will kann man sich den GCC selbst 
compilieren und ins Benutzerverzeichnis installieren, um an einen 
aktuellen Compiler zu kommen. Du willst aber scheinbar das fertig 
compilierte Programm auf mehreren PCs verteilen wenn ich dich richtig 
verstanden habe.

Porter schrieb:
> Peter II schrieb:
>> Wenn du dynamisch links, müssen die die passenden Libs (z.b. glibc) auf
>> dem System vorhanden sein (auch mit möglichst der gleichen Version).
>
> Ich möchte gerne dynamisch linken, da ich sehr viele Threads erzeuge und
> Performance sehr wichtig ist.

Was hat dynamisch/statisch linken mit Performance zu tun?

: Bearbeitet durch User
von Porter (Gast)


Lesenswert?

Sebastian V. schrieb:
> Was hat dynamisch/statisch linken mit Performance zu tun?

Das kann man hier nachlesen:
http://stackoverflow.com/questions/1993390/static-linking-vs-dynamic-linking

Sebastian V. schrieb:
> Ne nicht mitliefern. Ich glaube ich hatte dein Problem anfangs falsch
> verstanden. Wenn du keine root Rechte hast und dir der Admin auch kein
> Developer Toolkit installieren will kann man sich den GCC selbst
> compilieren und ins Benutzerverzeichnis installieren, um an einen
> aktuellen Compiler zu kommen. Du willst aber scheinbar das fertig
> compilierte Programm auf mehreren PCs verteilen wenn ich dich richtig
> verstanden habe.

Es ist mir eigentlich egal, ob ich das fertig kompilierte Programm 
verteile, oder auf dem PC auf dem es laufen soll kompiliere.
Ich entwickele halt auf einem PC, muss das fertige Programm aber auf 
einem anderen ausführen, wo die Admins mir fast nichts erlauben (auch 
nicht das Developer Toolkit). Dort habe ich auch keinen Internetzugriff.
Wenn ich dich richtig verstanden habe, muss ich GCC selbst kompilieren 
(muss das auf dem auszuführenden Rechner passieren, einer VM mit 
demselben System oder egal wo?) und dann meinen Code mit deisem GCC auf 
dem auszuführenden Rechner kompilieren. Ist das richtig so?

von nfet (Gast)


Lesenswert?

Machs wie alle Welt?! Ich mein, wer entwickelt groß Programme auf dem PC 
auf dem sie auch ausgeführt werden.
Liefere einfach das Binary mit allen dazu nötigen Libs.
Wenn du nicht mehrere Prozesse ausführst, die dieselbe lib teilen, dann 
sollte es so gut wie gar keinen Unterschied machen, ob das ganze 
statisch oder dynamisch ist. Und selbst wenn das der (unwahrscheinliche) 
Fall ist, dann ist es immer noch sehr ungewöhnlich, wenn das aufrufen 
der Funktionen aus der Lib die meiste Zeit in Anspruch nehmen sollte.

von Porter (Gast)


Lesenswert?

Das Problem ist, dass ich sehr viele Prozesse ausführe, die sehr 
rechenaufwendig und zeitkritisch sind und alle hauptsächlich ein und 
dieselbe Funktion ausführen. Deswegen ist es hier sinnvoll, dynamisch zu 
linken, da so der Cache geschohnt wird.

von Sebastian V. (sebi_s)


Lesenswert?

Was jetzt? Viele Prozesse oder Threads? Bei Threads dürfte es egal sein 
und ich vermute auch bei Prozessen ist Linux nicht so blöd das es das 
gleiche Programm Tausend mal in den RAM läd. Von wie vielen Prozessen 
reden wir überhaupt? Und bevor wir hier noch ewig über Performance 
reden, hast du schonmal nachgemessen wie schnell es läuft?

von Peter II (Gast)


Lesenswert?

Porter schrieb:
> Das Problem ist, dass ich sehr viele Prozesse ausführe, die sehr
> rechenaufwendig und zeitkritisch sind und alle hauptsächlich ein und
> dieselbe Funktion ausführen. Deswegen ist es hier sinnvoll, dynamisch zu
> linken, da so der Cache geschohnt wird.

die paar Bytes die die libs brauchen, spielt doch keine Rolle bei großen 
Aufgaben. Oder musst du die Programm in der sekunde tausendfach starten?

von Gerd E. (robberknight)


Lesenswert?

Porter schrieb:
> und
> Performance sehr wichtig ist.

und

> RHEL5

passt nicht wirklich gut zusammen.

Das wurde 2007 freigegeben, der Kernel 2.6.18 der da verwendet wird ist 
von 2006. In den 10 Jahren hat sich bei Linux einiges getan was die 
Performance angeht.

von Porter (Gast)


Lesenswert?

Sebastian V. schrieb:
> Was jetzt? Viele Prozesse oder Threads? Bei Threads dürfte es egal sein
> und ich vermute auch bei Prozessen ist Linux nicht so blöd das es das
> gleiche Programm Tausend mal in den RAM läd.

Obs Prozesse oder Threads sind, werde ich prüfen (ich schreibe nur einen 
seriellen Teil des Programms, nicht die Parallelisierung).

Sebastian V. schrieb:
> Von wie vielen Prozessen
> reden wir überhaupt?

Das ist noch nicht sicher. So viele, dass die Performance ein Maximum 
erreicht. Ich gehe aber von einigen Dutzend aus.

Sebastian V. schrieb:
> Und bevor wir hier noch ewig über Performance
> reden, hast du schonmal nachgemessen wie schnell es läuft?

Du meinst den Unterschied zwischen dynamisch und statisch? Nein, noch 
nicht. Kann ich vorerst auch nicht machen, weil ich momentan keinen 
Zugriff auf den Rechner habe.
Aber ansonsten messe ich die Laufzeiten natürlich...

Peter II schrieb:
> die paar Bytes die die libs brauchen, spielt doch keine Rolle bei großen
> Aufgaben. Oder musst du die Programm in der sekunde tausendfach starten?

Es geht vor allem um den Cache. Je weniger Neuladen in den (Istruction) 
Caches ergibt mehr Performance.

Gerd E. schrieb:
>> und
>> Performance sehr wichtig ist.
>
> und
>
>> RHEL5
>
> passt nicht wirklich gut zusammen.

Ich bin auch nicht glücklich darüber, aber es ist wies ist... Leider ist 
es oft so, dass wenn etwas ein mal läuft, es nie wieder angefasst wird. 
Auch nicht, wenn es wirklich Zeit wird...

von Peter II (Gast)


Lesenswert?

Porter schrieb:
> Es geht vor allem um den Cache. Je weniger Neuladen in den (Istruction)
> Caches ergibt mehr Performance.

ja und? Warum sollte Linux sind die beiden Binary die statisch gelinkt 
sind in den selber Speicher laden?

von Porter (Gast)


Lesenswert?

Peter II schrieb:
> Porter schrieb:
>> Es geht vor allem um den Cache. Je weniger Neuladen in den (Istruction)
>> Caches ergibt mehr Performance.
>
> ja und? Warum sollte Linux sind die beiden Binary die statisch gelinkt
> sind in den selber Speicher laden?

Er lädt sie eben nicht in denselben Speicher... Deswegen wird derselbe 
Code mehrmals geladen, was den Cache füllt.

von Porter (Gast)


Lesenswert?

Also... es wird über MPI in mehreren Prozessen ausgeführt.

Ist es möglich, ein portables GCC zu verwenden? Mit dynamischem Linken?

von Gerd E. (robberknight)


Lesenswert?

Porter schrieb:
> Ist es möglich, ein portables GCC zu verwenden? Mit dynamischem Linken?

Du kannst einen ganz normalen gcc verwenden. Wenn Du dynamisch linken 
willst, musst Du aber alle gelinkten libs in den passenden Versionen mit 
auf das Zielsystem kopieren.

Also auf dem Entwicklungssystem mit ldd rausfinden was alles für libs in 
Dein Programm gelinkt wurden.

Die dann alle mit auf das Zielsystem kopieren. Am besten trägt der Admin 
den Pfad dieser Libs dann in die /etc/ld.so.conf ein und ruft einmal 
ldconfig auf.

Wenn er sich weigert, musst Du vor jedem Start Deines Progs den Pfad der 
libs in der environment-variable LD_LIBRARY_PATH hinterlegen damit der 
Linker die findet.

von Peter II (Gast)


Lesenswert?

Porter schrieb:
> Er lädt sie eben nicht in denselben Speicher... Deswegen wird derselbe
> Code mehrmals geladen, was den Cache füllt.

woran sieht du das?

von Mark B. (markbrandis)


Lesenswert?

Porter schrieb:
> Ich entwickele halt auf einem PC, muss das fertige Programm aber auf
> einem anderen ausführen, wo die Admins mir fast nichts erlauben (auch
> nicht das Developer Toolkit).

Warum sollte das Developer Toolkit auf dem Zielrechner nötig sein?

Du entwickelst auf Deinem Entwicklungsrechner, wo Du die Tools 
installieren kannst die Du brauchst. Auf dem Zielrechner hast Du dann 
eben die fertig übersetzten Binaries und ggf. Bibliotheken.

: Bearbeitet durch User
von Jay (Gast)


Lesenswert?

Porter schrieb:
> muss das fertige Programm aber auf
> einem anderen ausführen, wo die Admins mir fast nichts erlauben

Ich frag mal ganz direkt, musst du den Job machen? Manchmal ist die 
beste Lösung einen Idiotenjob nicht zu machen und das Weite zu suchen. 
Wenn einem die Hände auf dem Rücken festgebunden werden muss man nicht 
unbedingt versuchen durch den Ärmelkanal zu schwimmen.

Porter schrieb:
> ich möchte gerne C++11 Code

Auch hier die direkte Frage, gibt es für C++11 einen echten technischen 
Grund oder ist das nur so eine Art Liebesbeziehung und du willst nicht 
über deinen Schatten springen?

Man könnte ja auch den alten Compiler (keine Ahnung was der Standard bei 
RHEL5 war) auf neuere Sprachkonstrukte verzichten und notfalls 
vorhandenen neueren Code zurück portieren.

von Sebastian V. (sebi_s)


Lesenswert?

Jay schrieb:
> Porter schrieb:
>> ich möchte gerne C++11 Code
>
> Auch hier die direkte Frage, gibt es für C++11 einen echten technischen
> Grund oder ist das nur so eine Art Liebesbeziehung und du willst nicht
> über deinen Schatten springen?

Dann können wir ja auch wieder zurückgehen und programmieren alles in C 
oder Assembler. Technische Gründe in einer Hochsprache zu programmieren 
gibt es nicht.

: Bearbeitet durch User
von Mark B. (markbrandis)


Lesenswert?

Sebastian V. schrieb:
> Dann können wir ja auch wieder zurückgehen und programmieren alles in C
> oder Assembler. Technische Gründe in einer Hochsprache zu programmieren
> gibt es nicht.

Nö. Es gibt massenhaft Code und Projekte die heute noch nicht beim C99 
Standard angekommen sind. Es ist ja nicht so dass die Entwickler nicht 
gerne etwas Moderneres nehmen würden. Nur am Ende entscheidet halt der, 
der einem das Gehalt auszahlt. Und das ist nicht immer jemand, der 
softwaretechnisch die besten Entscheidungen trifft.

Außerdem, warum nicht gleich C++14 nehmen? C++11 ist doch sowas von 
veraltet... ;-)

: Bearbeitet durch User
von Jay (Gast)


Lesenswert?

Sebastian V. schrieb:
> Dann können wir ja auch wieder zurückgehen und programmieren alles in C
> oder Assembler. Technische Gründe in einer Hochsprache zu programmieren
> gibt es nicht.

Ja, ja, alles was alt ist ist scheiße, bla, bla, bla, hat nie 
funktioniert, bla bla bla ... und ohne Hipster-Bärtchen und Narrenkappe 
(https://de.wikipedia.org/wiki/Baseballcap oder 
https://de.wikipedia.org/wiki/M%C3%BCtze#/media/File:DreiPudelm%C3%BCtzen.jpg) 
kann man sowieso gar nicht programmieren.

von Sebastian V. (sebi_s)


Lesenswert?

Ich sag ja nicht, dass alles was noch nicht C++14 nutzt scheiße ist oder 
es keinen Grund gibt etwas in C zu programmieren. Ich sage nur, dass ich 
keine Lust hätte bereits vorhandenen C++11/C++14 Code zurück auf C++98 
zu portieren, nur damit es unter RHEL5 compiliert.

: Bearbeitet durch User
von Fritz G. (fritzg)


Lesenswert?

Porter schrieb:
> Er lädt sie eben nicht in denselben Speicher... Deswegen wird derselbe
> Code mehrmals geladen, was den Cache füllt.

Wie kommst du darauf? Eigentlich läuft das so ab (ich gehe jetzt davon 
aus, dass dasselbe Programm mehrmals gestartet wird).
Linux liest das Binary von der HDD um es in das RAM zu laden. Dabei 
steht das Gelesene erstmal im Filesystem Cache. Linux spart sich jetzt, 
das Binary in den "Arbeitsspeicher" zu laden um es dort auszuführen, 
weil es liegt ja schon im RAM (der FS-Cache). Deswegen wird dieser Teil 
des Cache zum Arbeitsspeicher erklärt und das Programm wird im 
ehemaligen Cache ausgeführt. Damit wird ein Kopiervorgang gespart.
Wird das Programm nochmal gestartet, merkt der Kernel, dass es schon im 
Speicher steht und startet es dort. Da wird nix kopiert. Erst wenn Daten 
im  Programm beschrieben werden, werden diese einen eigenen RAM-Bereich 
bekommen (copy on write).
Dynamisch linken spart den RAM-Platz für die Bibliotheken, falls diese 
schon geladen wurden. Aber eben auch nur einmal, nicht 1000x bei 1000 
Instanzen vom Programm.

: Bearbeitet durch User
von Porter (Gast)


Lesenswert?

Gerd E. schrieb:
> Porter schrieb:
>> Ist es möglich, ein portables GCC zu verwenden? Mit dynamischem Linken?
>
> Du kannst einen ganz normalen gcc verwenden. Wenn Du dynamisch linken
> willst, musst Du aber alle gelinkten libs in den passenden Versionen mit
> auf das Zielsystem kopieren.
>
> Also auf dem Entwicklungssystem mit ldd rausfinden was alles für libs in
> Dein Programm gelinkt wurden.
>
> Die dann alle mit auf das Zielsystem kopieren. Am besten trägt der Admin
> den Pfad dieser Libs dann in die /etc/ld.so.conf ein und ruft einmal
> ldconfig auf.
>
> Wenn er sich weigert, musst Du vor jedem Start Deines Progs den Pfad der
> libs in der environment-variable LD_LIBRARY_PATH hinterlegen damit der
> Linker die findet.

Ich denke das ist die beste Lösung, danke schön!


Peter II schrieb:
> Porter schrieb:
>> Er lädt sie eben nicht in denselben Speicher... Deswegen wird derselbe
>> Code mehrmals geladen, was den Cache füllt.
>
> woran sieht du das?

Ich kann die Links gerade nicht finden, aber ich habe es in vielen 
Quellen so gelesen.

Mark B. schrieb:
> Warum sollte das Developer Toolkit auf dem Zielrechner nötig sein?
>
> Du entwickelst auf Deinem Entwicklungsrechner, wo Du die Tools
> installieren kannst die Du brauchst. Auf dem Zielrechner hast Du dann
> eben die fertig übersetzten Binaries und ggf. Bibliotheken.

Damit man zu den libraries ohne Umwege dynamisch linken kann.

Jay schrieb:
> Auch hier die direkte Frage, gibt es für C++11 einen echten technischen
> Grund oder ist das nur so eine Art Liebesbeziehung und du willst nicht
> über deinen Schatten springen?

Speziell bei mir habe ich viele Datentypen und möchte den Ausgabewert 
einer Funktion von den Typen der Eingabeparameter abhängig machen. (z.B. 
8uin8 + uint16 = uint16 usw.). Das geht in C++11 in einer Funktion, bei 
C++03 müste ich sehr viele überlagerte Funktionen schreiben.

Jay schrieb:
> Ich frag mal ganz direkt, musst du den Job machen? Manchmal ist die
> beste Lösung einen Idiotenjob nicht zu machen und das Weite zu suchen.
> Wenn einem die Hände auf dem Rücken festgebunden werden muss man nicht
> unbedingt versuchen durch den Ärmelkanal zu schwimmen.

Das Problem ist, dass nicht immer alles so einfach ist... Die Admins 
haben einen guten Grund, missrauisch zu sein. Die Maschine kostet 50k 
die Stunde, da kann man sich keinen Ausfall erlauben...

von Sebastian V. (sebi_s)


Lesenswert?

Porter schrieb:
> Die Maschine kostet 50k
> die Stunde, da kann man sich keinen Ausfall erlauben...

50k pro Stunde?! Mietet ihr den Tianhe-2 oder was?

von Mark B. (markbrandis)


Lesenswert?

Porter schrieb:
> Die Maschine kostet 50k die Stunde, da kann man sich keinen Ausfall
> erlauben...

Was ist denn das für ein wundersames Ding? Ein Steuergerät in einer 
Anlage, die Medikamente produziert oder sowas in der Art?

Du meinst vielleicht eher "wenn die Maschine ausfiele, dann würde das 
50k pro Stunde Ausfallzeit kosten"?

: Bearbeitet durch User
von Gerd E. (robberknight)


Lesenswert?

Porter schrieb:
>>> Er lädt sie eben nicht in denselben Speicher... Deswegen wird derselbe
>>> Code mehrmals geladen, was den Cache füllt.
>>
>> woran sieht du das?
>
> Ich kann die Links gerade nicht finden, aber ich habe es in vielen
> Quellen so gelesen.

Die solltest Du vielleicht nochmal genauer lesen, vermutlich hast Du sie 
nicht richtig verstanden. Oder diese Quellen sind falsch oder unpräzise.

Diese Beschreibung hier von Fritz:
Beitrag "Re: C++ auf default Red Hat Enterprise Linux 5"
ist richtig.

Das dynamische Linken spart Dir nur dann Speicher, wenn Du 
verschiedene Programme, die mit der selben Library gelinkt sind, 
gleichzeitig ausführst. Wenn Du das selbe Programm mehrfach ausführst 
ist es für den Speicherverbrauch egal welches Linking Du verwendest.

Das dynamische Linken ist aber schon seit längerem die üblichere 
Variante. Daher werden statische Libraries (.a-Dateien) oft nicht mehr 
fertig mitgeliefert und das statische Linken ist bei vielen Programmen 
in den mitgelieferten Makefiles etc. nicht mehr vorgesehen. Daher macht 
es erfahrungsgemäß mehr Aufwand etwas statisch zu linken.

von Porter (Gast)


Lesenswert?

Sebastian V. schrieb:
> 50k pro Stunde?! Mietet ihr den Tianhe-2 oder was?

Nein, einen FEL.

Gerd E. schrieb:
> Die solltest Du vielleicht nochmal genauer lesen, vermutlich hast Du sie
> nicht richtig verstanden. Oder diese Quellen sind falsch oder unpräzise.

Ein Beispiel ist hier: 
https://www.google.de/url?sa=t&rct=j&q=&esrc=s&source=web&cd=5&ved=0ahUKEwjq0uaH96zKAhWLlCwKHQotB8cQFghBMAQ&url=http%3A%2F%2Fwww3.nd.edu%2F~dthain%2Fcourses%2Fclassconf%2Fworts2006%2FWangLiu.ppt&usg=AFQjCNHJpaWhMgpt7OusGNeU53tbiuRwUg&cad=rja
auf Folie 4.

Ich hatte auch eine andere Quelle, die genau dieses Problem anging... 
Die sagte, dass die aktuellen Betriebssysteme das nicht unterstützen. 
Ich kann sie leider nicht mehr finden.

Hier: 
http://stackoverflow.com/questions/1993390/static-linking-vs-dynamic-linking
wird das auch angeschnitten (Unless modern OS's are smart enough to 
notice identical segments in statically linked binaries. Seems hard, 
anyone know?), aber nicht wirklich geklärt.

Theoretisch ist das natürlich möglich, aber praktisch ist das nicht ganz 
so einfach. Ein neuer Prozess hat seinen eigenen virtuellen Speicher, 
der auf physikalischen Speicher (RAM) gemapped wird. Theoretisch könnte 
man diesn Speicher also auf denselben physikalischen Speicher (RAM) 
mappen. Dafür müsste man aber wissen, dass genau das schon im RAM liegt 
und wo es liegt. Aktuell schein es noch nicht implementiert zu sein (wie 
gesagt, für verschiedene Prozesse, natürlich geht das für threads). Wenn 
ein Programm auf Speicher zugreift, dann gibt es ja eine virtuelle 
Adresse an, die in seinem Virtuellen RAM liegt. Dass diese virtuelle 
Adresse auf eine Library zeigt, die gerade im Speicher eines anderen 
Prozesses liegt ist ja nicht trivial.

von nfet (Gast)


Lesenswert?

Anstatt hier ewig rumzuspekulieren, teste doch einfach ob es einen 
Unterschied macht.
Ist doch jetzt nicht so der riesen Act und mich würde das Ergebnis auch 
mal interessieren.

von Sheeva P. (sheevaplug)


Lesenswert?

Peter II schrieb:
> Porter schrieb:
>> Es geht vor allem um den Cache. Je weniger Neuladen in den (Istruction)
>> Caches ergibt mehr Performance.
>
> ja und? Warum sollte Linux sind die beiden Binary die statisch gelinkt
> sind in den selber Speicher laden?

Copy-On-Write.

von Sheeva P. (sheevaplug)


Lesenswert?

Porter schrieb:
> Theoretisch ist das natürlich möglich, aber praktisch ist das nicht ganz
> so einfach. Ein neuer Prozess hat seinen eigenen virtuellen Speicher,
> der auf physikalischen Speicher (RAM) gemapped wird. Theoretisch könnte
> man diesn Speicher also auf denselben physikalischen Speicher (RAM)
> mappen. Dafür müsste man aber wissen, dass genau das schon im RAM liegt
> und wo es liegt. Aktuell schein es noch nicht implementiert zu sein (wie
> gesagt, für verschiedene Prozesse, natürlich geht das für threads). Wenn
> ein Programm auf Speicher zugreift, dann gibt es ja eine virtuelle
> Adresse an, die in seinem Virtuellen RAM liegt. Dass diese virtuelle
> Adresse auf eine Library zeigt, die gerade im Speicher eines anderen
> Prozesses liegt ist ja nicht trivial.

Mit einer Ausnahme gibt es unter UNIX-Systemen nur eine Möglichkeit, 
einen neuen Prozeß zu starten: nämlich den Systembefehl fork(). Dieser 
erzeugt immer eine exakte Kopie des Prozesses, der ihn aufgerufen hat. 
Um ein anderes Programm zu starten, braucht es daher eine Systemfunktion 
aus der exec()-Familie, die das Abbild des aufrufenden Prozesses durch 
ein neues Prozeßabbild ersetzt. (Auf clone() gehen wir jetzt mal nicht 
näher ein.)


Mit diesem Vorwissen können wir uns jetzt ein wenig genauer anschauen, 
was beim fork() mit dem Speicher passiert. Da dasselbe Programm bereits 
in den Speicher geladen ist und unser Betriebssystem das weiß, 
reserviert es zwar Speicher für das Prozeßabbild, mappt aber in 
Wirklichkeit das Prozeßabbild des existierenden Prozesses readonly in 
den neuen Prozeß, der davon jedoch nichts mitbekommt.

Als Ergebnis haben wir jetzt zwei Prozesse, für die zwar jeweils 
Speicher reserviert worden ist, von dem aber nur der Speicher des ersten 
Prozesses benutzt wird. Erst wenn der zweite Prozeß auf eine der 
Speicherseiten des ersten Prozesses schreibt, wird diese Speicherseite 
physikalisch kopiert und verändert. Allerdings ist ein reservierter, 
aber ungenutzter Speicher nicht verloren. Sobald unserem System der 
Speicher ausgeht, beginnt es nämlich, den ungenutzten Speicher 
anderweitig zu benutzen.

Dieses Verhalten ist sehr klug, denn es führt teure Kopieroperationen im 
Speicher nur dann aus, wenn sie wirklich notwendig sind. Andererseits 
ist es für Menschen, die damit nicht vertraut sind, ziemlich verwirrend, 
denn aus der Sicht der meisten Werkzeuge sieht es tatsächlich so aus, 
als ob Speicher für den geforkten Prozesses tatsächlich benutzt würde -- 
auch wenn das in Wirklichkeit gar nicht der Fall ist.

Du siehst auch, daß die Sache aus Betriebssystemsicht vollkommen trivial 
ist, weil das System sich immer darauf verlassen kann, daß ein fork() 
nur eine virtuelle Kopie des laufenden Prozesses erzeugt. Die beiden 
Techniken, auf denen dieses Systemverhalten beruht, heißen übrigens 
"Copy-On-Write" und "Overcommit Memory". Letzteres wird zwar heute 
vorwiegend im Kontext von Virtualisierung diskutiert, jedoch in den 
Kerneln von Linux, Solaris und AIX (sowie mW. auch WinNT) auch für 
normale Prozesse angewendet.

Das bisher Gesagte funktioniert natürlich nur, wenn das Betriebssystem 
wie im Falle des fork()-Systembefehls weiß, daß der neu erzeugte Prozeß 
eine exakte Kopie eines existierenden Prozesses ist, beziehungsweise: 
daß eine neu reservierte Speicherseite eine exakte Kopie einer bereits 
bestehenden Speicherseite ist. Wenn der geforkte Prozeß eine Funktion 
aus der exec()-Familie ausführt, ist das aber (meistens) gerade nicht 
der Fall. Wie kann unser Betriebssystem nun feststellen, daß eine neu 
geladene Speicherseite denselben Inhalt hat wie eine bereits 
existierende? Nicht mit vertretbarem Verwaltungsaufwand, das kann es gar 
nicht.


Deswegen wurde die Technik des dynamischen Linkens erfunden. Dabei wird 
der Code so übersetzt, daß er positionsunabhängig aufgerufen werden 
kann, mithin also keine absoluten Speicheradressen benutzt, sondern nur 
relative Sprünge. Außerdem enthält solcher Code eine Sprungtabelle, in 
welcher die vorhandenen Symbole auf die Einsprungpunkte der jeweiligen 
Funktionen gemappt werden. Das Ergebnis eines solchen 
Übersetzungsvorganges nennt sich unter UNIX-Systemen "shared object" 
(.so) und unter Windows "dynamik link library" (.dll).

Neben dem speziell übersetzten Code gehört zum dynamischen Linken auch 
noch ein dynamischer Linker. Der führt Buch darüber, in welche 
Speicherseite er welche Bibliothek geladen hat, slädt noch nicht 
geladene Bibliotheken und tellt den Programmen die darin befindlichen 
Funktionen bereit.

Selbstverständlich müssen Programme, die die Funktionalität des 
dynamischen Linkers benutzen wollen, dazu entsprechend vorbereitet sein. 
Sie können nicht einfach, wie bei herkömmlichen Funktionsaufrufen, in 
die Funktion springen, deren Adresse ihnen bekannt ist. Stattdessen 
müssen sie die dynamische Lib mit dlopen() laden, mit dlsym() einen 
Zeiger auf die gewünschte Funktion holen, und diese dann aufrufen -- mit 
dem gesamten Overhead (Sicherung der Register, Positionierung der 
Parameter auf dem Stack, ...). Wohlerzogene Compiler verbergen das vor 
dem Entwickler... :-)

...

von Gerd E. (robberknight)


Lesenswert?

Porter schrieb:
> Ein Beispiel ist hier:
> 
https://www.google.de/url?sa=t&rct=j&q=&esrc=s&source=web&cd=5&ved=0ahUKEwjq0uaH96zKAhWLlCwKHQotB8cQFghBMAQ&url=http%3A%2F%2Fwww3.nd.edu%2F~dthain%2Fcourses%2Fclassconf%2Fworts2006%2FWangLiu.ppt&usg=AFQjCNHJpaWhMgpt7OusGNeU53tbiuRwUg&cad=rja
> auf Folie 4.
>
> Ich hatte auch eine andere Quelle, die genau dieses Problem anging...
> Die sagte, dass die aktuellen Betriebssysteme das nicht unterstützen.
> Ich kann sie leider nicht mehr finden.
>
> Hier:
> http://stackoverflow.com/questions/1993390/static-linking-vs-dynamic-linking
> wird das auch angeschnitten (Unless modern OS's are smart enough to
> notice identical segments in statically linked binaries. Seems hard,
> anyone know?), aber nicht wirklich geklärt.

Die beschreiben alle ein anderes Problem als Deines:

Viele verschiedene Programme nutzen die selbe Bibliothek. Da ist in der 
Tat dynamisches Linken die richtige Methode.

Du hast aber einen Spezialfall: Du hast ein einziges Programm, welches 
mehrfach gestartet wird, also als mehrere Prozesse läuft. Zumindest hab 
ich Dich so verstanden.

Dieses eine Programm liegt nur einmal im Dateisystem und wird von dort 
auch nur einmal in den Arbeitsspeicher geladen. Egal wie häufig Du es 
startest, egal wieviele Prozesse Du damit erzeugst. Die genauen Schritte 
hat Fritz oben beschrieben. Lies Dir seinen Post nochmal ganz genau 
durch.

> Theoretisch könnte
> man diesn Speicher also auf denselben physikalischen Speicher (RAM)
> mappen. Dafür müsste man aber wissen, dass genau das schon im RAM liegt
> und wo es liegt.

In Deinem Fall geht das nicht nur theoretisch, sondern wird in der 
Praxis von Linux seit Jahrzehnten so gemacht: Anhand der Inode-Nummer 
vom Dateisystem kann der Kernel ganz genau wissen, ob dieser Teil schon 
im RAM vorliegt oder nicht. Und wenn ja, dann lädt er ihn nicht nochmal, 
sondern verknüpft ihn einfach mit der entsprechenden Page im RAM.

: Bearbeitet durch User
von Porter (Gast)


Lesenswert?

@ Sheeva Plug: Danke für die ausführliche Beschreibung!
Ich befürchte, dass MPI es genau mit exec() ausführt, weil man es ja 
sogar auf verschiedene PCs verteilen kann.

Gerd E. schrieb:
> Du hast aber einen Spezialfall: Du hast ein einziges Programm, welches
> mehrfach gestartet wird, also als mehrere Prozesse läuft. Zumindest hab
> ich Dich so verstanden.

Genau richtig. Wie Sheeva Plug es beschrieben hat, hängt es davon ab, ob 
die Prozesse mit fork() oder exec() gestartet wurden. Ich habe vorher 
nicht an fork gedacht und dem dazugehörigen copy on write Mechanismus.

von Carl D. (jcw2)


Lesenswert?

Sebastian V. schrieb:
> Was jetzt? Viele Prozesse oder Threads? Bei Threads dürfte es egal sein
> und ich vermute auch bei Prozessen ist Linux nicht so blöd das es das
> gleiche Programm Tausend mal in den RAM läd. Von wie vielen Prozessen
> reden wir überhaupt? Und bevor wir hier noch ewig über Performance
> reden, hast du schonmal nachgemessen wie schnell es läuft?

Beim statisch gelinkten Programm gibt es das .text-Segment auch bei 1000 
laufenden Proczessen nur ein mal. Dynamisch gelinkt natürlich auch, nur 
braucht man zwischen den verschiedenen .text-Segmenten, jede Shared-Lib 
hat ja ihr eigenes, irgendwelchen Glue-Code. Die Lib's werden ja nicht 
an für jede Lib eindeutige Adressen geladen, es muß also in jedem 
Prozess privaten Code dafür geben. Selbst wenn 100 Prozesse das selbe 
Programm ausführen und somit auch der Glue-Code geshared werden könnte: 
ohne Glue-Code, sprich statisch gelinkt ist schneller. Resourcenschonend 
wird "dynamisch" erst wenn unterschiedliche Programme die selbe 
SharedLib verwenden.

BTW, bei WinNT braucht der Glue-Code je DLL mindestens eine 4k-Page, 
auch wenn es nur um eine exportiert Funktion geht. Oder man weißt der 
DLL eine eindeutige Adresse zu, die dann in allen Prozess-Adressräumen 
immer an der selben Stelle eingeblendet wird (falls das heute noch so 
ist, hab vor 15Jahren zuletzt auf der Ebene mit NT zu tun gehabt).
Linux dürfte die Selbe Lösung für das gleiche Problem benutzen. Nur hat 
man heute nur ungern exakt vorhersehbare Lade-Adressen, denn diese sind 
vor allem bei Viren beliebt.

von Gerd E. (robberknight)


Lesenswert?

Porter schrieb:
>> Du hast aber einen Spezialfall: Du hast ein einziges Programm, welches
>> mehrfach gestartet wird, also als mehrere Prozesse läuft. Zumindest hab
>> ich Dich so verstanden.
>
> Genau richtig. Wie Sheeva Plug es beschrieben hat, hängt es davon ab, ob
> die Prozesse mit fork() oder exec() gestartet wurden. Ich habe vorher
> nicht an fork gedacht und dem dazugehörigen copy on write Mechanismus.

Nö. Das mit dem Dateisystemcache und dessen Mapping in den Speicher 
willst Du wohl irgendwie nicht verstehen. Es ist ganz egal ob fork() 
oder exec() verwendet wird, wenn das ausgeführte Programm nur einmal im 
Dateisystem liegt.

von Sheeva P. (sheevaplug)


Lesenswert?

Gerd E. schrieb:
> Porter schrieb:
>>> Du hast aber einen Spezialfall: Du hast ein einziges Programm, welches
>>> mehrfach gestartet wird, also als mehrere Prozesse läuft. Zumindest hab
>>> ich Dich so verstanden.
>>
>> Genau richtig. Wie Sheeva Plug es beschrieben hat, hängt es davon ab, ob
>> die Prozesse mit fork() oder exec() gestartet wurden. Ich habe vorher
>> nicht an fork gedacht und dem dazugehörigen copy on write Mechanismus.

Die Funktionen der exec()-Familie starten keine Prozesse, sondern 
ersetzen ein existierendes Prozessabbild durch ein neues.

> Nö. Das mit dem Dateisystemcache und dessen Mapping in den Speicher
> willst Du wohl irgendwie nicht verstehen. Es ist ganz egal ob fork()
> oder exec() verwendet wird, wenn das ausgeführte Programm nur einmal im
> Dateisystem liegt.

Das oben Beschriebene hat mit dem Dateisystem nur insoweit zu tun, als 
die Funktionen der exec()-Familie das neue Programm tatsächlich aus dem 
Dateisystem laden. Daß sich eine ausgeführte Programmdatei bereits im 
Dateisystempuffer befindet, ist ziemlich wahrscheinlich, aber für den 
oben erklärten Teil ist das irrelevant.

Ein fork() hingegen erzeugt (startet) einen neuen Prozeß als Kopie des 
aufrufenden Prozesses. Das Betriebssystem weiß deswegen a) daß es das 
neu gestartete Programm bereits im Arbeitsspeicher hat, sowie b) sogar 
ganz genau, wo es zu finden ist. Damit haben das Dateisystem und sein 
Puffer aber nichts zu tun.

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.