Array lockere Bereichs- (Range-) check

Für Fragen rund um die Ide und zum Debugger
Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Array lockere Bereichs- (Range-) check

Beitrag von Warf »

Was mir zu dem Thema Heap collision noch eingefallen ist, man kann um dynamisch speicher auf dem Stack zu alloziieren die libc funktion alloca verwenden. Diese funktion alloziert einen Speicherblock mit dynamischer größe auf dem Stack welcher ja beim return wieder freigegeben wird. Wenn man da einen Stack overflow hat wirds richtig lustig, denn anstatt nem runtime error wie man ihn normalerweise erhält (z.B. bei einer endlosen rekursion), gilt laut Linux Man page:

Code: Alles auswählen

RETURN VALUE The alloca() function returns a pointer to the beginning of the allocated space. If the allocation causes stack overflow, program behaviour is undefined.


Das heißt, im worst case bekommst du einen Pointer der bereits schon in den heap zeigt, und man überschreibt sich einfach fröhlich seine Daten im heap, ohne es zu merken

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Array lockere Bereichs- (Range-) check

Beitrag von Mathias »

Wenn du die Unit LazLogger verwendest und von WriteLn() auf DebugLn() umstellst, musst du deinen Code nicht ändern. Du kannst das ganze sogar produktiv verwendet und deine Anwender erstellen im Hintergrund eine Logdatei, die du einfach auswerten kannst.
Direkt mit writeln sieht man es in Echtzeit und muss nicht noch eine Datei angucken. Wie ich schon gesagt habe, wen man man schnell einen wert angucken will. Für Debugln() muss man schon wieder eine Unit einbinden. Auch kann man mit Writeln in der Schnelle einen Integer oder Boolean ausgeben, ohne grosse Typenumwandlung.
Hast du mal die Deklaration von Debugln angeguckt, das sieht aus wie zu Turbo-Pascal Zeit, als es noch keine offene Arrays gab. :mrgreen:
Da war Write(ln) schon sehr fortschrittlich. Ein Befehl, der (fast) alles frisst. :wink:
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6198
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Array lockere Bereichs- (Range-) check

Beitrag von af0815 »

Jeder der mehr und intensiver Programmiert hat so seine Rezepte. Vor allen auch die Plattformen unterscheiden sich.

Ich verwende den Modus default nie. Ich ezeuge immer Release und Debug. Zusätzlich mache ich meistens direktiven. Wie zB. -dRelease, dazu verwende ich relativ häufig den LazLogger.

Code: Alles auswählen

 
uses
  {$ifndef Release}LazLogger,{$endif}
....
 {$ifndef Release} DebugLnEnter({$I %FILE%} + '->' +{$I %CURRENTROUTINE%}); {$endif}


geht mit aktuell fpc trunk, ob es der aktuelle fpc-stable kann weis ich nicht so genau.

Ach ja, meine *.lpr werdem esitens so geändert

Code: Alles auswählen

 
..
{$R *.res}
 
{$if declared(UseHeapTrace)}
const
  co_heaptrc = 'heaptrace.txt';
{$endif}
 
 
begin
// If you want to show heaptrc report dialog only if there were leaks
//   in your application, then put this command somewhere
//   in your main project source file:
{$if declared(UseHeapTrace)}
  GlobalSkipIfNoLeaks := true; // supported as of debugger version 3.1.1
  if FileExists(co_heaptrc) then
      DeleteFile(co_heaptrc);
  SetHeapTraceOutput(co_heaptrc); // supported as of debugger version 3.1.1
  //   HaltOnError := false;             // dont halt a the end of the programm
{$endif}
  DebugLnEnter('****************************************************************');
  DebugLn('Starting...');
  RequireDerivedFormResource:=True;
  Application.Initialize;
  Application.CreateForm(TFormXXX, FormXXX);
  Application.Run;
  DebugLnExit('...Finishing');
end.
 
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Array lockere Bereichs- (Range-) check

Beitrag von Warf »

Mathias hat geschrieben:
Wenn du die Unit LazLogger verwendest und von WriteLn() auf DebugLn() umstellst, musst du deinen Code nicht ändern. Du kannst das ganze sogar produktiv verwendet und deine Anwender erstellen im Hintergrund eine Logdatei, die du einfach auswerten kannst.
Direkt mit writeln sieht man es in Echtzeit und muss nicht noch eine Datei angucken. Wie ich schon gesagt habe, wen man man schnell einen wert angucken will. Für Debugln() muss man schon wieder eine Unit einbinden. Auch kann man mit Writeln in der Schnelle einen Integer oder Boolean ausgeben, ohne grosse Typenumwandlung.
Hast du mal die Deklaration von Debugln angeguckt, das sieht aus wie zu Turbo-Pascal Zeit, als es noch keine offene Arrays gab. :mrgreen:
Da war Write(ln) schon sehr fortschrittlich. Ein Befehl, der (fast) alles frisst. :wink:


