Uso del sistema operativo Windows
Windows impone esta restricción para garantizar que la estructura de objetos del kernel permanezca en un estado consistente. Lo siguiente trata sobre el uso del sistema operativo Windows. ¡Espero que lo leas atentamente!
1. Espacio de direcciones virtuales del proceso
En el sistema operativo Windows, cada proceso tiene su propio espacio de direcciones privado, por lo que los subprocesos de un proceso solo pueden acceder a la memoria que pertenece a este proceso. Espacio, es decir, los procesos tienen direcciones aisladas. En Windows 2000, el espacio de direcciones virtuales del proceso se puede dividir en las siguientes cuatro partes:
1) Área NULL (0x 00000000 ~ 0x 0000 ffff): si un subproceso en el proceso intenta manipular los datos en esta partición, la CPU provocará un acceso ilegal. Su función es que al llamar a funciones de asignación de memoria como malloc, si no se encuentra suficiente espacio de memoria, se devolverá NULL. No hay control de seguridad. Simplemente supone que la asignación de direcciones se realizó correctamente y comienza a acceder a la dirección de memoria 0x00000000 (nula). Debido a que el acceso a esta partición de memoria está prohibido, se producirá un acceso ilegal y el proceso finalizará.
2) Partición en modo usuario (0x00010000 ~ 0xffff): el espacio de direcciones privado del proceso se almacena en esta partición. Un proceso no puede de ninguna manera acceder a los datos que otro proceso reside en esa partición (mismo exe, el aislamiento de direcciones se realiza mediante copia en escritura). (En Windows, todas las bibliotecas .exe y de enlaces dinámicos se cargan en esta área. Al mismo tiempo, el sistema asignará todos los archivos asignados en memoria a los que el proceso puede acceder a esta partición).
2) Área de aislamiento (0xBFFF0000~0xBFFFFFFF): Está prohibido el ingreso a esta área. Cualquier intento de acceder a esta partición de memoria es ilegal. Microsoft conserva esta partición para simplificar la realidad del sistema operativo.
3) Área del kernel (0xC0000000~0xFFFFFFFF): Esta partición almacena el código residente del sistema operativo. En esta partición se cargan la programación de subprocesos, la administración de memoria, la compatibilidad con el sistema de archivos, la compatibilidad con la red y todo el código del controlador del dispositivo. Esta partición es compartida por todos los procesos.
En primer lugar, esta sección analiza en detalle la partición en modo de usuario. La partición en modo de usuario desde la dirección terrestre hasta la dirección de orden superior es la siguiente:
1) Segmento de código, que almacena el código binario del cuerpo de la función.
2) Las variables globales y las variables estáticas del área de datos estáticos (divididas en segmento de datos inicializados y segmentos de datos no inicializados) se almacenan juntas. Las variables globales inicializadas y las variables estáticas están en un área, y las variables globales no inicializadas y. Las variables estáticas no inicializadas se encuentran en otra área adyacente. El sistema lo publica una vez finalizado el programa.
3) El montón generalmente lo asigna y libera el programador. Si el programador no lo libera, el sistema operativo puede reciclarlo al final del programa. Tenga en cuenta que la estructura de datos es diferente de la del montón y el método de asignación es similar al de la lista vinculada.
....(Parte no asignada) (Esta parte contiene varios archivos DLL importados, etc.)
4) El compilador asigna y libera automáticamente la pila para almacenar funciones. valores, valores de variables locales, etc. Funciona como una pila en una estructura de datos.
En segundo lugar, a continuación se presenta en detalle todo el proceso desde la importación del exe hasta la ejecución y la carga del espacio de direcciones.
1) El sistema encontró el archivo exe especificado al llamar a CreateProcess.
2) El sistema crea un objeto kernel para un nuevo proceso.
3) El sistema crea un espacio de direcciones privado para este nuevo proceso.
4) El sistema reserva un área de espacio de direcciones lo suficientemente grande para almacenar archivos exe. La ubicación de esta área se establece en el archivo exe. De forma predeterminada, la dirección base del archivo exe es 0x000000. (1. El compilador procesa cada módulo de código fuente y genera un archivo obj.
2.Linker combina el contenido de todos los módulos obj para generar un único archivo de mapeo ejecutable, es decir, exe, que contiene todos los códigos binarios y variables de datos globales/estáticos del módulo ejecutable, y también contiene una sección de importación que enumera los nombres de todos los dll. módulos requeridos por el módulo ejecutable. Para cada nombre de dll enumerado, la sección de importación indica a qué función y símbolos de variable hace referencia el código binario ejecutable. )
5) Después de que el archivo exe se asigna al espacio de direcciones del proceso, el sistema accederá a una sección del archivo exe (esta sección enumera algunos archivos dll) y enumera la parte del archivo exe que llama a la función del archivo DLL como parte del código. Luego, el sistema llama a la función loadlibrary para cada archivo dll. Si un archivo dll necesita llamar a más archivos DLL, el sistema llamará a la función loadlibrary nuevamente para cargar los archivos DLL. El sistema reserva un área de espacio de direcciones lo suficientemente grande para almacenar este archivo dll. De forma predeterminada, la dirección base de los archivos dll creados por Microsoft es 0x1000000. Todas las DLL del sistema estándar proporcionadas por Windows tienen diferentes direcciones base para que no se superpongan incluso cuando se cargan en un único espacio de direcciones. (1. El compilador procesa cada módulo de código fuente y genera un módulo obj. 2. El vinculador combina el contenido de todos los módulos obj para generar un archivo de imagen dll que contiene todo el código binario y los datos globales/estáticos de las variables dll. 3. Si el vinculador verifica que al menos una función o variable se haya exportado desde el módulo de código fuente del dll, el vinculador también generará un archivo lib separado. Este archivo lib es muy pequeño y solo enumera todas las funciones y variables exportadas. nombre de la variable. )
6) Cuando todos los archivos exe y dll se asignan al espacio de direcciones del proceso, el sistema creará un objeto de núcleo de subproceso y utilizará el subproceso para llamar a la función DllMain de. cada DLL. Tome DLL_PROCESS_ATTACH como parámetro. Cuando todos los archivos DLL asignados respondan a esta notificación, el sistema impulsará el hilo principal para comenzar a ejecutar el código de inicio del archivo exe (función winmainCRTStartup), que es responsable de inicializar la biblioteca de tiempo de ejecución c/c y llamar a la función de entrada de función (principal). o winmain).
Algunas de las diferencias de carga entre dll y lib se destacan a continuación:
Dll permite que un módulo ejecutable (archivo .dll o .exe) contenga solo funciones que ubican el dll en tiempo de ejecución La información requerida para el código ejecutable (es decir, cargar la biblioteca adjunta a la DLL en el módulo ejecutable).
Para archivos lib, el vinculador obtiene todas las funciones a las que se hace referencia de la biblioteca de vínculos estáticos LIB y coloca la biblioteca junto con el código en el archivo ejecutable.
3. Conocimiento teórico de montón y pila
3.1 Método de aplicación
Pila: asignada automáticamente por el sistema. Por ejemplo, declare una variable local int b en una función; el sistema crea automáticamente espacio para B en la pila.
Montón: los programadores deben solicitarlo y especificar el tamaño. función malloc en C y nuevo operador en C.
3.2 Respuesta del sistema después de la aplicación
Pila: siempre que el espacio restante de la pila sea mayor que el espacio solicitado, el sistema proporcionará memoria para el programa, de lo contrario se producirá un desbordamiento de la pila. Se informará la excepción.
Montón: En primer lugar, debes saber que el sistema operativo tiene una lista vinculada de direcciones de memoria libres. Cuando el sistema recibe la aplicación del programa,
recorrerá la lista, encontrará el primer nodo del montón con un espacio mayor que el espacio de la aplicación, luego eliminará el nodo de la lista de nodos libres y asignará el nodo. Espacio para programar. Además, para la mayoría de los sistemas, el tamaño de esta asignación se registrará en la primera dirección de este espacio de memoria, de modo que la declaración de eliminación en el código pueda liberar correctamente este espacio de memoria. Además, dado que el tamaño del nodo del montón encontrado no es necesariamente exactamente igual al tamaño de la aplicación, el sistema automáticamente colocará la parte sobrante nuevamente en la lista libre.
3.3 Limitaciones en la escala de la aplicación
Pila: en Windows, la pila es una estructura de datos que se extiende a direcciones de orden inferior y áreas de memoria continua. El sistema especifica previamente la dirección de la parte superior de la pila y el tamaño máximo de la pila. En WINDOWS, el tamaño de la pila es 2 M (algunos dicen que es 1 M, que es una constante determinada en el momento de la compilación). Si el espacio de la aplicación excede el espacio restante de la pila, se generará un desbordamiento. Por tanto, se puede obtener menos espacio de la pila.
Montón: El montón es una estructura de datos que se extiende a direcciones de orden superior y es un área de memoria discontinua. Esto se debe a que el sistema utiliza una lista vinculada para almacenar direcciones de memoria libres. Dichas direcciones de memoria son naturalmente discontinuas. La dirección transversal de la lista vinculada es desde la dirección baja hasta la dirección alta. El tamaño del montón está limitado por la memoria virtual disponible en el sistema informático. Se puede observar que el espacio obtenido por el montón es más flexible y mayor.
3.4 Comparación de eficiencia de la aplicación:
El sistema asigna automáticamente la pila y es más rápida. Pero el programador no tiene control sobre ello.
El montón es memoria asignada por nuevos. Generalmente es lento y propenso a la fragmentación de la memoria, pero es el más conveniente de usar.
Además, en WINDOWS, la mejor manera es usar VirtualAlloc para asignar memoria, no en el montón o la pila, sino directamente en el espacio de direcciones del proceso, aunque es el más inconveniente de usar. Pero es rápido y más flexible.
3.5 Apilamiento y almacenamiento en pila
Apilar: Cuando se llama a una función, lo primero que se apila es la dirección de la siguiente instrucción de la función principal (el siguiente ejecutable de la declaración de llamada de función), seguida de los parámetros de la función. En la mayoría de los compiladores de C, los argumentos se apilan de derecha a izquierda, seguidos de las variables locales dentro de la función. Tenga en cuenta que las variables estáticas no están apiladas.
Cuando se completa esta llamada a la función, primero se extraen las variables locales, luego los parámetros y, finalmente, el puntero superior de la pila apunta a la dirección de almacenamiento inicial, que es la siguiente instrucción en la función principal. y el programa continúa ejecutándose desde aquí.
Montón: normalmente, el tamaño del montón se almacena en un byte en el encabezado del montón. El contenido específico del montón lo organiza el programador.
3.6 ?Pila? Entonces qué. ¿montón? Son dos áreas de datos dinámicos diferentes. La pila es una estructura lineal de primero en entrar y último en salir. La dirección superior de la pila es siempre menor o igual que la dirección base de la pila. El montón es una estructura de cadena. Cada hilo del proceso tiene uno privado. ¿Pila? Por tanto, aunque cada hilo tiene el mismo código, los datos de las variables locales no interferirán entre sí. ¿Puede pasar una pila? ¿Dirección básica? Entonces qué. ¿La parte superior de la pila? La dirección descrita. Las variables globales y las variables estáticas se distribuyen en el área de datos estáticos y las variables locales se distribuyen en el área de datos dinámicos, que es la pila. El programa accede a las variables locales a través de la dirección base y el desplazamiento de la pila.
Cuarto, expliquemos la transformación de la pila de llamadas de la función ah para comprender mejor el principio de la pila. (Prueba VS2005)
El orden de apilamiento es de dirección alta a dirección baja.
1) Los parámetros se insertan en la pila de derecha a izquierda.
2) Inserte el valor de EBP (el análisis en el libro insertó una dirección de instrucción de retorno de función en esta ubicación, pero no se encontró porque el intervalo era de solo 4 bytes).
3) Presione la variable local.
4) Coloque el valor de retorno en el registro EAX. Debido a que los ensamblados win32 generalmente usan eax para devolver resultados, si el resultado final no está en eax, debe colocarse en eax. Por lo tanto, el proceso de liberación del valor de retorno ocurre después de los parámetros.
;