Section 2 TClass Atome
Dans l'unité System.pas, TClass est défini comme ceci :
TClass = classe de TObject ;
Cela signifie que TClass est la classe de TObject. Parce que TObject lui-même est une classe, TClass est ce qu'on appelle la classe des classes.
Conceptuellement, TClass est un type de classe, c'est-à-dire une classe. Cependant, nous savons qu'une classe de DELPHI représente une donnée VMT. La classe peut donc être considérée comme le type défini pour la donnée VMT. En fait, il s’agit d’un type pointeur pointant vers les données VMT !
Dans le langage C++ traditionnel précédent, le type d’une classe ne pouvait pas être défini. Une fois l'objet compilé, il est corrigé, les informations structurelles de la classe ont été converties en code machine absolu et les informations complètes de la classe n'existeront pas dans la mémoire. Certains langages orientés objet de niveau supérieur peuvent prendre en charge l'accès dynamique et l'invocation d'informations de classe, mais ils nécessitent souvent un mécanisme d'interprétation interne complexe et davantage de ressources système. Le langage Pascal Objet de DELPHI absorbe certaines des excellentes fonctionnalités des langages orientés objet de haut niveau, tout en conservant l'avantage traditionnel de compiler directement les programmes en code machine, ce qui résout parfaitement les problèmes de fonctions avancées et d'efficacité des programmes.
C'est précisément parce que DELPHI conserve des informations complètes sur les classes dans l'application qu'il peut fournir des fonctions avancées orientées objet telles que la conversion et l'identification des classes au moment de l'exécution, dans lesquelles les données VMT de la classe jouent un rôle central clé. Les amis intéressés peuvent lire les deux processus d'assemblage d'AsClass et IsClass dans l'unité Système. Ce sont les codes d'implémentation des opérateurs as et is pour approfondir leur compréhension des classes et des données VMT.
Avec le type de classe, vous pouvez utiliser la classe comme variable. Une variable de classe peut être comprise comme un objet spécial et vous pouvez accéder aux méthodes d'une variable de classe comme un objet. Par exemple : Jetons un coup d'œil au fragment de programme suivant :
taper
TSampleClass = classe de TSampleObject ;
TSampleObject = classe (TObject)
publique
constructeur Créer ;
destructeur Détruire ;
fonction de classe GetSampleObjectCount:Integer ;
PROcédure GetObjectIndex:Integer ;
fin;
var
aSampleClass : TSampleClass ;
aClasse : TClass ;
Dans ce code, nous définissons une classe TSampleObject et son type de classe associé TSampleClass, ainsi que deux variables de classe aSampleClass et aClass. De plus, nous avons également défini un constructeur, un destructeur, une méthode de classe GetSampleObjectCount et une méthode objet GetObjectIndex pour la classe TSampleObject.
Tout d'abord, comprenons la signification des variables de classe aSampleClass et aClass.
Évidemment, vous pouvez traiter TSampleObject et TObject comme des valeurs constantes et les affecter à des variables aClass, tout comme attribuer 123 valeurs constantes à la variable entière i. Par conséquent, la relation entre les types de classe, les classes et les variables de classe est la relation entre les types, les constantes et les variables, mais au niveau de la classe plutôt qu'au niveau de l'objet. Bien sûr, il n'est pas légal d'attribuer directement TObject à aSampleClass, car aSampleClass est une variable de classe de la classe TSampleObject dérivée de TObject, et TObject ne contient pas toutes les définitions compatibles avec le type TSampleClass. Au contraire, il est légal d'affecter TSampleObject à une variable Class, car TSampleObject est une classe dérivée de TObject et est compatible avec le type TClass. Ceci est exactement similaire à la relation d'affectation et de correspondance de type des variables d'objet.
Voyons ensuite ce que sont les méthodes de classe.
La méthode dite de classe fait référence à la méthode appelée au niveau de la classe, comme la méthode GetSampleObjectCount définie ci-dessus, qui est une méthode déclarée avec le mot réservé class. Les méthodes de classe sont différentes des méthodes d'objet appelées au niveau de l'objet. Les méthodes d'objet nous sont déjà familières, et les méthodes de classe sont toujours utilisées au niveau de l'accès et du contrôle des caractéristiques communes de tous les objets de classe et de la gestion centralisée des objets. Dans la définition de TObject, on peut trouver un grand nombre de méthodes de classe, telles que ClassName, ClassInfo, NewInstance, etc. Parmi eux, NewInstance est également défini comme virtuel, c'est-à-dire une méthode de classe virtuelle. Cela signifie que vous pouvez réécrire la méthode d'implémentation de NewInstance dans une sous-classe dérivée pour construire des instances d'objet de cette classe d'une manière spéciale.
Vous pouvez également utiliser l'identifiant self dans les méthodes de classe, mais sa signification est différente de celle de self dans les méthodes objet. Le self dans la méthode de classe représente sa propre classe, c'est-à-dire le pointeur vers le VMT, tandis que le self dans la méthode objet représente l'objet lui-même, c'est-à-dire le pointeur vers l'espace de données de l'objet. Bien que les méthodes de classe ne puissent être utilisées qu'au niveau de la classe, vous pouvez toujours appeler des méthodes de classe via un objet. Par exemple, la méthode de classe ClassName de l'objet TObject peut être appelée via l'instruction aObject.ClassName, car les 4 premiers octets de l'espace de données de l'objet pointés par le pointeur d'objet sont des pointeurs vers la classe VMT. Au contraire, vous ne pouvez pas appeler de méthodes objet au niveau de la classe et les instructions telles que TObject.Free doivent être illégales.
Il est à noter que le constructeur est une méthode de classe et le destructeur est une méthode objet !
Quoi? Les constructeurs sont des méthodes de classe et les destructeurs sont des méthodes d'objet ! Y a-t-il eu une erreur ?
Vous voyez, lorsque vous créez un objet, vous utilisez clairement une instruction similaire à la suivante :
aObject := TObject.Create;
Il appelle clairement la méthode Create de la classe TObject. Lors de la suppression d'un objet, utilisez l'instruction suivante :
aObject.Destroy;
Même si vous utilisez la méthode Free pour libérer l'objet, la méthode Destroy de l'objet est appelée indirectement.
La raison est très simple. Avant que l'objet ne soit construit, l'objet n'existe pas encore, seule la classe existe. Vous ne pouvez utiliser que des méthodes de classe pour créer des objets. Au contraire, la suppression d'un objet doit supprimer l'objet existant, c'est l'objet qui est libéré, pas la classe.
Enfin, abordons la question des constructeurs fictifs.
Dans le langage C++ traditionnel, des destructeurs virtuels peuvent être implémentés, mais l'implémentation de constructeurs virtuels est un problème difficile. Parce que, dans le langage C++ traditionnel, il n’existe pas de types de classes. Des instances d'objets globaux existent dans l'espace de données global au moment de la compilation, et les objets locaux des fonctions sont également des instances mappées dans l'espace de pile au moment de la compilation. Même les objets créés dynamiquement sont placés dans la structure de classe fixe à l'aide de l'opérateur new Instances allouées dans. l'espace du tas, et le constructeur n'est qu'une méthode objet qui initialise l'instance d'objet générée. Il n'existe pas de véritables méthodes de classe dans le langage C++ traditionnel. Même si des méthodes dites statiques basées sur des classes peuvent être définies, elles sont finalement implémentées en tant que fonction globale spéciale, sans parler des méthodes de classe virtuelle qui ne peuvent cibler que des objets spécifiques. instances. Par conséquent, le langage C++ traditionnel estime qu'avant qu'une instance d'objet spécifique ne soit générée, il est impossible de construire l'objet lui-même en fonction de l'objet à générer. C’est en effet impossible, car cela créerait un paradoxe logique contradictoire !
Cependant, c'est précisément grâce aux concepts clés d'informations sur les types de classes dynamiques, de méthodes de classes véritablement virtuelles et de constructeurs implémentés sur la base de classes dans DELPHI que les constructeurs virtuels peuvent être implémentés. Les objets sont produits par les classes. L'objet est comme un bébé qui grandit, et la classe est sa mère. Le bébé lui-même ne sait pas quel genre de personne il deviendra dans le futur, mais les mères utilisent leurs propres méthodes éducatives pour éduquer différents enfants. . Les gens, les principes sont les mêmes.
C'est dans la définition de la classe TComponent que le constructeur Create est défini comme virtuel afin que différents types de contrôles puissent implémenter leurs propres méthodes de construction. C'est la grandeur des concepts comme les classes créées par TClass, et aussi la grandeur de DELPHI.
.................................................................. ..
Chapitre 3 La vue du temps et de l'espace dans WIN32
Mon vieux père a regardé son petit-fils jouer avec des jouets par terre, puis m'a dit : « Cet enfant est comme toi quand tu étais jeune. Il aime démonter les choses et ne s'arrête qu'après les avoir vus jusqu'au bout. En repensant à mon enfance, je démontais souvent des petites voitures, des petits réveils, des boîtes à musique, etc., et j'étais souvent grondé par ma mère.
La première fois que j'ai compris les principes de base de l'informatique, c'était avec une boîte à musique que j'avais démontée. C'était dans une bande dessinée quand j'étais au lycée. Un vieil homme à la barbe blanche expliquait la théorie des machines intelligentes, et un oncle moustachu parlait d'ordinateurs et de boîtes à musique. Ils ont dit que l'unité centrale d'un ordinateur est la rangée d'anches musicales utilisée pour la prononciation dans la boîte à musique, et que le programme informatique est constitué des bosses densément emballées sur le petit cylindre de la boîte à musique. La rotation du petit cylindre est équivalente. à la rotation de l'unité centrale. Le mouvement naturel du pointeur d'instruction, tandis que les bosses représentant la musique sur le petit cylindre contrôlent la vibration de l'anche musicale pour produire des instructions équivalentes à l'exécution du programme par le processeur central. La boîte à musique émet une belle mélodie, qui est jouée selon la partition musicale gravée sur le petit cylindre par l'artisan. L'ordinateur effectue un traitement complexe basé sur le programme préprogrammé par le programmeur. Après mes études universitaires, j’ai appris que le vieil homme à la barbe blanche était le géant scientifique Turing. Sa théorie des automates finis favorisait le développement de toute la révolution de l’information, et l’oncle à la moustache était le père des ordinateurs, von Neumann. L'architecture informatique reste la principale structure architecturale des ordinateurs. La boîte à musique n'a pas été démontée en vain, maman peut se rassurer.
Ce n'est qu'avec une compréhension simple et profonde que nous pouvons créer des créations profondes et concises.
Dans ce chapitre, nous aborderons les concepts de base liés à notre programmation dans le système d'exploitation Windows 32 bits et établirons la vision correcte du temps et de l'espace dans WIN32. J'espère qu'après avoir lu ce chapitre, nous pourrons avoir une compréhension plus approfondie des programmes, des processus et des threads, comprendre les principes des fichiers exécutables, des bibliothèques de liens dynamiques et des packages d'exécution, et voir clairement la vérité sur les données globales, les données locales et les paramètres en mémoire. .
Section 1 Comprendre le processus
Pour des raisons historiques, Windows provient du DOS. À l’ère DOS, nous n’avions toujours que la notion de programme, mais pas la notion de processus. À cette époque, seuls les systèmes d'exploitation classiques, tels qu'UNIX et VMS, avaient le concept de processus, et multi-processus signifiait des mini-ordinateurs, des terminaux et des utilisateurs multiples, ce qui signifiait également de l'argent. La plupart du temps, je ne pouvais utiliser que des micro-ordinateurs et des systèmes DOS relativement bon marché. Ce n'est que lorsque j'ai étudié les systèmes d'exploitation que j'ai commencé à entrer en contact avec les processus et les mini-ordinateurs.
Ce n'était qu'après Windows 3. Dans le passé, sous DOS, un seul programme pouvait être exécuté en même temps, mais sous Windows, plusieurs programmes pouvaient être exécutés en même temps. Lors de l'exécution d'un programme sous DOS, le même programme ne peut pas être exécuté en même temps, mais sous Windows, plus de deux copies du même programme peuvent être exécutées en même temps, et chaque copie en cours d'exécution du programme est un processus. Pour être plus précis, chaque exécution d’un programme génère une tâche, et chaque tâche est un processus.
Lorsque les programmes et les processus sont compris ensemble, le mot programme peut être considéré comme faisant référence à des éléments statiques. Un programme typique est un code statique et des données composés d'un fichier EXE ou d'un fichier EXE plus plusieurs fichiers DLL. Un processus est une exécution d'un programme, qui est du code et des données changeant dynamiquement qui s'exécutent dynamiquement en mémoire. Lorsqu'un programme statique doit être exécuté, le système d'exploitation fournira un certain espace mémoire pour cette opération, transférera le code du programme statique et les données dans ces espaces mémoire, et repositionnera et mappera le code du programme et les données dans cet espace. exécuté à l’intérieur, créant ainsi un processus dynamique.
Deux copies du même programme exécutées en même temps signifient qu'il existe deux espaces de processus dans la mémoire système, mais leurs fonctions de programme sont les mêmes, mais ils sont dans des états différents changeant dynamiquement.
En termes de durée d'exécution du processus, chaque processus est exécuté en même temps. Le terme professionnel est appelé exécution parallèle ou exécution concurrente. Mais c'est principalement le sentiment superficiel que nous donne le système d'exploitation. En fait, chaque processus est exécuté en temps partagé, c'est-à-dire que chaque processus occupe à tour de rôle le temps CPU pour exécuter les instructions du programme du processus. Pour un CPU, seules les instructions d'un processus sont exécutées en même temps. Le système d'exploitation est le manipulateur derrière le fonctionnement du processus planifié. Il enregistre et change constamment l'état actuel de chaque processus exécuté dans le CPU, de sorte que chaque processus planifié pense qu'il s'exécute complètement et en continu. La planification des processus en temps partagé étant très rapide, cela nous donne l’impression que les processus s’exécutent tous en même temps. En fait, un véritable fonctionnement simultané n’est possible que dans un environnement matériel multi-CPU. Lorsque nous parlerons des threads plus tard, nous constaterons que ce sont les threads qui pilotent réellement le processus et, plus important encore, ils fournissent de l'espace de processus.
En termes d'espace occupé par le processus, chaque espace de processus est relativement indépendant et chaque processus s'exécute dans son propre espace indépendant. Un programme comprend à la fois un espace de code et un espace de données. Le code et les données occupent l'espace de processus. Windows alloue de la mémoire réelle pour l'espace de données requis par chaque processus et utilise généralement des méthodes de partage pour l'espace de code, mappant un code d'un programme à plusieurs processus du programme. Cela signifie que si un programme contient 100 Ko de code et nécessite 100 Ko d'espace de données, ce qui signifie qu'un total de 200 Ko d'espace de processus est requis, le système d'exploitation allouera 200 Ko d'espace de processus lors de la première exécution du programme et 200 Ko d'espace de processus. l'espace sera alloué lors de la deuxième exécution du programme. Lorsqu'un processus est démarré, le système d'exploitation n'alloue que 100 Ko d'espace de données, tandis que l'espace de code partage l'espace du processus précédent.
Ce qui précède est la vue temporelle et spatiale de base du processus dans le système d'exploitation Windows. En fait, il existe une grande différence dans la vue temporelle et spatiale du processus entre les systèmes d'exploitation 16 bits et 32 bits de Windows.
En termes de temps, la gestion des processus des systèmes d'exploitation Windows 16 bits, tels que Windows 3.x, est très simple. Il s'agit en fait d'un système d'exploitation de gestion multitâche. De plus, la planification des tâches du système d'exploitation est passive. Si une tâche n'abandonne pas le traitement du message, le système d'exploitation doit attendre. En raison des failles dans la gestion des processus du système Windows 16 bits, lorsqu'un processus est en cours d'exécution, il occupe entièrement les ressources du processeur. À cette époque, pour que Windows 16 bits ait la possibilité de planifier d'autres tâches, Microsoft félicitait les développeurs d'applications Windows d'être des programmeurs à l'esprit large, de sorte qu'ils étaient prêts à écrire quelques lignes de code supplémentaires pour offrir le système opérateur. Au contraire, les systèmes d'exploitation WIN32, tels que Windows 95 et NT, disposent de véritables capacités de système d'exploitation multi-processus et multi-tâches. Le processus dans WIN32 est entièrement planifié par le système d'exploitation. Une fois la tranche de temps du processus en cours d'exécution terminée, le système d'exploitation passera activement au processus suivant, que le processus soit ou non en train de traiter des données. À proprement parler, le système d'exploitation Windows 16 bits ne peut pas être considéré comme un système d'exploitation complet, mais le système d'exploitation WIN32 32 bits est le véritable système d'exploitation. Bien entendu, Microsoft ne dira pas que WIN32 compense les défauts de Windows 16 bits, mais affirme que WIN32 implémente une technologie avancée appelée « multitâche préemptif », qui est une méthode commerciale.
D'un point de vue spatial, bien que l'espace des processus dans le système d'exploitation Windows 16 bits soit relativement indépendant, les processus peuvent facilement accéder à l'espace de données des autres. Étant donné que ces processus sont en réalité différents segments de données dans le même espace physique, des opérations d'adressage incorrectes peuvent facilement entraîner une lecture et une écriture incorrectes de l'espace et faire planter le système d'exploitation. Cependant, dans le système d'exploitation WIN32, chaque espace de processus est totalement indépendant. WIN32 fournit à chaque processus un espace d'adressage virtuel et continu allant jusqu'à 4G. Ce que l'on appelle l'espace d'adressage continu signifie que chaque processus dispose d'un espace d'adressage compris entre 00000000 $ et FFFFFFFF, plutôt que l'espace segmenté de Windows 16 bits. Dans WIN32, vous n'avez pas à vous soucier de vos opérations de lecture et d'écriture affectant involontairement les données dans d'autres espaces de processus, et vous n'avez pas à vous soucier que d'autres processus viennent harceler votre travail. Dans le même temps, l'espace virtuel 4G continu fourni par WIN32 pour votre processus est la mémoire physique mappée vers vous par le système d'exploitation avec le support du matériel. Bien que vous disposiez d'un espace virtuel si vaste, le système ne perdra jamais un octet. .mémoire physique.
Section 2 Espace de processus
Lorsque nous utilisons DELPHI pour écrire des applications WIN32, nous nous soucions rarement du monde interne du processus lorsqu'il est en cours d'exécution. Étant donné que WIN32 fournit 4G d'espace de processus virtuel continu pour notre processus, peut-être que la plus grande application au monde n'en utilise actuellement qu'une partie. Il semble que l'espace de processus soit illimité, mais l'espace de processus 4G est virtuel et la mémoire réelle de votre machine peut être loin d'être la même. Bien que le processus dispose d'un espace si vaste, certains programmes d'algorithmes complexes ne pourront toujours pas s'exécuter en raison d'un débordement de pile, en particulier les programmes contenant un grand nombre d'algorithmes récursifs.
Par conséquent, une compréhension approfondie de la structure de l'espace de processus 4G, de sa relation avec la mémoire physique, etc. nous aidera à comprendre plus clairement le monde spatio-temporel de WIN32, afin que nous puissions utiliser les méthodes correctes dans le travail de développement réel. . Vision du monde et méthodologie pour résoudre divers problèmes difficiles.
Ensuite, nous utiliserons une expérience simple pour comprendre le monde interne de l'espace de processus de WIN32. Cela peut nécessiter une certaine connaissance des registres CUP et du langage assembleur, mais j'ai essayé de l'expliquer dans un langage simple.
Au démarrage de DELPHI, un projet Project1 sera automatiquement généré et nous commencerons par celui-ci. Définissez un point d'arrêt n'importe où dans le programme d'origine de Project1.dpr, par exemple, définissez un point d'arrêt au début de la phrase. Ensuite, exécutez le programme et il s'arrêtera automatiquement lorsqu'il atteindra le point d'arrêt. À ce stade, nous pouvons ouvrir la fenêtre CPU dans l'outil de débogage pour observer la structure interne de l'espace de processus.
Le registre de pointeur d'instruction actuel Eip est arrêté à $0043E4B8. À partir des deux chiffres hexadécimaux les plus élevés de l'adresse où se trouve l'instruction du programme sont tous deux des zéros, on peut voir que le programme actuel est à la position d'adresse en bas du 4G. espace de processus, qui occupe 00000000 $ à assez petit espace d'adressage pour $FFFFFFFF.
Dans la zone de commande de la fenêtre CPU, vous pouvez consulter le contenu de l'espace de processus. Lors de la visualisation du contenu de l'espace inférieur à 00400000 $, vous trouverez une série de points d'interrogation "????" apparaissant dans le contenu inférieur à 00400000 $. Cela est dû au fait que l'espace d'adressage n'a pas été mappé à l'espace physique réel. Si vous regardez la valeur hexadécimale de la variable globale HInstance à ce moment-là, vous constaterez qu'elle est également de 00400000 $. Bien que HInstance reflète le handle de l'instance de processus, il s'agit en fait de la valeur de l'adresse de départ lorsque le programme est chargé en mémoire, également dans Windows 16 bits. Par conséquent, nous pouvons penser que le programme du processus est chargé à partir de 00400000 $, c'est-à-dire que l'espace à partir de 4M dans l'espace virtuel 4G est l'espace où le programme est chargé.
À partir de 00400000 $ et avant 0044D000 $, il s'agit principalement de l'espace d'adressage du code du programme et des données globales. Dans la zone de pile de la fenêtre CPU, vous pouvez afficher l'adresse de la pile actuelle. De même, vous constaterez que l’espace d’adressage actuel de la pile est compris entre 0067B000 $ et 00680000 $, avec une longueur de 5 000 $. En fait, la taille minimale de l'espace de pile du processus est de 5 000 $, obtenue en fonction de la valeur Min stack size définie dans la page Linker de ProjectOptions lors de la compilation du programme DELPHI, plus 1 000 $. La pile s'agrandit de l'adresse supérieure vers le bas. Lorsque la pile lorsque le programme est en cours d'exécution n'est pas suffisante, le système augmentera automatiquement la taille de l'espace de la pile vers l'adresse inférieure. Ce processus mappera davantage de mémoire réelle vers l'adresse inférieure. espace de processus. Lors de la compilation d'un programme DELPHI, vous pouvez contrôler l'espace de pile maximum qui peut être augmenté en définissant la valeur de Taille maximale de la pile dans la page Linker dans ProjectOptions. En particulier dans les programmes qui contiennent des relations d'appel de sous-programmes approfondies ou qui utilisent des algorithmes récursifs, la valeur de la taille maximale de la pile doit être définie de manière raisonnable. Parce que l'appel d'un sous-programme nécessite de l'espace dans la pile, et une fois la pile épuisée, le système générera une erreur « Débordement de pile ».
Il semble que l'espace de processus après l'espace de pile devrait être de l'espace libre. En fait, ce n'est pas le cas. Les informations pertinentes de WIN32 indiquent que l'espace 2G après 80 000 000 $ est l'espace utilisé par le système. Il semble que le processus ne puisse réellement posséder que de l'espace 2G. En fait, l'espace qu'un processus peut réellement posséder n'est même pas 2G, car l'espace de 4 millions de dollars allant de 00000000 $ à 00400000 $ est également une zone restreinte.
Quoi qu’il en soit, les adresses que notre processus peut utiliser restent très larges. Surtout après l'espace de pile et entre 80 000 000 $, c'est le principal champ de bataille de l'espace de processus. L'espace mémoire alloué par le processus depuis le système sera mappé sur cet espace, la bibliothèque de liens dynamiques chargée par le processus sera mappée sur cet espace, l'espace de la pile de threads du nouveau thread sera également mappé sur cet espace, presque tous les opérations impliquant l'allocation de mémoire seront toutes mappées sur cet espace. Veuillez noter que le mappage mentionné ici signifie la correspondance entre la mémoire réelle et cet espace virtuel qui n'est pas mappé à la mémoire réelle, tout comme la chaîne de "" dans la boîte de commande de la fenêtre CPU lors du débogage. ???".
............
Merci d'avoir lu!