Newsgroup: it.comp.os.linux.development
Titolo: dimensione dello stack e compilatori gcc e vc++

Ho scritto un programma che funziona egregiamente sotto linux se lo 
compilo con gcc.
Adesso lo voglio portare sotto windows, ho dovuto fare un po' a botte 
con open, read e simili, ma la compilazione non ha dato grossi problemi.
I problemi sorgono a runtime, perche' il programma fa sollevare una 
eccezione di stack overflow.
Gironzolando nei newsgroup microsoft.* mi hanno indicato questo articolo:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore/html/_core_.2f.STACK_linker.asp
in cui dicono che la dimensione predefinita dello stack per i programmi 
compilati con vc++ e' di un mega.
Ho riletto in 
groups.google.com
 i vecchi post in questo ng relativi alla 
dimensione dello stack e chi ha postato qui sosteneva che lo stack in 
linux fosse illimitato.
Ma e' possibile? Insomma, tra un mega e illimitato la differenza non e' 
trascurabile....
Qualcuno mi puo' spiegare questa differenza?

 
non è illimitato su linux, ha un valore ben preciso. Lo si puo però
impostare a piacere in vari modi; ad esempio utilizzando funzioni come
get* e setrlimit() 
io spesso mi ritrovo 8 mega di stack, ma non ho capito bene da cosa
dipende questo valore di default, visto che, sviluppando un applicativo
una volta, mi è capitato di averne avuti a disposizione 2 (e mi diede un
po' di problemi capire da dove veniva quello strano segfault :P)
Cmq, di certo non è illimitato
Ciao
Mtc`

Matic ha scritto:

illimitato.. insomma, limitato dalla memorai virtuale :-)
Comunque sto porconando ancora con visual studio e mi ritrovo a dover 
iompostare uno stack di 64 mega o non va.
Facenso i conti a mano questi 64 mega sembrano spaventosamente troppi.
Posso in linux far partire il programma e farmi dire alla sua fine 
quanto e' stata la dimensione massima dello stack? giusto per fare due 
confronti....

In linux lo stack parte dal limite di memoria assegnabile ad un processo e
decresce verso lo 0. Al contrario lo spazio dati parte da 0 e cresce verso
il limite massimo. Quando i due puntatori si incontrano(o si
scontrano :-) )arrivano i problemi.
Più che indagare sul limite dello stack, prova a capire per quale motivo ti
serve tutto quello stack! Hai chiamate ricorsive a procedure? fai passaggi
di parametri tramite stack?
Bye,
Raktzo

raktzo ha scritto:

si, ma se chiamo 4 funzioni una dentro l'altra e passo per ognuna 4 
puntatori non arrivo comunque a riempire 64 mega.......

 puntatori non arrivo comunque a riempire 64 mega.......
...dipende dal puntatore,a cosa punta,dal linguaggio e dal compilatore...
Cmq, prova a far funzionare le funzioni una alla volta e verifica quale e
quando ti finisce lo stack
Verifica ache con linux la quantità di memoria usata per confrontare i
carichi.
Bye,
Raktzo

 Cmq, di certo non è illimitato
Dire che è "illimitato" non sarà rigorosamente corretto ma ci si avvicina
molto. Quando hai anche solo 1 GB di stack disponibile, direi che ti può
bastare e se non ti basta vuol dire che stai facendo qualcosa di MOLTO
sbagliato :)
Ciao,
-- 
Daniele Orlandi

 confronti....
Prova questo, un'idea te la dà:
--------------------
#include <stdio.h>
int a;
int main()
{
        int b;
        printf("Heap=%#p Stack=%#p\n", &a, &b);
}
--------------------
<sborone>
orlandi@nabla:~> ./a
Heap=0x500904 Stack=0x7fffffffeeec
</sborone>
Ciao,
-- 
Daniele Orlandi

 scontrano :-) )arrivano i problemi.
tutto questo per dire che: non ha senso parlare di "parte dal limite di
memoria assegnabile a un processo" e non "decresce verso le 0". Lo spazio
dati non parte da 0 e non cresce verso il limite massimo. I due puntatori
non si incontrano mai :) perché c'è abbastanza robbetta in mezzo
Ciau
Mtc`

 ...dipende dal puntatore,a cosa punta,dal linguaggio e dal compilatore...
non dipende dal puntatore, è di dimensione piccola e fissa sempre (qui 4
bytes), indipendentemente da cosa punta.. senno a che serve :)
Cia`
Mtc

 Ciao,
Ciau

 volendo non riuscirei a richiedere piu di 127 Mb. Erro ?
Se vedi l'output del programmino che ho postato, su una mia macchina ho a
disposizione poco meno di 128 TiB (TeraByte informatici), ovvero 140 TB,
sull'altra (32-bit) sono:
bastard:~ # ./a
Heap=0x8049634 Stack=0xbffff394
Ovvero circa 3GB.
Direi che la risposta giusta è "dipende", ma comunque "tanto" :)
Ciao,
-- 
Daniele Orlandi

 bytes), indipendentemente da cosa punta.. senno a che serve :)
Non sono sempre 4 byte, dipende dall'architettura.
Dipende anche dal puntatore, moltissimi leak sono legati ai puntatori, o
meglio, all'uso che se ne fa.
Bye,
Raktzo

 Direi che la risposta giusta č "dipende", ma comunque "tanto" :)
lo direi anche io a 'sto punto 
Ma.. anche quello dove risiedono le variabili globali č heap ? Qual č la
definizione di heap di preciso :P?

 Qual è la definizione di heap di preciso :P?
Oddio, quella precisa e formale non la so, comunque è tutto lo spazio 
(segmento dati) che allochi con brk(2).
Ciao,
-- 
Daniele Orlandi

 Daniele Orlandi
Ciao,
Scusate non mi piace contraddire ma quanto ho letto finora sul thread non č
tutto esatto. Facciamo un po' di sintesi e ordine:
La memoria fisica di una macchina č tutta la stessa e appunto la CPU non fa
nessuna differenza tra stack, heap e altro. Il sistema operativo assegna uno
spazio al processo, uno spazio di memoria virtuale che pur apparendo
contiguo č mappato a pagine di memoria fisica anche molto distanti tra loro
ed eventualmente anche a spazi su disco nel caso parte di questa memoria
virtuale sia stata swappata per mitivi di spazio.
Linux su IA-32 bits assegna quindi uno spazio virtuale ad ogni processo che
va da 0x08048000 a 0xBFFFFFFF e qui dentro ci sta tutto ciň che ha a che
fare con il processo stesso (dal codice ai dati globali, essi stessi in piů
sottosezioni, fino all'heap e allo stack).
Spero che il seguente schema non risulti troppo illegibile per motivi di
allineamento, comunque immagginate un rettangolo delineato a destra dal
tasto "|". Descrivo la situazione di tutto lo spazio di memoria virtuale di
un processo, ipotizzando anche una prima chiamata a funzione con parametri e
variabili automatiche (o temporanee per chi preferisce):
--------------------------
Variabili d'ambiente | <--- 0xBFFFFFFF
Argomenti |
Nome Programma |
numero argomenti |
parametri funzione |
return instruction pos |
old %ebp | <---- %ebp (base (o frame) pointer)
variabili automatiche | <----- %esp (stack pointer)
- - - - - - - - |
    |
 memoria |
 non | <--- memoria a disposizione sia di stack che di heap
 mappata |
    |
- - - - - - - - - -| <---- Break
    |
Codice programma |
 + |
 variabili |
 globali |
 + |
 statiche |
    |
-------------------------| <----- 0x08048000
Quindi lo spazio virtuale non comincia da zero ma da 0x08048000 ed č
occupato da quanto descritto nel riquadro piů in basso del disegno. Il
programma di Daniale stampa l'indirizzo di una variabile globale e quindi
compresa tra il suddetto indirizzo e il cosidetto "Break".
Infatti Daniele ha stampato per la globale 0x08049634 che č circa 1600 bytes
sopra il limite minimo di memoria indirizzabile, ma non ancora nello heap
perchč non c'č nessuna chiamata a "malloc()"->"brk()". Per la variabile
nello stack ha stampato 0xbffff394 che č di poco piů sotto del valore
massimo di memoria indirizzabile di 0xbfffffff e si tratta di spazio gestito
a stack. Comunque giustamente conclude che la memoria virtuale assegnata al
processo, in macchina Intel 32 bits, č circa 3 Giga. OK.
Lo spazio della parte superiore del disegno mappa cioň che č chiamato stack,
non per essere una differente memoria ma per il modo in cui č utilizzata,
cioč con meccanismo a pila. Tutto lo spazio tra il "break" e lo %esp (stack
pointer) č utilizzabile sia verso il basso, cioč incrementando lo stack con
le varie "push", che dallo stesso "break" verso l'alto allocando la memoria
con malloc() che, tramite brk(), chiede al sistema operativo di spostare il
limite "break" verso l'alto per fare posto a quello che si chiama "heap".
In sintesi lo spazio di "memoria non mappata", che č la parte piů grande
anche se dal disegno non si vede, č usato dallo stack verso il basso
automaticamente con le push senza coinvolgere il sistema operativo. Invece
per allocare alla maniera dell'heap č necessario chiedere al sistema di
spostare il "break" verso l'alto. Se una richiesta di spazio heap troppo
ampia vuole fare che il "break" si sovrapponga allo stack il sistema non lo
permette. Al contrario se una push in piů tenta di sovrapporre il "break" il
processo va in segfault e termina.
Nelle precedenti osservazioni ho immaginato di non avere nessun limite
settato con "setrlimit()". Tra l'altro non succede quasi mai che lo stack si
scontri con il break (che puň essere cresciuto per eventuali allocazioni di
spazio di heap) e se succede puň darsi che stai usando molto male la memoria
(stack e/o heap che sia).
Spero di essere stato chiaro anche se ripetitivo, anche perchč sto scrivendo
di fretta e non posso rileggere. Domani sono ancora qui per repliche,
osservazioni e per correggere gli errori che avrň sicuramente fatto.
Buon lavoro,
fabio de francesco
(fabiomdf)
PS. La memoria bassa (codice, variabili globali...) č a sua volta divisa in
"text", "bss", "data" e forse altre che al momento non mi vengono in mente.
--------------------------------
Inviato via 
http://arianna.libero.it/usenet/

 tutto esatto. Facciamo un po' di sintesi e ordine:
vabbè ma serviva scrivere tutta sta roba ? infondo era quello che avevamo
detto noi; per di piu, dici che, mentre la zona destinata alle malloc()
(A) cresce verso lo stack e lo stack (B) crescendo decresce verso suddetta
zona, se i due si vengono a sovrapporre il processo termina.
Ma questo non è vero: come ho riportato sull'altro post, tra A e B ci
sono zone di memoria in cui vengono mappate le librerie condivise, e se lo
stack va a sovrapporsi a queste (senza che neanche la malloc() sia mai
stata chiamata) il processo termina uguale (ben prima quindi di
sovrapporsi con A.
Bon, abbiamo detto anche troppo..
Cia`

 osservazioni e per correggere gli errori che avrò sicuramente fatto.
