Sección 2 Átomo de clase T
En la unidad System.pas, TClass se define así:
TClass = clase de TObject;
Significa que TClass es la clase de TObject. Debido a que TObject en sí es una clase, TClass es la llamada clase de clases.
Conceptualmente, TClass es un tipo de clase, es decir, una clase. Sin embargo, sabemos que una clase de DELPHI representa un dato de VMT. Por lo tanto, la clase puede considerarse como el tipo definido para el elemento de datos VMT. De hecho, es un tipo de puntero que apunta a los datos VMT.
En el lenguaje C++ tradicional anterior, no se podía definir el tipo de clase. Una vez que se compila el objeto, se repara, la información estructural de la clase se convierte en código de máquina absoluto y la información completa de la clase no existirá en la memoria. Algunos lenguajes orientados a objetos de nivel superior pueden admitir el acceso dinámico y la invocación de información de clase, pero a menudo requieren un mecanismo de interpretación interno complejo y más recursos del sistema. El lenguaje Object Pascal de DELPHI absorbe algunas de las excelentes características de los lenguajes orientados a objetos de alto nivel, al tiempo que conserva la ventaja tradicional de compilar programas directamente en código de máquina, lo que resuelve perfectamente los problemas de funciones avanzadas y eficiencia del programa.
Es precisamente porque DELPHI retiene información completa de la clase en la aplicación que puede proporcionar funciones avanzadas orientadas a objetos, como convertir e identificar clases en tiempo de ejecución, en las que los datos VMT de la clase desempeñan un papel central clave. Los amigos interesados pueden leer los dos procesos de ensamblaje de AsClass e IsClass en la unidad del sistema. Son los códigos de implementación de los operadores as e is para profundizar su comprensión de las clases y los datos VMT.
Con el tipo de clase, puedes usar la clase como variable. Una variable de clase puede entenderse como un objeto especial y puede acceder a los métodos de una variable de clase como un objeto. Por ejemplo: echemos un vistazo al siguiente fragmento de programa:
tipo
TSampleClass = clase de TSampleObject;
TSampleObject = clase (TObject)
público
constructor Crear;
destructor Destruir; anular;
función de clase GetSampleObjectCount:Integer;
Procedimiento GetObjectIndex:Integer;
fin;
var
aSampleClass: TSampleClass;
unaClase: TClass;
En este código, definimos una clase TSampleObject y su tipo de clase relacionada TSampleClass, así como dos variables de clase aSampleClass y aClass. Además, también definimos un constructor, un destructor, un método de clase GetSampleObjectCount y un método de objeto GetObjectIndex para la clase TSampleObject.
Primero, comprendamos el significado de las variables de clase aSampleClass y aClass.
Obviamente, puede tratar TSampleObject y TObject como valores constantes y asignarlos a variables aClass, al igual que asignar 123 valores constantes a la variable entera i. Por lo tanto, la relación entre tipos de clase, clases y variables de clase es la relación entre tipos, constantes y variables, pero a nivel de clase en lugar de a nivel de objeto. Por supuesto, no es legal asignar TObject directamente a aSampleClass, porque aSampleClass es una variable de clase de la clase TSampleObject derivada de TObject, y TObject no contiene todas las definiciones compatibles con el tipo TSampleClass. Por el contrario, es legal asignar TSampleObject a una variable Class, porque TSampleObject es una clase derivada de TObject y es compatible con el tipo TClass. Esto es exactamente similar a la asignación y la relación de coincidencia de tipos de variables de objeto.
Luego, echemos un vistazo a qué son los métodos de clase.
El llamado método de clase se refiere al método llamado a nivel de clase, como el método GetSampleObjectCount definido anteriormente, que es un método declarado con la palabra reservada clase. Los métodos de clase son diferentes de los métodos de objeto llamados a nivel de objeto. Los métodos de objeto ya nos son familiares, y los métodos de clase siempre se utilizan en el nivel de acceso y control de las características comunes de todos los objetos de clase y en la gestión central de objetos. En la definición de TObject podemos encontrar una gran cantidad de métodos de clase, como ClassName, ClassInfo, NewInstance, etc. Entre ellos, NewInstance también se define como virtual, es decir, un método de clase virtual. Esto significa que puede reescribir el método de implementación de NewInstance en una subclase derivada para construir instancias de objetos de esa clase de una manera especial.
También puede utilizar el identificador self en los métodos de clase, pero su significado es diferente del de self en los métodos de objeto. El self en el método de clase representa su propia clase, es decir, el puntero al VMT, mientras que el self en el método de objeto representa el objeto mismo, es decir, el puntero al espacio de datos del objeto. Aunque los métodos de clase solo se pueden usar a nivel de clase, aún puedes llamar a métodos de clase a través de un objeto. Por ejemplo, el método de clase ClassName del objeto TObject se puede llamar mediante la declaración aObject.ClassName, porque los primeros 4 bytes en el espacio de datos del objeto señalado por el puntero del objeto son punteros a la clase VMT. Por el contrario, no se pueden llamar métodos de objetos a nivel de clase y declaraciones como TObject.Free deben ser ilegales.
¡Vale la pena señalar que el constructor es un método de clase y el destructor es un método de objeto!
¿Qué? ¡Los constructores son métodos de clase y los destructores son métodos de objetos! ¿Hubo algún error?
Verá, cuando crea un objeto, claramente usa una declaración similar a la siguiente:
unObjeto := TObject.Create;
Claramente está llamando al método Create de la clase TObject. Al eliminar un objeto, utilice la siguiente declaración:
unObjeto.Destruir;
Incluso si utiliza el método Free para liberar el objeto, se llama indirectamente al método Destroy del objeto.
La razón es muy simple: antes de que se construya el objeto, el objeto aún no existe, solo existe la clase. Solo puedes usar métodos de clase para crear objetos. Por el contrario, eliminar un objeto debe eliminar el objeto existente. El objeto se libera, no la clase.
Finalmente, analicemos el tema de los constructores ficticios.
En el lenguaje C++ tradicional, se pueden implementar destructores virtuales, pero implementar constructores virtuales es un problema difícil. Porque, en el lenguaje C++ tradicional, no hay tipos de clases. Las instancias de objetos globales existen en el espacio de datos global en el momento de la compilación, y los objetos locales de funciones también son instancias asignadas en el espacio de la pila en el momento de la compilación. Incluso los objetos creados dinámicamente se colocan en la estructura de clase fija utilizando el nuevo operador asignado en. el espacio del montón, y el constructor es solo un método de objeto que inicializa la instancia del objeto generado. No existen métodos de clase reales en el lenguaje C++ tradicional. Incluso si se pueden definir los llamados métodos estáticos basados en clases, en última instancia se implementan como una función global especial, sin mencionar que los métodos de clases virtuales solo pueden apuntar a objetos específicos. instancias eficientes. Por lo tanto, el lenguaje C ++ tradicional cree que antes de generar una instancia de objeto específica, es imposible construir el objeto en sí en función del objeto que se generará. ¡De hecho es imposible, porque esto crearía una paradoja lógica contradictoria!
Sin embargo, es precisamente debido a los conceptos clave de información de tipo de clase dinámica, métodos de clase verdaderamente virtuales y constructores implementados en base a clases en DELPHI que se pueden implementar constructores virtuales. Los objetos son producidos por clases. El objeto es como un bebé en crecimiento, y la clase es su madre. El bebé mismo no sabe en qué tipo de persona se convertirá en el futuro, pero las madres utilizan sus propios métodos educativos para cultivar a diferentes niños. . Gente, los principios son los mismos.
Es en la definición de la clase TComponent que el constructor Create se define como virtual para que diferentes tipos de controles puedan implementar sus propios métodos de construcción. Ésta es la grandeza de conceptos como las clases creadas por TClass, y también la grandeza de DELPHI.
................................................. ..
Capítulo 3 La visión del tiempo y el espacio en WIN32
Mi anciano padre miró a su nieto pequeño jugando con juguetes en el suelo y luego me dijo: "Este niño es como tú cuando eras pequeño. Le gusta desarmar cosas y sólo se detiene después de verlas hasta el final". Pensando en cuando era niño, a menudo desmantelaba carritos de juguete, pequeños despertadores, cajas de música, etc., y mi madre a menudo me regañaba.
La primera vez que entendí los principios básicos de las computadoras tuvo que ver con una caja de música que desarmé. Fue en un cómic cuando estaba en la escuela secundaria. Un anciano con barba blanca explicaba la teoría de las máquinas inteligentes y un tío con bigote hablaba de computadoras y cajas de música. Dijeron que la unidad central de procesamiento de una computadora es la fila de lengüetas musicales que se usan para la pronunciación en la caja de música, y el programa de computadora son las protuberancias densamente empaquetadas en el pequeño cilindro de la caja de música. La rotación del pequeño cilindro es equivalente. a la rotación de la unidad central de procesamiento. El movimiento natural del puntero de instrucción, mientras que los golpes que representan la música en el pequeño cilindro controlan la vibración de la lengüeta musical para producir instrucciones equivalentes a la ejecución del programa por parte del procesador central. La caja de música emite una hermosa melodía, que se reproduce de acuerdo con la partitura musical grabada en el pequeño cilindro por el artesano. La computadora completa un procesamiento complejo basado en el programa preprogramado por el programador. Después de ir a la universidad, aprendí que el anciano de barba blanca era el gigante científico Turing. Su teoría de los autómatas finitos promovió el desarrollo de toda la revolución de la información, y el tío del bigote era el padre de las computadoras, von Neumann. La arquitectura informática sigue siendo la principal estructura arquitectónica de las computadoras. La caja de música no fue desmantelada en vano, mamá puede estar tranquila.
Sólo con una comprensión simple y profunda podemos crear creaciones profundas y concisas.
En este capítulo discutiremos los conceptos básicos relacionados con nuestra programación en el sistema operativo Windows de 32 bits y estableceremos la visión correcta del tiempo y el espacio en WIN32. Espero que después de leer este capítulo, podamos tener una comprensión más profunda de los programas, procesos y subprocesos, comprender los principios de los archivos ejecutables, las bibliotecas de enlaces dinámicos y los paquetes de tiempo de ejecución, y ver claramente la verdad sobre los datos globales, los datos locales y los parámetros en la memoria. .
Sección 1 Comprender el proceso
Por razones históricas, Windows se originó a partir de DOS. En la era DOS, siempre tuvimos solo el concepto de programa, pero no el concepto de proceso. En aquella época, sólo los sistemas operativos normales, como UNIX y VMS, tenían el concepto de procesos, y multiproceso significaba miniordenadores, terminales y múltiples usuarios, lo que también significaba dinero. La mayor parte del tiempo solo podía usar microcomputadoras y sistemas DOS relativamente baratos. Solo comencé a entrar en contacto con procesos y minicomputadoras cuando estaba estudiando sistemas operativos.
Fue sólo después de Windows 3. En el pasado, en DOS solo se podía ejecutar un programa al mismo tiempo, pero en Windows se podían ejecutar varios programas al mismo tiempo. Mientras se ejecuta un programa en DOS, el mismo programa no se puede ejecutar al mismo tiempo, pero en Windows, se pueden ejecutar más de dos copias del mismo programa al mismo tiempo, y cada copia en ejecución del programa es un proceso. Para ser más precisos, cada ejecución de cualquier programa genera una tarea y cada tarea es un proceso.
Cuando los programas y procesos se entienden juntos, se puede considerar que la palabra programa se refiere a cosas estáticas. Un programa típico es código estático y datos compuestos por un archivo EXE o un archivo EXE más varios archivos DLL. Un proceso es la ejecución de un programa, que es código y datos que cambian dinámicamente y que se ejecutan dinámicamente en la memoria. Cuando se requiere la ejecución de un programa estático, el sistema operativo proporcionará un cierto espacio de memoria para esta operación, transferirá el código y los datos del programa estático a estos espacios de memoria y reposicionará y asignará el código y los datos del programa en este espacio. ejecutado en su interior, creando así un proceso dinámico.
Dos copias del mismo programa ejecutándose al mismo tiempo significan que hay dos espacios de proceso en la memoria del sistema, pero las funciones de su programa son las mismas, pero se encuentran en diferentes estados que cambian dinámicamente.
En términos del tiempo de ejecución del proceso, cada proceso se ejecuta al mismo tiempo. El término profesional se denomina ejecución paralela o ejecución concurrente. Pero esta es principalmente la sensación superficial que nos da el sistema operativo. De hecho, cada proceso se ejecuta en tiempo compartido, es decir, cada proceso se turna para ocupar el tiempo de la CPU para ejecutar las instrucciones del programa del proceso. Para una CPU, solo se ejecutan al mismo tiempo las instrucciones de un proceso. El sistema operativo es el manipulador detrás de la operación del proceso programado. Guarda y cambia constantemente el estado actual de cada proceso ejecutado en la CPU, de modo que cada proceso programado piense que se está ejecutando de forma completa y continua. Dado que la programación de procesos en tiempo compartido es muy rápida, nos da la impresión de que todos los procesos se están ejecutando al mismo tiempo. De hecho, una verdadera operación simultánea sólo es posible en un entorno de hardware con múltiples CPU. Cuando hablemos de subprocesos más adelante, encontraremos que los subprocesos son los que realmente impulsan el proceso y, lo que es más importante, proporcionan espacio para el proceso.
En términos del espacio ocupado por el proceso, cada espacio de proceso es relativamente independiente y cada proceso se ejecuta en su propio espacio independiente. Un programa incluye tanto espacio de código como espacio de datos. Tanto el código como los datos ocupan espacio de proceso. Windows asigna memoria real para el espacio de datos requerido por cada proceso y generalmente usa métodos compartidos para el espacio de código, asignando un código de un programa a múltiples procesos del programa. Esto significa que si un programa tiene 100 KB de código y requiere 100 KB de espacio de datos, lo que significa que se requiere un total de 200 KB de espacio de proceso, el sistema operativo asignará 200 KB de espacio de proceso la primera vez que se ejecute el programa y 200 KB de espacio de proceso. El espacio se asignará la segunda vez que se ejecute el programa. Cuando se inicia un proceso, el sistema operativo solo asigna 100 K de espacio de datos, mientras que el espacio de código comparte el espacio del proceso anterior.
Lo anterior es la vista básica de tiempo y espacio del proceso en el sistema operativo Windows. De hecho, existe una gran diferencia en la vista de tiempo y espacio del proceso entre los sistemas operativos de 16 y 32 bits de Windows.
En términos de tiempo, la gestión de procesos de los sistemas operativos Windows de 16 bits, como Windows 3.x, es muy sencilla. En realidad, es solo un sistema operativo de gestión multitarea. Además, la programación de tareas del sistema operativo es pasiva. Si una tarea no deja de procesar el mensaje, el sistema operativo debe esperar. Debido a fallas en la gestión de procesos del sistema Windows de 16 bits, cuando un proceso se está ejecutando, ocupa por completo los recursos de la CPU. En aquellos días, para que Windows de 16 bits tuviera la oportunidad de programar otras tareas, Microsoft elogiaba a los desarrolladores de aplicaciones de Windows por ser programadores de mentalidad amplia, por lo que estaban dispuestos a escribir unas cuantas líneas más de código para regalarle al Windows de 16 bits la posibilidad de programar otras tareas. Sistema operativo. Por el contrario, los sistemas operativos WIN32, como Windows 95 y NT, tienen capacidades reales de sistema operativo multiproceso y multitarea. El proceso en WIN32 está completamente programado por el sistema operativo. Una vez que finaliza el intervalo de tiempo del proceso en ejecución, el sistema operativo cambiará activamente al siguiente proceso independientemente de si el proceso todavía está procesando datos. Estrictamente hablando, el sistema operativo Windows de 16 bits no puede considerarse un sistema operativo completo, pero el sistema operativo WIN32 de 32 bits es el verdadero sistema operativo. Por supuesto, Microsoft no dirá que WIN32 compensa las deficiencias de Windows de 16 bits, pero afirma que WIN32 implementa una tecnología avanzada llamada "multitarea preventiva", que es un método comercial.
Desde una perspectiva espacial, aunque el espacio de proceso en el sistema operativo Windows de 16 bits es relativamente independiente, los procesos pueden acceder fácilmente al espacio de datos de los demás. Debido a que estos procesos son en realidad diferentes segmentos de datos en el mismo espacio físico, y las operaciones de dirección incorrectas pueden fácilmente provocar lecturas y escrituras incorrectas en el espacio y bloquear el sistema operativo. Sin embargo, en el sistema operativo WIN32, cada espacio de proceso es completamente independiente. WIN32 proporciona a cada proceso un espacio de direcciones virtual y continuo de hasta 4G. El llamado espacio de direcciones continuo significa que cada proceso tiene un espacio de direcciones de $00000000 a $FFFFFFFF, en lugar del espacio segmentado de Windows de 16 bits. En WIN32, no tiene que preocuparse de que sus operaciones de lectura y escritura afecten involuntariamente a los datos en otros espacios de proceso, y no tiene que preocuparse de que otros procesos vengan a acosar su trabajo. Al mismo tiempo, el espacio virtual 4G continuo proporcionado por WIN32 para su proceso es la memoria física que le asigna el sistema operativo con el soporte del hardware. Aunque tiene un espacio virtual tan vasto, el sistema nunca desperdiciará un byte. memoria física.
Sección 2 Espacio de proceso
Cuando usamos DELPHI para escribir aplicaciones WIN32, rara vez nos preocupamos por el mundo interno del proceso cuando se está ejecutando. Debido a que WIN32 proporciona 4G de espacio de proceso virtual continuo para nuestro proceso, quizás la aplicación más grande del mundo actualmente solo use una parte. Parece que el espacio de proceso es ilimitado, pero el espacio de proceso 4G es virtual y la memoria real de su máquina puede estar lejos de serlo. Aunque el proceso tiene un espacio tan grande, algunos programas de algoritmos complejos aún no podrán ejecutarse debido al desbordamiento de la pila, especialmente los programas que contienen una gran cantidad de algoritmos recursivos.
Por lo tanto, una comprensión profunda de la estructura del espacio de proceso 4G, su relación con la memoria física, etc. nos ayudará a comprender más claramente el mundo espacio-temporal de WIN32, de modo que podamos utilizar los métodos correctos en el trabajo de desarrollo real. Visión del mundo y metodología para resolver diversos problemas difíciles.
A continuación, utilizaremos un experimento simple para comprender el mundo interno del espacio de proceso de WIN32. Esto puede requerir cierto conocimiento de los registros CUP y del lenguaje ensamblador, pero intenté explicarlo en un lenguaje sencillo.
Cuando se inicia DELPHI, se generará automáticamente un proyecto Proyecto1 y comenzaremos con él. Establezca un punto de interrupción en cualquier parte del programa original de Project1.dpr, por ejemplo, establezca un punto de interrupción al comienzo de la oración. Luego ejecute el programa y se detendrá automáticamente cuando alcance el punto de interrupción. En este momento, podemos abrir la ventana de la CPU en la herramienta de depuración para observar la estructura interna del espacio del proceso.
El registro de puntero de instrucción actual Eip se detiene en $0043E4B8. Desde los dos dígitos hexadecimales más altos de la dirección donde se encuentra la instrucción del programa son ceros, se puede ver que el programa actual está en la posición de dirección en la parte inferior del 4G. espacio de proceso, que ocupa $00000000 a bastante poco espacio de direcciones para $FFFFFFFF.
En el cuadro de comando en la ventana de la CPU, puede buscar el contenido del espacio de proceso. Al ver el contenido del espacio inferior a $00400000, encontrará una serie de signos de interrogación "????" que aparecen en el contenido inferior a $00400000. Esto se debe a que el espacio de direcciones no se ha asignado al espacio físico real. Si observa el valor hexadecimal de la variable global HInstance en este momento, encontrará que también es $ 00400000. Aunque HInstance refleja el identificador de la instancia del proceso, de hecho, es el valor de la dirección inicial cuando el programa se carga en la memoria, también en Windows de 16 bits. Por tanto, podemos pensar que el programa del proceso se carga a partir de $00400000, es decir, el espacio a partir de 4M en el espacio virtual 4G es el espacio donde se carga el programa.
Desde $00400000 en adelante y antes de $0044D000, es principalmente el espacio de direcciones del código del programa y los datos globales. En el cuadro de pila en la ventana de la CPU, puede ver la dirección de la pila actual. De manera similar, encontrará que el espacio de direcciones de la pila actual es de $0067B000 a $00680000, con una longitud de $5000. De hecho, el tamaño mínimo del espacio de pila del proceso es $5000, que se obtiene en función del valor del tamaño mínimo de pila establecido en la página Linker de ProjectOptions al compilar el programa DELPHI, más $1000. La pila crece desde la dirección superior hasta la inferior. Cuando la pila cuando el programa se está ejecutando no es suficiente, el sistema aumentará automáticamente el tamaño del espacio de la pila hacia la dirección inferior. Este proceso asignará más memoria real a la dirección inferior. espacio de proceso. Al compilar un programa DELPHI, puede controlar el espacio máximo de pila que se puede aumentar estableciendo el valor de Tamaño máximo de pila en la página del vinculador en Opciones de proyecto. Especialmente en programas que contienen relaciones profundas de llamada de subrutinas o utilizan algoritmos recursivos, el valor del tamaño máximo de pila debe establecerse de manera razonable. Debido a que llamar a una subrutina requiere espacio de pila, y una vez que la pila se agota, el sistema arrojará un error de "Desbordamiento de pila".
Parece que el espacio de proceso después del espacio de la pila debería ser espacio libre. De hecho, este no es el caso. La información relevante de WIN32 dice que el espacio 2G después de $80,000,000 es el espacio utilizado por el sistema. Parece que el proceso realmente sólo puede poseer espacio 2G. De hecho, el espacio que realmente puede poseer un proceso ni siquiera es 2G, porque el espacio 4M de $00000000 a $00400000 también es un área restringida.
Pero pase lo que pase, las direcciones que nuestro proceso puede utilizar siguen siendo muy amplias. Especialmente después del espacio de pila y entre $ 80.000.000, es el principal campo de batalla del espacio de proceso. El espacio de memoria asignado por el proceso desde el sistema se asignará a este espacio, la biblioteca de enlaces dinámicos cargada por el proceso se asignará a este espacio y el espacio de pila de subprocesos del nuevo subproceso también se asignará a este espacio, casi todo operaciones que involucran asignación de memoria Todas se asignarán a este espacio. Tenga en cuenta que la asignación mencionada aquí significa que la correspondencia entre la memoria real y este espacio de proceso que no está asignada a la memoria real no se puede utilizar, al igual que la cadena "" en el cuadro de comando de la ventana de la CPU durante la depuración. ???".
............
¡Gracias por leer!