CRIU (application Checkpoint/Restore In Userspace) — это амбициозный, быстро развивающийся проект, который позволяет сохранить состояние программы в виде контрольной точки, и впоследствии возобновить работу приложения с этой точки.
Возможности применения программного обеспечения для создания контрольных точек достаточно разнообразны. К примеру, OpenVZ использует похожий механизм для “живой” миграции. Parallels Virtuozzo использует подобный механизм для быстрого возобновления работы контейнеров после обновления ядра. CRIU уже используется в высокопроизводительных кластерах для для сохранения промежуточных результатов вычислительных процессов, используемых для возобновления работы приложения в случае сбоя.
В этой статье рассказывается, как CRIU сохраняет и восстанавливает состояние программы, и почему этот проект может быть успешнее своих предшественников.
Немного истории
CRIU — это далеко не первая попытка реализовать некий механизм сохранения и восстановления программ в Linux. Существует как минимум две рабочие реализации C/R: OpenVZ и проект linux-cr, лидером которого является Oren Laadan.
Проблема обоих проектов в том, что они реализуют весь C/R практически полностью в пространстве ядра. Однако эти проекты не стали частью основного ядро Linux из-за большого объёма и сложного кода.
Лидер команды разработчиков OpenVZ Павел Емельянов предложил изменить сам подход к C/R и перенести основную работу в пространство пользователя. Сообщество хорошо приняло эту идею и появился проект CRIU.
Сохранение состояния приложения
Приложение может состоять как из одного, так и из множества запущенных процессов. CRIU поддерживает оба типа.
Для получения информации о состоянии процесса первое, что приходит на ум, это использование механизма, которым пользуется отладчик (ptrace). Но он не предоставляет всей информации. Часть состояния процесса можно почерпнуть из файловой системы procfs и системного вызова prctl, однако и этого недостаточно.
Для получения недостающей информации в проекте CRIU был использован механизм внедрения исполняемого кода в процесс (т.н. паразитный код), который был разработан и любезно предоставлен Tejun Heo, одним из основных на сегодняшний день разработчиков ядра Linux.
Восстановление состояния приложения
Для восстановления приложения с контрольной точки необходимо сначала создать дерево его процессов. Для этого в ядре Linux был разработан отдельный механизм для создания процессов с заданными идентификаторами. После старта каждый процесс восстанавливает свою память, открытые файлы, сокеты, пайпы, IPC и т д.
Однако на самом деле всё не так просто, ведь ресурсы могут быть общими для нескольких процессов.
Регионы адресного пространства процесса могут быть уникальны (MAP_PRIVATE) либо доступны одновременно несколькими процессами (MAP_SHARED). Первый тип восстанавливается достаточно просто (если не задумываться о технологии “copy on write” — она остаётся за скобками этой статьи).
В чем же проблема во втором варианте? Если у нас регион адресного пространства отображается в файл, то все в порядке — для восстановления такого куска адресного пространства можно использовать стандартные механизмы Linux.
Но что делать, если регион анонимный (MAP_ANONYMOUS), т.е. отображается в оперативную память? Для поддержки восстановления такого региона пришлось опять подровнять ядро напильником, а именно реализовать отображение каждого в файловой системе procfs — для каждого распределенного региона памяти ядром создаётся файл в /proc/self/map_files/<start_addr>-<end_addr>. Благодаря этом файлам анонимные регионы распределённой памяти можно восстанавливать как файловые.
Следующей проблемой на очереди были открытые файлы. Они тоже могут использоваться совместно несколькими процессами. Здесь пригодилась возможность передачи файловых дескрипторов через Unix сокеты. Один из процессов-пользователей открывает файл, и пересылает его дескриптор остальным.
Итак, память восстановлена, файлы открыты. Как же передаётся управление процессу? Передача управления основана системном вызове sigreturn(): когда процессу приходит сигнал, ядро сохраняет состояние процесса и передает управление обработчику сигнала, который по окончанию вызывает sigreturn(). Таким образом для того, чтобы запустить процесс с желаемой точки достаточно восстановить состояние процесса в нужном формате и позвать sigreturn().
И вот процесс восстановлен и продолжает работать.
Интересное
CRIU умеет сохранять и восстанавливать TCP соединения. Это может использоваться для живой миграции. Пользователь заметит лишь небольшую задержку в сетевой активности при переезде приложения со всеми своими соединениями на другой сервер.
CRIU сохраняет данные процесса на диске с помощью формата Google’s Protocol Buffers. Программные библиотеки для работы с этим протоколом существуют для большинства популярных языков.
При разработке вся возможности CRIU постоянно проверяются встроенной тестовой системой. Для этого используется переработанный и улучшенный фреймворк, заимствованный из проектов по разработке OpenVZ и Virtuozzo.
CRIU разрабатывается компанией Parallels, в рамках проекта по переносу кода OpenVZ в основное ядро Linux.
Цели
Основная цель проекта на ближайшее будущее — это научиться сохранять и восстанавливать состояние Linux Containers (LXC). Для этого необходимо реализовать сохранение и восстановление терминалов, полученных, но ещё не обработанных сигналов, пространств имен сетевого окружения (network namespace) и иерархии файлов (mount namespace; находится в разработке).
Ссылки
ru.wikipedia.org/wiki/CRIU
ckpt.wiki.kernel.org/index.php/Main_Page
wiki.openvz.org/Checkpointing_and_live_migration
www.parallels.com/
code.google.com/p/protobuf/
Автор: avagin