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

ОСлаб5_Мячина_БФИ2203

.docx
Скачиваний:
0
Добавлен:
02.08.2026
Размер:
281 Кб
Скачать

МИНИСТЕРСТВО ЦИФРОВОГО РАЗВИТИЯ, СВЯЗИ И МАССОВЫХ КОММУНИКАЦИЙ РОССИЙСКОЙ ФЕДЕРАЦИИ

Ордена Трудового Красного Знамени федеральное государственное бюджетное образовательное учреждение высшего образования

Московский технический университет связи и информатики

(МТУСИ)

Кафедра «Системное программирование»

Лабораторная работа №5

«ИССЛЕДОВАНИЕ МЕХАНИЗМОВ ВЫДЕЛЕНИЯ ПАМЯТИ»

по дисциплине «Операционные системы»

Выполнила:

студент группы БФИ2203

Мячина Лада Андреевна

Проверила:

Старший преподаватель

Алексанян Диана Ашотовна

Москва, 2024 г.

Цель работы:

 Изучение системных функций выделения памяти в ОС GNU/LINUX

 Получение практических навыков работы с динамической памятью в ОС GNU/LINUX

Задание:

1. Написать программу, работающую в одном из 2х режимов, задающимся при запуске в качестве аргумента командной строки:

  1. Режим выделение памяти malloc() равно освобождению памяти free()

  2. Режим выделение памяти больше malloc(), чем освобождение памяти free()

Выделение и освобождение памяти проводить параллельно с задержкой 100 – 1000 нс в бесконечном цикле. Предусмотреть прерывание процесса по сигналу, направленному асинхронно, с освобождением всей динамически выделенной памяти.

2. Написать программу для создания карты виртуальной памяти процесса, идентификатор которого передается первым аргументом, а полный путь до директории сохранения файла карты памяти – вторым аргументом. Именовать файлы карты по шаблону map_<pid>_<date&time>, где <pid> - идентификатор процесса, переданный в программу и соответствующий процессу, для которого создается карта, <date&time> - дата и время создания карты в формате yyyy-mm-dd_h:m:s. Наполнение карты взять из соответствующего процесса в /proc/ или использовать утилиту pmap.

3. Создать службу, запускающуюся каждые 30 секунд и выполняющую программу из п.2., проверить работу и получить 10-15 файлов с картами памяти для из п.1.

4. Проанализировать полученные результаты, написав программу, строящую график по данным карт виртуальной памяти из п.3 (для всех файлов карт в указанной при запуске директории). Выявить изменение размера кучи, при необходимости провести корректировку размеров выделяемой malloc() памяти для наглядного отображения (для данной программы можно использовать, например, Python).

5. Включить в отчет ответы на вопросы в свободной форме:

  1. что такое стек и куча?

  2. как происходит статическое и динамическое выделение памяти?

каковы плюсы и минусы статического и динамического выделения памяти?

Ход работы

Напишем программу, работающую в одном из 2х режимов, задающимся при запуске в качестве аргумента командной строки:

  1. Режим выделение памяти malloc() равно освобождению памяти free()

  2. Режим выделение памяти больше malloc(), чем освобождение памяти free()

Выделение и освобождение памяти проводим параллельно с задержкой 100 нс в бесконечном цикле. Предусмотрим прерывание процесса по сигналу, направленному асинхронно, с освобождением всей динамически выделенной памяти. Код программы показан на листинге 1.

Листинг 1 – Код первой программы

#include <stdio.h>

#include <stdlib.h>

#include <signal.h>

#include <unistd.h>

#include <string.h>

#include <time.h>

#define ALLOC_COUNT 1000

volatile int running = 1;

void handle_signal(int signum) {

running = 0; // Установка флага завершения программы

}

// Функция для управления выделением и освобождением памяти

void allocate_memory(int mode) {

void* allocated[ALLOC_COUNT]; // Массив для хранения указателей на выделенную память

memset(allocated, 0, sizeof(allocated));

while (running) {

// Этап выделения памяти

for (int i = 0; i < ALLOC_COUNT && running; i++) {

allocated[i] = malloc(1024); // 1 КБ памяти

if (allocated[i] == NULL) {

fprintf(stderr, "Memory allocation failed\n");

running = 0; // Завершаем

break;

}

usleep(rand() % 901 + 100); // Задержка от 100 до 1000 мкс

// Режим A: освобождаем сразу после выделения

if (mode == 0) {

free(allocated[i]);

allocated[i] = NULL;

}

}

// Режим B: освобождаем только часть выделенной памяти

if (mode == 1) {

for (int i = 0; i < ALLOC_COUNT; i++) {

if (i % 20 == 0 && allocated[i] != NULL) {

free(allocated[i]);

allocated[i] = NULL;

}

}

}

// Очистка оставшейся памяти, чтобы избежать утечек

for (int i = 0; i < ALLOC_COUNT; i++) {

if (allocated[i] != NULL) {

free(allocated[i]);

allocated[i] = NULL;

}

}

}

}

int main(int argc, char* argv[]) {

if (argc != 2) {

fprintf(stderr, "Usage: %s <mode>\n", argv[0]);

exit(EXIT_FAILURE);

}

int mode = atoi(argv[1]); // Режим работы

if (mode < 0 || mode > 1) {

fprintf(stderr, "Invalid mode. Use 0 or 1.\n");

exit(EXIT_FAILURE);

}

// Установка обработчика сигнала SIGINT

signal(SIGINT, handle_signal);

// Инициализация генератора случайных чисел

srand(time(NULL));

// Выполнение функции выделения памяти

allocate_memory(mode);

printf("Program terminated gracefully.\n");

return 0;

}

Написала программу для создания карты виртуальной памяти процесса, идентификатор которого передается первым аргументом, а полный путь до директории сохранения файла карты памяти – вторым аргументом. Именовать файлы карты по шаблону map_<pid>_<date&time>, где <pid> - идентификатор процесса, переданный в программу и соответствующий процессу, для которого создается карта, <date&time> - дата и время создания карты в формате yyyy-mm-dd_h:m:s.

Листинг 2 - Код второй программы

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <time.h>

#include <unistd.h>

// Функция для создания файла с картой памяти процесса

void create_map_file(int pid, const char* directory) {

// Буфер для хранения имени файла

char filename[256];

// Получение текущего времени

time_t now = time(NULL);

struct tm* t = localtime(&now); // Преобразуем текущее время в структуру tm

// Форматирование имени файла, включая директорию, PID и текущие дату и время

snprintf(filename, sizeof(filename), "%s/map_%d_%04d-%02d-%02d_%02d:%02d:%02d",

directory, pid, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,

t->tm_hour, t->tm_min, t->tm_sec);

// Буфер для формирования команды

char command[512];

// Формирование команды для вызова pmap и записи вывода в файл

snprintf(command, sizeof(command), "pmap %d > %s", pid, filename);

// Выполнение команды system() для получения карты памяти процесса

if (system(command) == -1) {

// Если команда не выполнена, выводим ошибку и завершаем программу

fprintf(stderr, "Failed to execute pmap command.\n");

exit(EXIT_FAILURE);

}

// Выводим сообщение о том, что файл с картой памяти был создан

printf("Memory map saved to: %s\n", filename);

}

int main(int argc, char* argv[]) {

// Проверка корректности количества аргументов

if (argc != 3) {

// Если аргументов недостаточно или их слишком много, выводим сообщение о неправильном использовании программы

fprintf(stderr, "Usage: %s <pid> <directory>\n", argv[0]);

exit(EXIT_FAILURE);

}

// Преобразуем первый аргумент (PID) в целое число

int pid = atoi(argv[1]);

// Второй аргумент — путь к директории для сохранения файла

const char* directory = argv[2];

// Вызываем функцию для создания файла с картой памяти

create_map_file(pid, directory);

// Возвращаем 0, чтобы завершить программу без ошибок

return 0;

}

Работа представлена на рисунке 2.

Рисунок 2 - Выполнение задания 2

Создала службу, запускающуюся каждые 30 секунд и выполняющую программу из п.2., проверить работу и получить 10-15 файлов с картами памяти для из п.1.

Рисунок 3 - Создание службы

Рисунок 4 – Создание файла службы

Рисунок 5 - Таймер

Проанализировала полученные результаты, написав программу, строящую график по данным карт виртуальной памяти из п.3 (для всех файлов карт в казанной при запуске директории).

Листинг 3 – Код программы, строящий график по данным карт виртуальной памяти

import os

import re

import matplotlib.pyplot as plt

from datetime import datetime

def parse_memory_file(file_path):

размер памяти."""

heap_size = None

timestamp = None

# Открываем файл и читаем все строки

with open(file_path, 'r') as file:

lines = file.readlines()

# Ищем строку с общим размером памяти

for line in lines:

if line.strip().startswith("total"):

match = re.search(r"total\s+(\d+)K", line)

if match:

# Если нашли, извлекаем размер кучи в КБ

heap_size = int(match.group(1))

break

# Извлекаем имя файла и пытаемся получить временную метку из имени

base_name = os.path.basename(file_path)

try:

# Предполагается, что имя файла в формате map_<pid>_<date&time>.txt

name_parts = base_name.replace(".txt", "").split('_')

if len(name_parts) >= 4:

# Форматируем метку времени как "дата время"

timestamp = name_parts[2] + " " + name_parts[3]

except ValueError as e:

# В случае ошибки при разборе имени файла выводим ошибку

print(f"Error parsing timestamp from {base_name}: {e}")

return timestamp, heap_size

def plot_heap_usage(directory, output_file):

"""Строит и сохраняет график изменения размера кучи."""

timestamps = [] # Список для хранения временных меток

heap_sizes = [] # Список для хранения размеров кучи

# Получаем список всех файлов в директории

file_names = os.listdir(directory)

# Сортируем файлы по времени, извлекаем из имени файла временные метки

file_names.sort(key=lambda file: re.sub(r'(?<=\d{2}):(\d{1})\b', r':0\1', file.split('_')[2] + ' ' + file.split('_')[3]))

# Проходим по всем файлам в директории

for file_name in file_names:

file_path = os.path.join(directory, file_name)

# Если это файл и его имя начинается с "map_", обрабатываем его

if os.path.isfile(file_path) and file_name.startswith("map_"):

try:

# Парсим файл для получения метки времени и размера кучи

timestamp, heap_size = parse_memory_file(file_path)

if heap_size is not None:

# Добавляем данные в списки

timestamps.append(timestamp)

heap_sizes.append(heap_size)

except Exception as e:

# В случае ошибки при обработке файла выводим сообщение

print(f"Error processing file {file_name}: {e}")

# Если нет данных для построения графика, выводим сообщение и завершаем функцию

if not timestamps:

print("No valid data found to plot.")

return

# Построение графика с использованием matplotlib

plt.figure(figsize=(10, 6))

plt.plot(timestamps, heap_sizes, marker='o', linestyle='-', color='b') plt.title('Изменение размера кучи от времени')

plt.xlabel('Время')

plt.ylabel('Размер кучи (kB)')

plt.xticks(rotation=45)

plt.tight_layout() #

plt.grid(True)

# Сохранение графика в файл

plt.savefig(output_file)

print(f"График сохранён в файл: {output_file}")

plt.show() # Отображаем график

if __name__ == "__main__":

# Вводим путь к директории с файлами и имя для сохранения графика

directory = input("Введите путь к папке с файлами: ")

output_file = input("Введите имя файла для сохранения графика (например, graph.png): ")

# Строим график

plot_heap_usage(directory, output_file)

Рисунок 6 – Работа программы в режиме 0

Рисунок 7 – Работа программы в режиме 1

Вопросы:

Что такое стек и куча?

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

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

Как происходит статическое и динамическое выделение памяти?

Статическое выделение памяти происходит на этапе компиляции.

Динамическое выделение памяти происходит на этапе выполнения программы.

Каковы плюсы и минусы статического и динамического выделения памяти?

Статическое выделение происходит быстрее, а также порой требует меньшее необходимой памяти.

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

Вывод

В результате проделанной работы мы изучили системные функций выделения памяти в ОС GNU/LINUX и получили практические навыки работы с динамической памятью в ОС GNU/LINUX.