пользователей: 30398
предметов: 12406
вопросов: 234839
Конспект-online
РЕГИСТРАЦИЯ ЭКСКУРСИЯ

Архитектура памяти в OC Windows. Механизмы управления памятью в приложениях. Проецируемые в память файлы. Преимущества и недостатки

В Win32 виртуальное адресное пространство каждого процесса составляет 4 Гб, т.е. соответствующий 32-битный указатель может быть числом от $00000000 до $FFFFFFFF. 

Для 64-разрядных процессов размер адресного пространства равен 16 экзабайтам, поскольку 64-битный указатель может быть любым числом от 0x00000000 00000000 до 0xFFFFFFFF FFFFFFFF.

 В DOS или в 16-разрядной Windows все процессы находились в едином адресном пространстве, поэтому сбой в процессе A мог привести к краху процесса B или всей ОС.

В Win32 каждому процессу отводится закрытое адресное пространство:

Например, процесс А хранит массив информации по адресу $12345678. В то же время процесс B также хранит свою информацию по тому же адресу $12345678.

Но процесс A имеет доступ к своему массиву, а процесс В к своей информации. Обращаясь к памяти по адресу 0x12345678, потоки, выполняемые в процессе А, получают доступ к структуре данных процесса А, Но, когда по тому же адресу обращаются потоки, выполняемые в процессе В, они получают доступ к структуре данных процесса В. Иначе говоря, потоки процесса А не могут обратиться к структуре данных в адресном пространстве процесса В, и наоборот

Операции с файлами – это то, что рано или поздно приходится делать практически во всех программах, и всегда это вызывает массу проблем.

В Windows многие из этих проблем решаются очень изящно – с помощью проецируемых в память файлов (memory-mapped files).

Как и виртуальная память, проецируемые файлы позволяют резервировать регион адресного пространства и передавать ему физическую память.

Отличие состоит в том, что физическая память не выделяется из страничного файла, а берется из файла, который проецируется.

Как только файл спроецирован в память, к нему можно обращаться так, будто он целиком в нее загружен.

Проецируемые файлы применяются для:

загрузки и выполнения EXE- и DLL-файлов. Это позволяет существенно экономить как на размере страничного файла, так и на времени, необходимом для подготовки приложения к выполнению;

доступа к файлу данных, размещенному на диске. Это позволяет обойтись без операций файлового ввода-вывода и буферизации его содержимого;

разделения данных между несколькими процессами, выполняемыми на одной машине (в Windows есть и другие методы для совместного доступа разных процессов к одним данным — но все они так или иначе реализованы на основе проецируемых в память файлов).

При вызове из потока функции CreateProcess система действует так:

Отыскивает ЕХЕ-файл, указанный при вызове CreateProcess. Если файл не найден, новый процесс не создастся, а функция возвращает FALSE;

Создает новый объект ядра «процесс»;

Создает адресное пространство нового процесса;

Резервирует регион адресного пространства — такой, чтобы в него поместился данный ЕХЕ-файл. Желательное расположение этого региона указывается внутри самого ЕХЕ-файла. По умолчанию базовый адрес ЕХЕ-файла — 0x00400000 (в 64-разрядном приложении под управлением 64-разрядной Windows этот адрес может быть другим). При создании исполняемого файла приложения базовый адрес может быть изменен через параметр компоновщика /BASE;

Отмечает, что физическая память, связанная с зарезервированным регионом, — ЕХЕ-файл на диске, а не страничный файл.

Если система почему-либо не свяжет ЕХЕ-файл с необходимыми ему DLL, на экране появится соответствующее сообщение, а адресное пространство процесса и объект «процесс» будут освобождены. При этом CreateProcess вернет FALSE; прояснить причину сбоя поможет функция GetLastError.

После увязки EXE- и DLL-файлов с адресным пространством процесса начинает исполняться стартовый код EXE-файла.

Подкачку страниц, буферизацию и кэширование система берет на себя:

Если код в ЕХЕ-файле переходит к команде, не загруженной в память, возникает ошибка.

Обнаружив ее, система перекачивает нужную страницу кода из образа файла на страницу оперативной памяти.

Затем отображает страницу оперативной памяти на должный участок адресного пространства процесса, тем самым позволяя потоку продолжить выполнение кода.

Все эти операции скрыты от приложения и периодически повторяются при каждой попытке процесса обратиться к коду или данным, отсутствующим в оперативной памяти. 

Статические данные не разделяются несколькими экземплярами EXE или DLL!

С помощью проецируемых в память файлов несколько одновременно выполняемых экземпляров приложения могут совместно использовать один и тот же код, загруженный в оперативную память. Любая программа представляет собой цельное пространство, разделенное на разделы: раздел кода и раздел данных. При проецировании в память раздел данных следует за разделом кода.

