- •Лабораторный практикум «Основы разработки приложений Windows» Книга 2
- •Часть 1. Теоретические сведения4
- •Часть 2. Лабораторный практикум73
- •Часть 1 Теоретические сведения
- •1. Основы архитектурЫ защищенного режима Регистры процессора
- •Адресация памяти
- •2. Логические шрифты Создание логических шрифтов
- •Вывод на экран текстовых строк
- •3. Таймеры Windows Организация и обслуживание таймеров
- •Мультимедийные таймеры
- •Измерение интервалов времени
- •Организация периодического процесса
- •Задание однократного интервала времени
- •4. Дочерние окна Создание и использование дочерних окон
- •Окна предопределенных классов в главном окне
- •5. Вывод растровых изображений
- •Процедура вывода растрового изображения
- •Компоновка составных изображений
- •6. Обслуживание файлов в 32-разрядных приложениях Windows
- •Базовые операции с файлами Открытие и создание файла
- •Запись и чтение файла
- •Файлы, проецируемые в память
- •7. Процессы и потоки
- •Создание дочернего процесса
- •Создание дочернего потока
- •Синхронизация потоков Общие характеристики объектов Windows
- •Синхронизация с помощью состояний потока
- •Синхронизация с помощью событий
- •Критические секции и защита данных
- •8. Библиотеки динамической компоновки
- •Часть 2 Лабораторный практикум Работы лабораторного практикума Работа 1. Создание логических шрифтов
- •Работа 2. Таймеры Windows(индивидуальное задание а)
- •Работа 3. Дочернее окно в главном окне приложения
- •Работа 4. Вывод растровых изображений с использованием совместимой памяти
- •Работа 5. Измерение временных характеристик программы с помощью мультимедийного таймера
- •Работа 6. Вывод движущихся изображений с синхронизацией от системного таймера (индивидуальное задание b)
- •Работа 7. Повышение качества движущихся изображений с помощью совместимой памяти
- •Работа 8. Движение изображения по фоновому рисунку
- •Работа 9. Работа с файлами (индивидуальное задание c)
- •Работа 10. Стандартные диалоги Windows для работы с файлами
- •Работа 11. Проецирование файла в память
- •Работа 12. Потоки (индивидуальное задание d)
- •Работа 13. Синхронизация потоков с помощью событий
- •Работа 14. Защита данных с помощью критической секции
- •Работа 15. Библиотеки динамической компоновки
- •Работа 16. Передача параметров в функции dll-библиотек
- •Индивидуальные задания лабораторного практикума
- •Задание c2.Массив записываемых в файл данных должен представлять собой последовательный ряд из 2000 целых четных чисел.
- •Лабораторный практикум «Основы разработки приложений Windows» Книга 2
Адресация памяти
Как уже отмечалось, адрес любого байта памяти состоит из двух компонентов – содержимого сегментного регистра (оно называется селектором) и смещения. Получение из этих двух компонентов истинного физического адреса памяти осуществляется процессором посредством довольно сложной цепочки преобразований (рис. 1.5).
Рис. 1.5. Цепочка преобразования адресов в защищенном режиме
При расшифровке кодов команд процессор имеет дело с содержимым сегментных регистров (селекторами) и смещениями, которые могут храниться в тех или иных регистрах или входить в коды команд в виде операндов. Их же наблюдает программист, отлаживая программу с помощью отладчика. Комбинацию селектора и смещения иногда называют виртуальным адресом.
Получив из программы виртуальный адрес, процессор преобразует его в линейный. Делается это следующим образом.
В состав селекторов входят номера (индексы) ячеек специальной таблицы, содержащей дескрипторы сегментов программы. Эта таблица заранее строится операционной системой и размещается в системных полях памяти. Каждый дескриптор таблицы дескрипторов имеет размер 8 байт, и в нем хранятся все характеристики, необходимые процессору для обслуживания этого сегмента: базовый линейный адрес сегмента, его граница, которая представляет собой номер последнего байта сегмента, а также атрибуты сегмента, определяющие его свойства (рис. 1.6). Таким образом, селекторы в конечном счете характеризуют сегменты памяти.
Рис. 1.6. Преобразование виртуального адреса в линейный
Процессор с помощью селектора определяет индекс дескриптора адресуемого сегмента, извлекает из него базовый линейный 32-разрядный адрес сегмента и, прибавив к нему 32-разрядное же смещение, образует адрес адресуемой ячейки памяти. Этот адрес называется линейным, потому что в отличие от виртуального адреса, состоящего из двух частей, он представляет собой одно 32-разрядное число, которое может принимать, в принципе, любое значение от 0 до 0xFFFFFFFF, т. е. до 4 Г – 1.
Существенной характеристикой 32-разрядных приложений Windows является то, что они работают в так называемой плоской модели памяти (иногда для обозначения плоской памяти используют англоязычный термин “FLAT”, являющийся, кстати, ключевым словом некоторых языков программирования). Базовым адресам всех сегментов, хранящимся в таблице дескрипторов, присваивается значение 0, а границам сегментов – значение 4 Г – 1. Таким образом, все сегменты в плоской модели памяти имеют размер 4 Гбайт и накладываются друг на друга. Однако различным участкам программ (как прикладных, так и системных) назначаются различные смещения. Поскольку базовые адреса всех сегментов равны 0, смещения, действующие в программе, совпадают с линейными адресами. Это дает основание говорить, что 32-разрядные программы работают в линейном пространстве адресов.
Из всего пространства линейных адресов система Windows выделяет первые два гигабайта для прикладных программ, а вторые два гигабайта – для системных (рис. 1.7).
Рис. 1.7.Распределение линейного адресного пространства
Прикладные программы размещаются начиная с 5-го мегабайта линейного пространства, т. е. с адреса 0x400000 (первые 4 мегабайта зарезервированы для системного использования), поэтому функцияWinMainможет начинаться, например, с адреса 0x40107C, глобальные данные размещаться по адресам, начинающимся с 0x402074, а стек начинаться с адреса 0x68FE30.
Реально размер прикладных программ редко превышает несколько единиц или десятков мегабайт (часто гораздо меньше), поэтому “залезть” во второй гигабайт прикладная программа может лишь в том случае, если она обрабатывает очень большие массивы данных, например массив из 250 миллионов целых чисел, который займет в памяти, действительно, около одного гигабайта.
Большая часть системных программ располагается в 4-м гигабайте. Поэтому линейные адреса (и смещения) системных программ обычно имеют значения, начинающиеся с 0xC(например, 0xC0141A90).
Получив линейный адрес адресуемого байта, процессор с помощью таблиц страниц (их еще называют таблицами страничной трансляции) преобразует его в 32-разрядный физический адрес того байта памяти, где находится или, точнее, с которого начинается конкретная команда или конкретное данное. Этот адрес зависит от объема памяти, установленной на компьютере, и может располагаться в любом месте физической памяти (кроме 1-го мегабайта, где размещаются программы MS-DOS, видеопамять и ПЗУBIOS).
Страничная трансляция представляет собой довольно сложный механизм, в котором принимают участие аппаратные средства процессора и находящиеся в памяти таблицы страничной трансляции. Упрощенно процедуру страничной трансляции можно представить следующим образом.
Все пространство линейных адресов так же, как и пространство физических адресов реально установленной на компьютере памяти, делится на страницы объемом по 4 Кбайт. Каждой странице линейных адресов соответствует своя ячейка, или запись в таблице страниц. В эту ячейку (она имеет размер всего 4 байта) система заносит физический адрес той страницы памяти, в которую в настоящий момент загружена часть программы, соответствующая данной странице линейных адресов, чем и осуществляется отображение смещений программы на физические адреса памяти (рис. 1.8).
Если в процессе поддержки многозадачного режима система выгрузит из памяти текущую программу (или ее часть), в соответствующих ячейках таблицы страниц делается отметка “не загружена”, и связь между линейными и физическими адресами для данной программы разрывается. В дальнейшем, при повторной загрузке той же программы, она может попасть уже в другие участки физической памяти, и система соответствующим образом модифицирует таблицу страниц. В результате физические адреса, по которым загружаются части программы, в процессе работы системы динамически изменяются (и, разумеется, на разных компьютерах будут разными), в то время, как линейные адреса (смещения) конкретной программы, назначаемые ей в процессе компиляции, жестко фиксированы.
Рис. 1.8. Страничная трансляция адресов
Любопытным следствием из рассмотренного механизма преобразования адресов является тот факт, что всепрограммы в процессе компиляции получают одни и те же линейные адреса. Однако для каждой программы система образует свою таблицу страниц, переключая их при передаче управления от одной программы к другой. Поэтому, хотя все программы выполняются в одном и том же линейном адресном пространстве, их коды находятся в разных местах физической памяти и не затирают друг друга.