En la práctica, es difícil (ya veces imposible) hacer crecer la pila. Para comprender por qué se requiere cierta comprensión de la memoria virtual.
En Ye Olde Days de aplicaciones de un solo hilo y memoria contigua, tres eran tres componentes de un espacio de direcciones de proceso: el código, el montón y la pila. La forma en que se distribuyeron esos tres dependía del sistema operativo, pero generalmente el código era primero, comenzando en el fondo de la memoria, el montón venía y crecía hacia arriba, y la pila comenzaba en la parte superior de la memoria y crecía hacia abajo. También había algo de memoria reservada para el sistema operativo, pero podemos ignorarlo. Los programas en esos días tenían desbordamientos de pila algo más dramáticos: la pila se estrellaría en el montón, y dependiendo de lo que se actualizó primero, trabajaría con datos erróneos o regresaría de una subrutina a una parte arbitraria de la memoria.
La gestión de la memoria cambió este modelo de alguna manera: desde la perspectiva del programa, aún tenía los tres componentes de un mapa de memoria de proceso, y en general estaban organizados de la misma manera, pero ahora cada uno de los componentes se gestionaba como un segmento independiente y la MMU. señalaría al sistema operativo si el programa intentara acceder a la memoria fuera de un segmento. Una vez que tenía memoria virtual, no era necesario o desear para dar a un programa acceso a todo su espacio de direcciones. Así que a los segmentos se les asignaron límites fijos.
Entonces, ¿por qué no es deseable dar a un programa acceso a su espacio de direcciones completo? Porque esa memoria constituye un "cargo de comisión" contra el canje; en cualquier momento, cualquiera o toda la memoria de un programa puede tener que escribirse para intercambiar y dejar espacio para la memoria de otro programa. Si cada programa pudiera consumir 2GB de swap, entonces tendrías que proporcionar suficiente swap para todos tus programas o arriesgarte a que dos programas necesiten más de lo que podrían obtener.
En este punto, suponiendo que haya suficiente espacio de direcciones virtuales, podría extender estos segmentos si es necesario y, de hecho, el segmento de datos (montón) crece con el tiempo: comienza con un segmento de datos pequeño , y cuando el asignador de memoria solicita más espacio cuando es necesario. En este punto, con una sola pila, habría sido físicamente posible extender el segmento de la pila: el sistema operativo podría atrapar el intento de empujar algo fuera del segmento y agregar más memoria. Pero esto tampoco es particularmente deseable.
Introduzca multihilo. En este caso, cada hilo tiene un segmento de pila independiente, de nuevo de tamaño fijo. Pero ahora los segmentos se colocan uno tras otro en el espacio de direcciones virtuales, por lo que no hay forma de expandir un segmento sin mover otro, lo que no se puede hacer porque el programa potencialmente tendrá punteros a la memoria que se encuentra en la pila. Alternativamente, podría dejar algo de espacio entre los segmentos, pero ese espacio se desperdiciaría en casi todos los casos. Un mejor enfoque fue poner la carga en el desarrollador de la aplicación: si realmente necesitas pilas profundas, podrías especificar eso al crear el subproceso.
Hoy, con un espacio de direcciones virtuales de 64 bits, podríamos crear pilas infinitas para un número infinito de hilos. Pero, de nuevo, eso no es particularmente deseable: en casi todos los casos, un desbordamiento de pila indica un error en su código. Al proporcionarle una pila de 1 GB, simplemente difiere el descubrimiento de ese error.