ok.
ma allora viene da chiedersi... come cavolo funzionano windows e vc++, 
che prevedono uno stack di solo un mega di default?

 detto noi; per di piu, dici che, mentre la zona destinata alle malloc()
Mi ha rinfrescato molta roba...peccato che forse per stasera l'ho di nuovo
accantonata...
Bye,
Raktzo

 che prevedono uno stack di solo un mega di default?
Non e` che l'opzione indica la dimensione iniziale dello stack? Mi pare
di ricordare qualcosa a proposito dei PE... google...
Un dei link che ha catturato la mia attenzione:
http://bbs.ee.ntu.edu.tw/boards/Programming/12/18.html
Luca
-- 
Home: 
http://kronoz.cjb.net
42

 che prevedono uno stack di solo un mega di default?
Non credo che sia una questione di sistema operativo. Sono le 
applicazioni che usano lo stack. Per esempio, sia "Blocco note" 
(Notepad.exe), che "Pannello di controllo" (Control.exe), che la shell 
grafica di Windows (Explorer.exe) definiscono uno stack di 256 KB per il 
thread principale (i creati creati a run-time possono usare stack di 
dimensione diversa).
Normalmente, quello che porta via molto spazio sullo stack sono gli 
array dichiarati come variabili locali. Trasformali in array statici o 
in array allocati esplicitamente (in C con "malloc", in C++ con "new").
-- 
     Carlo Milanesi
 
http://digilander.libero.it/carlmila

 in array allocati esplicitamente (in C con "malloc", in C++ con "new").
ma mi sembra strano che se compili un programma il compilatore non sia 
in grado di determinare la dimensione dello stack.

 in array allocati esplicitamente (in C con "malloc", in C++ con "new").
Perchè? Non sono d'accordo e al tuo posto non lo farei...
In generale la memoria dinamica va usata quando non è possibile conoscere a
tempo di compilazione la quantità di memoria da riservare perchè magari non
si sa quanti oggetti saranno istanziati e la dimensione degli stessi.
Ovviamente questo capita in tutte le applicazioni non banali e quindi ben
vengano i "malloc()" e le "new". C'è però da dire che la gestione dello heap
è meno efficiente della gestione dello stack e delle variabili globali e
statiche.
Le operazioni per riservare memoria aggiuntiva, i retrieving degli oggetti e
le manipolazioni degli stessi sullo heap sono operazioni complesse e
relativamente lente. Inoltre un uso/abuso eccessivo e poco attento della
memoria dinamica è potenziale fonte di "memory leaks" e "dangling pointers".
Handle with care... :-)
Buon lavoro.
fabio de francesco
(fabiomdf)
