Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
6
Добавлен:
26.02.2022
Размер:
151.69 Кб
Скачать

ЛР 3 Вариант 3

Задание

Реализовать следующую программу для исследования свойств куч.

1.Создать новую кучу с максимальным размером 24 КБ при помощи функции HeapCreate(). Изначальный размер кучи можно задать 0.

2.Полностью использовать кучу, выделяя из нее функцией HeapAlloc() блоки случайного размера 32—1024 байт, пока это возможно.

3.При помощи функции HeapWalk() перебрать все блоки кучи и определить для выделенных блоков (поле wFlags

структуры PROCESS_HEAP_ENTRY включает флаг PROCESS_HEAP_ENTRY_BUSY): 1) суммарный объем хранимых данных (cbData);

2) суммарные накладные расходы (cbOverhead);

3) разность между размером кучи и суммой величин пунктов 1) и 2). Указание. Здесь и далее рассчитанные значения необходимо печатать.

4.При помощи функции HeapWalk() перебрать все блоки кучи, освобождая их с вероятностью ⅓ функцией

HeapFree().

Указание. Текущий блок в цикле можно освобождать только после перехода к следующему.

5.Повторить пункт 3, дополнительно подсчитывая для свободных блоков (поле wFlags равно 0):

1)суммарный объем;

2)максимальный размер.

6. Попытаться совершить следующие операции:

1)Выделить блок размером на 16 байт менее наибольшего свободного блока, затем освободить этот блок (если выделение успешно).

2)Выделить блок размером на 16 байт менее суммарного свободного объема памяти в куче.

Наблюдаемые результаты объяснить.

7.Уничтожить созданную кучу при помощи функции HeapDestroy().

8.Создать новую кучу такого же объема, как в пункте 1. Выделять блоки размером 1, 2, 3 и т. д. байт, пока не будет использована вся куча. При помощи функции HeapWalk() для каждого выделенного блока напечатать его размер и объем накладных расходов. Построить график зависимости накладных расходов от размера блока, выявить и объяснить закономерность.

Указание. В C и C++ для корректного вывода поля cbOverhead необходимо привести его к типу, например, unsigned int.

9.Пронаблюдать при помощи VMMap состояние памяти процесса:

1)перед выполнением п. 1;

2)после выполнения п. 1;

3)после выполнения п. 4;

4)перед выполнением п. 7;

5)после выполнения п. 7;

6)после выполнения п. 8.

Определить изменения, описать и объяснить их.

Выполнение:

#include <windows.h> #include <cstdlib> #include <cctype> #include <iostream> #include <iomanip> #include <random>

#define MY_HEAP_MAX_SIZE (24 * 1024)

//функция нажатия клавиши без эха, возвращает ее код

//аналог нестандартной _getch()

char Getch()

{

static HANDLE hStdOut = GetStdHandle(STD_INPUT_HANDLE); // получаем дескриптор ввода

DWORD mode;

GetConsoleMode(hStdOut, &mode); // получаем текущий режим ввода

// устанавливаем новый режим: отключаем обязательное нажатие <Enter> для ввода и эхо

SetConsoleMode(hStdOut, mode & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT));

1

char result = 0; DWORD nread;

ReadConsole(hStdOut, &result, 1, &nread, nullptr); // читаем символ SetConsoleMode(hStdOut, mode); // устанавливаем старый режим return result;

}

// запрос да/нет

bool RequestPausedOutput()

{

std::cout << "Would you like the program to pause at some points,\n" << "allowing you to examine results and/or VMMap? [y/n]\n";

while (true)

{

auto answer = Getch(); // получаем символ ответа

if (tolower(static_cast<unsigned char>(answer)) == 'y') return true; // да

if (tolower(static_cast<unsigned char>(answer)) == 'n') return false; // нет

// неверный ввод. еще раз

std::cout << "Please, press Y or N key!\n";

}

}

// пауза

void Pause(bool use_pause = true)

{

if (use_pause)

{

std::cout << "Press any key to continue...\n" << std::endl; Getch(); // пауза до нажатия клавиши

}

}

int main()

{

auto use_pauses = RequestPausedOutput(); // делаем ли паузы Pause(use_pauses);

// 1.

std::cout << "\t1. Creating a heap\n\n"; // создаем новую кучу размером 24 КБ

auto my_heap = HeapCreate(HEAP_NO_SERIALIZE, 0, MY_HEAP_MAX_SIZE); if (!my_heap)

{

return EXIT_FAILURE;

}

Pause(use_pauses);

// 2. полностью используем кучу, выделяя блоки случайного размера 32-1024 Б std::cout << "\t2. Consuming all memory in the heap\n\n";

std::mt19937 gen(std::random_device{}()); // инициализируем генератор случайных чисел std::uniform_int_distribution<size_t> size_dist(32, 1024); // равномерное распределение целых чисел while (true)

{

size_t sz = size_dist(gen); // получаем случайное значение из [32; 1024] if (!HeapAlloc(my_heap, 0, sz)) // выделяем память из кучи, пока можем

break;

}

2

// 3.

std::cout << "\t3. Measuring heap memory consumption\n"; size_t total = 0; // всего выделено

size_t overhead = 0; // накладные расходы

PROCESS_HEAP_ENTRY entry{}; // инициализируем структуру для прохода по куче while (HeapWalk(my_heap, &entry)) // перебираем все блоки кучи

{

if ((entry.wFlags & PROCESS_HEAP_ENTRY_BUSY)) // блок выделенной памяти

{

total += entry.cbData; // суммируем общее значение overhead += entry.cbOverhead; // и накладные расходы

}

}

// выводим результаты

auto consumed = total + overhead;

std::cout << "Total allocated: " << total << '\n'; std::cout << "Total overhead: " << overhead << '\n'; std::cout << "Total consumed: " << consumed << '\n';

std::cout << "Residual amount: " << MY_HEAP_MAX_SIZE - consumed << "\n\n";

// 4.

std::cout << "\t4. Partially deallocating heap blocks\n\n";

std::uniform_int_distribution<size_t> free_dist(1, 3); // равномерное распределение [1; 3] для получения вероятности 1/3

LPVOID block_to_free = nullptr; // указатель на блок, который будем освобождать entry.lpData = nullptr; // инициализируем для нового прохода по блокам кучи while (HeapWalk(my_heap, &entry)) // идем по блокам кучи

{

if (block_to_free) // есть, что освобождать

{

HeapFree(my_heap, 0, block_to_free); block_to_free = nullptr; // освободили

}

if ((entry.wFlags & PROCESS_HEAP_ENTRY_BUSY)) // блок выделенной памяти

{// 1, 2 или 3 выпадают с равной вероятностью 1/3.

//будем считать, что, если выпала 1, то запоминаем указатель для освобождения block_to_free = free_dist(gen) == 1 ? entry.lpData : nullptr;

}

}

if (block_to_free) // если остался блок, помеченный для освобождения

HeapFree(my_heap, 0, block_to_free); Pause(use_pauses);

// 5.

std::cout << "\t5. Measuring new heap memory consumption\n\n"; total = 0;

overhead = 0;

size_t total_free = 0; // суммарный объем свободных блоков size_t largest_free = 0; // наибольший размер из свободных

entry.lpData = nullptr; // инициализируем для нового прохода по блокам кучи while (HeapWalk(my_heap, &entry)) // повторяем п.3

{

if ((entry.wFlags & PROCESS_HEAP_ENTRY_BUSY)) // блок выделенной памяти

{

total += entry.cbData; overhead += entry.cbOverhead;

}

else if (!entry.wFlags) // свободный блок

{

3

total_free += entry.cbData; // прибавляем объем свободного блока if (largest_free < entry.cbData)

largest_free = entry.cbData; // запоминаем наибольший

}

 

 

}

 

 

// выводим результаты

 

 

consumed = total + overhead;

 

std::cout << "Total allocated:

 

" << total << '\n';

std::cout << "Total overhead:

 

" << overhead << '\n';

std::cout << "Total consumed:

" << consumed << '\n';

std::cout << "Residual amount:

" << MY_HEAP_MAX_SIZE - consumed << '\n';

std::cout << "Total free:

" << total_free << '\n';

std::cout << "Largest free block: " << largest_free << "\n\n";

// 6.

std::cout << "\t6. Investigating heap fragmentation issues\n"; LPVOID p = nullptr;

//пытаемся выделить блок размером на 16 Б менее самого большого свободного блока size_t alloc_size = largest_free - 16 > 0 ? largest_free - 16 : largest_free;

std::cout << "Allocating " << alloc_size << " bytes... "; if ((p = HeapAlloc(my_heap, 0, alloc_size)))

{ // успех

std::cout << "OK.\n"; HeapFree(my_heap, 0, p);

}

else

{ // неудача

std::cout << "FAILED.\n";

}

//пытаемся выделить блок размером на 16 Б менее суммарного свободного объема памяти alloc_size = total_free - 16 > 0 ? total_free - 16 : total_free;

std::cout << "Allocating " << alloc_size << " bytes... "; if ((p = HeapAlloc(my_heap, 0, alloc_size)))

{ // успех

std::cout << "OK.\n"; HeapFree(my_heap, 0, p);

}

else

{ // неудача

std::cout << "FAILED.\n";

}

std::cout << '\n'; Pause(use_pauses);

//7. уничтожаем кучу

std::cout << "\t7. Destroying the heap\n\n"; HeapDestroy(my_heap); Pause(use_pauses);

// 8. создаем заново такую же кучу

my_heap = HeapCreate(HEAP_NO_SERIALIZE, 0, MY_HEAP_MAX_SIZE); if (!my_heap)

{

return EXIT_FAILURE;

}

alloc_size = 1; // пока можно, выделяем блоки размером 1,2,3,4,5,...

while (HeapAlloc(my_heap, 0, alloc_size++))

;

// выводим размер каждого выделенного блока и накладные расходы std::cout << "\t8. Investigating allocation overhead\n\n";

4

std::cout.setf(std::ios::left);

std::cout << std::setw(16) << "Block size" << "Overhead\n"; //для этого

entry.lpData = nullptr; // инициализируем для нового прохода по блокам кучи while (HeapWalk(my_heap, &entry)) // и идем по блокам кучи

{

if ((entry.wFlags & PROCESS_HEAP_ENTRY_BUSY)) // // блок выделенной памяти

{

// печатаем значения

std::cout << std::setw(16) << entry.cbData << (unsigned)entry.cbOverhead << '\n';

}

}

std::cout << '\n';

// уничтожаем кучу

HeapDestroy(my_heap); Pause();

return 0;

}

5

Соседние файлы в папке Windows лаб 1-4