Есть вещи, в которые многие программисты верят по учебникам, а они уже несколько лет, как потеряли свою актуальность. Большой набор сюжетов такого рода, которые я помню из детства, — про оптимизацию производительности. Сегодня же в 80% случаев лучший совет по ускорению программ, который можно дать программисту, — пиши понятно и сэкономь время друга, который будет читать твой код.
Сюжет, про который пойдет речь в данной статье — почему аллокатор malloc
под Linux возвращает очищенную память. Почти как calloc
. Более точное утверждение — чистую память возвращают аллокаторы, которые вызываются до первой команды free
. Благодаря этому замечательному обстоятельству память, которую процесс qemu-kvm
выделяет для создаваемой виртуальной машины, заполнена нулями. Тем самым, создаваемая виртуальная машина не может получить доступ к служебным данным гипервизора и данным других виртуальных машин.
Основные понятия механизмов распределения памяти
В данном разделе определяются основные понятия управления памятью, необходимые для понимания механизма выделения памяти пользовательским процессам и виртуальным машинам.
- Аллокаторы и деаллокаторы
- Анонимные отображения памяти
- Быстрый эмулятор QEMU
- Виртуальная машина
- Гипервизор
- Гостевая операционная система
- Копирование при записи
- Куча
- Модули ядра KVM
- Операционная система
- Отложенная инициализация
- Отображение памяти
- Сервер управления доступом FreeIPA
- Система управления виртуализацией oVirt
- Среда виртуализации Red Hat Enterprise Virtualization
Аллокаторы и деаллокаторы
Для размещения и удаления объектов в куче используются примитивы создать объект и удалить объект, так называемые аллокаторы и деаллокаторы.
Все аллокаторы и деаллокаторы, включая конструкцию new
языка C++, построены на базовых библиотечных функциях malloc
и free
. Эти функции определяются в стандартной библиотеке языка C, и декларируются в <stdlib.h>
.
Данные функции реализуют сложные и эффективные алгоритмы перераспределения и учета памяти. Например, современные реализации, адаптированные под многопоточное исполнение и многопроцессорную аппаратуру, используют отдельную кучу на каждый поток исполнения. Данный подход позволяет избежать дорогих операций синхронизации при выделении локальной памяти для потока.
В Юниксах предыдущего поколения заказ памяти у операционной системы производился утилитами сдвига границы выделяемой памяти brk
(sbrk
). В современных Linux используется ленивое выделение памяти с помощью утилиты mmap
, создающей анонимные отображения страниц памяти. Добавлю, что утилита brk
присутствует в Линукс и реализована на базе тех же анонимных отображений.
Для темы, обсуждаемой в статье, важно, что какой бы системный вызов мы не использовали, выделяемая операционной системой память инициализируется нулями.
Анонимные отображения памяти
Анонимные отображения памяти — отображения памяти в пространство виртуальной памяти процесса, а не файла в пространстве файловой системы. Анонимное отображение схоже со стандартным аллокатором памяти функцией malloc
и используется в реализации malloc
для запроса памяти у операционной системы. Как следует из описания соответствующего механизма, память при этом инициализируется нулями.
Быстрый эмулятор QEMU
QEMU (Quick EMUlator) — свободная программа с открытым исходным кодом для эмуляции аппаратного обеспечения различных платформ. Автор программы — гениальный французский программист Фабрис Беллар, создатель популярной библиотеки libavcodec
и многих других чудесных произведений:
QEMU включает в себя эмуляцию процессоров Intel x86 и устройств ввода-вывода, а также других процессоров. В режиме эмуляции на порядок замедляет исполнение кода. При использовании технологий аппаратной виртуализации (Intel VT и AMD SVM) на x86-совместимых процессорах выдает сопоставимую производительность. Является частью среды виртуализации.
Из исходного кода проекта собирается программа qemu-kvm
, которая эмулирует отдельную виртуальную машину. При заказе памяти данная программа пользуется аллокатором phys_mem_alloc
:
Описание функции qemu_anon_ram_alloc
приводится в файлах oslib-win32.c
, oslib-posix.c
. В Linux оно основывается на анонимном отображении памяти:
В связи с тем, что данная программа при заказе памяти пользуется стандартным аллокатором, она получает память от операционной системы, заполненную нулями.
Виртуальная машина
Виртуальная машина (ВМ, VM, virtual machine) — в данном контексте это программная система, эмулирующая аппаратное обеспечение x86-совместимого компьютера (включая BIOS, оперативную память, жёсткий диск и другие периферийные устройства) на базе этой же платформы. На ВМ, как и на реальный компьютер, можно устанавливать операционные системы (например, Windows можно запускать в виртуальной машине под Linux или наоборот). На одном сервере может функционировать несколько виртуальных машин. Использование виртуальных машин обеспечивает изоляцию данных между пользователями, работающими в одной вычислительной системе, обеспечивая дополнительный уровень защиты данных.
Концепция виртуальной машины как совокупности ресурсов, которые эмулируют поведение реальной машины, появилась в Кембридже в конце 1960-х годов в виде расширения концепции виртуальной памяти манчестерской вычислительной машины Atlas. В целом вычислительный процесс определяется в рамках этой концепции содержимым того рабочего пространства памяти, к которому он имеет доступ. При условии, что конкретная ситуация в этом рабочем пространстве соответствует ожидаемой, процесс не имеет никаких средств для определения того, является ли представленный ему ресурс действительно физическим ресурсом этого типа, или же он имитируется действиями других ресурсов, которые приводят к аналогичным изменениям содержимого рабочего пространства процесса.
Гипервизор
Гипервизор (hypervisor, монитор виртуальных машин) — минимальная операционная система, обеспечивающая одновременное, параллельное выполнение нескольких или многих гостевых операционных систем на сервере или кластере виртуализации. Он предоставляет запущенным под его управлением операционным системам виртуальные машины. Гипервизор позволяет осуществить независимое включение, выключение, перезагрузку любой из виртуальных машин.
Хороший гипервизор предоставляет дополнительные удобные средства управления виртуальными машинами, такие как сохранение и восстановление состояния машины.
Гипервизор обеспечивает изоляцию виртуальных машин и запущенных ОС друг от друга, защиту и безопасность, средства связи и взаимодействия между собой (например, через обмен файлами или сетевые соединения) так, как если бы эти ОС выполнялись на разных физических компьютерах.
Гостевая операционная система
Гостевая операционная система — это операционная система, устанавливаемая на виртуальную машину. Гостевой она называется в отличие от ОС гипервизора, устанавливаемой на физический сервер.
Копирование при записи
Механизм копирования при записи (copy-on-write) используется для оптимизации процесса копирования страниц виртуальной памяти. Сначала в таблице виртуальных страниц обе виртуальные страницы указывают на одну физическую и защищены от записи. При попытке записи создается копия страницы в физической памяти.
Это позволяет существенно ускорить работу функции fork
, а также новых файловых систем, таких как ZFS и Btrfs. Другим важным использованием является зануление страниц виртуальной памяти при выделении: они просто помечаются, как копии нулевой страницы ZERO_PAGE
.
entry = pte_wrprotect(mk_pte(ZERO_PAGE(addr), vma->vm_page_prot));
Куча
Куча (heap) — название структуры данных, с помощью которой реализована динамически распределяемая память приложения, а также объём памяти, зарезервированный под эту структуру.
Куча использует память, лениво запрашиваемую по мере необходимости у операционной системы. Эта память используется для размещения объектов, динамически создаваемых и уничтожаемых программой с помощью аллокаторов и деаллокаторов.
Модули ядра KVM
Модули ядра KVM (Kernel-based Virtual Machine) — свободное программное обеспечение, обеспечивающее аппаратную виртуализацию в среде Linux на платформе x86. Базовый сервис виртуализации предоставляется модулем kvm.ko
, процессорная специфика содержится в модулях kvm-amd.ko
либо kvm-intel.ko
.
Эмуляция виртуальных машин осуществляется эмулятором Фабриса Белларда QEMU. Эта программа, работающая в пространстве пользователя, использует интерфейс /dev/kvm
для настройки адресного пространства виртуальной машины, через него же эмулирует устройства ввода-вывода и видеоадаптер.
Для данного обсуждения необходимо понимать, что хотя KVM является важной и сложной частью процесса виртуализации, он не участвует в выделении оперативной памяти для виртуальных машин. Этим занимается QEMU, или, более точно, программа qemu-kvm
, которая, в свою очередь, использует традиционные аллокаторы памяти.
Операционная система
Операционная система (ОС, operating system, OS) — комплекс программ, которые, с одной стороны, выступают как унифицированный интерфейс между устройствами вычислительной системы и прикладными программами, а с другой стороны предназначены для управления устройствами, вычислительными процессами, распределения вычислительных ресурсов и организации надежных и безопасных вычислений.
Отложенная инициализация
Отложенная (ленивая) инициализация, (lazy initialization) — прием в программировании, когда некоторая ресурсоемкая операция (создание объекта, вычисление значения) выполняется непосредственно перед тем, как будет использован ее результат. Таким образом, инициализация выполняется по требованию, а не заблаговременно. Аналогичная идея находит применение в самых разных областях:
- при компиляции на лету метод компилируется непосредственно перед исполнением;
- при использовании отображений памяти страница загружается с диска, когда к ней происходит обращение;
- при динамическом управлении памятью новая страница памяти для кучи запрашивается у операционной системы только в случае необходимости.
Отображение памяти
Вызов mmap
— вызов Linux, позволяющий выполнить отображение файла или устройства на память. Используется для ленивого ввода-вывода. Файловые отображения позволяют отобразить файл в виртуальную память. Доступ к этим участкам памяти приводит к чтению или изменению файла.
Также используется как менеджер памяти для выделения страниц по запросу (в анонимном режиме). При этом операционная система обнуляет выделяемую область памяти.
Отображаемая память при завершении процесса или использовании munmap
, если только отображение не является публичным и не зарезервировано в файле. Сегмент памяти, созданный mmap
, автоматически удаляется ядром системы, когда завершается его использование.
Вызов mmap
, который может использоваться пользовательской программой для создания отображений памяти, описывается в стандартной библиотеке glibc
и выполняется ядром операционной системы.
Сервер управления доступом FreeIPA
Сервер управления доступом IPA (Identity, Policy and Audit) это свободный программный продукт для создания централизованной системы для идентификации пользователей и задания политик доступа, система обеспечения безопасности в средах виртуализации.
Система управления виртуализацией oVirt
oVirt
— свободная, кроссплатформенная система управления виртуализацией. Позволяет управлять гипервизором и образами виртуальных машин на базе технологии KVM+QEMU посредством веб-интерфейса, используя для администрирования библиотеку libvirt
.
Среда виртуализации Red Hat Enterprise Virtualization
Red Hat Enterprise Virtualization (RHEV) — корпоративный продукт, обеспечивающий возможности виртуализации. Основан на свободных продуктах: модуле ядра KVM, эмуляторе QEMU, сервере управления доступом FreeIPA и веб-приложении управления виртуализацией oVirt.
Механизм, реализующий анонимные отображения памяти
Вызов mmap
, который может использоваться пользовательской программой для создания отображений памяти, описывается в стандартной библиотеке glibc
в соответствии с таблицей системных вызовов ядра. Соответствующий системный вызов sys_mmap
определяется в sys_x86_64.c
: он конвертирует смещение offset
из байтов в страницы и вызывает системный вызов sys_mmap_pgoff
. Префикс sys_
добавляется макросами SYSCALL_DEFINE6
и SYSCALL_DEFINEx
.
Системный вызов sys_mmap_pgoff
определяется следующим образом: системный вызов находит файл, соответствующий аргументу типа struct file *
и вызывает vm_mmap_pgoff
.
Вызов vm_mmap_pgoff
после проверок безопасности вызывает do_mmap_pgoff
.
Функция do_mmap_pgoff
определяет ищет цепочку последовательных свободных страниц для создания отображения с помощью функций get_unmapped_area
, находит адрес начала отображения addr
и передает его в mmap_region
. Для определенных устройств, которые умеют себя отображать только на конкретные адреса памяти и определяют свой f_op->mmap
, значение addr
может поменяться, что требует дополнительных проверок и потенциально может привести к неуспеху mmap
, если мы попадем на занятую память.
Функция mmap_region
создает структуру типа vm_area_struct
, описывающую новый регион памяти.
Выделяемые страницы отображения изначально защищены от обращений. Когда программа обращается к защищенной странице, срабатывает перехватчик handle_pte_fault
:
Перехватчик handle_pte_fault
вызывает do_no_page
:
Если размер страницы виртуальной памяти равен 4 Кбайт, то в функции do_anonymous_page
выдаваемая страница помечается копией в режиме копирования при записи от ZERO_PAGE
(нулевой страницы). Если система переконфигурирована для работы со страницами большого (2 Мбайт или 1 Гбайт) размера, то обнуление происходит в функции do_huge_pmd_anonymous_page
. Размер страницы можно узнать с помощью getconf PAGESIZE
.
Механизм выделения памяти для виртуальных машин
Когда все понятия определены и механизм создания отображений памяти ясен, понять причину изоляции памяти виртуальных машин легко:
- Администратор создает виртуальную машину.
- Память виртуальной машины создается библиотечным вызовом
mmap
— анонимно для новой машины или из файла. - Как следует из описания работы отображений памяти, память заполняется нулями.
Спасибо Michal Hocko.
Комментариев нет :
Отправить комментарий