Darum hab ich mir meine eigne Unit geschrieben (siehe ein paar Posts vorher) der beim Debüt Build auf stdout schreibt und beim Release Build eine Datei schreibt. Ich muss aber auch ehrlich sagen, ich hab die Unit bereits schon genutzt bevor ich von lazlogger überhaupt wusste

Benutzeravatar
kupferstecher
Beiträge: 418
Registriert: Do 17. Nov 2016, 11:52

Re: Array lockere Bereichs- (Range-) check

Beitrag von kupferstecher »

Warf hat geschrieben:Bist du dir da sicher? denn die Stack Limits werden afaik vom Betriebsystem enforced (z.b. unter linux in der limits Datei), zwar muss das OS kein limit haben (z.b. linux: ulimit -s unlimited), aber normalerweise ist ein Limit eingestellt.

Nein, bin mir da gar nicht sicher. Ich kenn die Methode (regelmaessig den Stack-Zeiger abzufragen und eine initialisierte "Schutzvariable" am Ende des vorgesehenen Stackbereichs abzufragen ob sie ihren initialisierten Wert verloren hat) von der Mikrocontrollerprogrammierung. Konnte mir hier nichts anderes drunter vorstellen. Die von dir zitierte Fehlermeldung spricht auch nicht dafuer, dass der Stack vom Betriebssystem getrennt ueberwacht wird oder in einem getrennten Speicherbereich liegen wuerde. Aber ich sollt vielleicht nichts mehr dazu sagen, sind schon zu viele Spekulationen~

Erwin
Beiträge: 286
Registriert: Mi 16. Sep 2009, 14:15
OS, Lazarus, FPC: Xubuntu 22.04 / x86_64_linux-gtk 2 / L 2.2.0 / FPC 3.2.2

Re: Array lockere Bereichs- (Range-) check

Beitrag von Erwin »

Mathias hat geschrieben:Ich finde es gut, das es per Default deaktiviert ist. Wegen mir könnte man auch den Debugger deaktivieren. Da kommen sowieso im nur Fragen bei Anfängern, wieso die EXE so aufgebläht ist.
Wen jemand fortgeschritten ist, und weis wie das Debugging funktioniert, dann weis er auch wo man es einschaltet.

Weil Anfänger dies fragen, willst Du, dass Anfänger ihre Fehler (was Anfängern nun mal meist passiert) durchgehen? Das kann schnell zu viel mehr Fragen führen, und zwar jedes mal, wenn das Programm nicht stabil läuft, und zugleich auch von Lazarus dazu keine Fehlermeldung gibt. Und das wird dann besonders 'lustig' dann heraus zu finden, wo der Fehler liegt, während eine besser Einstellung der Debugger einem dies längst mitgeteilt hätte. Hinzu komme, je später man lernt, dass es so nicht gemacht werden darf, desto schwerer wird es, sich das richtige dann anzugewöhnen. Gerade für Neulinge sollte es daher jeden Fehler aufzeigen.
Und das mit der größeren Exe-Datei, dieses Unwissenheit kommt doch genau aus der selben Ecke wie die Einstellungen mit den Debugger: Fehlende Aufklärung. Es wird zu Lazarus viel geschrieben, vieles was banal ist, aber das wesentliche darf man mit der Lupe suchen. Anstatt auf den ersten Seiten, ist es irgendwo vergraben. Anstatt ausführlich erklärt, wird es nur kurz angesprochen.

Warf hat geschrieben:
Mathias hat geschrieben:Zufällig war der Speicherbereich frei, ansonsten hättest eine SIGSEV gehabt.


Zufällig ist es nicht ganz, er befindet sich ganz oben auf dem Stack (welcher nach oben wächst) was also bedeutet, wenn er nach oben über den array schreibt müsste er schon über alle Stackframes drüber schreiben bis es zu einer Zugriffsverletzung kommt, und der stack ist in produktiven programmen für gewöhnlich recht groß. Daher ist es recht unwahrscheinlich bei Stack-Arrays eine Zugriffsverletzung zu bekommen.

Ich bekomme mit 15 in meinen Beispiel ja auch keine SIGSEV, sondern beim beenden des Programms eine verwirrende Fehlermeldung.

Warf hat geschrieben:Nein standardmäßig ist praktisch die minimale funktionalität gegeben um den GDB zu verwenden. Für kleinere sachen ausreichend, für größere Projekte würde ich einfach die Default Build-Modes Debug und Release verwenden.

