Zdá se, že limit paměti zásobníku není přidělen (v každém případě by to nešlo s neomezeným zásobníkem). https://www.kernel.org/doc/Documentation/vm/overcommit-accounting říká:
Růst zásobníku jazyka C provádí implicitní mremap. Pokud chcete absolutní záruky a běžet blízko okraje, MUSÍTE namapovat svůj stack na největší velikost, kterou si myslíte, že budete potřebovat. Pro typické použití zásobníku na tom moc nezáleží, ale pokud vám na tom opravdu záleží
, jedná se o rohový případ
Mmapování zásobníku by však bylo cílem kompilátoru (pokud pro to má možnost).
EDIT:Po několika testech na počítači x84_64 Debian jsem zjistil, že zásobník roste bez jakéhokoli systémového volání (podle strace
). To tedy znamená, že jádro roste automaticky (to je to, co znamená „implicitní“ výše), tj. bez explicitního mmap
/mremap
z procesu.
Bylo docela těžké najít podrobné informace, které by to potvrzovaly. Doporučuji Understanding The Linux Virtual Memory Manager od Mela Gormana. Předpokládám, že odpověď je v části 4.6.1 Řešení chyby stránky s výjimkou „Oblast není platná, ale nachází se vedle rozšiřitelné oblasti, jako je zásobník“ a odpovídající akce „Rozbalit oblast a přidělit stránku“. Viz také D.5.2 Rozbalení zásobníku .
Další odkazy na správu paměti Linuxu (ale téměř nic o zásobníku):
- Nejčastější dotazy týkající se paměti
- Co by měl každý programátor vědět o paměti od Ulricha Dreppera
EDIT 2:Tato implementace má nevýhodu:v rohových případech nemusí být detekována kolize mezi hromádkou a hromádkou, a to ani v případě, že by stoh byl větší než limit! Důvodem je, že zápis do proměnné v zásobníku může skončit v alokované paměti haldy, v takovém případě nedochází k chybě stránky a jádro nemůže vědět, že je potřeba zásobník rozšířit. Viz můj příklad v diskuzi Tichá kolize stack-heap pod GNU/Linuxem, kterou jsem začal v seznamu gcc-help. Aby se tomu zabránilo, kompilátor potřebuje přidat nějaký kód při volání funkce; to lze provést pomocí -fstack-check
pro GCC (podrobnosti viz odpověď Iana Lance Taylora a manuálová stránka GCC).
Linuxové jádro 4.2
- mm/mmap.c#acct_stack_growth rozhodne, zda dojde k segfaultu nebo ne. Používá
rlim[RLIMIT_STACK]
což odpovídá POSIXgerlimit(RLIMIT_STACK)
- arch/x86/mm/fault.c#do_page_fault je obsluha přerušení, která spustí řetězec, který skončí voláním
acct_stack_growth
- arch/x86/entry/entry_64.S nastaví obsluhu chyb stránky. Musíte vědět něco o stránkování, abyste pochopili tuto část:Jak funguje stránkování x86? | Přetečení zásobníku
Minimální testovací program
Pak to můžeme otestovat pomocí minimálního 64bitového programu NASM:
global _start
_start:
sub rsp, 0x7FF000
mov [rsp], rax
mov rax, 60
mov rdi, 0
syscall
Ujistěte se, že jste vypnuli ASLR a odebrali proměnné prostředí, protože ty půjdou do zásobníku a zaberou místo:
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
env -i ./main.out
Limit je někde mírně pod mými ulimit -s
(8MiB pro mě). Vypadá to, že je to kvůli zvláštním datům specifikovaným systémem V, která byla původně vložena do zásobníku kromě prostředí:Parametry příkazového řádku Linuxu 64 v Assembly | Přetečení zásobníku
Pokud to myslíte vážně, TODO vytvořte minimální obraz initrd, který začne zapisovat od vrcholu zásobníku a jde dolů, a poté jej spusťte pomocí QEMU + GDB. Zadejte dprintf
na smyčce tiskne adresu zásobníku a bod přerušení na acct_stack_growth
. Bude to skvělé.
Související:
- https://softwareengineering.stackexchange.com/questions/207386/how-are-the-size-of-the-stack-and-heap-limited-by-the-os
- Odkud je alokována paměť zásobníku pro proces Linux? | Přetečení zásobníku
- Co je sada Linuxu? | Přetečení zásobníku
- Jaká je maximální hloubka rekurze v Pythonu a jak ji zvýšit? na Stack Overflow
Ve výchozím nastavení je maximální velikost zásobníku nakonfigurována na 8 MB na proces,
ale lze to změnit pomocí ulimit
:
Zobrazení výchozího nastavení v kB:
$ ulimit -s
8192
Nastaveno na neomezené:
ulimit -s unlimited
ovlivňující aktuální shell a podskořápky a jejich podřízené procesy.
(ulimit
je vestavěný příkaz shellu)
Skutečný rozsah adres zásobníku můžete zobrazit pomocí:
cat /proc/$PID/maps | grep -F '[stack]'
v systému Linux.