Open Way | Systems | Distro | Shell | Desktop | Applications
Network | Development | Download | OfftopicКарта сайта
FreeNotesФорум POSIX.ru
На главную страницу

QEMU-tech.html или технология QEMU

Перевод: Александр Поляков aka polachok
2005.01.16
Оригинал

Часть 1. Введение

1.1 Возможности

QEMU - это быстрый эмулятор процессора, использующий портируемый динамический транслятор.

QEMU имеет 2 режима работы:

Так как QEMU не требует модуля в ядре для работы, его безопасно и легко использовать.

Общие возможности QEMU:

Виртуальный CPU это библиотека (libqemu) которая может использоваться в других проектах(пример использования в qemu/tests/qruncom.c).

Возможности пользовательского режима QEMU:

Возможности полной эмуляции:

1.2. Эмуляция x86

Возможности эмуляции цели x86:

Текущие ограничения QEMU:

1.3 Эмуляция ARM

1.4. Эмуляция PowerPC

1.5. Эмуляция SPARC

Часть 2. QEMU изнутри

2.1. QEMU в сравнении с другими эмуляторами

Как bochs [3], QEMU эмулирует процессор х86. Но QEMU намного быстрее чем bochs так как он использует динамическую компиляцию. Bochs полностью привязан к эмуляции х86 РС, а QEMU может эмулировать несколько процессоров.

Как Valgrind [2], QEMU выполняет эмуляцию на уровне пользователя и использует динамическую трансляцию. Valgrind в большей cтепени отладчик памяти, а QEMU не поддерживает это. (QEMU может быть использован для выявления "скачкового" (bound) доступа к памяти как Valgrind, но не может отслеживать неинициализированные данные как Valgrind). Динамический транслятор Valgrind генерирует лучший код, чем QEMU(например он производит register allocation) но он привязан к х86 и не поддерживает исключений и системной эмуляции.

ЕМ86 [4] наиболее близкий к пользовательскому QEMU проект (и QEMU еще использует его код, например загрузчик файлов ELF). ЕМ86 ограничен на alpha-хостах и использует проприетарный и медленный интерпретатор(из FX!32 Digital Win32 code translator [5]).

TWIN [6] - это эмулятор Windows API типа Wine. Он менее аккуратен чем wine, но включает x86-интерпретатор защищенного режима чтобы запускать исполняемые файлы Windows. Такой подход обладает большим потенциалом т.к. большая часть Windows API выполняется nativly (вероятно имеется ввиду прямо на железе - прим. переводчика), но разработка его намного сложнее т.к. все структуры данных и параметры функций обмена между API и x86-кодом должны быть конвертированы.

User Mode Linux [7] был единственным решением до QEMU, чтобы запустить ядро LInux как процесс без патчей ядра хоста. Но User Mode Linux требует много патчей для гостевого ядра, а QEMU использует обычный ядра Linux. Цена этого - QEMU работает медленнее.

Новый Plex86 [8] PC Virtualizer сделан в том же духе что и qemu-fast системный эмулятор. Он требует пропатченого ядра Linux для работы(невозможно запустить то же ядро на обычном PC), но патчей на самом деле немного. Так как это виртуализатор PC (никакой эмуляции кроме некоторых привелегированных интсрукций нет), он может быть потенциально быстрее QEMU. Другая сторона медали - требуется увесистый ( и потенциально небезопасный) патч ядра.

Коммерческие виртуализаторы PC (VMWare [9], VirtualPC [10], TwoOStwo [11]) бычтрее чем QEMU, но требуют специфичных, проприетарных и потенциально небезопасных драйверов для хоста. Более того, они не способны обеспечить точную эмуляцию как может симулятор.

2.2. Переносимая динамическая трансляция

QEMU - динамический транслятор. Когда он встречает кусок кода, он переводит его в набор инструкций хоста. Обычно динамические трансляторы очень увесисты и процессорно-зависимы. QEMU использует несколько ухищрений которые делают его относительно легко портируемым и простым, тем не менее достигая хорошей производительности.

Основополагающая идея состоит в разделении каждой x86 инструкции на несколько простых инструкций. Каждая простая инструкция осуществляется отрезком кода на Си.(см. target-i386/op.c). Затем утилита компиляции(dyngen) берет соответствующий объектный файл (op.o) чтобы породить генератор динамического кода, который объединяет простые инструкции в функции(см. op.h:dyngen_code()). В сущности, процесс подобен [1], но больше работы производится во время компиляции.

Ключевая идея получения оптимальной производительности в том что постоянные параметры могут быть переданы простым операциям. Для этой цели, простые ELF relocations генерируются с помощью GCC для каждого постоянного параметра. Затем, утилита(dyngen) может определить relocations и вызвать соответствующий код на Си, чтобы их решить во время сборки динамического кода.

