Sembra che il limite di memoria dello stack non sia allocato (comunque, non potrebbe con uno stack illimitato). https://www.kernel.org/doc/Documentation/vm/overcommit-accounting dice:
La crescita dello stack del linguaggio C esegue una mremap implicita. Se vuoi garanzie assolute e corri vicino al limite, DEVI mappare il tuo stack per la dimensione più grande di cui pensi di aver bisogno. Per l'utilizzo tipico dello stack questo non ha molta importanza, ma è un caso limite se ti interessa veramente
Tuttavia l'mmapping dello stack sarebbe l'obiettivo di un compilatore (se ha un'opzione per questo).
EDIT:Dopo alcuni test su una macchina Debian x84_64, ho scoperto che lo stack cresce senza alcuna chiamata di sistema (secondo strace
). Quindi, questo significa che il kernel lo fa crescere automaticamente (questo è ciò che significa "implicito" sopra), cioè senza mmap
esplicito /mremap
dal processo.
È stato piuttosto difficile trovare informazioni dettagliate che lo confermassero. Consiglio Understanding The Linux Virtual Memory Manager di Mel Gorman. Suppongo che la risposta sia nella Sezione 4.6.1 Gestione di un errore di pagina , con l'eccezione "Area non valida ma accanto a un'area espandibile come lo stack" e l'azione corrispondente "Espandi l'area e alloca una pagina". Vedi anche D.5.2 Espandere lo Stack .
Altri riferimenti sulla gestione della memoria di Linux (ma con quasi nulla sullo stack):
- Domande frequenti sulla memoria
- Quello che ogni programmatore dovrebbe sapere sulla memoria di Ulrich Drepper
EDIT 2:questa implementazione ha uno svantaggio:nei casi limite, una collisione stack-heap potrebbe non essere rilevata, anche nel caso in cui lo stack fosse maggiore del limite! Il motivo è che una scrittura in una variabile nello stack può finire nella memoria heap allocata, nel qual caso non si verifica alcun errore di pagina e il kernel non può sapere che lo stack deve essere esteso. Vedi il mio esempio nella discussione Collisione silenziosa stack-heap sotto GNU/Linux che ho iniziato nell'elenco gcc-help. Per evitarlo, il compilatore deve aggiungere del codice alla chiamata della funzione; questo può essere fatto con -fstack-check
per GCC (vedere la risposta di Ian Lance Taylor e la pagina man di GCC per i dettagli).
Kernel Linux 4.2
- mm/mmap.c#acct_stack_growth decide se eseguire il segfault o meno. Usa
rlim[RLIMIT_STACK]
che corrisponde al POSIXgerlimit(RLIMIT_STACK)
- arch/x86/mm/fault.c#do_page_fault è il gestore di interrupt che avvia una catena che finisce per chiamare
acct_stack_growth
- arch/x86/entry/entry_64.S imposta il gestore degli errori di pagina. Devi conoscere un po' il paging per capire quella parte:come funziona il paging x86? | Overflow dello stack
Programma di test minimo
Possiamo quindi testarlo con un programma minimo NASM a 64 bit:
global _start
_start:
sub rsp, 0x7FF000
mov [rsp], rax
mov rax, 60
mov rdi, 0
syscall
Assicurati di disattivare ASLR e di rimuovere le variabili di ambiente poiché andranno in pila e occuperanno spazio:
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
env -i ./main.out
Il limite è da qualche parte leggermente al di sotto del mio ulimit -s
(8MiB per me). Sembra che ciò sia dovuto a dati extra specificati da System V inizialmente messi nello stack oltre all'ambiente:Parametri della riga di comando di Linux 64 in Assembly | Overflow dello stack
Se sei serio su questo, TODO crea un'immagine initrd minima che inizia a scrivere dall'alto dello stack e scende, quindi eseguila con QEMU + GDB. Metti un dprintf
sul ciclo stampando l'indirizzo dello stack e un punto di interruzione in acct_stack_growth
. Sarà glorioso.
Correlati:
- https://softwareengineering.stackexchange.com/questions/207386/how-are-the-size-of-the-stack-and-heap-limited-by-the-os
- Da dove viene allocata la memoria dello stack per un processo Linux? | Overflow dello stack
- Che cos'è lo stack Linux? | Overflow dello stack
- Qual è la massima profondità di ricorsione in Python e come aumentarla? su Stack Overflow
Per impostazione predefinita, la dimensione massima dello stack è configurata su 8 MB per processo,
ma può essere modificato usando ulimit
:
Mostra l'impostazione predefinita in kB:
$ ulimit -s
8192
Imposta su illimitato:
ulimit -s unlimited
influenzando la shell e le subshell correnti e i loro processi figli.
(ulimit
è un comando integrato della shell)
Puoi mostrare l'attuale intervallo di indirizzi dello stack in uso con:
cat /proc/$PID/maps | grep -F '[stack]'
su Linux.