УДК 004.4'233
ПОДДЕРЖКА МНОГОПОТОЧНОСТИ СИМУЛЯТОРА QEMU
М.А. Климушенкова SUPPORT FOR MULTITHREADING OF THE EMULATOR QEMU М.А.Klimushenkova
Институт электронных и информационных систем НовГУ, maria.klimushenkova@ispras.ru
Описана проблема эмуляции многопоточных приложений в эмуляторе QEMU и ее причины, а также проблема их детерминированного воспроизведения при использовании журнала событий. Сделан обзор существующих вариантов реализации многопроцессорного воспроизведения приложений, выделены их недостатки. Ключевые слова: QEMU, виртуальные машины, многопоточность, многопроцессорные системы
The article describes the problem of multithreading applications' simulation in the emulator QEMU, as well as the problem of deterministic simulation with using an event log. The causes of the problems are also considered. The existing tools for multiprocessor applications' simulation are reviewed and their disadvantages are revealed. Keywords: QEMU, virtual machines, multithreading, multiprocessors
Введение
В настоящее время многопроцессорная архитектура является доминирующей архитектурой компьютеров. Возникает необходимость эффективно эмулировать многопроцессорные программные системы на многопроцессорных машинах. Преимущества многопроцессорных систем делают актуальной эмуляцию целой системы, а не отдельного приложения. Возрастает важность определения узких мест производительности, выявления и анализа ошибок в программах, особенно в параллельных. Иногда возникает потребность в разработке системного программного обеспечения раньше аппаратного. Для достижения этих целей больше всего подходит эму-
ляция целой системы, охватывающая весь стек программного обеспечения, включая операционную систему, библиотеки и пользовательские приложения [1].
Среди многих инструментов эмуляции и виртуализации QEMU является средством, основанным на бинарной трансляции кода, вследствие чего обеспечивается гибкость эмуляции процессорной архитектуры на хостовой машине другой процессорной архитектуры. Однако QEMU малоэффективен при эмуляции целевой многопроцессорной системы поверх хостовой системы, это существенно ограничивает применимость QEMU. Основной причиной низкой производительности является однопоточная реализация QEMU, которая идет корнями в 2005 год, когда многопроцессорные системы не были так актуальны, как сейчас [2].
Основные проблемы текущей реализации QEMU
QEMU-эмулятор, основанный на бинарной трансляции, работает в двух режимах: режиме пользователя и режиме системы. В режиме пользователя QEMU непосредственно взаимодействует с программой и относится к ней как к пользовательскому процессу. Пользовательская программа не может порождать потоков, так как концепция потоков не существует в пользовательском режиме эмуляции, и QEMU не поддерживает использование потоковых библиотек (таких как PTHREAD). В системном режиме эмуляции QEMU может эмулировать поведение операционной системы, выбранной пользователем, вместе с операциями, выполняемыми приложениями, которые работают поверх установленной ОС.
Вначале QEMU поддерживал эмуляцию только однопроцессорных систем. С увеличением популярности многопроцессорных систем в последние несколько лет QEMU начал поддерживать их эмуляцию. Текущая реализация QEMU может эмулировать среду c несколькими процессорами с разделяемой памятью. Однако, несмотря на возможность эмуляции целевой многопроцессорной системы, производительность этого процесса очень низкая.
Основная причина такой неэффективности заключается в цели, для которой был изначально спроектирован QEMU: эмуляция однопроцессорной целевой системы на однопроцессорной хостовой системе. В соответствии с этой целью, QEMU был реализован однопоточным: поскольку в целевой машине один процессор, в один момент необходимо было эмулировать только одну задачу, а так как в хостовой системе только один процессор, только одно задание в один момент может выполняться аппаратной платформой [3].
До версии 0.11 QEMU придерживался этой цели, и в нем не было поддержки нескольких потоков. В версии 0.11 QEMU начал использовать отдельный поток ввода-вывода, выполняющий задачи, которые могут потребовать длительное время. Однако операции процессора не распараллеливались и выполнялись в одном потоке. Это значит, что не важно, как много ядер в хостовой машине, QEMU будет создавать только два потока: один — для выполнения задач ввода-вывода, второй — для выполнения операций всех виртуальных процессоров. Понятно, что последний поток может являться узким местом при эмуляции системы, особенно когда в гостевой операционной системе несколько активно работающих процессоров [2].
Существует вариант решения для повышения скорости эмуляции, применяемый в QEMU версии 0.11. Это использование КУЛ для виртуализации вместо эмуляции. Каждому виртуальному процессору QEMU выделяет отдельный KVM-виртуальный процессор. Эмуляция, основанная на КУИ, не полагается на механизм динамической бинарной трансляции QEMU. Передача сигналов между виртуальными процессорами эмулируется с помощью внутренних механизмов коммуникации KVM. Основным ограничением этого решения является то, что KVM требует одинаковой архитектуры для целевого и хостового
процессоров [4]. Это позволяет избежать использования динамической бинарной трансляции и иметь приемлемую скорость эмуляции, однако накладывает серьезные ограничения на применимость. Особенно если учитывать, что одно из наиболее популярных применений QEMU — это эмуляция процессора ARM на x86 машинах. Решение с KVM этого сделать не позволяет.
Существующие варианты реализации поддержки многопоточности в QEMU
Есть несколько проектов на базе QEMU, которые реализуют многопоточную симуляцию многопроцессорных систем, поддерживая при этом динамическую трансляцию кода.
В работе [2] описывается попытка реализации параллелизма на самом низком уровне эмулируемой системы — аппаратном уровне. Основным преимуществом этого подхода является то, что ядра процессора работают по сути параллельно. В этом случае не имеет значения, какой вид параллелизма реализован в операционной системе или пользовательском приложении (например, создается несколько потоков или процессов). Вместо одного потока, выполняющего процессорные инструкции, создается несколько, по одному для каждого виртуального процессора, при этом устраняется большинство общих для виртуальных процессоров переменных и записей. Когда потоки созданы, они могут эмулировать выполнение всех виртуальных процессоров одновременно с минимальным количеством точек синхронизации. Каждый поток получает задание на выполнение отдельного блока кода, полученного от генератора кода (TCG), транслирующего его из языка гостевой машины в язык хостовой.
Реализация оказалась не очень удачной, скорость эмуляции многопоточной системы оказалась ниже, чем скорость эмуляции этой же системы одно-поточным QEMU. Основными причинами этого являлись по-прежнему большое количество структур данных, разделяемых между виртуальными процессорами, и некоторые критические секции кода, защищенные мьютексами. Вследствие этого большая часть кода выполняется последовательно, несмотря на использование нескольких процессорных потоков.
Другой попыткой реализации поддержки многопоточности в QEMU является проект COREMU [1]. COREMU — это фреймворк для масштабируемой параллельной эмуляции, который отделяет сложность распараллеливания симуляторов от их реализации. Ключевое наблюдение — это то, что ядра процессора и устройства в современных многоядерных процессорах взаимодействуют через хорошо определенные интерфейсы. Основываясь на этом наблюдении, COREMU эмулирует несколько ядер путем создания нескольких экземпляров существующих последовательных эмуляторов (конкретно QEMU) и использует библиотеку для управления синхронизацией и взаимодействием между процессорами и устройствами, чтобы поддерживать согласованное представление о системных ресурсах.
Работающий прототип COREMU поддерживает платформы x64 и ARM, может эмулировать до 255
ядер. Сравнение с QEMU показало, что COREMU, имея незначительные накладные расходы, функционирует и масштабируется значительно лучше QEMU. В настоящее время этот проект не развивается и не поддерживается разработчиками.
Сейчас нет инструментов, позволяющих эмулировать работу многопоточных приложений без существенного замедления. Вследствие этого усложняется процесс исследования и проверки подобных приложений.
Заключение
Для обнаружения ошибок и уязвимостей в программном обеспечении необходим анализ процесса выполнения программы. Из-за существенного замедления при эмуляции многопроцессорных систем пользовательские приложения могут обнаружить то, что они запущены внутри симулятора, изменить свое поведение или вовсе прекратить выполнение. Решить эту проблему позволяет детерминированное воспроизведение программы. В процессе выполнения происходит запись журнала асинхронных событий (прерывания таймера, нажатие клавиш, движение мыши или приход сетевого пакета), после
чего можно анализировать программу во время ее воспроизведения, когда замедление не будет влиять на ее поведение.
Запись журнала многопроцессорной системы и его детерминированное воспроизведение не являются тривиальной задачей, так как из-за многопоточности может возникнуть состояние гонки, вследствие чего воспроизведение программы уже не будет являться детерминированным. Одним из вариантов решения этой проблемы может являться запись дополнительных событий, например обращений к жесткому диску, в журнал.
Работа поддержана грантом РФФИ (11-0700353).
1. Zhaoguo Wang, Ran Liu, Yufei Chen, et. al. COREMU: a scalable and portable parallel full-system emulator // Proc. of the 16th ACM symposium on Principles and practice of parallel programming. 2011. r.213-222.
2. Tzu-Han Hung, Alex Wauck. Towards a Fully Multithreading Support for QEMU. URL: http://www-users.cs.umn.edu/~hsu/pub/JackQEMU.pdf.
3. KVM: Kernel-based Virtual Machine. http://www.Hnux-kvm.org.
4. QEMU: Open Source Processor Emulator, http://www.qemu.org.