Поэтому, QEMU не сложнее портировать чем динамический линкер(компоновщик).

2.3. Размещение(allocation) регистров

Так как QEMU использует фиксированные простые инструкции, эффективного размещения регистров быть не может. Ведь т.к. процессоры RISC имеют много регистров, большая часть состояния виртуального пройессора может быть размешена в регистрах без тяжелой процедуры размещения регистров.

2.4. Оптимизации кода условий

Хорошая эмуляция кода условий(регистр EFLAGS на х86) - критическая точка в для получения хорошей производительности. QEMU использует "ленивое" вычисление кода условий: вместо вычисления кода условия после каждой инструкции, он просто хранит один операнд(CC_SRC),результат(CC_DST) и тип операции (CC_OP).

CC_OP почти никогда не задается в генерируемом коде т.к. он известен во время трансляции.

Для повышения производительность генерируемыми простыми инструкциями выполняется ход назад(см. target-i386/translate.c:optimize_flags()). Когда может быть доказано что код условий не требуется для следующей инструкции, он не вычисляется.

2.5. Оптимизации состояния процессора

х86 процессор имеет много внутренних положений, которые изменяют метод оценки им инструкций. Чтобы достигнуть хорошей скорости, на фазе трансляции считается что некоторые значения состояния виртуального процессора не могут изменяться. Например, если сегменты SS,DS,и ES имеют нулевую базу, то транслятор даже не будет генерировать добавление для сегментной базы.

2.6. Кэш трансляций

16-ти мегабайтный кэш содержит недавно использованные трансляции. Для простоты, он полностью очищается после наполнения. Блок трансляции содержит просто один базовый блок (блок x86-инструкций завершенный переходом или состоянием виртуального процесоора, которое транслятор не может считать постоянным).

2.7. Прямая цепь блоков

После выполнения каждого базового блока QEMU использует симулируемый Program Counter (PC) и другие значения состояния процессора(как например базовое значениесегмента CS) чтобы найти следующий блок.

Чтобы ускорить наиболее общие случаи в которых может побывать симулируемый PC,QEMU может пропатчить базовый блок чтобы он сразу переходил к следующему.

Наиболее портируемый код использукт непрямые переходы. Непрямой переход делаетпроще изменение цели перехода. На некоторых архитектурах(таких как х86 и PowerPC) JUMP патчится таким образом что цепь блоков не имеет преимуществ.

2.8. Самоизменяющийся код и аннулирование транслируемого кода

Самоизменяющийся код особое требование в эмуляции x86, т.к. аннулирование кэша инструкций не сигнализируется приложением когда код изменяется.

Когда транслируемый код генерируется для базового блока, соответствующая страница памяти хоста защищается от записи,если она уже не только для чтения(с помощью системного вызова mprotect()). Если же производится запись, Linux выдает сигнал SEGV. QEMU затем аннулирует весь транслированный код на странице и позволяет доступ на запись.

Корректное аннулирование кода производится эффективно при помощи поддержки списка соединений каждого транслируемого блока, содержащегося на данной странице. Обновляются и другие списки чтобы разовать прямую цепь блоков.

Хотя преимущества вызовов mprotect() важны, большинство программ MSDOS могут быть эмулируемы на приемлимой скорости при помощи QEMU и DOSEMU.

Заметьте, QEMU также аннулирует страницы транслируемого кода, когда он выявляет, что память изменена с помощью mmap() или munmap().

Когда используется программный MMU, аннулирование кода наиболее эффективно6 если данная страница кода аннулируется слишком часто из-за записи на нее, тогдастроится битовая карта показывающая весь код страницы. Каждое сохранение на эту страницу проверяет требуется ли аннулирование кода. Аннулирование кода избегается только если данные изменились.

2.9. Поддержка исключений

Когда встречаются исключения(например деление на 0) используется longjump().

Хостовые обработчики SIGSEGV и SIGBUS используется для обработки недопустимого доступа к памяти. Точное состояние процессора может быть получено т.к. все регистры x86 хранится в фиксированных регистрах хоста. Симулируемый program counter основан на ретрансляции соответствующего базого блока и поиске точки исключения хостового program counter'a.

Виртуальный процессор не может обнаружить точный регистр EFLAGS, т.к. в некоторых случаях он не подсчитывается для оптимизации кода. Это не особо важно, т.к. эмулируемый код может быть перезапущен в любом случае.

2.10. Эмуляция MMU

Для системной эмуляции QEMU использует системный вызов mmap() для эмуляции MMU нужного процессора. Это работает пока эмулируемая ОС не использует области памяти зарезервированные для хостовой ОС (например 0xc0000000 на Linux x86).

Чтобы можно было запускать любую ОС QEMU также поддерживает программный MMU. В этом режиме MMU транслирует виртуальный адрес в физический при каждом доступе к памяти. QEMU использует кэш адресов трансляции чтобы ускорить их.

