¿Cómo cambiar el tamaño de la pila usando ulimit o por proceso en Mac OS X para un programa C o Ruby?

10 minutos de lectura

avatar de usuario
no polaridad

Parece que la forma recomendada de establecer el tamaño de la pila para un programa C o un programa Ruby (que usa la pila C), es usando ulimit en el caparazón Bash. Pero

$ ulimit -s
8192

$ ulimit -s 16384
-bash: ulimit: stack size: cannot modify limit: Operation not permitted

y sudo tampoco ayuda ¿Hay alguna forma de configurarlo en 16 MB, 32 MB o 64 MB? Pensé que debería haber una manera de configurarlo por invocación de programa en lugar de configurar también un parámetro de todo el sistema.

Ahora mismo 8192 probablemente signifique 8 MB, que es bastante pequeño, si se compara con la cantidad que puede usar un proceso, a veces hasta 2 GB de RAM.

(nota actualizada: ulimit -a puede mostrar sus valores actuales).

(actualización 2: en realidad parece que ulimit -s <value> es por shell, y si lo configura la primera vez, generalmente funciona. El problema es cuando lo configura por segunda vez, entonces puede devolver un error)

  • Me pregunto si este error está relacionado con el “límite duro” frente a “límite suave” con ulimit.

    – pje

    6 de noviembre de 2012 a las 6:44

  • El tamaño de pila ulimit solo se puede reducir una vez establecido, he intentado responder a todo, avíseme si tiene alguna otra pregunta.

    – Samy Vilar

    7 de noviembre de 2012 a las 20:51


avatar de usuario
Samy Vilar

Aparentemente, hay un límite estricto en el tamaño de la pila para mac os x, tomado de http://lists.apple.com/archives/scitech/2004/Oct/msg00124.html De acuerdo, esto es bastante antiguo, y no estoy seguro de si todavía es cierto, pero para configurarlo simplemente llame a ulimit -s hard, es 65532. o alrededor de 65 megas.

Hice algunas pruebas en Snow Leopard, 10.6.8, y parece ser cierto.

$ ulimit -a
...
stack size              (kbytes, -s) 8192
...
$ ulimit -s 65533
-bash: ulimit: stack size: cannot modify limit: Operation not permitted
$ ulimit -s 65532
$

tambien encontre esto http://linuxtoosx.blogspot.com/2010/10/stack-overflow-increasing-stack-limit.html aunque no lo he probado, así que no puedo decir mucho al respecto.

Cuando las aplicaciones consumen gigas de memoria que generalmente se toman del montón, la pila generalmente se reserva para las variables automáticas locales que existen durante una cantidad de tiempo relativamente pequeña equivalente a la vida útil de la llamada a la función, el montón es donde vive la mayoría de los datos persistentes. .

aquí hay un tutorial rápido:

#include <stdlib.h>

#define NUMBER_OF_BYTES 10000000 // about 10 megs
void test()
{
   char stack_data[NUMBER_OF_BYTES];          // allocating on the stack.
   char *heap_data = malloc(NUMBER_OF_BYTES); // pointer (heap_data) lives on the stack, the actual data lives on the heap.
}

int main()
{   
    test(); 
    // at this point stack_data[NUMBER_OF_BYTES] and *heap_data have being removed, but malloc(NUMBER_OF_BYTES) persists.
    // depending on the calling convention either main or test are responssible for resetting the stack.
    // on most compilers including gcc, the caller (main) is responssible.

    return 0;
}

$ ulimit -a
...
stack size              (kbytes, -s) 8192
...
$ gcc m.c
$ ./a.out
Segmentation fault
$ ulimit -s hard
$ ./a.out
$

ulimit es solo temporal, tendría que actualizarlo cada vez, o actualizar su script bash correspondiente para configurarlo automáticamente.

