Урок 80 - Работа с памятью в системе Windows32 (часть 1/3)

Исследуем проблему накопления потоковых данных в специальных потоковых хранилищах. Работа с памятью является одной из важнейших функций любой программы. Выделение участков памяти для структур программы должно быть эффективным, поэтому программист должен хорошо разбираться в особенностях этого процесса.
В уроке приводятся только основные принципы работы с памятью в системе Windows 32. Для подробного изучения всех тонкостей этого сложного процесса читатель может обратиться к специальной литературе. Особенно хочется отметить книгу: Дж.Рихтер, "Windows для профессионалов".

Как известно, Windows 32 - тридцатидвуразрядная операционная система (число 32 как раз это и обозначает). Прежде всего, из этого следует, что запущенная программа может адресовать линейное адресное пространство размером 2^32 байт = 4 ГБ, при этом адресация производится при помощи тридцатидвуразрядных регистров-указателей. Каждый запущенный в системе процесс обладает своим собственным адресным пространством, каждое из которых не пересекается с адресными пространствами других процессов. Распределение системных областей в адресном пространстве систем Windows 95/98 и Windows NT различно.
Программа может адресовать любую ячейку памяти в диапазоне адресов своего адресного пространства. Однако, это не значит, что программа может записать или считать данные из этой ячейки. Адресное пространство в Windows - виртуально, оно не связано напрямую с физическим пространством оперативной памяти вашего компьютера.
Механизм выделения памяти в Windows состоит из двух фаз. Первая фаза выделения памяти состоит в резервировании (захвате) участка необходимого размера в адресном пространстве процесса. При этом не выделяется ни байта реальной памяти (не считая системных структур ядра). Вы можете спокойно зарезервировать участок адресного пространства размером 100 мегабайт, и это ничего не будет стоить системе. Вы можете указать какие адреса вы хотели бы занять, а можете предоставить выбор участка необходимого размера самой системе. Стоит отметить, что адресное пространство резервируется с гранулярностью 64 кБ. Это значит, что несмотря на указанные вами адреса, базовый адрес реально зарезервированного участка памяти будет кратен 64 кБ. При резервировании участка в адресном пространстве можно указать желаемый аттрибут, который регулирует доступ к этой памяти: запись данных, чтение данных, выполнение кода или комбинацию этих признаков. Нарушение правил доступа к памяти приводит к генерации системой исключения.
Вторая фаза выделения памяти в Windows - это выделение реальной, физической памяти в зарезервированный участок виртуального адресного пространства. На этом этапе системой выделяется реальная память, со всеми вытекающими из этого последствиями. Выделение реальной памяти также гранулярно. Минимальный блок реальной памяти, которым оперирует система, и который можно выделить, называеся страницей памяти. Размер страницы зависит от типа операционной системы и составляет для Windows 95/98 - 4 кБ, а для Windows NT - 8 кБ. Гранулярность при резервировании участка памяти и при выделении реальной памяти призвана облегчить нагрузку на ядро системы.
Выделение реальной памяти происходит постранично, при этом существует возможность выделения произвольного количества страниц в произвольные (кратные размеру страницы) адреса заранее зарезервированного участка адресного пространства процесса. Каждой странице может быть назначен свой собственный атрибут доступа. Желательно указывать тот же самый атрибут, что имеет зарезервированный участок адресного пространства в котором происходит выделение страницы реальной памяти.
Важным моментом в механизме выделения памяти является механизм динамической выгрузки и загрузки страниц памяти. В самом деле, современный компьютер имеет оперативную память объемом 16-256 МБ, а для совместной работы нескольких программ необходимо гораздо больше. Windows, как и большинство современных операционных систем, выгружает страницы памяти, к которым давно не было обращений, на жесткий диск в так называемый своп-файл. При этом размер реальной памяти, доступной для программ, становиться равным суммарному объему оперативной памяти и своп-файла. По возможности, система старается держать все страницы в оперативной памяти, однако когда суммарный размер выделенных всеми процессами страниц превышает ее размер, система выгружает страницы с давним доступом на диск, а на их месте выделяет новые страницы. Если же выгруженная на диск страница затребуется владеющим ею процессом, система освободит для нее место в оперативной памяти путем выгрузки редко используемой страницы, загрузит затребованную страницу на ее место и вернет управление процессу.
Весь механизм динамической загрузки и выгрузки страниц абсолюдно прозрачен для процессов, а реализация этого механизма полностью обеспечивается операционной системой. Процесс может абсолюдно ничего не знать о сущесвовании такого механизма - он обращается к своим ячейкам памяти, а система автоматически выгружает редко используемые страницы и загружает необходимые страницы; процесс может только регулировать некоторые нюансы работы этого механизма.
На основании вышеперечисленного можно сделать следующие выводы:
каждый процесс со всеми своими потоками имеет отдельное и независимое линейное адресное пространство размером 4 Гб; выделение памяти состоит из двух фаз: резервирования адресного пространства и выделение в нем реальной памяти; при резервировании участка адресного пространства существует гранулярность размером 64 кБ; выделение реальной памяти производится постранично, размер страницы записит от типа операционной системы; каждой странице моежт быть назначен свой собственный атрибут доступа, нарушение которого приводит к генерации исключения системой; операционная система динамически выгружает редко используемый страницы памяти на из оперативной памяти на жесткий диск, причем этот механизм прозрачен для всех процессов.