Операционная система позволяет проецировать на адресное пространство процесса и файл данных.

Это очень удобно при манипуляциях с большими потоками данных.

Чтобы представить всю мощь такого применения механизма проецирования файлов, рассмотрим четыре возможных метода реализации программы, меняющей порядок следования всех байтов в файле на обратный.

Метод 1: один файл, один буфер

Метод 2: два файла, один буфер

Метод 3: один файл, два буфера

Метод 4: один файл и никаких буферов (использовать проекцию).

Метод 1: один файл, один буфер

Первый (и теоретически простейший) метод — выделение блока памяти, достаточного для размещения всего файла. Открываем файл, считываем его содержимое в блок памяти, закрываем. Располагая в памяти содержимым файла, можно поменять первый байт с последним, второй — с предпоследним и т. д. Этот процесс будет продолжаться, пока мы не поменяем местами два смежных байта, находящихся в середине файла. Закончив эту операцию, вновь открываем файл и перезаписываем его содержимое.

Этот довольно простой в реализации метод имеет два существенных недостатка.

Во-первых, придется выделить блок памяти такого же размера, что и файл.

Во-вторых, если перезапись вдруг прервется, содержимое файла будет испорчено. Простейшая мера предосторожности — создать копию исходного файла (потом ее можно удалить), но это потребует дополнительного дискового пространства.

Метод 2: два файла, один буфер

Открываем существующий файл и создаем на диске новый — нулевой длины. Затем выделяем небольшой внутренний буфер размером, скажем, 8 Кб. Устанавливаем указатель файла в позицию 8 Кб от конца, считываем в буфер последние 8 Кб содержимого файла, меняем в нем порядок следования байтов на обратный и переписываем буфер в только что созданный файл. Повторяем эти операции, пока не дойдём до начала исходного файла. Закончив обработку, закрываем оба файла и удаляем исходный файл.

Этот метод посложнее первого, зато позволяет гораздо эффективнее использовать память, так как требует выделения лишь 8 Кб. Во-первых, обработка идет медленнее, чем при первом методе: на каждой итерации перед считыванием приходится находить нужный фрагмент исходного файла. Во-вторых, может понадобиться огромное пространство на жестком диске. Если длина исходного файла 400 Мб, новый файл постепенно вырастет до этой величины, и перед самым удалением исходного файла будет занято 800 Мб, т. e. на 400 Мб больше, чем следовало бы.

Метод 3: один файл, два буфера

Программа инициализирует два раздельных буфера, допустим, по 8 Кб и считывает первые 8 Кб файла в один буфер, а последние 8 Кб — в другой. Далее содержимое обоих буферов обменивается в обратном порядке и первый буфер записывается в конец, а второй — в начало того же файла. На каждой итерации программа перемещает восьмикилобайтовые блоки из одной половины файла в другую.

По сравнению с первыми двумя этот метод позволяет экономить пространство на жестком диске, так как все операции чтения и записи протекают в рамках одного файла. Что же касается памяти, то и здесь данный метод довольно эффективен, используя всего 16 Кб. Однако он, по-видимому, самый сложный в реализации. И, кроме того, как и первый метод, он может испортить файл данных, если процесс вдруг прервется. 

Метод 4: один файл и никаких буферов

Вы открываете файл, указывая системе зарезервировать регион виртуального адресного пространства. Затем сообщаете, что первый байт файла следует спроецировать на первый байт этого региона, и обращаетесь к региону так, будто он на самом деле содержит файл.

Если в конце файла есть отдельный нулевой байт, можно вызвать библиотечную функцию _strrev и поменять порядок следования байтов на обратный.

Огромный плюс этого метода в том, что всю работу по кэшированию файла выполняет сама система: не надо выделить память, загружать данные из файла в память, переписывать их обратно в файл и т. д. и т. п.

Но, увы, вероятность прерывания процесса, например из-за сбоя в электросети, по-прежнему сохраняется, и от порчи данных Вы не застрахованы. 

Использование проекций файлов в программах чрезвычайно удобно при манипуляции с большими потоками данных.

Для создания и использования проекций файлов необходимо:

1. Создать и открыть объект ядра "файл", который хотите спроецировать.

2. Создать объект ядра "проекция файла".

3. Указать системе, что вы собираетесь спроецировать объект ядра "проекция файла" на адресное пространство вашего процесса.

Закончив работу с проекцией, необходимо:

1. Сообщить системе об отмене проекции файла.

2. Закрыть объект "проекция файла".

3. Закрыть объект "файл".


26.12.2015; 23:13
хиты: 0
рейтинг:0
Точные науки
информатика
для добавления комментариев необходимо авторизироваться.
  Copyright © 2013-2024. All Rights Reserved. помощь