Una vez que se establece ulimit, solo se puede bajar nunca subir.

  • Sí, si las aplicaciones consumen gigas de memoria, deberían ganar espacio del montón, no de la pila. No es razonable asignar objetos grandes o arreglos grandes en la pila. Si la aplicación desea usar 2 GB de RAM como pila, ¿qué tamaño de espacio de memoria debe reservarse para el almacenamiento dinámico?

    – jclin

    7 de noviembre de 2012 a las 2:20

  • @jclin no estoy seguro de lo que quiere decir, fundamentalmente el sistema operativo está a cargo de la memoria, ya sea que lo llamemos pila o montón, como tal, su sistema operativo depende de lo que sucede, con algunos esquemas de memoria bastante complejos, en Linux tenemos memoria virtual asignada a una tabla de páginas que contiene páginas, algunas de las cuales pueden no ser válidas, como tal, el sistema operativo realmente no asigna 2 GB de pila a menos que realmente lo necesite, en su lugar obtendrá fallas de página que hacen que el sistema operativo asigne una nueva página, por supuesto si no hay más páginas libres, entonces puede detener su programa o fallar.

    – Samy Vilar

    7 de noviembre de 2012 a las 3:47

  • Entiendo tu argumento. El sistema operativo realmente no asigna 2 GB si solo especifica el tamaño, pero la aplicación no usa hasta 2 GB. El sistema operativo administra la memoria en páginas y asigna páginas reales según las demandas. Si el programa falla debido a insuficiencia. tamaño de pila, definitivamente significa que la aplicación requiere más tamaño de pila. Por lo tanto, si una aplicación debe ejecutarse lo más bien posible, como 2 GB, creo que una gran pila no tiene sentido, no le gusta el montón para que un proceso pueda usar tanto como 2 GB de RAM. Es por eso que muchas computadoras de escritorio o servidores tienen 4GB, 8GB o más espacio de memoria, pero cada proceso todavía tiene una pila de 4MB/8MB por defecto.

    – jclin

    7 de noviembre de 2012 a las 7:07

  • Rara vez el sistema operativo le dirá algo interesante además de la falla de segmentación obicua, cuando haya excedido su pila o los recursos de memoria, esto se debe a que ni la pila ni el montón están realmente en la memoria física contigua, incluso si para el programa puede parecer la pila. es continuo en realidad, está por todas partes, en cuanto a la pequeña pila predeterminada, hay dos razones para eso 1) en promedio, la mayoría de los programas no usan tanto espacio de pila 2) protege contra bucles infinitos, si el tamaño predeterminado de la pila era ilimitado un solo bucle infinito en cualquier programa consumiría toda la memoria.

    – Samy Vilar

    7 de noviembre de 2012 a las 16:41

En mi opinión, la respuesta aceptada no es totalmente correcta y conduce a una falta de comprensión, más específicamente, la última afirmación no es cierta.

Una vez que se establece ulimit, solo se puede bajar nunca subir.

De hecho, hay suaves (visualizables con ulimit -s o ulimit -Ss) y duro (visualizable con ulimit -Hs) límites. Pero al establecer el límite a través de ulimit -s afectará suave y valores duros.

Una vez duro se establece el límite, solo se puede bajar, nunca subir, pero el límite blando se puede bajar o subir siempre que el valor se mantenga por debajo del límite duro.

Esto funcionará:

# base values
$ ulimit -s
100
$ ulimit -Hs
100
$ ulimit -Ss
100
# lower soft limit only
$ ulimit -Ss 50
$ ulimit -s
50
$ ulimit -Hs
100
$ ulimit -Ss
50
# raise soft limit only
$ ulimit -Ss 100
$ ulimit -s
100
$ ulimit -Hs
100
$ ulimit -Ss
100
# lower soft and hard limit
$ ulimit -s 50
$ ulimit -s
50
$ ulimit -Hs
50
$ ulimit -Ss
50
# then impossible to raise soft limit due to hard limit
$ ulimit -s 100
-bash: ulimit: stack size: cannot modify limit: Operation not permitted
$ ulimit -Ss 100
-bash: ulimit: stack size: cannot modify limit: Invalid argument

  • en bash no puede aumentar el límite estricto como dice, pero en zsh puede aumentarlo, pero no mayor que el límite estricto original, por ejemplo, suponga que su límite estricto es X, puede disminuirlo a Y, ejecutar algo (por ejemplo, segunda copia de zsh) y luego vuelva a aumentarlo a X. Pero la segunda copia no podrá exceder Y

    – RiaD

    30 oct 2019 a las 18:30

  • Además, algunas aplicaciones/servicios se envían de manera que no son capaces de cambiar el límite suave a un valor más alto, incluso si el acceso no lo bloquea. Es más seguro pensar que el límite suave puede ser el límite real para su proceso. La pila se aplica por proceso, el único que se aplica al usuario/sesión es nproc de la lista de parámetros ulimit.

    – laimison

    2 de diciembre de 2019 a las 15:05