Bin der Ansicht, der Debugger sollte so viele Fehler wie möglich finden. Erst recht bei Anfänger, damit die gleich das richtige Wissen, was eben geht, und was nicht, vermittelt bekommen. Anstatt Programme zu schreiben, die einem später dann unerwartet um die Ohren fliegen.

Warf hat geschrieben:Lazarus kann dir den Standard Release und Debug Modus per klick erstellen, die haben ziemlich gute voreinstellungen: Projekteinstellungen->Compilereinstellungen, oben Erstellmodi haken setzten, auf den ... button clicken und dann auf Debug Und Release Modus erstellen klicken. Den Default modus kannst du dann löschen.
Über die Dropdown box kannst du dann den aktiven modus wählen

Danke. Kann mich nicht erinnern, dass der wichtige Teil wo im Buch beschrieben war. Allerdings ist das wichtige ja eher versteckt, falls überhaupt darüber geschrieben wird, und belangloses am Anfang.

Warf hat geschrieben:Wie gesagt in den meißten fällen wird dir das Betriebsystem nicht genug speicher zur Verfügung stellen damit du überhaupt eine so genannte heap collision (stack der nach oben wächst und heap der nach unten wächst überschneiden sich) bekommen kannst. Man muss schon aktiv, mit administratorrechten das bei seinem Betriebsystem einstellen damit man da was kaputt machen kann.

Wie ich bereits selber sagte: Blicke da eh nicht so ganz durch. Aber genau das ist immer das Gefährliche, wenn Jemand etwas nicht selber erklärt (in dem Fall Lazarus/-Buch etc.), sondern es anderen überlässt. Irgendwann müssen die dann doch erklären, was die Wahrheit ist und haben viel Aufräumarbeit (Beseitigung von Falschinfos und Überzeugung) dann vor sich.

Warf hat geschrieben:Das hauptproblem für eine Stacküberlauf sind aber auch keine angreifer, sondern dumme programmierer. z.B funktionen mit großem stack:

Code: Alles auswählen

procedure SomeRecursiveProcWithVeryLargeStack(i: Cardinal);
var arr: array[0..1000000] of Byte; // 1MB stack size
begin
 if LongBool(i) then SomeRecursiveProcWithVeryLargeStack(i-1);
end;

Ein entsprechend großes i würde dir damit eine heap collisiob geben. Aber, mit dynamischen arrays oder pointer auf statische arrays und new-dispose, wäre das ganze kein problem

Moment mal, soll das heißen, dass allein die Größe für einen Überlauf sorgt? Wenn ich jetzt zum Beispiel folgendes machen würde:

Code: Alles auswählen

 
var arrd3: array[1..1000000,1..20] of Integer; // also mind. 20 MB, würde das dann auch einen Überlauf verursachen?
 


Warf hat geschrieben:Es bräuchte einfach eine vernünftige Wikiseite die alles wenigstens kurz erklärt (und vor allem alles auf einer Seite)

Sehe ich genau so. Und falls es die bereits gibt, so scheint die dann gut versteckt zu sein.


Man kann die Fehlermeldung auch im Terminal ausgeben lassen? Also davon bin ich nicht begeistert. Immerhin wirken sich Befehle im Terminal ja auf das BS aus. Das ist mir einfach zu gefährlich.


Jedenfalls Danke. Jetzt weiß ich schon wieder einiges mehr. Besonders das mit den Voreinstellungen der Debuggeinstellungen scheint interessant zu sein. Damit kann man dann also vorgefertigte Einstellungen abspeichern und auswählen? Und somit ganz einfach Tagsüber eine schnelle Einstellung auswählen, und gegen ende des Tages jene, die alles noch mal ordentlich überprüft, ohne dass man jedes mal explizit die Hacken überall umsetzten muss? Werde ich mir heute noch ansehen.
Lazarus 2.2.0 / FP 3.2.4

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Array lockere Bereichs- (Range-) check

Beitrag von Warf »

Erwin hat geschrieben:Moment mal, soll das heißen, dass allein die Größe für einen Überlauf sorgt? Wenn ich jetzt zum Beispiel folgendes machen würde:

Code: Alles auswählen

 
var arrd3: array[1..1000000,1..20] of Integer; // also mind. 20 MB, würde das dann auch einen Überlauf verursachen?
 


Ja, unter meinem OpenSuse Linux ist die standard größe für den Stack (ulitmit -s) eta 8MB. Das heißt(ich habs auch grad ausgeführt):

Code: Alles auswählen

procedure foo();
var arrd3: array[1..1000000,1..20] of Integer;
begin
  arrd3[1,1] := 0;
end;
begin
  Foo;
end.   

geht in die Hose.

Ich versuche mal dir das ganze (für x86 Architekturen) zu erklären. Der speicher eines Programmes ist in verschiedene Segmente aufgeteilt:

