GNU/Linux >> Znalost Linux >  >> Linux

Jak funguje alokace zásobníku v Linuxu?

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á POSIX gerlimit(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.


Linux
  1. Co je NGINX? Jak to funguje?

  2. Jak Awk ‚!a[$0]++‘ funguje?

  3. Jak Sticky Bit funguje?

  1. Linux – Jak funguje průměrná zátěž u moderních procesorů?

  2. Jak funguje příkaz 'ls' v Linuxu/Unixu?

  3. Je alokace paměti v linuxu neblokující?

  1. Jak interně funguje copy_from_user z jádra Linuxu?

  2. ZFS pod Linuxem, funguje to?

  3. Jak funguje debugger v Linuxu?