Pregunta

Hay ciertas condiciones que pueden causar desbordamientos de pila en un sistema Linux x86:

  • struct my_big_object [HUGE_NUMBER] en la pila. Caminar a través de él finalmente causa SIGSEGV .
  • La rutina alloca () (como malloc () , pero usa la pila, se libera automáticamente y también explota con SIGSEGV si es muy grande) Actualización: alloca () no está formalmente en desuso como dije originalmente; simplemente se desaconseja .

¿Hay alguna manera de detectar mediante programación si la pila local es lo suficientemente grande para un objeto dado? Sé que el tamaño de la pila es ajustable a través de ulimit , así que espero que haya una manera (por muy poco portátil que sea). Idealmente, me gustaría poder hacer algo como esto:

int min_stack_space_available = /* ??? */;
if (object_size < min_stack_space_available)
{
    char *foo = alloca(object_size);
    do_stuff(foo);
}
else
{
    char *foo = malloc(object_size);
    do_stuff(foo);
    free(foo);
}
¿Fue útil?

Solución

Puede determinar el espacio de pila que el proceso tiene disponible al encontrar el tamaño del espacio de pila de un proceso y luego restar la cantidad utilizada.

ulimit -s

muestra el tamaño de la pila en un sistema Linux. Para un enfoque programático, consulte getrlimit () . Luego, para determinar la profundidad de la pila actual, reste un puntero a la parte superior de la pila de uno a la parte inferior. Por ejemplo (código no probado):

unsigned char *bottom_of_stack_ptr;

void call_function(int argc, char *argv) {
    unsigned char top_of_stack;
    unsigned int depth = (&top_of_stack > bottom_of_stack_ptr) ? 
        &top_of_stack-bottom_of_stack_ptr : 
        bottom_of_stack_ptr-&top_of_stack;

    if( depth+100 < PROGRAMMATICALLY_DETERMINED_STACK_SIZE ) {
        ...
    }
}

int main(int argc, char *argv) {
    unsigned char bottom_of_stack;
    bottom_of_stack_ptr = &bottom_of_stack;
    my_function();
    return 0;
}

Otros consejos

  

La rutina alloca () en desuso (como malloc (), pero usa la pila,   se libera automáticamente y también explota con SIGSEGV si es demasiado grande).

¿Por qué está desaprobada alloca?

De todos modos, ¿cuánto más rápido en su caso es alloca vs malloc? (¿Vale la pena?)

¿Y no vuelves nulo de alloca si no queda suficiente espacio? (¿De la misma manera que malloc?)

Y cuando su código falla, ¿dónde se bloquea? ¿Está en alloca o está en doStuff ()?

/ Johan

No estoy seguro de si esto se aplica en Linux, pero en Windows es posible encontrarse con violaciones de acceso con grandes asignaciones de pila ¡incluso si tienen éxito!

Esto se debe a que, de manera predeterminada, el VMM de Windows solo marca las pocas páginas principales (no estoy seguro de cuántas exactamente) de 4096 bytes de RAM de la pila como paginable (es decir, respaldado por el archivo de paginación), ya que cree que los accesos a la pila generalmente marchar hacia abajo desde la cima; A medida que los accesos se acercan más y más al límite actual, las páginas inferior y inferior se marcan como paginables. ¡Pero esto significa que una lectura / escritura de memoria temprana muy por debajo de la parte superior de la pila activará una infracción de acceso ya que esa memoria aún no está asignada!

alloca () va a devolver NULL en caso de falla, creo que el comportamiento de alloca (0) es indefinido y variante de plataforma. Si verifica eso antes de do_something (), nunca debería recibir un SEGV.

Tengo un par de preguntas:

  1. ¿Por qué, oh por qué, necesitas algo tan grande en la pila? El tamaño predeterminado en la mayoría de los sistemas es de 8M, ¿todavía es demasiado pequeño?
  2. Si la función que llama bloques alloca (), protegería la misma cantidad de almacenamiento dinámico a través de mlock () / mlockall () garantizaría cerca del mismo rendimiento de acceso (es decir, "¡No me intercambies, hermano!") ¿hora? Si está utilizando un programador 'rt' más agresivo, se recomienda llamarlos de todos modos.

