Лекция 4

05.09.2025 Обновлено: 05.09.2025

Лекция 4. Работа с памятью

Архитектура: фон Неймана vs Гарвардская

  • 2 потока: поток данных и поток команд-вычислений.
    • В архитектуре фон Неймана они идут через одну шину памяти.
    • В Гарвардской — данные и команды разделены.
  • 2 блока: блок процессора и блоки, отвечающие за память:
    • SSD (сотни ГБ) и HDD (ГБ–ТБ) — жёсткие диски.
    • Оперативная память (~16 ГБ).
    • Кэш процессора (3 уровня: L1 — ~24 КБ, L2 — ~112 КБ, L3 — ~4 МБ) — сверхоперативная память, используемая микропроцессором для уменьшения среднего времени доступа к компьютерной памяти.

“Процесс”

  • Под каждый процесс отводится своя память.
  • Своё адресное пространство.
  • Свои потоки для каждого процесса.
  • ОС для каждого процесса создаёт иллюзию того, что он может пользоваться всей виртуальной памятью, хотя на самом деле она разделена между процессами.
  • ОС хранит взаимно-однозначное соответствие между виртуальной памятью и физической памятью с помощью Page Table (выдаёт страницы памяти, забирает, отдаёт дальше).
  • Для каждого процесса создаётся своя виртуальная таблица — одним и тем же виртуальным адресам в разных процессах могут соответствовать разные места физической памяти.
  • ОС может swap-ать часть информации на жёсткий диск, если она используется редко.

Сегменты памяти

  • Сегмент ОС (виртуальная таблица) — доступа у пользователя нет.
  • 3 сегмента фиксированного размера:
    • Text — исполняемый код.
    • Data — инициализированные глобальные/статические переменные.
    • BSS — неинициализированные глобальные/статические переменные.
  • 3 сегмента, меняющих размер:
    • Stack — стек вызовов.
    • Heap — куча.
    • Memory mapping segment — для маппинга файлов.
Process id: 18636
Data segment:    00007ff6cc079000
BSS  segment:    00007ff6cc078000
Text segment:    00007ff6cc079004
Code segment:    00007ff6cc071430
Stack segment:   0000007 8c0bff884

Первые сегменты расположены близко друг к другу, а Stack — где-то далеко (растёт в обратном направлении).

Stack (стек)

  • Чтение и запись.
  • Локальные переменные функций.
  • Почему размер стека такой небольшой? — Чтобы обеспечить возможность быть достаточно близким к кэшу процессора, но при этом достаточно большим для типичных задач.
  • Растёт в сторону уменьшения адресов.
  • Управляется автоматически (LIFO).

Heap (куча)

  • В отличие от стека, позволяет создавать динамические структуры большого размера.
  • Управление жизнью объектов в куче — «ручное».

Семейство C-функций:

  • malloc / free — оперируют байтами.
    • malloc не гарантирует выделение памяти (может вернуть NULL).
    • Не забывать выставлять указатель в NULL после освобождения (избегаем dangling pointer).
    • free(NULL) ничего не делает (безопасно).
  • calloc / realloc — оперируют не байтами, а массивами:
    • calloc дополнительно зануляет память.
    • realloc изменяет размер ранее выделенного блока.
  • Если не освобождать память — объём используемой памяти будет раздуваться (memory leak).

Операторы C++:

  • new / delete:
    • При нехватке памяти выкинет исключение (std::bad_alloc), в отличие от C-функций.
    • Занимается инициализацией и вызовом конструктора классов (в отличие от C-функций).
    • Для массивов используется new[] / delete[].

Segmentation fault

Возникает при:

  • Обращении к несуществующему адресу.
  • Обращении к сегменту, прав для которого нет.

    Если объявить массив char через [] (char s[] = "abc") — ему можно менять элементы. А если через * (char* s = "abc") — это указатель на глобальный строковый литерал, и его уже нельзя менять.

  • Попытке поменять данные в read-only сегменте.
  • Обращении по нулевому указателю.
  • Обращении по указателю на удалённый объект (dangling pointer).
  • Переполнении стека (stack overflow).
  • Переполнении буфера (buffer overflow).

Memory mapping segment

  • Для физических файлов, которые мы можем загружать на компьютер (через mmap).
  • Используется также для подгрузки разделяемых библиотек.

Text

  • Тексты, строчки (строковые литералы), исполняемый код.
  • Не доступен для записи (read-only).

Data

  • Статические переменные и константы (инициализированные).
  • В случае констант — только для чтения.

BSS

  • Глобальные/статические неинициализированные переменные.
  • Запись и чтение.
  • Инициализируются нулями при старте программы.

Регистры процессора

  • Регистр — поле заданной длины во внутрипроцессорной сверхбыстрой оперативной памяти (СОЗУ).
  • Используется самим процессором; может быть как доступным, так и не доступным программно.
  • Например, при выборке из памяти очередной команды она помещается в регистр команд, обращение к которому программист прописать не может.
  • Виды регистров:
    • Регистры общего назначения.
    • Специальные регистры для приложений.
    • Сегментные регистры.
    • Специальные регистры режима ядра.

Кадр (stack frame)

  • Кадр — множество данных: аргументы функции, все локальные переменные функции и адрес, куда функция должна вернуть результат.
  • Размер кадра известен на этапе компиляции.

Важно

  • Указатель находится на стэке! (Сам указатель — локальная переменная; объект, на который он указывает, может быть в куче.)
  • Из-за того, что процессор отдаёт процессам отдельные страницы виртуальной таблицы, при завершении процесса данные участки памяти будут очищены и отданы другим процессам.