Как процессу выделяется память в Linux

«Как процессу выделяется память в Linux» — вопрос из категории Linux, который задают на 23% собеседований Devops Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В Linux выделение памяти процессу — это многоуровневый процесс, управляемый ядром, который я часто анализирую при оптимизации потребления ресурсов или отладке утечек.

Основные этапы выделения памяти:

  1. Запрос виртуальной памяти (userspace): Приложение через библиотеку C (glibc) вызывает malloc() или напрямую системный вызов mmap(). На этом этапе процессу выделяется только виртуальное адресное пространство в его page tables, но физическая память еще не зарезервирована.

    // Пример: выделение 1 МБ памяти
    void *ptr = malloc(1024 * 1024);
    // Или через mmap для больших регионов
    void *ptr = mmap(NULL, 1024*1024, PROT_READ|PROT_WRITE,
                     MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
  2. Page Fault и выделение физической памяти (ядро): При первой попытке записи или чтения по адресу ptr процессор генерирует page fault. Ядро Linux перехватывает его, находит свободную физическую страницу (обычно 4 KiB) через buddy allocator и обновляет таблицу страниц процесса, отображая виртуальный адрес на физический. Это называется lazy allocation.

  3. Управление памятью через slab allocator: Для мелких и частых выделений (как в malloc для маленьких объектов) ядро использует slab allocator, который кэширует заранее выделенные страницы, разбитые на объекты фиксированного размера, чтобы уменьшить фрагментацию и ускорить выделение.

Ключевые механизмы, которые я учитываю в работе:

  • Copy-on-Write (CoW): При вызове fork() страницы памяти родительского процесса помечаются как read-only. Физическое копирование происходит только когда один из процессов (родитель или ребенок) пытается записать в такую страницу. Это критически важно для эффективного запуска процессов в Docker.
  • Swap: Если физической памяти не хватает, ядро может выгрузить (swap out) неактивные страницы на диск (в swap-файл или раздел). Это видно в выводе free -h или vmstat 1.
  • OOM Killer: В крайних случаях, когда память исчерпана и swap тоже, ядро запускает Out-Of-Memory Killer, который на основе сложного алгоритма (oom_score) выбирает и принудительно завершает процесс. Я настраиваю oom_score_adj для критичных системных процессов (например, sshd), чтобы защитить их.

Инструменты для мониторинга:

# Показать использование памяти процессами
ps aux --sort=-%mem | head -10

# Детальная статистика по памяти
cat /proc/meminfo

# Отслеживание page faults процесса
/usr/bin/time -v <command>  # Смотрим на 'Major (requiring I/O) page faults'

# Анализ памяти процесса через /proc
cat /proc/<PID>/smaps  # Детальная карта памяти процесса

На практике, при настройке серверов приложений (например, JVM для Java-сервисов) я всегда ограничиваю максимальный размер heap через -Xmx, исходя из доступной памяти на ноде, чтобы избежать излишнего swapping и вмешательства OOM Killer.