Parece que el límite de memoria de la pila no está asignado (de todos modos, no podría con una pila ilimitada). https://www.kernel.org/doc/Documentation/vm/overcommit-accounting dice:
El crecimiento de la pila del lenguaje C hace un mremap implícito. Si desea garantías absolutas y correr cerca del borde, DEBE mapear su pila para obtener el tamaño más grande que cree que necesitará. Para el uso típico de la pila, esto no importa mucho, pero es un caso de esquina si realmente te importa
Sin embargo, mapear la pila sería el objetivo de un compilador (si tiene una opción para eso).
EDITAR:después de algunas pruebas en una máquina Debian x84_64, descubrí que la pila crece sin ninguna llamada al sistema (según strace
). Entonces, esto significa que el kernel lo hace crecer automáticamente (esto es lo que significa "implícito" arriba), es decir, sin mmap
explícito /mremap
del proceso.
Fue bastante difícil encontrar información detallada que confirmara esto. Recomiendo Comprender el administrador de memoria virtual de Linux por Mel Gorman. Supongo que la respuesta está en la Sección 4.6.1 Manejo de un error de página , con la excepción "La región no es válida pero está al lado de una región expandible como la pila" y la acción correspondiente "Expandir la región y asignar una página". Consulte también D.5.2 Expandir la pila .
Otras referencias sobre gestión de memoria Linux (pero sin casi nada sobre la pila):
- Preguntas frecuentes sobre la memoria
- Lo que todo programador debe saber sobre la memoria por Ulrich Drepper
EDICIÓN 2:esta implementación tiene un inconveniente:en los casos de esquina, es posible que no se detecte una colisión pila-montón, ¡incluso en el caso de que la pila sea más grande que el límite! La razón es que una escritura en una variable en la pila puede terminar en la memoria del montón asignada, en cuyo caso no hay falla de página y el núcleo no puede saber que la pila necesita ser extendida. Vea mi ejemplo en la discusión Silent stack-heap colision bajo GNU/Linux que comencé en la lista de ayuda de gcc. Para evitar eso, el compilador necesita agregar algo de código en la llamada a la función; esto se puede hacer con -fstack-check
para GCC (consulte la respuesta de Ian Lance Taylor y la página del manual de GCC para obtener más detalles).
Núcleo de Linux 4.2
- mm/mmap.c#acct_stack_growth decide si fallará o no. Utiliza
rlim[RLIMIT_STACK]
que corresponde al POSIXgerlimit(RLIMIT_STACK)
- arch/x86/mm/fault.c#do_page_fault es el controlador de interrupciones que inicia una cadena que termina llamando a
acct_stack_growth
- arch/x86/entry/entry_64.S configura el controlador de errores de página. Necesita saber un poco sobre la paginación para entender esa parte:¿Cómo funciona la paginación x86? | Desbordamiento de pila
Programa de prueba mínimo
Luego podemos probarlo con un programa mínimo de NASM de 64 bits:
global _start
_start:
sub rsp, 0x7FF000
mov [rsp], rax
mov rax, 60
mov rdi, 0
syscall
Asegúrese de desactivar ASLR y eliminar las variables de entorno, ya que se colocarán en la pila y ocuparán espacio:
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
env -i ./main.out
El límite está ligeramente por debajo de mi ulimit -s
(8MiB para mí). Parece que esto se debe a los datos adicionales especificados de System V que se colocaron inicialmente en la pila además del entorno:parámetros de línea de comando de Linux 64 en Ensamblaje | Desbordamiento de pila
Si te tomas en serio esto, TODO crea una imagen initrd mínima que comience a escribir desde la parte superior de la pila y se vaya hacia abajo, y luego ejecútala con QEMU + GDB. Pon un dprintf
en el bucle imprimiendo la dirección de la pila y un punto de interrupción en acct_stack_growth
. Será glorioso.
Relacionado:
- https://softwareengineering.stackexchange.com/questions/207386/how-are-the-size-of-the-stack-and-heap-limited-by-the-os
- ¿De dónde se asigna la memoria de pila para un proceso de Linux? | Desbordamiento de pila
- ¿Qué es la pila de Linux? | Desbordamiento de pila
- ¿Cuál es la profundidad de recursión máxima en Python y cómo aumentarla? en el desbordamiento de pila
De forma predeterminada, el tamaño de pila máximo está configurado para ser de 8 MB por proceso,
pero se puede cambiar usando ulimit
:
Mostrando el valor predeterminado en kB:
$ ulimit -s
8192
Establecer en ilimitado:
ulimit -s unlimited
afectando el shell actual y los subshells y sus procesos secundarios.
(ulimit
es un comando integrado de shell)
Puede mostrar el rango de direcciones de la pila real en uso con:
cat /proc/$PID/maps | grep -F '[stack]'
en Linux.