WinFuture-Forum.de: Der Stack in C - WinFuture-Forum.de

Zum Inhalt wechseln

Nachrichten zum Thema: Entwicklung
Seite 1 von 1

Der Stack in C


#1 Mitglied ist offline   Sworddragon 

  • Gruppe: aktive Mitglieder
  • Beiträge: 27
  • Beigetreten: 23. November 12
  • Reputation: 0
  • Geschlecht:unbekannt

geschrieben 03. Dezember 2012 - 10:40

Da ich Sachen in der Programmierung gerne optimiere, habe ich mich auch mit der Speicherverwaltung von C ein wenig beschäftigt. Mein System ist ein Ubuntu 64 Bit, mit dem Linux Kernel 3.7.0 RC7. Hier ist erstmal der Testcode (kompiliert mit "gcc test.c -o test"):

#include <string.h>
#include <unistd.h>

int i = 0;

void test()
{
	char buffer[1048576];

	memset(buffer, 0, 1048576);
	usleep(3000000);
	++i;
	if(i == 1)
		test();
}

int main()
{
	usleep(3000000);
	test();
	usleep(3000000);
	test();
	usleep(60000000);
	return 0;
}



  • Als in Zeile 19 3 Sekunden lang gewartet wird, verbraucht der Prozess 360 KB an Speicher.
  • Nachdem test() in Zeile 20 aufgerufen wird, beträgt der Speicherverbrauch 1408 KB, da die Variable buffer mit 1 MB beschrieben wurde.
  • Der 1. Aufruf von test() erzeugt eine Rekursion. test() ruft sich also selber auf und es wird ein neuer Speicherbereich für buffer reserviert. Der Speicherverbrauch vom Prozess beträgt nach dem erneuten Beschreiben von buffer 2200 KB.
  • Nachdem test() in Zeile 22 aufgerufen wird, beträgt der Speicherverbrauch immer noch 2200 KB. Es wurde also kein 3. mal ein neuer Speicherbereich für buffer vorreserviert, da der Speicherbereich vom 1. Durchlauf benutzt wurde.


Was mich allerdings wundert ist, dass der Stack von lokalen Variablen nach dem Beenden der Funktion nicht freigegeben wird. Es wird also erst beim Benutzen der lokalen Variable der Speicherbereich reserviert, entsprechend rekursiven Funktionsaufrufen wird das dann in mehreren Ebenen gemacht. Wird eine bereits benutzte Ebene einer Rekursion erneut aufgerufen, wird auch der dementsprechende Speicherbereich benutzt.

Allerdings stört mich dieses Verhalten ein wenig. Der Stack ist normalerweise recht klein (standardmäßig 8 MB bei Ubuntu) und wenn man eine größere Anwendung hat, die aus vielen Funktionen und Variablen besteht, wo es eventuell ein paar Rekursionen gibt, wird der Stack immer weiter gefüllt ohne freigegeben zu werden. Direkt mit free() kann man den Speicher nicht freigeben, aber gibt es eine andere Möglichkeit, den Speicher von diesen Variablen nach dem Beschreiben freizugeben?

Dieser Beitrag wurde von Sworddragon bearbeitet: 03. Dezember 2012 - 10:42

0

Anzeige



#2 Mitglied ist offline   Kirill 

  • Gruppe: aktive Mitglieder
  • Beiträge: 3.587
  • Beigetreten: 04. Dezember 06
  • Reputation: 121
  • Geschlecht:Männlich
  • Wohnort:BT

geschrieben 03. Dezember 2012 - 17:02

Wird das Ganze eigentlich aufm Stack oder aufm Heap abgelegt?
Most rethrashing{
DiskCache=AllocateMemory(GetTotalAmountOfAvailableMemory);}
0

#3 Mitglied ist offline   Sworddragon 

  • Gruppe: aktive Mitglieder
  • Beiträge: 27
  • Beigetreten: 23. November 12
  • Reputation: 0
  • Geschlecht:unbekannt

geschrieben 03. Dezember 2012 - 22:38

Diese Art von Variablen werden auf dem Stack abgelegt. Das lässt sich auch sehr gut mit einem Stack-Overflow testen. Wenn ich die Rekursion weiterlaufen lasse, ist die letzte Menge an Speicher, die ich bei dem Prozess sehe 7,x MB, bevor er dann abstürzt.

Dieser Beitrag wurde von Sworddragon bearbeitet: 03. Dezember 2012 - 22:41

0

#4 Mitglied ist offline   Witi 

  • Gruppe: aktive Mitglieder
  • Beiträge: 5.940
  • Beigetreten: 13. Dezember 04
  • Reputation: 43
  • Geschlecht:Männlich
  • Wohnort:Kingsvillage
  • Interessen:Frickeln

geschrieben 04. Dezember 2012 - 18:43

Zitat

Direkt mit free() kann man den Speicher nicht freigebe

Warum soll ein free(buffer) am Ende von test nicht funktionieren?
0

#5 Mitglied ist offline   Mr. Floppy 

  • Gruppe: VIP Mitglieder
  • Beiträge: 4.115
  • Beigetreten: 01. Juli 08
  • Reputation: 271
  • Geschlecht:Männlich

geschrieben 04. Dezember 2012 - 20:51