Code: Alles auswählen

Min Addr
#----------#
#   CODE   #
#   (RX)   #
#----------#
#----------#
#  Const   #
#   (R)    #
#----------#
#----------#
#   Data   #
#   (RW)   #
#----------#
#----------#
#   Heap   #
#   (RW)   #
#vvvvvvvvvv#
............
#^^^^^^^^^^#
#   Stack  #
#   (RW)   #
#----------#
Max Addr

In den klammern stehen die berechtigungen (R = Read, X = Execute, W = Write). Code enthält deinen Sourcecode (den assembly). Der Const teil enthält konstaten und der Data teil Globale variablen (der ist nochmal unterteilt in Mit 0 initialisierte und uninitialisierte konstanten).
Darunter kommt der Heap. Dieser wächst nach unten wenn mehr benötigt wird (darum kümmert sich der memory manager). Das heißt am anfang ist der Heap recht klein, aber mit jedem new, Create, GetMem, etc. wächst er nach unten. Am unteren ende beginnt dann der Stack. Auf dem stack liegt alles was deine Funktionen als Variablen verwenden. Wenn immer einen funktion aufgerufen wird, werden alle parameter, sowie alle lokalen variablen der Funktion zusammen in einem so genannten stackframe auf den stack oben drauf gepackt. Wird eine Funktion verlassen, wird dieser Stackframe einfach wieder gelöscht.

Das heißt für eine Heap/Stack collision muss der Heap und der Stack soweit wachsen bis sie sich treffen. Bei windows 10 64 bit (Home) bedeutet das, es müssten 128 GB von Stack und heap verbraucht werden (vorrausgesetzt stack limit im OS ist unlimited).

Der grund warum du übrigens den Fehler beim beenden erst bekommen hast ist auch recht simpel. Denke mal an diesen Stack

Code: Alles auswählen

 
| FormButtonClick |
|-----------------|
|ButtonHandleClick|
|-----------------|
(.................)
|-----------------|
|      Main       |

mit dem TestArray[5]:=25; schreibst du 8 Byte über deine Framgerenze mindestens ins ButtonHandleClick oder so rein. Das heißt. Sagen wir mal du würdest in die Main funktion schreiben (keine lust auszurechnen wo du landen würdest, vor allem mit LCL funktionen keine ahnung), dann würdest du den fehler erst bemerken wenn du zurück in die Main kommen würdest, also erst wenn dien LCL anwendung beendet wäre, da bis dahin du dich ja in den immernoch intakten frames aufhälsts und eigentlich vom main frame nichts wissen solltest

Man kann aber auch im eigenen stack frame echt coole sachen machen

Code: Alles auswählen

procedure InfiniteLoop;
var i: Integer;
  arr: array[1..1] of Integer;
begin
  for i:=0 to Length(array)-1 do
    arr[i] := -1; // das überschreibt i da i vor arr liegt
end;

Erwin
Beiträge: 286
Registriert: Mi 16. Sep 2009, 14:15
OS, Lazarus, FPC: Xubuntu 22.04 / x86_64_linux-gtk 2 / L 2.2.0 / FPC 3.2.2

Re: Array lockere Bereichs- (Range-) check

Beitrag von Erwin »

Danke erst mal, für all diese Erklärungen. Da weiß man zumindest schon mal wieder vieles mehr.

Main frame? Von dem ich nichts wissen soll?
Warf hat geschrieben:mit dem TestArray[5]:=25; schreibst du 8 Byte über deine Framgerenze mindestens ins ButtonHandleClick oder so rein.

Damit hat er kein Problem, aber mit 15: TestArray[15]:=25;
Aber läuft der Erklärung betreffend vermutlich auf das selbe hinaus. Nur halt Ordnungshaber, falls jemand anders dies ebenfalls testet.