El tamaño de pila predeterminado del sistema varía de una versión diferente de kernel a kernel. Mi 10.7 es 16384, de modo que mi Mac acepta ulimit -s 16384. Puedes probar sysctl kern.stack_size y muestra el tamaño de la pila de solo lectura. el mio es 16384
Puedes ver este artículo técnico, http://developer.apple.com/library/mac/#qa/qa1419/_index.html, para ver cómo cambiar el tamaño de pila predeterminado para el programa C. Para Ruby, debido a que es un lenguaje de secuencias de comandos, debe aumentar el tamaño de su pila durante la vinculación del intérprete de Ruby. Excepto por tener llamadas a funciones muy profundas o recursividad, o tener una matriz y objetos muy grandes que se asignan en la pila, su programa no debería tener un gran espacio de pila. En cambio, el uso de la asignación dinámica o del montón puede usar hasta 2 GB de RAM como desee.

  • También me pregunto por qué debe hacerse durante el tiempo de enlace, no durante el tiempo de ejecución, y si Ruby realmente crea un nuevo hilo con un tamaño de pila para ejecutar el programa Ruby, entonces tal vez Ruby pueda establecer un tamaño de pila usando una línea de comando. ruby --stack-size 16384 foo.rb

    – no polaridad

    6 de noviembre de 2012 a las 6:09


  • Si. Mi sistema operativo acepta ulimit -s 32767 (Creo que el valor predeterminado para ulimit es ilimitado, pero el kernel del sistema operativo tiene el tamaño predeterminado). Pero una vez que establece el valor, no puede establecer un valor que sea mayor que el anterior. De lo contrario, aparece el mensaje de error “operación no permitida”.

    – jclin

    6 de noviembre de 2012 a las 6:37

  • El tamaño de pila predeterminado establecido en el tiempo de vinculación es razonable porque cuando el sistema operativo carga el ejecutable, el kernel debe tener todo preparado antes de saltar al programa. La opción de tiempo de vinculación marca el tamaño de la pila en el formato de archivo ejecutable de Mach-O, y OS/Kernel puede ver la opción para crear un tamaño de pila diferente para el entorno ejecutable. Ruby puede crear diferentes tamaños de pila para sus nuevos subprocesos, pero la primera y predeterminada pila para ejecutar ruby en sí mismo está determinado por el sistema operativo y la opción de tiempo de vinculación.

    – jclin

    6 de noviembre de 2012 a las 6:41

Descubrí que usando /bin/zsh en lugar de /bin/sh hizo que este error desapareciera.

Para mí, el error estaba ocurriendo en un script de shell que llamaba ulimit -s unlimited. Cuando el guión fue interpretado por /bin/sh (es decir, tenía #!/bin/sh como la primera línea del archivo de script), vomitó con este error. Por el contrario, al cambiarlo para usar zshtodo parecía funcionar bien. zsh fue lo suficientemente inteligente como para interpretar unlimited como “dame el límite más grande que me permita el sistema operativo”, y todo funcionó como usted quisiera.

Todos los límites que el construido ulimit los controles están realmente implementados en el kernel del sistema operativo y, como tal, debería ver la documentación de la interfaz C para todo el sistema. Aquí está la documentación de Apple para setrlimit(): https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/setrlimit.2.html

(Tenga en cuenta que la ruta de ese documento parece decir iPhoneOS pero el contenido todavía habla de “Mac OS X”. Si tiene la documentación adecuada instalada localmente, ejecute man setrlimit en su terminal debe emitir la documentación actualizada.)

Los procesos recién creados heredan los límites del fork() padre o el proceso anterior que se ejecuta exec().

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad