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)
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.
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.)
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!
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?
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-linkingSebastian 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?
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.
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.
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?
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?
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.
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...
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?
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.
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.
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?
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.
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.
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.
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... ;-)
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.
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.
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...
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"?
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.
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.
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.
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.
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... :-)
...
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.
@ 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.
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.
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.
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.