Los rincones del API Win32: La pila - Implementación interna

8 - Implementación interna

[editar]
Tutorial creado por Juan Manuel. Extraido de: http://www.lawebdejm.com
30 de Noviembre de 1999
Antes de comenzar este punto, , hay que decir que este aspecto varía mucho dependiendo del tipo de sistema Win32, ya sea un sistema NT (Windows NT, 2000 y XP) ó 95 (Windows 95, 98 y Me). La explicación que vamos a dar aquí se refiere a la implementación en los sistemas NT/2000/XP, ya que son los más evolucionados y su uso tiene a generalizarse.

La pila, como cualquier estructura que reside en memoria, ocupará páginas (de 4KB cada una en procesadores x86). Así que una pila de 1MB ocupará 256 páginas de memoria.

Al crearse la pila, se compromete el espacio indicado durante el enlazado, que como ya sabemos es por defecto de 16 KB, es decir 4 páginas. La última página que se comprometa tendrá una característica especial: se marcará con la bandera de protección PAGE_GUARD.

Si recordamos la descripción de la función VirtualAlloc, veíamos que su cuarto parámetro hacía referencia a la protección que se le daba a las páginas que estábamos reservando y/o comprometiendo. Ya explicamos que podíamos asignar banderas de PAGE_READWRITE, PAGE_NOACCESS y otras, pero no explicamos la existencia de la bandera PAGE_GUARD.

Esta bandera puede combinarse con otras durante la llamada a VirtualAlloc, y hace que cuando dicha página sea accedida, se lance la excepción de sistema STATUS_GUARD_PAGE y no se permita el acceso a dicha página. Hasta aquí su comportamiento es similar a una página marcada con PAGE_NOACCESS, excepto que una vez que se ha lanzado esta excepción, el sistema elimina automáticamente la marca de PAGE_GUARD, y los siguientes accesos se harán correctamente.

Por ejemplo, el siguiente código reserva y compromete una página y la marca como PAGE_GUARD. De los accesos siguientes tan sólo tendría éxito el segundo:

// ATENCIÓN: este código sólo funciona en sistemas Win NT/2000/XP, ya que los 
 // sistemas 95/98/Me no soportan la bandera PAGE_GUARD en la llamada a VirtualAlloc
 {
     LPSTR    p;
 
     p = ::VirtualAlloc( NULL,
                         4096, 
                         MEM_RESERVE | MEM_COMMIT, 
                         PAGE_READWRITE | PAGE_GUARD );
 
     try
     {
         strcpy( p, "intento de grabar dato, pero no lo voy a conseguir" );
     }
     catch (...)
     {
         ::MessageBox( GetActiveWindow(), 
                       "Excepción de sistema: STATUS_GUARD_PAGE",
                       "Excepción capturada", MB_ICONSTOP ); 
     }
 
     strcpy( p, "este dato sí que voy a poder guardarlo" );
 
     ::MessageBox( ::GetActiveWindow(), p, "Dato guardado", MB_ICONINFORMATION );
 }
 

Bien, ahora ya sabemos cómo funcionan las páginas de guarda, y también sabemos que la última página que ha sido comprometida en la pila, estará marcada con la bandera PAGE_GUARD.

El aspecto de la pila de un hilo recién iniciado puede ser como el que aparece en la siguiente figura:

La pila completa está en estado reservado (páginas de color blanco), las 4 primeras páginas han sido comprometidas (en color gris) y la última página comprometida ha sido marcada como página de guarda (borde en rojo). Como ya hemos visto, el contenido de las páginas se marca con un interrogante porque contendrán datos basura, posiblemente restos de las últimas variables que han residido en esas posiciones de memoria.

Bien, conforme va evolucionando la ejecución de nuestro proceso, la pila se va llenando (cuanto más profundo sea el árbol de llamadas a funciones) o vaciando (según estas funciones van retornando). Puede ser que no sea necesario sobrepasar el almacenamiento de las 3 primeras páginas (12 KB), así que en este caso tan sólo habremos consumido 16 KB de memoria para almacenar la pila. Pero en otros muchos casos, llegará un momento en que algún dato sea necesario guardarlo en la cuarta página. En ese momento, al ser una página de guarda, se producirá una excepción de tipo STATUS_GUARD_PAGE. El sistema es capaz de gestionar sus propias excepciones, así que la capturará y realizará las siguientes operaciones:

  1. Comprometer una página más de memoria (en nuestro caso, la 5ª)

  2. Marcar con PAGE_GUARD las nueva página comprometida.

  3. Volver a guardar el dato que no ha podido ser guardado por la excepción (recordad que la bandera PAGE_GUARD desaparece después del primer acceso).

El nuevo aspecto de la pila puede observarse en el siguiente esquema:

Como hemos visto, este mecanismo permite que la pila vaya creciendo conforme necesita más espacio. Si el árbol de llamadas diminuye, las páginas restantes de la pila no se liberan, sino que permanecen comprometidas para acelerar su acceso en un futuro.

Ahora supongamos que el árbol de llamadas crece hasta que es necesario acceder a la página 8 (la penúltima). Al ser una página de guarda, se producirá una excepción y el sistema la capturará del mismo modo y realizará las mismas operaciones, excepto que si la nueva página comprometida (la 9ª) es la última, no se marcará como página de guarda. El estado en que quedaría la pila sería el siguiente:

Si siguen produciéndose llamadas anidadas (y/o recursivas) y la pila intenta acceder a la 9ª página (que está reservada, pero no comprometida), se producirá una violación de acceso, y para evitar males mayores el sistema elimina automáticamente el proceso padre del hilo.

Como hemos visto, el sistema es capaz, tanto de hacer que la pila crezca automáticamente, como de asegurarse de que no crece indefinidamente. Pero para realizar esta última tarea, el tamaño útil de la pila se ve reducido en una página, ya que la página superior nunca será comprometida.

Esto se hace así para evitar sobrescribir páginas de memoria que estén por encima de la pila. Si suponemos que la última página se compromete normalmente, el sistema podría continuar almacenando datos en la pila hasta su límite superior, y si al intentar acceder a la siguiente página (la que esté por encima de la 9ª, que ya no pertenece a la pila). Si además se da la casualidad de que esa página ha sido comprometida (porque en ese punto reside otra estructura de datos), no se produciría ninguna excepción y todo parecería funcionar correctamente, por lo que el error sería difícilmente detectable.

[editar]

Sé el primero en opinar


Tutoriales relacionados con 'Los rincones del API Win32: La pila'

En esta ocasión profundizamos en la implementación que hace Win32 de la pila. Todos la... Más »
Curso que profundiza en el gestor de montones (o montículos) dentro de Win32, así como... Más »
El protocolo FTP desde el API WinInet. Con este curso aprenderás a hacer un pequeño... Más »
Este curso trata el tema de la memoria en la plataforma Win32. En esta ocasión... Más »

Autor y licencia de 'Los rincones del API Win32: La pila'


Tutorial de Juan Manuel. Extraido de: http://www.lawebdejm.com CopyLeft
Este contenido ha sido recopilado por el equipo de Wikilearning. Todo el contenido recopilado se ha obtenido respetando y comunicando en nuestro site la licencia de cada fuente.
Wikilearning tiene permiso expreso por escrito de los autores para publicar los contenidos que ha extraído de otras webs, incluyendo su uso comercial.