Das erinnert mich ein wenig an DOS: Dort gibt es einen unteren Speicherbereich und einen Oberen. In Oberen darf nur bestimmtes laufen, im Unterem alles. Und wenn ein (DOS-)Programm größer ist, als der noch frei untere Speicherbereich, muss man entweder andere Programme von DOS und Co. raus werfen, oder das andere Programm geht nicht, wegen fehlenden Speicher.
Dachte seit spätestens XP gehöre dies der Vergangenheit an. Und bei Linux so wie so.
Ebenso ging ich sogar davon aus, dass seit Delphi man sogar unter Win95 (was ja teils auf DOS basiert, so weit ich weiß) nicht mehr auf die Speicherverwaltung achten müsste?
Dann ist da also alles Beschränkt und Aufgeteilt? Aber Heap und Stack teilen sich Dynamisch den Speicherbereich zwischen sich auf, aus gegen gesetzten Richtungen, nach dem Motto, wer zuerst kommt ...? Und wenn die sich treffen, scheppert es im Arbeitsspeicher?
Wenn ich mir das so ansehe, dann bekomme ich den Eindruck, dass es aus Speichergründen Sinn machen könnte, Variablen die sich nicht ändern, dennoch als Const zu deklarieren?
Aber dennoch sind bei mir Array-Größen teils ganz schön beschränkt.
Irgendwie verwirrend.
Kann es sein, dass einerseits ein großes 8MB Array nicht geht, aber 100 x 1 MB Array schon?
Steht der Speicherbereich der Segmente für Code, Const und Data Anfangs fest, oder wird nach dem Start je nach Bedarf dann erst mal Reserviert und steht erst danach dann fest?
Und den Rest teilt sich generell dann je nach Bedarf Heap und Stack? Oder steht deren gemeinsame Größe ebenfalls generell fest?
Kann es überhaupt den ganzen Speicherbereich nutzten, oder wird auch da ihnen nur ein Teil zugewiesen?

Das alles habe ich irgendwie ein wenig ... sparsamer erklärt bekommen. Deshalb ist es für mich sehr ... verwirrend ... überraschend. Derweil scheint mir dies, zumindest ab einer bestimmten Programmgröße, sehr wichtig zu sein, zu wissen.

Ich habe aber ein 64-Bit Computer und BS, wenn mich nicht alles täuscht. Also BS ist 64-Bit. So weit ich weiß, muss es dann der Computer auch 64-Bit sein. Sollte da dann also die Grenze doch bei 128 GB liegen?
Lazarus 2.2.0 / FP 3.2.4

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Array lockere Bereichs- (Range-) check

Beitrag von Mathias »

Ich habe aber ein 64-Bit Computer und BS, wenn mich nicht alles täuscht. Also BS ist 64-Bit. So weit ich weiß, muss es dann der Computer auch 64-Bit sein. Sollte da dann also die Grenze doch bei 128 GB liegen?

Ist die Grenze nicht bei 16 Exbibyte ?
Vorausgesetzt, es sind genügend RAM-Riegel verbaut. :wink:
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Array lockere Bereichs- (Range-) check

Beitrag von Warf »

Mathias hat geschrieben:
Ich habe aber ein 64-Bit Computer und BS, wenn mich nicht alles täuscht. Also BS ist 64-Bit. So weit ich weiß, muss es dann der Computer auch 64-Bit sein. Sollte da dann also die Grenze doch bei 128 GB liegen?

Ist die Grenze nicht bei 16 Exbibyte ?
Vorausgesetzt, es sind genügend RAM-Riegel verbaut. :wink:


Die Theoretische grenze ja, aber man darf nicht vergessen, je größer der Addressraum desto aufwendiger ist es diesen zu organisieren (die allokationstabellen werden viel größer und komplizierter), daher implementieren Windows, und OSX nur deutlich kleinere Addressräume, das gibt schlicht weg eine bessere performance und weniger overhead. Windows 10 Home kann 128G und Pro 512G. OSX befindet sich in ähnlichen größenordnungen. Linux kennt glaube ich keine beschränkung und kann die vollen 16 EB ausnutzen, ist aber wahrscheinlich bei sehr großen ramzahlen dann eventuell sogar deutlich langsamer.

Erwin hat geschrieben:Danke erst mal, für all diese Erklärungen. Da weiß man zumindest schon mal wieder vieles mehr.

Main frame? Von dem ich nichts wissen soll?

das ist der Stackframe der main-funktion, also die erste Funktion die beim start des programmes ausgeführt wird (im pascal ist das der begin-end block der lpr datei)

Erwin hat geschrieben:Damit hat er kein Problem, aber mit 15: TestArray[15]:=25;
Aber läuft der Erklärung betreffend vermutlich auf das selbe hinaus. Nur halt Ordnungshaber, falls jemand anders dies ebenfalls testet.

Ja muss auch sagen 5 hätte mich etwas gewundert. 12*4 sind 48 Byte, damit reist du den stack ganz schon tief runter und landest entweder in der LCL internen update funktion oder in der main funktion

Erwin hat geschrieben:Aber dennoch sind bei mir Array-Größen teils ganz schön beschränkt.
Irgendwie verwirrend.
Kann es sein, dass einerseits ein großes 8MB Array nicht geht, aber 100 x 1 MB Array schon?
Steht der Speicherbereich der Segmente für Code, Const und Data Anfangs fest, oder wird nach dem Start je nach Bedarf dann erst mal Reserviert und steht erst danach dann fest?

Ne, 100x 1MB wird genauso die stack größe belegen, durch alignment eventuell sogar mehr. Und der Data, Code und Const bereich steht zur Compilezeit fest. Das sind alle globalen variablen, konstanten und natürlich dein sourcecode selbst. Nur heap und Stack sind dynamisch. Aber es kann natürlich sein das das betriebsystem irgendwas unanständiges macht, was ich nicht kenne (so gut kenne ich mich da auch nicht aus)
Erwin hat geschrieben:Und den Rest teilt sich generell dann je nach Bedarf Heap und Stack? Oder steht deren gemeinsame Größe ebenfalls generell fest?
Kann es überhaupt den ganzen Speicherbereich nutzten, oder wird auch da ihnen nur ein Teil zugewiesen?

Das alles habe ich irgendwie ein wenig ... sparsamer erklärt bekommen. Deshalb ist es für mich sehr ... verwirrend ... überraschend. Derweil scheint mir dies, zumindest ab einer bestimmten Programmgröße, sehr wichtig zu sein, zu wissen.

Ich habe aber ein 64-Bit Computer und BS, wenn mich nicht alles täuscht. Also BS ist 64-Bit. So weit ich weiß, muss es dann der Computer auch 64-Bit sein. Sollte da dann also die Grenze doch bei 128 GB liegen?

Der heap und der stack können so viel platz belegen wie noch frei ist. Auf einem 32 bit windows, da Code und Data oft vernachlässigbar sind(<100mb), hätte man immernoch fast 3 GB für heap und Stack frei. Die einzige begrenzung sind die stacklimits wie ich bereits gesagt habe.

Um genau zu sein ist all das was ich oben erklärt habe nur Virutell, das Betriebsystem stellt jedem prozess einen Virutellen addressraum einer festen größe bereit (Windows 32bit, 3 GB, windows 10 64 bit pro, 512 GB), von kontinuierlichem speicher. Wie das am ende physisch auf den riegeln liegt ist sache des betriebsystems. Damit sind vor allem die addressräume verschiedener prozesse getrennt, und man kann nicht einfach in anderen prozessen rumpanschen. Das heißt aber auch, selbst wenn dein rechner nur 2GB ram hat, hat jeder 32 bit windows prozess logisch 3 GB speicher zur verfügung (Durch schreiben auf die festplatte wird das ermöglicht). Wenn du jetzt aber mehr speicher verbrauchst als physisch vorhanden ist, dann wirft dein System ein OOM fehler, den du abfangen solltest (natürlich nur wenn deine anwendung viel speicher frisst), ansonsten wird das betriebsystem einfach anfangen prozesse zu killen.
Daher ist addresse 0xXXXXXX nicht die selbe in prozess 1 wie in prozess2. Das betriebsystem kümmert sich darum welchen realen speicherbereich das jetzt abbildet

Erwin
Beiträge: 286
Registriert: Mi 16. Sep 2009, 14:15
OS, Lazarus, FPC: Xubuntu 22.04 / x86_64_linux-gtk 2 / L 2.2.0 / FPC 3.2.2

Re: Array lockere Bereichs- (Range-) check

Beitrag von Erwin »

Warf hat geschrieben:Ne, 100x 1MB wird genauso die stack größe belegen, durch alignment eventuell sogar mehr. Und der Data, Code und Const bereich steht zur Compilezeit fest. Das sind alle globalen variablen, konstanten und natürlich dein sourcecode selbst. Nur heap und Stack sind dynamisch. Aber es kann natürlich sein das das betriebsystem irgendwas unanständiges macht, was ich nicht kenne (so gut kenne ich mich da auch nicht aus)

Heißt dass, man kann die Größe in den Compiler-Einstellungen einstellen?
Und hängt der feste Bereich dennoch von den Arbeitsspeicher ab, oder ist er überall dann gleich?

Habe jetzt mal mit Array getestet. Bei mir geht das bis 530 Millionen (also [0..530000000]). Single und Integer. String halb so viel.
Wenn ich recht bedenke, dazu kommt doch bei 64-bit-System noch mal mindestens 4 mal so viel Speicherplatz .... also ca. 2-3 GB dann insgesamt, was es dann beansprucht? Kann das sein?
Eigentlich groß genug. Will ja gar nicht so viel Platz mit meinen Programmen beanspruchen.

Ein Test, ob man mit Const genau so viel beanspruchen kann, ging leider schief, da man ja die Array sofort 'füllen' müsste, und ich nicht wüsste, wie das geht, ohne sich die Finger wund zu schreiben.

Die genauen Zahlen würden mich dennoch interessieren. Wie groß ein Programm selbst (also CODE-Teil) dann sein dar/istf? etc. Aber vermute mal, dass kann man nicht mal so ohne weiteres festlegen/feststellen?
Lazarus 2.2.0 / FP 3.2.4

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Array lockere Bereichs- (Range-) check

Beitrag von Warf »

Erwin hat geschrieben:Heißt dass, man kann die Größe in den Compiler-Einstellungen einstellen?
Und hängt der feste Bereich dennoch von den Arbeitsspeicher ab, oder ist er überall dann gleich?

Ups da hab ich mich falsch ausgedrückt, es belegt nicht gleich viel speicher, sondern nicht weniger, also das problem von zu großen objekten kann man nicht mit vielen kleinen objekten beheben. Das einzige was einstellbar ist, ist das alignment, es wird normalerweise immer auf 4 Byte aufgerundet, damit es besser optimiert werden kann.

Der virtuelle Adressraum hängt indirekt von Arbeitsspeicher ab. Theoretisch hat ein prozess immer die 4 GB (32 bit) addressraum, aber wenn nicht genug speicher verfügbar ist, kann das betriebsystem einfach speicher allokation verweigern, oder den prozess killen (ich glaub linux killt den prozess nach ein paar verweigerungen weiß es aber nicht auswendig). Diese verweigerung äußert sich dann so das GetMem, new oder Create, nil zurücksetzen und errno ist dann auf OOM (out of memory) gesetzt. Allerdings wird der Speicher nicht nur auf den RAM gehalten sonder auch geswapt. Das heißt speicherzellen die selten verwendet werden, werden auf die festplatte geschrieben (Linux hat eine eigene swap partition, windows den pagefile in C:\). Erst wenn die Platte und der Ram voll sind streikt gibts OOM.
Wenn dir der Addressraum ausgeht, dann ist das kein OOM sondern die Heap/Stack Colliosion. (sollte eigentlich auch einen Runtime error werfen)

Erwin hat geschrieben:Habe jetzt mal mit Array getestet. Bei mir geht das bis 530 Millionen (also [0..530000000]). Single und Integer. String halb so viel.
Wenn ich recht bedenke, dazu kommt doch bei 64-bit-System noch mal mindestens 4 mal so viel Speicherplatz .... also ca. 2-3 GB dann insgesamt, was es dann beansprucht? Kann das sein?
Eigentlich groß genug. Will ja gar nicht so viel Platz mit meinen Programmen beanspruchen.

Taskmanager öffnen und auf den Ramverbrauch achten (oder unter linux htop)
Ansonsten, wenn du windows verwendest, ich kenn das stacklimit nicht von windows, ich hab unter Linux getestet, da ich da ulimit verwenden kann um das limit zu setzen und rauszufinden

Erwin hat geschrieben:Ein Test, ob man mit Const genau so viel beanspruchen kann, ging leider schief, da man ja die Array sofort 'füllen' müsste, und ich nicht wüsste, wie das geht, ohne sich die Finger wund zu schreiben.


Musst du dir halt generieren lassen

Erwin hat geschrieben:Die genauen Zahlen würden mich dennoch interessieren. Wie groß ein Programm selbst (also CODE-Teil) dann sein dar/istf? etc. Aber vermute mal, dass kann man nicht mal so ohne weiteres festlegen/feststellen?
[/quote]

Der Code Teil hat keine beschränkung außer natürlich den Addressraum (also 4 gb für 32 bit). Wenn du dir mal ansehen möchtest was es für limits gibt, für linux gibts ja manpages:
https://ss64.com/bash/ulimit.html

Für die Code page, schau dir mal DDetours an: Link
Das ist eine Bibliothek die es unter windows erlaubt funktionen zu hooken, also funktionsaufrufe in einem programm durch eigene funktionen zu ersetzen. Das macht das indem es durch die Code page scannt, und jedes vorkommen des Funktionszeigers durch das Vorkommen der eigenen Funktion zu ersetzen. Dafür muss es ja irgendwie wissen wie groß die Codepage ist.

Erwin
Beiträge: 286
Registriert: Mi 16. Sep 2009, 14:15
OS, Lazarus, FPC: Xubuntu 22.04 / x86_64_linux-gtk 2 / L 2.2.0 / FPC 3.2.2

Re: Array lockere Bereichs- (Range-) check

Beitrag von Erwin »

Warf hat geschrieben:Taskmanager öffnen und auf den Ramverbrauch achten (oder unter linux htop)
Ansonsten, wenn du windows verwendest, ich kenn das stacklimit nicht von windows, ich hab unter Linux getestet, da ich da ulimit verwenden kann um das limit zu setzen und rauszufinden

Also der Tipp hört sich für mich wieder so an, als würden doch alle 5 sich den Speicherbereich je nach Bedarf teilen. Weil man sieht ja meist nur insgesamt, was an Speicher von einem Programm benötigt wird, aber nicht ob der Platz für CODE, Variablen, etc. zu eng wird?