La pregunta es interesante pero levanta una ceja. Levanta la aguja en mi metro cuadrado de clavija redonda.

No dice mucho sobre por qué desea asignar en la pila, pero si es el modelo de memoria de pila lo que es atractivo, también podría implementar la asignación de pila en la pila. Asigne una gran cantidad de memoria al comienzo del programa y mantenga una pila de punteros para esto que correspondería a los marcos en la pila normal. Solo necesita recordar hacer estallar su puntero de pila privada cuando regrese la función.

Varios compiladores, por ejemplo Abre Watcom C / C ++ , admite la función stackavail () que te permite hacer exactamente eso

Puede usar GNU libsigsegv para manejar un error de página, incluidos los casos en que se produce un desbordamiento de pila (desde su sitio web):

  

En algunas aplicaciones, el controlador de desbordamiento de pila realiza una limpieza o notifica al usuario y luego finaliza inmediatamente la aplicación. En otras aplicaciones, el controlador de desbordamiento de pila regresa a un punto central de la aplicación. Esta biblioteca admite ambos usos. En el segundo caso, el controlador debe asegurarse de restaurar la máscara de señal normal (porque muchas señales se bloquean mientras se ejecuta el controlador), y también debe llamar a sigsegv_leave_handler () para transferir el control; entonces solo puede salirse de largo.

La función alloca está no en desuso. Sin embargo, no está en POSIX y también depende de la máquina y el compilador. La página de manual de Linux para alloca señala que `` para ciertas aplicaciones, su uso puede mejorar la eficiencia en comparación con el uso de malloc, y en ciertos casos también puede simplificar la desasignación de memoria en aplicaciones que usan longjmp () o siglongjmp (). De lo contrario, se desaconseja su uso. & Quot;

La página de manual también dice que " no hay indicación de error si el marco de la pila no se puede extender. Sin embargo, después de una asignación fallida, es probable que el programa reciba un SIGSEGV. & Quot;

El rendimiento de malloc se mencionó realmente en el Stackoverflow Podcast # 36 .

(Sé que esta no es una respuesta adecuada a su pregunta, pero pensé que podría ser útil de todos modos.)

Incluso si esta no es una respuesta directa a su pregunta, espero que sepa de la existencia de valgrind : una herramienta maravillosa para detectar tales problemas en tiempo de ejecución, en Linux.

Con respecto al problema de la pila, puede intentar asignar objetos dinámicamente desde un grupo fijo que detecta estos desbordamientos. Con una simple macro-hechicería puede hacer que esto se ejecute en el momento de la depuración, con código real ejecutándose en el momento del lanzamiento, y así saber (al menos para los escenarios que está ejecutando) que no está tomando demasiado. Aquí hay más información y un enlace a una implementación de muestra.

No hay una buena manera de pensar. ¿Tal vez es posible usando getrlimit () (sugerido antes) y algo de aritmética de puntero? Pero primero pregúntate si realmente quieres esto.

void *closeToBase;

main () {
  int closeToBase;
  stackTop = &closeToBase;
}

int stackHasRoomFor(int bytes) {
  int currentTop;
  return getrlimit(...) - (¤tTop  - closeToBase) > bytes + SomeExtra;
}

Personalmente, no haría esto. Asigne cosas grandes en el montón, la pila no fue diseñada para eso.

El sistema operativo determina dinámicamente el final del área de la pila. Aunque puede encontrar el " estático " límites de la pila mirando las áreas de memoria virtual (VMA) de una manera altamente dependiente del sistema operativo (vea los archivos stackvma * en libsigsegv / src / ), también deberá considerar

Pido disculpas si esto indica lo obvio, pero podría escribir fácilmente una función para probar un tamaño de asignación de pila específico simplemente probando la asignación (de ese tamaño) y detectando una excepción de desbordamiento de pila. Si lo desea, puede ponerlo en una función, con algunas matemáticas predeterminadas para la sobrecarga de la pila de funciones. Por ejemplo:

bool CanFitOnStack( size_t num_bytes )
{
    int stack_offset_for_function = 4; // <- Determine this
    try
    {
        alloca( num_bytes - stack_offset_for_function );
    }
    catch ( ... )
    {
        return false;
    }

    return true;
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top