Керування динамічною ділянкою пам’яті процесу



Кожний процес має доступ до спеціальної ділянки пам’яті, яку називають динамічною ділянкою пам’яті, або кучею (heap). Початкова і кінцева адреси цієї ділянки містяться в полях start_brk і brk дескриптора пам’яті процесу.

Для доступу до динамічної ділянки пам’яті використовують системний виклик brk(). Він намагається змінити її розмір, за параметр приймає нову кінцеву адресу цієї ділянки.

#include <unistd.h>

int brk(void *new_brk);

Зміна може зводитися до розширення або скорочення динамічної ділянки пам’яті. У разі збільшення ділянки, виконують різні перевірки (чи коректна нова адреса, чи дозволено процесу використати такий обсяг пам’яті) і після цього виділяють новий інтервал адрес, як описано у розділі 9.8.1, у разі зменшення, зайву пам’ять вивільняють. Якщо виклик brk() завершився коректно, поле brk дескриптора пам’яті відображатиме новий розмір динамічної ділянки.

Виклик brk() мало підходить для безпосереднього використання у прикладних програмах, оскільки з його допомогою не можна визначити поточне значення межі brk. На практиці як засіб керування межею динамічної ділянки використовують пакувальник цього виклику – функцію sbrk().

#include <unistd.h>

void *sbrk(ptrdiff_t increment):

Вона переміщує межу динамічної ділянки на ціле число байтів increment і повертає покажчик на початок виділеної ділянки (фактично, старе значення межі). Якщо increment дорівнює нулю, sbrk() повертає поточне значення межі динамічної ділянки пам’яті.

char *oldbrk, *newbrk;

//переміщення межі динамічної ділянки

oldbrk = (char *)sbrk(10000);

//визначення нової межі динамічної ділянки

newbrk = (char *)sbrk(0);

printf(‘’Старе значення brk: %d, нове: %d\n”, oldbrk, newbrk);

Із використанням виклику brk () у бібліотеці мови С для Linux реалізовані стандартні функції роботи із динамічною пам’яттю malloc(), calloc() і realloc(). Зазначимо, під час виклику цих функцій у режимі ядра відбувається тільки зміна brk на величину, кратну 4 Кбайт (у разі необхідності), подальше виділення фрагмента пам’яті потрібного розміру здійснюють у режимі користувача.

Реалізація динамічного керування пам’яттю в Windows XP

Windows XP реалізує два способи динамічного розподілу пам’яті для використання в режимі ядра: системні пули пам’яті (system memory pools) і списки передісторії (look-aside lists).

Системні пули пам’яті ядра

Розрізняють невивантажувані (nonpaged) і вивантажувані (paged) системні пули пам’яті. Обидва види пулів перебувають у системній ділянці пам’яті та відображаються в адресний простір будь-якого процесу.

Невивантажувані містять діапазони адрес, які завжди відповідають фізичній пам’яті, тому доступ до них ніколи не спричиняє сторінкового переривання.

Вивантажувані відповідають пам’яті, сторінки якої можуть бути вивантажені на диск. Обидва види системних пулів можна використати для виділення блоків пам’яті довільного наперед невідомого розміру.

Списки передісторії

Списки передісторії є швидшим способом розподілу пам’яті і багато в чому схожі на кеші кускового розподілювача Linux.

Вони дають змогу виділяти пам’ять для блоків одного наперед відомого розміру. Звичайно їх спеціально створюють для виділення часто використовуваних об’єктів (наприклад, такі списки формують різні компоненти виконавчої системи для своїх структур даних). Як і для кешів кускового розподілювача, є списки загального призначення для виділення блоків заданого розміру. У разі вивільнення об’єктів вони повертаються назад у відповідний список. Якщо список довго не використовували, його автоматично скорочують.

Динамічні ділянки пам’яті

У Windows XP, як і в Linux, кожний процес має доступ до спеціальної ділянки пам’яті, її також називають динамічною ділянкою пам’яті або кучею (heap). Особливістю цієї ОС є те, що таких динамічних ділянок для процесу може бути створено кілька, і всередині кожної з них розподілювач пам’яті може окремо виділяти блоки меншого розміру [31, 50].

Розподілювач пам’яті у Windows XP називають менеджером динамічних ділянок пам’яті (heap manager). Цей менеджер дає змогу процесам розподіляти па­м’ять блоками довільного розміру, а не тільки кратними розміру сторінки, як під час керування регіонами.

Кожний процес запускають із динамічною ділянкою за замовчуванням, розмір якої становить 1 Мбайт (під час компонування програми цей розмір може бути змінений). Для роботи із цією ділянкою процес має спочатку отримати її дескриптор за допомогою виклику GetProcessHeap().

HANDLE default_heap = GetProcessHeap();

Виділення блоків пам’яті

Для виділення блоків пам’яті використовують функцію НеарAlloc(), для вивільнення – HeapFree().

//виділення в ділянці heap блоку пам’яті розміру size

LPVOID addr = HeapAlloc(heap. options, size);

// вивільнення в ділянці heap блоку пам’яті за адресою addr

HeapFree(heap, options, addr);

Першим параметром для цих функцій є дескриптор динамічної ділянки, одним із можливих значень параметра options для НеарАlloc() є HEAP_ZERO_MEMORY, який свідчить, що виділена пам’ять буде ініціалізована нулями.

Додаткові динамічні ділянки

Для своїх потреб процес може створювати додаткові динамічні ділянки за допомогою функції HeapCreate() і знищувати їх за допомогою HeapDestroy(). Функція HeapCreate() приймає три цілочислові параметри і повертає дескриптор створеної ділянки.

HANDLE heap = HeapCreate(options, initial_size, max_size);

Перший параметр задає режим створення ділянки (нульове значення задає режим за замовчуванням), другий – її початковий розмір, третій – максимальний. Якщо max_size дорівнює нулю, динамічна ділянка може збільшуватися необмежене. Задання ділянок обмеженого розміру дає змогу уникнути помилок, пов’язаних із втратами пам’яті (втрата у кожному разі не зможе перевищити максимального розміру ділянки).

Розглянемо приклад використання додаткової динамічної ділянки пам’яті для розміщення масиву зі 100 елементів типу Int.

int array_size = 100 * sizeof(int);

//розміщення динамічної ділянки

HANDLE heap – HeapCreate(0,. array_size, array_size);

//розміщення масиву в цій ділянці, заповненого нулями

int *array = (int *)HeapAlloc(heap, HEAP_ZERO_MEMORY, array_size);

//робота із масивом у динамічній ділянці

аггау[20] = 1;

//знищення динамічної ділянки та вивільнення пам’яті

HeapDestroy(heap);

Зазначимо, що виклик HeapFree() у цьому прикладі не потрібний, тому що виклик HeapDestroy() призводить до коректного вивільнення всієї пам’яті, виділеної в heap. Це є однією із переваг використання окремих динамічних ділянок.

Висновки

Динамічний розподіл пам’яті – це керування ділянкою пам’яті, якою процес може розпоряджатися на свій розсуд. Засоби керування динамічною пам’яттю працюють винятково з логічними адресами і ґрунтуються на реалізації віртуальної пам’яті. Основною проблемою динамічного розподілу пам’яті є фрагментація. Для боротьби з нею використовують різні підходи.

Найбільш розповсюдженими технологіями динамічного розподілу пам’яті є система двійників, динамічні списки вільних блоків і послідовний пошук підходящого вільного блоку,

 


Дата добавления: 2018-04-05; просмотров: 364; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!