В процессе использования DELPHI для разработки программного обеспечения мы подобны группе счастливых коров и овец на лугу, беззаботно наслаждающихся солнечным светом, который дарит нам язык Object Pascal, и богатыми водными растениями, обеспечиваемыми различными элементами управления VCL. Глядя на безграничное голубое небо, глядя вниз на пышную зеленую траву на земле, кто бы мог подумать о том, насколько велика Вселенная и какие вещи меньше молекул и атомов? Это дело философов. В это время философ сидел на вершине высокой горы, смотрел вверх на изменения в туманностях Вселенной, смотрел на ползающих по земле насекомых, внезапно оглядываясь назад, кивая и улыбаясь нашей группе пасущихся крупный рогатый скот и овцы. Он взял травинку, осторожно подержал ее во рту, закрыл глаза и внимательно попробовал ее на вкус. Интересно, какой вкус был у этой травки во рту философа? Однако на его лице всегда была довольная улыбка.
Знание и понимание микроскопического атомарного мира DELPHI может позволить нам полностью понять макроскопическую структуру приложений DELPHI, тем самым развивая наше программное обеспечение в более широком идеологическом пространстве. Это похоже на то, как будто Ньютон открыл движение макроскопических объектов, но был обеспокоен тем, что не мог понять, почему объекты движутся именно так. Напротив, Эйнштейн пережил счастливую жизнь относительности между законами основных частиц и движением макроскопических объектов. !
Раздел 1. Атом объекта TObject.
Что такое ТОбъект?
Это базовое ядро архитектуры языка Object Pascal и источник различных элементов управления VCL. Мы можем думать о TObject как об одном из атомов, составляющих приложение DELPHI. Конечно, они состоят из более тонких частиц, таких как базовые элементы синтаксиса Паскаля.
Говорят, что TObject является атомом программы DELPHI, поскольку TObject поддерживается внутри компилятора DELPHI. Все классы объектов являются производными от TObject, даже если вы не указываете TObject в качестве класса-предка. TObject определяется в модуле System, который является частью системы. В начале модуля System.pas есть текст комментария:
{Предопределенные константы, типы, процедуры, }
{ и функции (например, True, Integer или }
{Writeln) не имеют фактических деклараций.}
{ Вместо этого они встроены в компилятор }
{ и рассматриваются так, как если бы они были объявлены }
{ в начале системного блока }
Это означает, что этот модуль содержит предопределенные константы, типы, процедуры и функции (например: True, Integer или Writeln). Они фактически не объявлены, но встроены компилятором и используются в начале компиляции. быть установленным определением. Вы можете добавить другие файлы исходной программы, такие как Classes.pas или Windows.pas, в файл проекта для компиляции и отладки исходного кода, но вы абсолютно не можете добавить файл исходной программы System.pas в файл проекта для компиляции! DELPHI сообщит об ошибках компиляции для повторяющихся определений System!
Таким образом, TObject — это определение, предоставляемое компилятором внутри. Для тех из нас, кто использует DELPHI для разработки программ, TObject — это атомарная вещь.
Определение TObject в модуле System следующее:
ТОбъект = класс
конструктор Создать;
процедура Бесплатно;
функция класса InitInstance (Экземпляр: Указатель): TObject;
процедура CleanupInstance;
функция ClassType: TClass;
функция класса ClassName: ShortString;
функция класса ClassNameIs (const Name: string): Boolean;
функция класса ClassParent: TClass;
функция класса ClassInfo: Указатель;
функция класса InstanceSize: Longint;
функция класса InheritsFrom(AClass: TClass): Boolean;
функция класса MethodAddress(const Name: ShortString): Указатель;
функция класса MethodName (Адрес: Указатель): ShortString;
функция FieldAddress(const Name: ShortString): Указатель;
функция GetInterface(const IID: TGUID; out Obj): Boolean;
функция класса GetInterfaceEntry(const IID: TGUID): PInterfaceEntry;
функция класса GetInterfaceTable: PInterfaceTable;
функция SafeCallException (ExceptObject: TObject;
ExceptAddr: Указатель): HResult virtual;
процедура AfterConstruction виртуальная;
процедура BeforeDestruction виртуальная;
процедура Dispatch (вар Сообщение виртуальное);
процедура DefaultHandler (вар Сообщение виртуальное);
функция класса NewInstance: TObject виртуальный;
процедура FreeInstance виртуальная;
деструктор Уничтожить виртуальный;
конец;
Далее мы будем постепенно постучаться в дверь атомов TObject, чтобы посмотреть, какая структура находится внутри.
Мы знаем, что TObject — это базовый класс всех объектов. Так что же такое объект?
Любой объект в DELPHI — это указатель, который указывает место, занимаемое объектом в памяти! Хотя объект является указателем, когда мы ссылаемся на члены объекта, нам не нужно писать код MyObject^.GetName, а можно только написать MyObject.GetName. Это расширенный синтаксис языка Object Pascal. поддерживается компилятором. Друзья, использующие C++ Builder, очень четко понимают взаимосвязь между объектами и указателями, поскольку объекты в C++ Builder должны определяться как указатели. Место, на которое указывает указатель объекта, — это пространство объекта, в котором объект хранит данные. Давайте проанализируем структуру данных пространства памяти, на которое указывает указатель объекта.
Первые 4 байта пространства объектов указывают на таблицу адресов виртуальных методов (VMT – Таблица виртуальных методов) класса объектов. Следующее пространство — это пространство для хранения данных-членов самого объекта, оно хранится в общем порядке от элементов данных самого примитивного класса-предка объекта до членов данных объектного класса, а также в том порядке, в котором члены данных определяются на каждом уровне класса.
Таблица виртуальных методов класса (VMT) содержит адреса процедур виртуальных методов всех классов, производных от исходного класса-предка. Виртуальный метод класса — это метод, объявленный с зарезервированным словом virtual. Виртуальный метод — это базовый механизм достижения полиморфизма объектов. Хотя динамические методы, объявленные с зарезервированным словом Dynamic, также могут обеспечивать полиморфизм объектов, такие методы не хранятся в таблице адресов виртуальных методов (VMT). Это всего лишь еще один метод, предоставляемый Object Pascal, который может сэкономить пространство для хранения классов. Механизм полиморфной реализации. но в ущерб скорости звонка.
Даже если мы сами не определяем какой-либо виртуальный метод класса, объект класса все равно имеет указатель на таблицу адресов виртуального метода, но длина записи адреса равна нулю. Однако где хранятся виртуальные методы, определенные в TObject, такие как Destroy, FreeInstance и т. д.? Оказывается, адреса их методов хранятся в пространстве со смещением в отрицательном направлении относительно указателя VMT. Фактически, смещение пространства данных на 76 байт в отрицательном направлении таблицы VMT представляет собой системную структуру данных класса объектов. Эти структуры данных связаны с компилятором и могут быть изменены в будущих версиях DELPHI.
Следовательно, можно думать, что VMT — это структура данных, начинающаяся с адресного пространства с отрицательным смещением. Область данных с отрицательным смещением — это область системных данных VMT, а данные с положительным смещением VMT — это область пользовательских данных (настраиваемый виртуальный метод). адресная таблица). Функции и процедуры, связанные с информацией о классе или информацией о времени выполнения объекта, определенной в TObject, обычно связаны с системными данными VMT.
Данные VMT представляют собой класс. Фактически, VMT — это класс! В Object Pascal мы используем такие идентификаторы, как TObject, TComponent и т. д., для представления классов, которые реализуются как соответствующие им данные VMT внутри DELPHI. Тип класса, определенный с помощью класса зарезервированного слова, на самом деле является указателем на соответствующие данные VMT.
Для нашего приложения данные VMT являются статическими данными. После того, как компилятор скомпилировал наше приложение, эта информация данных была определена и инициализирована. Программные операторы, которые мы пишем, могут получать доступ к информации, связанной с VMT, получать такую информацию, как размер объекта, имя класса или данные атрибутов времени выполнения, или вызывать виртуальные методы, или читать имя и адрес метода и т. д.
Когда объект создается, система выделяет для него пространство памяти и связывает его с соответствующим классом. Таким образом, первые 4 байта в пространстве данных, выделенном для объекта, становятся указателями на данные класса VMT.
Давайте посмотрим, как объекты рождаются и умирают. Наблюдая за тем, как мой трехлетний сын прыгает по траве, именно потому, что я стал свидетелем процесса рождения жизни, я могу по-настоящему понять смысл и величие жизни. Только те, кто пережил смерть, поймут и будут больше ценить жизнь. Итак, давайте разберемся в процессе создания и смерти объектов!
Мы все знаем, что простейший объект можно построить с помощью следующего утверждения:
AnObject := TObject.Create;
Компилятор реализует свою компиляцию как:
На основе VMT, соответствующего TObject, вызовите конструктор Create TObject. Конструктор Create вызывает системный процесс ClassCreate, а системный процесс ClassCreate вызывает виртуальный метод NewInstance через хранящийся в нем класс VMT. Цель вызова метода NewInstance — установить пространство экземпляра объекта. Поскольку мы не перегрузили этот метод, это NewInstance класса TObject. Метод NewInstance класса TObjec вызовет процедуру GetMem для выделения памяти для объекта на основе размера экземпляра объекта (InstanceSize), инициализированного компилятором в таблице VMT, а затем вызовет метод InitInstance для инициализации выделенного пространства. Метод InitInstance сначала инициализирует первые 4 байта пространства объектов указателем на VMT, соответствующий классу объектов, а затем очищает оставшееся пространство. После создания экземпляра объекта также вызывается виртуальный метод AfterConstruction. Наконец, сохраните указатель адреса данных экземпляра объекта в переменной AnObject, и таким образом появится объект AnObject.
Аналогично, объект можно уничтожить, используя следующий оператор:
АнОбъект.Уничтожить;
Деструктор TObject, Destroy, объявлен как виртуальный метод, который также является одним из встроенных виртуальных методов системы. Метод Destory сначала вызывает виртуальный метод BeforeDestruction, а затем вызывает системный процесс ClassDestroy. Процесс ClassDestory вызывает виртуальный метод FreeInstance через класс VMT, а метод FreeInstance вызывает процесс FreeMem для освобождения пространства памяти объекта. Вот так объект исчезает из системы.
Процесс разрушения объектов проще, чем процесс создания объектов, точно так же, как зарождение жизни — это длительный процесс вынашивания, но смерть относительно недолговечна. Это кажется неизбежным правилом.
В процессе создания и уничтожения объекта вызываются две виртуальные функции, NewInstance и FreeInstance, для создания и освобождения пространства памяти экземпляра объекта. Причина, по которой эти две функции объявлены как виртуальные, заключается в том, чтобы предоставить пользователям возможность расширения при написании специальных классов объектов, которые требуют от пользователей управления собственной памятью (например, в некоторых специальных программах промышленного управления).
Объявление AfterConstruction и BeforeDestruction как виртуальных функций также должно дать производному классу в будущем возможность позволить новорожденному объекту вдохнуть первый глоток свежего воздуха после создания объекта и позволить объекту завершить последствия до того, как объект умрет. . Это все имеет смысл. Фактически, событие OnCreate и событие OnDestroy объекта TForm и объекта TDataModule запускаются соответственно в двух процессах виртуальных функций: перегрузке TForm и TDataModule.
Кроме того, TObjec также предоставляет метод Free, который не является виртуальным методом. Он специально предназначен для безопасного освобождения объекта, когда неясно, является ли объект пустым (ноль). Фактически, если вы не можете выяснить, пуст ли объект, возникает проблема неясной логики программы. Однако никто не идеален и может совершать ошибки. Использование Free также полезно, чтобы избежать случайных ошибок. Однако написание правильных программ не может полагаться исключительно на такие решения. Первой целью программирования должно быть обеспечение логической корректности программы!
Заинтересованные друзья могут прочитать оригинальный код модуля System, где большой объем кода написан на языке ассемблера. Внимательные друзья могут обнаружить, что конструктор Create и деструктор Destory TObject не написали никакого кода. Фактически, через окно Debug CPU в состоянии отладки можно четко отобразить ассемблерный код Create и Destory. Потому что мастера, создавшие DELPHI, не хотели предоставлять пользователям слишком много сложных вещей. Они хотели, чтобы пользователи писали приложения, основанные на простых концепциях, и скрывали для них сложную работу внутри системы. Поэтому при публикации модуля System.pas коды этих двух функций специально удаляются, чтобы заставить пользователей думать, что TObject — это источник всего сущего, а пользовательские классы и вовсе начинаются с небытия. Это само по себе не является неправильным. Хотя чтение этих наиболее важных кодов DELPHI требует небольшого знания языка ассемблера, чтение таких кодов может дать нам более глубокое понимание происхождения и развития мира DELPHI. Даже если вы мало что понимаете, способность понимать хотя бы некоторые базовые вещи окажет нам большую помощь при написании программ DELPHI.
Раздел 2. TClass Atom
В модуле System.pas TClass определяется следующим образом:
TClass = класс TObject;
Это означает, что TClass — это класс TObject. Поскольку TObject сам по себе является классом, TClass — это так называемый класс классов.
Концептуально TClass — это тип класса, то есть класс. Однако мы знаем, что класс DELPHI представляет собой часть данных VMT. Таким образом, класс можно рассматривать как тип, определенный для элемента данных VMT. Фактически, это тип указателя, указывающий на данные VMT!
В предыдущем традиционном языке C++ тип класса не мог быть определен. После компиляции объекта он фиксируется, структурная информация класса преобразуется в абсолютный машинный код, и полная информация о классе не будет существовать в памяти. Некоторые объектно-ориентированные языки более высокого уровня могут поддерживать динамический доступ и вызов информации о классе, но они часто требуют сложного внутреннего механизма интерпретации и большего количества системных ресурсов. Язык Object Pascal DELPHI вобрал в себя некоторые замечательные особенности объектно-ориентированных языков высокого уровня, сохраняя при этом традиционное преимущество прямой компиляции программ в машинный код, что прекрасно решает проблемы расширенных функций и эффективности программ.
Именно потому, что DELPHI сохраняет в приложении полную информацию о классах, он может предоставлять расширенные объектно-ориентированные функции, такие как преобразование и идентификация классов во время выполнения, в которых ключевую роль играют данные VMT класса. Заинтересованные друзья могут прочитать два процесса сборки AsClass и IsClass в модуле System. Это коды реализации операторов as и is, позволяющие глубже понять классы и данные VMT.
Атомный мир DELPHI (2)
Ключевые слова: Delphi контролирует разное.
Раздел 2. TClass Atom
В модуле System.pas TClass определяется следующим образом:
TClass = класс TObject;
Это означает, что TClass — это класс TObject. Поскольку TObject сам по себе является классом, TClass — это так называемый класс классов.
Концептуально TClass — это тип класса, то есть класс. Однако мы знаем, что класс DELPHI представляет собой часть данных VMT. Таким образом, класс можно рассматривать как тип, определенный для элемента данных VMT. Фактически, это тип указателя, указывающий на данные VMT!
В предыдущем традиционном языке C++ тип класса не мог быть определен. После компиляции объекта он фиксируется, структурная информация класса преобразуется в абсолютный машинный код, и полная информация о классе не будет существовать в памяти. Некоторые объектно-ориентированные языки более высокого уровня могут поддерживать динамический доступ и вызов информации о классе, но они часто требуют сложного внутреннего механизма интерпретации и большего количества системных ресурсов. Язык Object Pascal DELPHI вобрал в себя некоторые замечательные особенности объектно-ориентированных языков высокого уровня, сохраняя при этом традиционное преимущество прямой компиляции программ в машинный код, что прекрасно решает проблемы расширенных функций и эффективности программ.
Именно потому, что DELPHI сохраняет в приложении полную информацию о классах, он может предоставлять расширенные объектно-ориентированные функции, такие как преобразование и идентификация классов во время выполнения, в которых ключевую роль играют данные VMT класса. Заинтересованные друзья могут прочитать два процесса сборки AsClass и IsClass в модуле System. Это коды реализации операторов as и is, позволяющие глубже понять классы и данные VMT.
Учитывая тип класса, вы можете использовать его как переменную. Переменную класса можно понимать как специальный объект, и вы можете обращаться к методам переменной класса точно так же, как к объекту. Например: Давайте посмотрим на следующий фрагмент программы:
тип
TSampleClass = класс TSampleObject;
TSampleObject = класс (TObject)
общественный
конструктор Создать;
деструктор Уничтожить;
функция класса GetSampleObjectCount:Integer;
процедура GetObjectIndex:Integer;
конец;
вар
аSampleClass: TSampleClass;
аКласс: ТКласс;
В этом коде мы определяем класс TSampleObject и связанный с ним тип класса TSampleClass, а также две переменные класса aSampleClass и aClass. Кроме того, мы также определили конструктор, деструктор, метод класса GetSampleObjectCount и метод объекта GetObjectIndex для класса TSampleObject.
Во-первых, давайте поймем значение переменных класса aSampleClass и aClass.
Очевидно, что вы можете рассматривать TSampleObject и TObject как постоянные значения и присваивать их переменным aClass, точно так же, как присваиваете 123 константных значения целочисленной переменной i. Следовательно, связь между типами классов, классами и переменными класса — это связь между типами, константами и переменными, но на уровне класса, а не на уровне объекта. Конечно, недопустимо напрямую присваивать TObject aSampleClass, поскольку aSampleClass — это переменная класса TSampleObject, производного от TObject, а TObject не содержит всех определений, совместимых с типом TSampleClass. Напротив, присвоение TSampleObject переменной Class является законным, поскольку TSampleObject является производным классом TObject и совместим с типом TClass. Это в точности похоже на отношение присваивания и сопоставления типов объектных переменных.
Затем давайте посмотрим, что такое методы класса.
Так называемый метод класса относится к методу, вызываемому на уровне класса, например к методу GetSampleObjectCount, определенному выше, который представляет собой метод, объявленный с зарезервированным словом class. Методы класса отличаются от методов объекта, вызываемых на уровне объекта. Методы объекта уже знакомы нам, и методы класса всегда используются на уровне доступа и контроля общих характеристик всех объектов класса и централизованного управления объектами. В определении TObject мы можем найти большое количество методов класса, таких как ClassName, ClassInfo, NewInstance и т. д. Среди них NewInstance также определяется как виртуальный, то есть метод виртуального класса. Это означает, что вы можете переписать метод реализации NewInstance в производном подклассе, чтобы особым образом создавать экземпляры объектов этого класса.
Вы также можете использовать идентификатор self в методах класса, но его значение отличается от значения self в методах объекта. Self в методе класса представляет собственный класс, то есть указатель на VMT, тогда как self в методе объекта представляет сам объект, то есть указатель на пространство данных объекта. Хотя методы класса можно использовать только на уровне класса, вы все равно можете вызывать методы класса через объект. Например, метод класса ClassName объекта TObject можно вызвать с помощью оператора aObject.ClassName, поскольку первые 4 байта в пространстве данных объекта, на которое указывает указатель объекта, являются указателями на класс VMT. Напротив, вы не можете вызывать методы объекта на уровне класса, а такие операторы, как TObject.Free, должны быть незаконными.
Стоит отметить, что конструктор — это метод класса, а деструктор — метод объекта!
Что? Конструкторы — это методы класса, а деструкторы — это методы объекта! Была ли какая-то ошибка?
Видите ли, когда вы создаете объект, вы явно используете утверждение, подобное следующему:
aObject := TObject.Create;
Очевидно, что он вызывает метод Create класса TObject. При удалении объекта используйте следующий оператор:
aОбъект.Уничтожить;
Даже если вы используете метод Free для освобождения объекта, метод Destroy объекта вызывается косвенно.
Причина очень проста: до того, как объект создан, он еще не существует, существует только класс. Вы можете использовать методы класса только для создания объектов. Напротив, удаление объекта должно удалить существующий объект. Освобождается объект, а не класс.
Наконец, давайте обсудим вопрос о вымышленных конструкторах.
В традиционном языке C++ виртуальные деструкторы могут быть реализованы, но реализация виртуальных конструкторов представляет собой сложную проблему. Потому что в традиционном языке C++ нет типов классов. Экземпляры глобальных объектов существуют в глобальном пространстве данных во время компиляции, а локальные объекты функций также являются экземплярами, отображаемыми в пространстве стека во время компиляции. Даже динамически создаваемые объекты помещаются в фиксированную структуру класса с помощью нового оператора. в куче, а конструктор — это просто метод объекта, который инициализирует сгенерированный экземпляр объекта. В традиционном языке C++ нет реальных методов класса. Даже если можно определить так называемые статические методы на основе классов, они в конечном итоге реализуются как специальная глобальная функция, не говоря уже о методах виртуального класса, которые могут быть нацелены только на конкретный объект. случаи. Поэтому традиционный язык C++ считает, что до того, как будет сгенерирован конкретный экземпляр объекта, невозможно сконструировать сам объект на основе генерируемого объекта. Это действительно невозможно, потому что это создало бы противоречивый парадокс в логике!
Однако именно благодаря ключевым понятиям информации о типах динамических классов, истинно виртуальных методов классов и конструкторов, реализованных на основе классов в DELPHI, виртуальные конструкторы могут быть реализованы. Предметы производятся классами. Предмет подобен растущему младенцу, а класс – его матери. Малыш сам не знает, каким человеком он станет в будущем, но матери используют свои собственные методы воспитания для воспитания разных детей. Люди, принципы те же.
Именно в определении класса TComponent конструктор Create определен как виртуальный, чтобы разные типы элементов управления могли реализовывать свои собственные методы построения. В этом величие таких концепций, как классы, созданные TClass, а также величие DELPHI.
.................................................. ..
Глава 3. Представление времени и пространства в WIN32
Мой старый отец посмотрел на своего маленького внука, играющего с игрушками на земле, а потом сказал мне: «Этот ребенок такой же, как ты, когда ты был ребенком. Он любит разбирать вещи и останавливается только после того, как досмотрит их до конца. " Вспоминая то время, когда я был ребенком, я часто разбирал игрушечные машинки, маленькие будильники, музыкальные шкатулки и т. д., и мама часто меня ругала.
Впервые я понял основные принципы работы компьютеров, когда разобрал музыкальную шкатулку. Это было в комиксе, когда я учился в старшей школе. Старик с белой бородой объяснял теорию умных машин, а дядя с усами рассказывал о компьютерах и музыкальных шкатулках. Они сказали, что центральный процессор компьютера — это ряд музыкальных тростей, используемых для произношения в музыкальной шкатулке, а компьютерная программа — это плотно расположенные выступы на маленьком цилиндре в музыкальной шкатулке. Вращение маленького цилиндра эквивалентно. к вращению центрального процессора. Естественное движение указателя инструкций, в то время как выступы, изображающие музыку на маленьком цилиндре, управляют вибрацией музыкальной трости для создания инструкций, эквивалентных выполнению программы центральным процессором. Музыкальная шкатулка издает красивую мелодию, которая исполняется в соответствии с партитурой, выгравированной мастером на маленьком цилиндре. Компьютер выполняет сложную обработку на основе программы, заранее запрограммированной программистом. Поступив в колледж, я узнал, что старик с седой бородой был научным гигантом Тьюрингом. Его теория конечных автоматов способствовала развитию всей информационной революции, а дядя с усами был отцом компьютеров фон Нейманом. Компьютерная архитектура по-прежнему остается основной архитектурной структурой компьютеров. Музыкальную шкатулку разобрали не зря, мама может не сомневаться.
Только при простом и глубоком понимании мы можем создавать глубокие и лаконичные творения.
В этой главе мы обсудим основные концепции, связанные с нашим программированием в 32-битной операционной системе Windows, и установим правильное представление времени и пространства в WIN32. Я надеюсь, что после прочтения этой главы мы сможем глубже понять программы, процессы и потоки, понять принципы работы исполняемых файлов, динамически подключаемых библиотек и пакетов времени выполнения, а также ясно увидеть правду о глобальных данных, локальных данных и параметрах в памяти. .
Раздел 1. Понимание процесса
По историческим причинам Windows возникла из DOS. В эпоху DOS у нас всегда была только концепция программы, но не концепция процесса. В то время только обычные операционные системы, такие как UNIX и VMS, имели концепцию процессов, а мультипроцессы означали миникомпьютеры, терминалы и множество пользователей, что также означало деньги. Большую часть времени я мог использовать только относительно дешевые микрокомпьютеры и системы DOS. С процессами и миникомпьютерами я начал знакомиться только тогда, когда изучал операционные системы.
Это было только после Windows 3. Раньше в DOS одновременно могла выполняться только одна программа, но в Windows можно было одновременно выполнять несколько программ. Это многозадачность. При запуске программы под DOS одна и та же программа не может быть запущена одновременно, но под Windows одновременно может быть запущено более двух копий одной и той же программы, и каждая работающая копия программы является процессом. Точнее, каждый запуск любой программы порождает задачу, а каждая задача — это процесс.
Когда программы и процессы понимаются вместе, слово «программа» можно рассматривать как относящееся к статическим вещам. Типичная программа — это статический код и данные, состоящие из файла EXE или файла EXE плюс нескольких файлов DLL. Процесс — это запуск программы, которая представляет собой код и динамически изменяющиеся данные, которые динамически выполняются в памяти. Когда для запуска статической программы требуется выполнение, операционная система выделяет определенное пространство памяти для этой операции, переносит статический программный код и данные в эти области памяти, а также перемещает и отображает программный код и данные в этом пространстве. выполняется внутри, создавая таким образом динамический процесс.
Две копии одной и той же программы, работающие одновременно, означают, что в системной памяти имеются два пространства процессов, но их программные функции одинаковы, но они находятся в разных динамически изменяющихся состояниях.
Что касается времени выполнения процесса, каждый процесс выполняется одновременно. Профессиональный термин называется параллельным выполнением или параллельным выполнением. Но это в основном поверхностное ощущение, которое дает нам операционная система. На самом деле каждый процесс выполняется в режиме разделения времени, то есть каждый процесс по очереди занимает процессорное время для выполнения программных инструкций процесса. Для ЦП одновременно выполняются только инструкции одного процесса. Операционная система является манипулятором работы запланированного процесса. Она постоянно сохраняет и переключает текущее состояние каждого процесса, выполняемого в ЦП, так что каждый запланированный процесс считает, что он выполняется полностью и непрерывно. Поскольку планирование процессов с разделением времени происходит очень быстро, у нас создается впечатление, что все процессы выполняются одновременно. Фактически, настоящая одновременная работа возможна только в аппаратной среде с несколькими процессорами. Когда мы позже поговорим о потоках, мы обнаружим, что потоки — это то, что действительно управляет процессом, и, что более важно, они предоставляют пространство для процесса.
С точки зрения пространства, занимаемого процессом, каждое пространство процесса относительно независимо, и каждый процесс выполняется в своем собственном независимом пространстве. Программа включает в себя как пространство кода, так и пространство данных. И код, и данные занимают пространство процесса. Windows выделяет фактическую память для пространства данных, необходимого каждому процессу, и обычно использует методы совместного использования пространства кода, сопоставляя один код программы с несколькими процессами программы. Это означает, что если программа имеет 100 КБ кода и требует 100 КБ пространства данных, что означает, что в общей сложности требуется 200 КБ пространства процесса, операционная система выделит 200 КБ пространства процесса при первом запуске программы и 200 КБ пространства процесса. пространство будет выделено при втором запуске программы. При запуске процесса операционная система выделяет только 100 КБ пространства данных, в то время как пространство кода разделяет пространство предыдущего процесса.
Выше приведено базовое представление процесса во времени и пространстве в операционной системе Windows. Фактически, существует большая разница во времени и пространстве представления процесса между 16-битными и 32-битными операционными системами Windows.
С точки зрения времени управление процессами в 16-разрядных операционных системах Windows, таких как Windows 3.x, очень просто. На самом деле это просто многозадачная операционная система. Более того, планирование задач операционной системы является пассивным. Если задача не прекращает обработку сообщения, операционная система должна подождать. Из-за недостатков управления процессами 16-битной системы Windows, когда процесс запущен, он полностью занимает ресурсы ЦП. В те дни, чтобы у 16-битной Windows была возможность планировать другие задачи, Microsoft хвалила разработчиков приложений Windows за то, что они были программистами с широким кругозором, поэтому они были готовы написать еще несколько строк кода, чтобы подарить Операционная система. Напротив, операционные системы WIN32, такие как Windows 95 и NT, обладают реальными возможностями многопроцессной и многозадачной операционной системы. Процесс в WIN32 полностью запланирован операционной системой. Как только интервал времени выполнения процесса заканчивается, операционная система активно переключается на следующий процесс независимо от того, обрабатывает ли процесс данные. Строго говоря, 16-битная операционная система Windows не может рассматриваться как полная операционная система, но 32-разрядная операционная система Win32 является истинной операционной системой. Конечно, Microsoft не скажет, что Win32 компенсирует недостатки 16-битных Windows, но утверждает, что Win32 реализует передовую технологию, называемую «превентивную многозадачность», которая является коммерческим методом.
С точки зрения пространства, хотя пространство процесса в 16-битной операционной системе Windows является относительно независимым, процессы могут легко получить доступ к пространству данных друг друга. Поскольку эти процессы на самом деле представляют собой различные сегменты данных в одном физическом пространстве, а неправильные адресные операции могут легко вызвать неправильное чтение пространства и письменность, а также разрушить операционную систему. Однако в операционной системе Win32 каждое пространство процесса является полностью независимым. Win32 предоставляет каждому процессу виртуальное и непрерывное адресное пространство до 4G. Так называемое непрерывное адресное пространство означает, что каждый процесс имеет адресное пространство от $ 00000000 до $ ffffffff, а не сегментированное пространство 16-битных окон. В Win32 вам не нужно беспокоиться о ваших операциях чтения и записи, непреднамеренно влияя на данные в других процессовых пространствах, и вам не нужно беспокоиться о других процессах, которые будут преследовать вашу работу. В то же время, непрерывное виртуальное пространство 4G, предоставленное Win32 для вашего процесса, - это физическая память, отображаемая вам операционной системой с поддержкой оборудования. ..
Раздел 2 Пространство процесса
Когда мы используем Delphi для написания приложений Win32, мы редко заботимся о внутреннем мире процесса, когда он работает. Поскольку Win32 предоставляет 4G непрерывного виртуального процесса пространства процесса для нашего процесса, возможно, крупнейшее приложение в мире в настоящее время использует его только часть. Кажется, что пространство процесса не ограничено, но пространство процесса 4G является виртуальным, и фактическая память о вашей машине может быть далеко от этого. Хотя этот процесс имеет такое обширное пространство, некоторые сложные алгоритмные программы по -прежнему не смогут работать из -за переполнения стека, особенно программ, содержащих большое количество рекурсивных алгоритмов.
Следовательно, глубокое понимание структуры пространства процесса 4G, его взаимосвязь с физической памятью и т. Д. поможет нам более четко понять пространство-время Win32, чтобы мы могли использовать правильные методы в реальной работе по разработке ..
Далее мы будем использовать простой эксперимент, чтобы понять внутренний мир процесса Win32. Это может потребовать некоторых знаний о регистрах CUP и языке сбора, но я попытался объяснить это на простом языке.
Когда Delphi будет запущен, проект Project1 будет создан автоматически, и мы начнем с него. Например, установите точку останова в любом месте исходной программы Project1.dpr, установите точку останова в начальном предложении. Затем запустите программу, и она автоматически остановится, когда достигнет точки останова. В настоящее время мы можем открыть окно ЦП в инструменте отладки, чтобы наблюдать внутреннюю структуру пространства процесса.
Текущий регистр указателя инструкций EIP останавливается на уровне $ 0043E4B8. Пространство процесса, которое занимает $ 00000000 для симпатичного маленького адресного пространства для $ fffffffff.
В командном поле в окне процессора вы можете посмотреть на содержимое пространства процесса. При просмотре содержания пространства менее чем $ 00400000 вы найдете серию вопросительных знаков "????" Если вы посмотрите на шестнадцатеричную ценность глобальной переменной hinstance в настоящее время, вы обнаружите, что это также $ 00400000. Хотя Hinstance отражает ручку экземпляра процесса, на самом деле, это исходное значение адреса, когда программа загружается в память, а также в 16-битные Windows. Таким образом, мы можем подумать, что программа процесса загружается, начиная с $ 00400000, то есть пространство, начинающееся с 4 м в виртуальном пространстве 4G, является пространством, где программа загружается.
Начиная с $ 00400000 и до $ 00444D000, это в основном адресное пространство программного кода и глобальные данные. В окне стека в окне процессора вы можете просмотреть адрес текущего стека. Точно так же вы обнаружите, что текущее адресное пространство стека составляет от $ 0067b000 до $ 00680000, с длиной 5000 долларов. Фактически, минимальный размер пространства стека в процессе составляет 5000 долл. США, который получается на основе значения размера стека MIN на странице Linker ProjectOptions при составлении программы Delphi, плюс 1000 долларов США. Степень растет от высококачественного адреса внизу. Пространство процесса. При составлении программы Delphi вы можете управлять максимальным пространством стека, которое можно увеличить, установив значение максимального размера стека на странице Linker в ProjectOptions. Особенно в программах, которые содержат глубокие отношения подпрограммы или используют рекурсивные алгоритмы, значение максимального размера стека должно быть установлено разумно. Поскольку вызов подпрограммы требует места для стека, и после того, как стек исчерпана, система добавит ошибку «переполнения стека».
Кажется, что пространство процесса после места стека должно быть свободным пространством. Фактически, это не так. Кажется, что процесс действительно может иметь только 2G -пространство. Фактически, пространство, которое может действительно владеть процессом, не является даже 2G, потому что пространство 4 м от $ 00000000 до 00400000 также является ограниченной зоной.
Но несмотря ни на что, адреса, которые может использовать наш процесс, все еще очень широкие. Особенно после места в стеке и от 80 000 000 долларов, это основное поле битвы процесса. Пространство памяти, выделяемое процессом из системы, будет сопоставлено с этим пространством, библиотека динамических ссылок, загруженная процессом Операции, включающие распределение памяти, все будут сопоставлены с этим пространством. Обратите внимание, что отображение здесь означает соответствие между фактической памятью и этим виртуальным пространством. ???? ".