Алгоритмы современных программ используют механизмы выделения и освобождения памяти очень интенсивно. Строки, динамические массивы, объекты, структуры, буфера - выделение и освобождение этих элементов происходит очень часто, при этом оказывается, что все эти элементы имеют небольшой размер.
Выделение у системы большого количества объектов небольшого размера оказывается неэффективным по следующим причинам.
частое обращение на выделение памяти снижает производительность, так как резервирование адресного пространства и выделение реальной памяти происходит на уровне ядра операционной системы;
из-за страничной организации памяти (гранулярности) выделение памяти происходит с большими издержками; запрос на выделение 100-байтного участка приводит к выделению одной страницы памяти с размером 4 или 8 кБ.
Решение этой проблемы организуется следующим образом: у операционной системы выделяется достаточно большой участок памяти, а уже из него для прикладной программы "нарезаются" небольшие участки. Такая организация называется кучей, а механизм, который следит за выделением и освобождением участков памяти называется менеджером кучи. Куча позволяет решить как проблему потери производительности - менеджер кучи может функционировать на уровне прикладной программы, так и проблему гранулярности - менеджер запрашивает у системы один большой участок памяти.
Windows имеет свою собственную реализацию менеджера кучи, который позволяет приложениям создавать, уничтожать кучи, а также производить с ними операции выделения и освобождения памяти в куче. Кроме того, для каждого вновь создаваемого процесса Windows специально создает кучу по умолчанию, которая используется при работе API функций, а также может быть использована прикладной программой. Все создаваемые Windows кучи потоко-безопасны, то есть существует возможность обращения к одной и той же куче из разных потоков одновременно.
Инженеры компании Borland по видимому не доверяют инженерам компании Microsoft, поэтому каждая программа на Delphi имеет свою собственную реализацию менеджера кучи, которая определена в системных модулях. Такое решение аргументируется инженерами Borland тем, что стандартный менеджер кучи Windows не обеспечивает достаточно эффективную работу с памятью. Конечно, как и любая Windows-программа, программа на Delphi имеет стандартную кучу по умолчанию, которую создает для нее система, однако функции New, Release, GetMem, FreeMem и некоторые другие оперируют с собственной реализацией менеджера куч. Менеджер кучи Delphi резервирует блоки адресного пространства размером 1 Мб, а выделяет блоки реальной памяти размером 16 Кб. Также вы можете написать и установить свою реализацию менеджера куч, если не доверяете ни инженерам Borland, ни инженерам Microsoft - для этого имеются все необходимые функции.
Хотя куча совершенно не предназначена для выделения больших участков памяти, запрос выделения большого участка у кучи не приведет к ошибке. Куча просто перенаправит ваш запрос операционной системе и вернет указатель на выделенный ею участок памяти.

Удачи!
Встретимся в следующем уроке!



    No results found.
Отменить.