Es scheint, dass das Stack-Speicherlimit nicht zugewiesen ist (jedenfalls konnte es nicht mit unbegrenztem Stack). https://www.kernel.org/doc/Documentation/vm/overcommit-accounting sagt:
Das Wachstum des C-Sprachstapels führt eine implizite mremap durch. Wenn Sie absolute Garantien wollen und nahe am Rand laufen, MÜSSEN Sie Ihren Stapel auf die größte Größe abbilden, die Sie Ihrer Meinung nach benötigen. Für die typische Stack-Nutzung spielt dies keine große Rolle, aber es ist ein Grenzfall, wenn Sie sich wirklich wirklich darum kümmern
Das Mapping des Stacks wäre jedoch das Ziel eines Compilers (falls er eine Option dafür hat).
BEARBEITEN:Nach einigen Tests auf einem x84_64-Debian-Rechner habe ich festgestellt, dass der Stack ohne Systemaufruf wächst (gemäß strace
). Das bedeutet also, dass der Kernel es automatisch vergrößert (das bedeutet „implizit“ oben), d. h. ohne explizites mmap
/mremap
aus dem Prozess.
Es war ziemlich schwierig, detaillierte Informationen zu finden, die dies bestätigen. Ich empfehle Understanding The Linux Virtual Memory Manager von Mel Gorman. Ich nehme an, dass die Antwort in Abschnitt 4.6.1 Handhabung eines Seitenfehlers steht , mit der Ausnahme "Region nicht gültig, aber neben einer erweiterbaren Region wie dem Stack" und der entsprechenden Aktion "Region erweitern und Seite zuweisen". Siehe auch D.5.2 Stapel erweitern .
Andere Referenzen zur Linux-Speicherverwaltung (aber fast nichts über den Stack):
- Speicher-FAQ
- Was jeder Programmierer über Speicher wissen sollte von Ulrich Drepper
EDIT 2:Diese Implementierung hat einen Nachteil:In Eckfällen wird eine Stack-Heap-Kollision möglicherweise nicht erkannt, selbst wenn der Stack größer als das Limit wäre! Der Grund dafür ist, dass ein Schreibvorgang in eine Variable im Stack im zugewiesenen Heap-Speicher landen kann, in diesem Fall gibt es keinen Seitenfehler und der Kernel kann nicht wissen, dass der Stack erweitert werden musste. Siehe mein Beispiel in der Diskussion Silent Stack-Heap Collision unter GNU/Linux, die ich in der gcc-Hilfeliste gestartet habe. Um dies zu vermeiden, muss der Compiler beim Funktionsaufruf Code hinzufügen; Dies kann mit -fstack-check
erfolgen für GCC (Einzelheiten finden Sie in der Antwort von Ian Lance Taylor und auf der GCC-Manpage).
Linux-Kernel 4.2
- mm/mmap.c#acct_stack_growth entscheidet, ob es einen Segfault gibt oder nicht. Es verwendet
rlim[RLIMIT_STACK]
was dem POSIXgerlimit(RLIMIT_STACK)
entspricht - arch/x86/mm/fault.c#do_page_fault ist der Interrupt-Handler, der eine Kette startet, die schließlich
acct_stack_growth
aufruft - arch/x86/entry/entry_64.S richtet den Seitenfehler-Handler ein. Sie müssen etwas über Paging wissen, um diesen Teil zu verstehen:Wie funktioniert x86-Paging? | Stapelüberlauf
Minimales Testprogramm
Wir können es dann mit einem minimalen NASM-64-Bit-Programm testen:
global _start
_start:
sub rsp, 0x7FF000
mov [rsp], rax
mov rax, 60
mov rdi, 0
syscall
Stellen Sie sicher, dass Sie ASLR deaktivieren und Umgebungsvariablen entfernen, da diese auf den Stapel gehen und Platz beanspruchen:
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
env -i ./main.out
Das Limit liegt etwas unter meinem ulimit -s
(8 MiB für mich). Es sieht so aus, als ob dies an zusätzlichen System V-spezifischen Daten liegt, die ursprünglich zusätzlich zur Umgebung auf den Stapel gelegt wurden:Linux 64-Befehlszeilenparameter in Assembly | Stapelüberlauf
Wenn Sie es ernst meinen, erstellen Sie TODO ein minimales initrd-Image, das vom Stack-Top aus zu schreiben beginnt und herunterfährt, und führen Sie es dann mit QEMU + GDB aus. Geben Sie dprintf
ein in der Schleife, die die Stapeladresse und einen Haltepunkt bei acct_stack_growth
ausgibt . Es wird herrlich sein.
Verwandte:
- https://softwareengineering.stackexchange.com/questions/207386/how-are-the-size-of-the-stack-and-heap-limited-by-the-os
- Woher wird der Stapelspeicher für einen Linux-Prozess zugewiesen? | Stapelüberlauf
- Was ist der Linux-Stack? | Stapelüberlauf
- Was ist die maximale Rekursionstiefe in Python und wie kann sie erhöht werden? auf Stapelüberlauf
Standardmäßig ist die maximale Stapelgröße auf 8 MB pro Prozess konfiguriert.
kann aber mit ulimit
geändert werden :
Anzeige der Vorgabe in kB:
$ ulimit -s
8192
Auf unbegrenzt setzen:
ulimit -s unlimited
Auswirkungen auf die aktuelle Shell und Subshells und ihre untergeordneten Prozesse.
(ulimit
ist ein in die Shell eingebauter Befehl)
Sie können den tatsächlich verwendeten Stack-Adressbereich anzeigen mit:
cat /proc/$PID/maps | grep -F '[stack]'
unter Linux.