Ohne Gewähr... Ich würde sagen, daß folgendes passiert. Du rufst test() auf und belegst 1 MB. Danach wird im selben Kontext (Rekursion) noch mal Speicher angefordert. Der wird hinter den ersten Buffer gepackt, insgesamt 2 MB. Danach kehrt der erste Aufruf von test() samt Rekursion wieder zurück. Auf den Buffer gibt's jetzt keine Referenz mehr, weil er lokal definiert wurde. Beim letzten test() wird der Speicher wahrscheinlich wieder an den Anfang des Stack gepackt, weshalb er nicht wächst. Wie gesagt, alles ohne Gewähr. Ist einfach schon zu lange her ;)
2

#6 Mitglied ist offline   Sworddragon 

  • Gruppe: aktive Mitglieder
  • Beiträge: 27
  • Beigetreten: 23. November 12
  • Reputation: 0
  • Geschlecht:unbekannt

geschrieben 05. Dezember 2012 - 00:19

Beitrag anzeigenZitat (Witi: 04. Dezember 2012 - 18:43)

Warum soll ein free(buffer) am Ende von test nicht funktionieren?


Weil nur Speicher, der im Heap ist mit free() freigegeben werden kann. Wenn ich nach memset() ein free(buffer) aufrufe (und auch stdlib.h eingebunden habe), kommt beim Kompilieren folgende Meldung:

[email protected]:~/data$ gcc test.c -o test
test.c: In Funktion »test«:
test.c:12:6: Warnung: Versuch, Nicht-Heap-Objekt »buffer« freizugeben [-Wfree-nonheap-object]



Beim Ausführen stürzt dann das Programm bei free() ab:

[email protected]:~/data$ ./test
*** glibc detected *** ./test: munmap_chunk(): invalid pointer: 0x00007fff599b8ef0 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x7e2c6)[0x7f918f6942c6]
./test[0x40066e]
./test[0x4006ca]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5)[0x7f918f637c15]
./test[0x400569]
======= Memory map: ========
00400000-00401000 r-xp 00000000 00:15 9568354                            /home/sworddragon/data/test
00600000-00601000 r--p 00000000 00:15 9568354                            /home/sworddragon/data/test
00601000-00602000 rw-p 00001000 00:15 9568354                            /home/sworddragon/data/test
01b96000-01bb7000 rw-p 00000000 00:00 0                                  [heap]
7f918f400000-7f918f415000 r-xp 00000000 08:01 3932260                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7f918f415000-7f918f614000 ---p 00015000 08:01 3932260                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7f918f614000-7f918f615000 r--p 00014000 08:01 3932260                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7f918f615000-7f918f616000 rw-p 00015000 08:01 3932260                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7f918f616000-7f918f7ce000 r-xp 00000000 08:01 3932438                    /lib/x86_64-linux-gnu/libc-2.16.so
7f918f7ce000-7f918f9cd000 ---p 001b8000 08:01 3932438                    /lib/x86_64-linux-gnu/libc-2.16.so
7f918f9cd000-7f918f9d1000 r--p 001b7000 08:01 3932438                    /lib/x86_64-linux-gnu/libc-2.16.so
7f918f9d1000-7f918f9d3000 rw-p 001bb000 08:01 3932438                    /lib/x86_64-linux-gnu/libc-2.16.so
7f918f9d3000-7f918f9d8000 rw-p 00000000 00:00 0 
7f918f9d8000-7f918f9fb000 r-xp 00000000 08:01 3932384                    /lib/x86_64-linux-gnu/ld-2.16.so
7f918fbe2000-7f918fbe5000 rw-p 00000000 00:00 0 
7f918fbf7000-7f918fbfa000 rw-p 00000000 00:00 0 
7f918fbfa000-7f918fbfb000 r--p 00022000 08:01 3932384                    /lib/x86_64-linux-gnu/ld-2.16.so
7f918fbfb000-7f918fbfd000 rw-p 00023000 08:01 3932384                    /lib/x86_64-linux-gnu/ld-2.16.so
7fff599b7000-7fff59aba000 rw-p 00000000 00:00 0                          [stack]
7fff59bc5000-7fff59bc6000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
Abgebrochen




Beitrag anzeigenZitat (Mr. Floppy: 04. Dezember 2012 - 20:51)

Ohne Gewähr... Ich würde sagen, daß folgendes passiert. Du rufst test() auf und belegst 1 MB. Danach wird im selben Kontext (Rekursion) noch mal Speicher angefordert. Der wird hinter den ersten Buffer gepackt, insgesamt 2 MB. Danach kehrt der erste Aufruf von test() samt Rekursion wieder zurück. Auf den Buffer gibt's jetzt keine Referenz mehr, weil er lokal definiert wurde. Beim letzten test() wird der Speicher wahrscheinlich wieder an den Anfang des Stack gepackt, weshalb er nicht wächst. Wie gesagt, alles ohne Gewähr. Ist einfach schon zu lange her ;)


Du bringst mich gerade auf eine Idee. Ich werde mal test2() deklarieren, welches auch 1 MB im Stack belegt und es durch den letzten Aufruf von test() ersetzen: Der Stack wird wie von dir vermutet am Anfang einfach angehangen. Damit sind zwar die 2 MB noch reserviert, aber jede beliebige Funktion kann sie problemlos überschreiben und die 8 MB vom Stack weiter benutzen.

Dieser Beitrag wurde von Sworddragon bearbeitet: 05. Dezember 2012 - 00:22

0

Thema verteilen:


Seite 1 von 1

1 Besucher lesen dieses Thema
Mitglieder: 0, Gäste: 1, unsichtbare Mitglieder: 0