Чтобы избежать уничтожение транслируемого кода каждый раз, когда MMU mappings изменяются, QEMU использует физически индексированный кэш трансляций. Это означает что каждый базовый блок индексируется своим физическим адресом.

Когда изменяются MMU mappings, происходит только cброс базовых блоков(т.к. базовый блок не может более переходить сразу к следующему).

2.11. Прерывания

Чтобы быть быстрее QEMU не проверяет каждый базовый блок если прерывание необработано. Вместо этого, пользователь асинхронно вызывает специфичную функцию чтобы сообщить что прерывание не обработано. Эта функция разрывает текущую цепь исполняющихся базовых блоков. Она гарантирует что выполнение скоро продолжится в главном цикле процессора. Затем главный цикл обрабатывает прерывание.

2.12. Эмуляция уровня пользователя

2.12.1. Трансляция системных вызовов Linux

QEMU включает в себя транслятор системных вызовов для Linux. Это значит чтопараметры системных вызовов конвертируются с исправлением endianness и 32/64 битissues(?). IOCTL конвертируются с помощью системы общих описаний.(см. ioctls.h и thunk.c).

QEMU поддерживает хостовый процессоры имеющий страницы более 4Кб. Он записывает все mappings, которые делает процесс и пытается эмулировать системный вызов mmap() если хостовый mmap() валится из-за "плохой" страницы.

2.12.2. Сигналы Linux

Нормальные сигналы и сигналы реального времени ставятся в очередь со своей информацией (siginfo_t) как это делается в ядре Linux. Затем делается запрос прерывания к виртуальному процессору. Когда он прерывается, один сигнал обрабатывается созданием frame stack в виртуальном процессоре как это делается в ядре Linux. Системный вызов sigreturn() эмулирует возвращение из виртуального обработчика сигналов.

Некоторые сигналы(напр. SIGALRM) приходят прямо с хоста. Другие синтезируются из исключений виртуального процессора, как SIGFPE когда производится деление на 0.(см.main.c:cpu_loop()).

Маска блокированного сигнала обрабатывается хостовым linux-ядром, поэтому большинство сигналов могут быть перенаправлены прямо хостовому ядру. Только системные вызовы sigaction() и sigreturn() нуждаются в полной эмуляции.(см. signal.c).

2.12.3. Системный вызов clone() и потоки

Системный вызов Linux clone() обычно используется для создания потоков. QEMU использует системный вызов clone() хоста так что один реальный поток создается для каждого эмулирующего потока. Один виртуальный процессор создается для каждого потока.

Заметьте, в настоящее время есть несколько проблем с блокировкой в QEMU. Например, кэш трансляций не защищен от повторного входа.

2.12.4. Самовиртуализация

QEMU может эмулировать себя. Хотя это не особо полезно, это важный тест мощи эмулятора. Самовиртуализация не простая задача, т.к. могут быть конфликты адресов. QEMU решает эту проблему тем что он является исполняемым ELF-объектом как и ld-linux.so ELF(интрерпретатор ELF). Поэтому он может быть перемещен во время загрузки.(вероятно имеются ввиду адреса - прим. переводчика).

Bibliography

[1] http://citeseer.nj.nec.com/piumarta98optimizing.html, Optimizing direct threaded code by selective inlining (1998) by Ian Piumarta, Fabio Riccardi.

[2] http://developer.kde.org/~sewardj/, Valgrind, an open-source memory debugger for x86-GNU/Linux, by Julian Seward.

[3] http://bochs.sourceforge.net/, the Bochs IA-32 Emulator Project, by Kevin Lawton et al.

[4] http://www.cs.rose-hulman.edu/~donaldlf/em86/index.html, the EM86 x86 emulator on Alpha-Linux.

[5] http://www.usenix.org/publications/library/proceedings/usenix-nt97/full_papers/chernoff/chernoff.pdf, DIGITAL FX!32: Running 32-Bit x86 Applications on Alpha NT, by Anton Chernoff and Ray Hookway.

[6] http://www.willows.com/, Windows API library emulation from Willows Software.

[7] http://user-mode-linux.sourceforge.net/, The User-mode Linux Kernel.

[8] http://www.plex86.org/, The new Plex86 project.

[9] http://www.vmware.com/, The VMWare PC virtualizer.

[10] http://www.microsoft.com/windowsxp/virtualpc/, The VirtualPC PC virtualizer.

[11] http://www.twoostwo.org/, The TwoOStwo PC virtualizer.


Каталог ювелирных украшений: обручальные кольца. Ищете обручальное кольцо?. N 3 в Мире N 1 в Европе - ITV - системы видеонаблюдения. Системы охраны . Цены удивят.. Все для нового дела. Растущий бетонный завод предоставит вам свою продукцию.