Außerdem gewinne ich den Eindruck, dass es also generell von Computer zu Computer unterschiedlich ist, ungeachtet dessen, ob dies sein müsste?
Während meiner also noch einen Adressraum von ... 530 Millionen (Single oder Integer) Array-Größe hat, kann es auf einem anderen Computer zu groß sein? Auch dann, wenn dieser gleich viel Arbeitsspeicher hat?

Vermutlich mache ich mir wohl eh zu viele Gedanken. Also was den CODE betrifft, da werde ich wohl eh Jahre proggen müssen, um dessen Rahmen zu sprengen, vermute ich mal?
Und auch sonst ... 3-4 GB Arbeitsspeicher sollte ein Programm eigentlich nicht brauchen, oder?
Ach, was variablen betrifft, bis ich da was schreibe, wo dies wirklich so groß werden könnte, ist vielleicht eh einiges anders. Auch sonst sollte ich erst warten, bis das Problem akut wird.

Jedenfalls scheint es wohl dennoch so viel Platz zu geben, dass ich mir darüber nicht wirklich den Kopf zerbrechen muss, oder?
Lazarus 2.2.0 / FP 3.2.4

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Array lockere Bereichs- (Range-) check

Beitrag von Warf »

Erwin hat geschrieben:Also der Tipp hört sich für mich wieder so an, als würden doch alle 5 sich den Speicherbereich je nach Bedarf teilen. Weil man sieht ja meist nur insgesamt, was an Speicher von einem Programm benötigt wird, aber nicht ob der Platz für CODE, Variablen, etc. zu eng wird?

Außerdem gewinne ich den Eindruck, dass es also generell von Computer zu Computer unterschiedlich ist, ungeachtet dessen, ob dies sein müsste?
Während meiner also noch einen Adressraum von ... 530 Millionen (Single oder Integer) Array-Größe hat, kann es auf einem anderen Computer zu groß sein? Auch dann, wenn dieser gleich viel Arbeitsspeicher hat?

Vermutlich mache ich mir wohl eh zu viele Gedanken. Also was den CODE betrifft, da werde ich wohl eh Jahre proggen müssen, um dessen Rahmen zu sprengen, vermute ich mal?
Und auch sonst ... 3-4 GB Arbeitsspeicher sollte ein Programm eigentlich nicht brauchen, oder?
Ach, was variablen betrifft, bis ich da was schreibe, wo dies wirklich so groß werden könnte, ist vielleicht eh einiges anders. Auch sonst sollte ich erst warten, bis das Problem akut wird.

Jedenfalls scheint es wohl dennoch so viel Platz zu geben, dass ich mir darüber nicht wirklich den Kopf zerbrechen muss, oder?


Um den Code mach dir gar keine gedanken, darum kümmert sich der Compiler. Solang der nicht meckert ist alles in ordnung. Und das ganze ist nicht von Computer zu Computer sondern von Architektur zu Architektur und zwischen Optimierungsstufen anders sein. z.B. unter AVR architekturen gibt es normalerweise einen ROM (Flash) auf dem Code und Consts liegt, und einen RAM in dem dann dein stack und die globalen variablen liegen. Den heap darfst du selbst implementieren. Gleichzeitig sind AVR's RISC's (Reduced Instruction set Computer) und x86 sind CISC's (complete ...), daher haben x86 viel mehr instructions, und damit kannst du den selben code in weniger instructions speichern.
Aber die größe von datentypen ändert sich in Pascal (abgesehen von alignment) zum glück nicht (anders als in c, da kann int 16, 32 oder 64 bit groß sein je nach compiler, sogar noch besser theoretisch darf short int auch größer sein als long int, gibt aber zum glück keinen compiler der das einaut), also ein array[0..4] of Byte wird immer 5 byte groß sein, egal auf welchem PC

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6198
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Array lockere Bereichs- (Range-) check

Beitrag von af0815 »

Warf hat geschrieben:Um den Code mach dir gar keine gedanken, darum kümmert sich der Compiler. Solang der nicht meckert ist alles in ordnung.

Ein klares nein. Nur weil ein Compiler nicht meckert ist der Code nicht in Ordnung. Wenn der Compiler nicht meckert heisst das nur das die Syntax stimmt - mehr nicht. Ob der Code korrekt ist, kann er nicht feststellen. Vor allen wenn man in Richtung Pointer oder Casts geht, kann der Compiler nicht alles prüfen.

Auch die größe der Datentypen sind nicht in Stein gemeisselt. Es gibt Typen die haben immer die gleiche Größe, andere könne je nach Plattform sich ändern.

So sind die Aussagen zu einfach gehalten.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Antworten