Пару лет назад на Мат-Мехе проходил студпроект по превращению Embox в идеальную платформу для столь модного нынче облака. Одной из очевидных частей этой задачи, было портирование на какую-нибудь платформу виртуализации, и выбор пал на Xen. В данной статье я расскажу о процессе портирования студентами ОС под Xen, добавлении новой платформы в Embox, ну и, конечно, зачем вообще всё это затевалось.
Идея идеальной платформы для облака заключается в следующем:
Есть концепция unikernel. Если коротко, то разработчик единственного приложения выбирает нужный набор функциональности для работы. Только эта функциональность включается в операционную систему, которая линкуется в единый образ вместе с приложением. При таком подходе, очевидно, экономятся ресурсы. Конечно, это единственное приложение обладает не очень большой функциональностью, но если в облако поместить множество различных приложений (по сути дела, сервисов), можно получить очень широкую функциональность.
Любое портирование чего-либо на что-либо стоит начинать с проверки уже существующих решений. Под Xen уже работает довольно много операционных систем. Конечно, прежде всего нас интересует открытый Linux. У Xen есть одна особенность: прежде чем запускать что-то, нужно сначала научиться создавать экземпляры этой платформы и управлять ими. Другими словами, нужно научиться создавать отдельные виртуальные машины (домены).
Для работы с Xen нужно установить соответствующие пакеты:
После этого нужно перегрузиться и во время загрузки выбрать вариант вашей ОС на Xen (обычно стоит по умолчанию).
Для проверки того, что теперь мы находимся под управлением гипервизора, достаточно проверить, что вывод команды
Ещё один вариант проверки — использовать команду “xl”, которая нам потребуется в будущем
Команда
В Xen есть несколько способов создания и управления виртуальными машинами.
Первый — установка с помощью сторонних программ (например, virt-manager или oVirt). Обычно у них графический интерфейс, и программа сама <<копается>> в файлах системы, внося необходимые изменения.
Второй способ — работа в консоли.
В случае виртуализации Linux будем использовать xen-tools + xl (or xm). Это удобно тем, что многие дистрибутивы Linux уже подготовлены для паравиртуализации.
Параметры с которыми создаются новые виртуальные машины, находятся в файле
Есть несколько способов организации сети между доменами. Выбираем самую простую — Bridge Network (виртуальные машины будут видны в локальной сети как физические устройства наравне с dom0).
Установим пакеты:
Далее редактируем файл
Было
Стало
Пример создания виртуальной машины с Ubuntu:
Флаг
Убедиться в том, что образ создан, можно с помощью команды:
В моем случае команда выдала
Теперь виртуалка test появиться при вызове команды «sudo xl list»:
Осталось подключиться к её консоли:
Выход из консоли виртуальной машины осуществляется комбинацией
Посмотрим, что это за образ:
Из предыдущей части стало ясно, что для работы необходимо создать домен (инстанс). Создается он с помощью утилиты xl на основе файла конфигурации.
То увидим, что там необходимо наличие образов диска, присутствует сеть и так далее. Скорее всего, это слишком сложно для наших целей. Но Xen поддерживает и другие ОС. Базовой является так называемая Mini-OS . Причем она предназначена для тех, кто хочет портировать свои операционные системы на XEN, то есть это как раз наш вариант.
Код на git-е лежит отдельно от XEN.
Давайте сразу проделаем инструкции из README
Выход из консоли, как обычно, ctrl + ]
Проверим что у нас запустилась какая-то машина
Для возврата в консоль
Для удаление домена
Видно, что можно указывать не диски, а напрямую образ с помощью параметра kernel, а также не указывать настройки сети и другие параметры, которые нам не нужны на первом этапе.
Для начала для запуска домена с Embox сделаем конфигурационный файл по образу и подобию Mini-OS.
В Embox есть диплом Антона Козлова (antonkozlov) о переносе его на новую платформу “Портирование операционной системы с модульным HAL в пользовательский режим”. Точнее, диплом был не о переносе Embox, а об организации операционных систем вообще, и Embox использовался в качестве подопытного кролика. Конкретно про Xen есть курсовая Андрея Голикова “Портирование операционной системы Embox на платформу Xen”. Чтобы не пересказывать диплом и курсовую, ограничусь несколькими важными и специфическими для Xen частями.
Всю необходимую информацию, как вы наверное догадались, можно получить из Mini-OS.
Одно из первых, с чего начинается портирование, это карта памяти (нет, не SD-карточка, о которой вы, возможно, подумали, а о разных регионах памяти — memory map), традиционно описываемая в lds-скрипте. Линкеру нужно указать, где же должен лежать код программы, данные, точка входа в программу и так далее. В нашем случае к этому прибавляется ещё и то, что перед загрузкой образа должны считываться некоторые особенности из секций “.note.Xen”, точнее заголовок в ELF PT_NOTE формате. Карта памяти вместе с особенностями описана тут.
В линкер-скрипт Embox для Xen добавлено следующее
Ну и ещё добавился ассемблерный файл, содержащий наполнение для этого заголовка
Кроме этого, есть ещё пара структур, необходимых для взаимодействия между образом и Xen struct start_info и struct shared_info.
struct start_info нужна, если мы хотим получать информацию о машине при старте образа, то есть без неё, в принципе, можно обойтись.
Располагается эта структура со смещением 4кб
И наш линкер-скрипт выглядит так
Собственно, на этом хотелось бы закончить, поскольку разбор всего кода явно выходит за рамки одной статьи. Скажу лишь, что на сегодняшний момент студентами реализован запуск машины с отладочным выводом.
Для того чтобы в этом убедиться, достаточно сделать в Embox следующее;
Создастся домен, и пойдет вывод самого Embox. Выход из консоли, как всегда, ctrl + ] и потом не забудьте удалить машину:
Естественно, сначала нужно установить окружение. Но для простоты, уже мы, а не студенты, добавили возможность поставить всё через vagrand. Это описано на wiki. Вам потребуется только VirtualBox и vagrand. Ставить нужно не под Xen-ом.
В конце хотелось бы вновь затронуть тему обучения в IT. Как я уже писал в статье: “Как поймать программиста на списывании и стоит ли этим заниматься“, у нас в проекте мы пропагандируем погружение студентов в реальный проект. В этом случае студенты получают куда больше опыта, чем при выполнении учебных задач, пусть и довольно масштабных. И главное они не только обучаются какому-то конкретному языку, а учатся создавать продукты, решать задачи самостоятельно и попросту уметь мыслить. В качестве примера я сошлюсь на автора идеи про идеальную облачную платформу, Антона Козлова (antonkozlov), ведь именно на его диплом я ссылался ранее. По итогам обучения он смог не только писать код, но и предлагать собственные идеи, реализовывать их, разбивать задачу на составляющие и обучать студентов. Подобных примеров у нас много.
P.S.: Видео с той конференции доступно тут.
Идея идеальной платформы для облака заключается в следующем:
Есть концепция unikernel. Если коротко, то разработчик единственного приложения выбирает нужный набор функциональности для работы. Только эта функциональность включается в операционную систему, которая линкуется в единый образ вместе с приложением. При таком подходе, очевидно, экономятся ресурсы. Конечно, это единственное приложение обладает не очень большой функциональностью, но если в облако поместить множество различных приложений (по сути дела, сервисов), можно получить очень широкую функциональность.
Портирование на Xen
Проверка работоспособности Xen
Любое портирование чего-либо на что-либо стоит начинать с проверки уже существующих решений. Под Xen уже работает довольно много операционных систем. Конечно, прежде всего нас интересует открытый Linux. У Xen есть одна особенность: прежде чем запускать что-то, нужно сначала научиться создавать экземпляры этой платформы и управлять ими. Другими словами, нужно научиться создавать отдельные виртуальные машины (домены).
Для работы с Xen нужно установить соответствующие пакеты:
sudo apt-get install xen-hypervisor-4.8-amd64 xen-tools
После этого нужно перегрузиться и во время загрузки выбрать вариант вашей ОС на Xen (обычно стоит по умолчанию).
Для проверки того, что теперь мы находимся под управлением гипервизора, достаточно проверить, что вывод команды
cat /proc/cpuinfo | grep hypervisor
не пустой. Аналогичным образом можно проверить вывод команды virt-what
, он должен содержать домен xen-dom0Ещё один вариант проверки — использовать команду “xl”, которая нам потребуется в будущем
Команда
sudo xl list
должна выводить что-то вроде этого:Name ID Mem VCPUs State Time(s)
Domain-0 0 945 1 r----- 11.3
Управление доменами
В Xen есть несколько способов создания и управления виртуальными машинами.
Первый — установка с помощью сторонних программ (например, virt-manager или oVirt). Обычно у них графический интерфейс, и программа сама <<копается>> в файлах системы, внося необходимые изменения.
Второй способ — работа в консоли.
В случае виртуализации Linux будем использовать xen-tools + xl (or xm). Это удобно тем, что многие дистрибутивы Linux уже подготовлены для паравиртуализации.
Параметры с которыми создаются новые виртуальные машины, находятся в файле
/etc/xen-tools/xen-tools.conf
, их можно переопределить. Все параметры также можно переопределить при создании машины.Настройка сетевого окружения
Есть несколько способов организации сети между доменами. Выбираем самую простую — Bridge Network (виртуальные машины будут видны в локальной сети как физические устройства наравне с dom0).
Установим пакеты:
sudo apt-get install bridge-utils
Далее редактируем файл
/etc/network/interfaces
Было
# The loopback network interface
auto lo
iface lo inet loopback
# The primary network interface
allow-hotplug enp5s0
iface enp5s0 inet dhcp
Стало
# The loopback network interface
auto lo
iface lo inet loopback
# The primary network interface
allow-hotplug enp5s0
iface enp5s0 inet manual
auto xenbr0
iface xenbr0 inet dhcp
bridge_ports enp5s0
Создание образа виртуальной машины.
Пример создания виртуальной машины с Ubuntu:
sudo xen-create-image --hostname test --dhcp --pygrub --dist precise --mirror="http://mirror.yandex.ru/ubuntu" --dir /srv/xen/test
Флаг
--pygrub
указывает, что в виртуальной машине будет использоваться загрузчик PyGrub, позволяющий каждой виртуальной машине использовать своё собственное ядро вместо ядра из dom0. Параметр --dir
указывает, что вы хотите использовать директорию для образов, а не отдельный раздел заранее разбитого диска.Вывод приблизительно следующий:
General Information
— Hostname: test
Distribution: precise
Mirror: mirror.yandex.ru/ubuntu
Partitions: swap 512M (swap)
/ 4G (ext4)
Image type: sparse
Memory size: 256M
Bootloader: pygrub
Networking Information
— IP Address: DHCP [MAC: 00:16:3E:8E:3C:E0]
Creating partition image: /srv/xen/test/domains/test/swap.img
Done
Creating swap on /srv/xen/test/domains/test/swap.img
Done
Creating partition image: /srv/xen/test/domains/test/disk.img
Done
Creating ext4 filesystem on /srv/xen/test/domains/test/disk.img
Done
Installation method: debootstrap
Done
Running hooks
Done
No role scripts were specified. Skipping
Creating Xen configuration file
Done
No role scripts were specified. Skipping
Setting up root password
Generating a password for the new guest.
All done
Logfile produced at:
/var/log/xen-tools/test.log
Installation Summary
— Hostname: test
Distribution: precise
MAC Address: 00:16:3E:8E:3C:E0
IP Address(es): dynamic
SSH Fingerprint: SHA256:H49PEnPv0k0tw2faq1CStkR6KFlHF0GkUOWvYaeiqOU (DSA)
SSH Fingerprint: SHA256:5gIsrTAriqEiwdkVCygOtLOi9uOd2DJWFBlJKxdJfUw (ECDSA)
SSH Fingerprint: SHA256:SB+bTbkIUr2Qn019xT8AFtAKO5f6xlkbt8juVBq6zTE (RSA)
Root Password: RJpaLfBFseH9YJX77ScxRwP
— Hostname: test
Distribution: precise
Mirror: mirror.yandex.ru/ubuntu
Partitions: swap 512M (swap)
/ 4G (ext4)
Image type: sparse
Memory size: 256M
Bootloader: pygrub
Networking Information
— IP Address: DHCP [MAC: 00:16:3E:8E:3C:E0]
Creating partition image: /srv/xen/test/domains/test/swap.img
Done
Creating swap on /srv/xen/test/domains/test/swap.img
Done
Creating partition image: /srv/xen/test/domains/test/disk.img
Done
Creating ext4 filesystem on /srv/xen/test/domains/test/disk.img
Done
Installation method: debootstrap
Done
Running hooks
Done
No role scripts were specified. Skipping
Creating Xen configuration file
Done
No role scripts were specified. Skipping
Setting up root password
Generating a password for the new guest.
All done
Logfile produced at:
/var/log/xen-tools/test.log
Installation Summary
— Hostname: test
Distribution: precise
MAC Address: 00:16:3E:8E:3C:E0
IP Address(es): dynamic
SSH Fingerprint: SHA256:H49PEnPv0k0tw2faq1CStkR6KFlHF0GkUOWvYaeiqOU (DSA)
SSH Fingerprint: SHA256:5gIsrTAriqEiwdkVCygOtLOi9uOd2DJWFBlJKxdJfUw (ECDSA)
SSH Fingerprint: SHA256:SB+bTbkIUr2Qn019xT8AFtAKO5f6xlkbt8juVBq6zTE (RSA)
Root Password: RJpaLfBFseH9YJX77ScxRwP
Убедиться в том, что образ создан, можно с помощью команды:
sudo xen-list-images
В моем случае команда выдала
Name: test
Memory: 256 MB
Config: /etc/xen/test.cfg
Запуск (создание машины)
sudo xl create /etc/xen/test.cfg
Теперь виртуалка test появиться при вызове команды «sudo xl list»:
Name ID Mem VCPUs State Time(s)
Domain-0 0 15356 8 r----- 4283.8
test 2 256 1 -b---- 1.5
Осталось подключиться к её консоли:
sudo xl console test
Выход из консоли виртуальной машины осуществляется комбинацией
"ctrl + ]"
Полученный вывод
[ 0.000000] Initializing cgroup subsys cpuset
[ 0.000000] Initializing cgroup subsys cpu
[ 0.000000] Linux version 3.2.0-126-virtual (buildd@lcy01-11) (gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5) ) #169-Ubuntu SMP Fri Mar 31 14:47:56 UTC 2017 (Ubuntu 3.2.0-126.169-virtual 3.2.79)
[ 0.000000] Command line: root=/dev/xvda2 ro elevator=noop root=/dev/xvda2 ro
[ 0.000000] KERNEL supported cpus:
[ 0.000000] Intel GenuineIntel
[ 0.000000] AMD AuthenticAMD
[ 0.000000] Centaur CentaurHauls
[ 0.000000] ACPI in unprivileged domain disabled
[ 0.000000] Released 0 pages of unused memory
[ 0.000000] Set 0 page(s) to 1-1 mapping
[ 0.000000] BIOS-provided physical RAM map:
[ 0.000000] Xen: 0000000000000000 - 00000000000a0000 (usable)
[ 0.000000] Xen: 00000000000a0000 - 0000000000100000 (reserved)
[ 0.000000] Xen: 0000000000100000 - 0000000010000000 (usable)
[ 0.000000] NX (Execute Disable) protection: active
[ 0.000000] DMI not present or invalid.
[ 0.000000] No AGP bridge found
[ 0.000000] last_pfn = 0x10000 max_arch_pfn = 0x400000000
[ 0.000000] init_memory_mapping: 0000000000000000-0000000010000000
[ 0.000000] RAMDISK: 0205c000 - 02c43000
[ 0.000000] NUMA turned off
[ 0.000000] Faking a node at 0000000000000000-0000000010000000
[ 0.000000] Initmem setup node 0 0000000000000000-0000000010000000
[ 0.000000] NODE_DATA [000000000fffb000 - 000000000fffffff]
[ 0.000000] Zone PFN ranges:
[ 0.000000] DMA 0x00000010 -> 0x00001000
[ 0.000000] DMA32 0x00001000 -> 0x00100000
[ 0.000000] Normal empty
[ 0.000000] Movable zone start PFN for each node
[ 0.000000] early_node_map[2] active PFN ranges
[ 0.000000] 0: 0x00000010 -> 0x000000a0
[ 0.000000] 0: 0x00000100 -> 0x00010000
[ 0.000000] SFI: Simple Firmware Interface v0.81 http://simplefirmware.org
[ 0.000000] SMP: Allowing 1 CPUs, 0 hotplug CPUs
[ 0.000000] No local APIC present
[ 0.000000] APIC: disable apic facility
[ 0.000000] APIC: switched to apic NOOP
[ 0.000000] PM: Registered nosave memory: 00000000000a0000 - 0000000000100000
[ 0.000000] Allocating PCI resources starting at 10000000 (gap: 10000000:f0000000)
[ 0.000000] Booting paravirtualized kernel on Xen
[ 0.000000] Xen version: 4.8.3-pre (preserve-AD)
[ 0.000000] setup_percpu: NR_CPUS:64 nr_cpumask_bits:64 nr_cpu_ids:1 nr_node_ids:1
[ 0.000000] PERCPU: Embedded 27 pages/cpu @ffff88000fc00000 s78848 r8192 d23552 u2097152
[ 0.000000] Built 1 zonelists in Node order, mobility grouping on. Total pages: 64395
[ 0.000000] Policy zone: DMA32
[ 0.000000] Kernel command line: root=/dev/xvda2 ro elevator=noop root=/dev/xvda2 ro
[ 0.000000] PID hash table entries: 1024 (order: 1, 8192 bytes)
[ 0.000000] xsave/xrstor: enabled xstate_bv 0x7, cntxt size 0x340
[ 0.000000] Checking aperture...
[ 0.000000] No AGP bridge found
[ 0.000000] Memory: 228408k/262144k available (6617k kernel code, 448k absent, 33288k reserved, 6579k data, 932k init)
[ 0.000000] SLUB: Genslabs=15, HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[ 0.000000] Hierarchical RCU implementation.
[ 0.000000] RCU dyntick-idle grace-period acceleration is enabled.
[ 0.000000] NR_IRQS:4352 nr_irqs:256 16
[ 0.000000] Console: colour dummy device 80x25
[ 0.000000] console [tty0] enabled
[ 0.000000] console [hvc0] enabled
[ 0.000000] allocated 2097152 bytes of page_cgroup
[ 0.000000] please try 'cgroup_disable=memory' option if you don't want memory cgroups
[ 0.000000] installing Xen timer for CPU 0
[ 0.000000] Detected 3194.398 MHz processor.
[ 0.004000] Calibrating delay loop (skipped), value calculated using timer frequency.. 6388.79 BogoMIPS (lpj=12777592)
[ 0.004000] pid_max: default: 32768 minimum: 301
[ 0.004000] Security Framework initialized
[ 0.004000] AppArmor: AppArmor initialized
[ 0.004000] Yama: becoming mindful.
[ 0.004000] Dentry cache hash table entries: 32768 (order: 6, 262144 bytes)
[ 0.004000] Inode-cache hash table entries: 16384 (order: 5, 131072 bytes)
[ 0.004000] Mount-cache hash table entries: 256
[ 0.004000] Initializing cgroup subsys cpuacct
[ 0.004000] Initializing cgroup subsys memory
[ 0.004000] Initializing cgroup subsys devices
[ 0.004000] Initializing cgroup subsys freezer
[ 0.004000] Initializing cgroup subsys blkio
[ 0.004000] Initializing cgroup subsys perf_event
[ 0.004000] CPU: Physical Processor ID: 0
[ 0.004000] CPU: Processor Core ID: 0
[ 0.006575] SMP alternatives: switching to UP code
[ 0.034623] Freeing SMP alternatives: 24k freed
[ 0.034647] ftrace: allocating 26699 entries in 105 pages
[ 0.036061] cpu 0 spinlock event irq 17
[ 0.036079] Performance Events:
[ 0.036082] no APIC, boot with the "lapic" boot parameter to force-enable it.
[ 0.036086] no hardware sampling interrupt available.
[ 0.036094] Broken PMU hardware detected, using software events only.
[ 0.036207] NMI watchdog disabled (cpu0): hardware events not enabled
[ 0.036229] Brought up 1 CPUs
[ 0.036343] devtmpfs: initialized
[ 0.036844] EVM: security.selinux
[ 0.036848] EVM: security.SMACK64
[ 0.036851] EVM: security.capability
[ 0.037364] Grant table initialized
[ 0.037410] print_constraints: dummy:
[ 0.057265] RTC time: 165:165:165, date: 165/165/65
[ 0.057318] NET: Registered protocol family 16
[ 0.057508] Extended Config Space enabled on 0 nodes
[ 0.057542] PCI: setting up Xen PCI frontend stub
[ 0.057542] bio: create slab <bio-0> at 0
[ 0.057542] ACPI: Interpreter disabled.
[ 0.057542] xen/balloon: Initialising balloon driver.
[ 0.057542] xen-balloon: Initialising balloon driver.
[ 0.057542] vgaarb: loaded
[ 0.057542] i2c-core: driver [aat2870] using legacy suspend method
[ 0.057542] i2c-core: driver [aat2870] using legacy resume method
[ 0.057542] SCSI subsystem initialized
[ 0.057542] usbcore: registered new interface driver usbfs
[ 0.057542] usbcore: registered new interface driver hub
[ 0.057542] usbcore: registered new device driver usb
[ 0.057542] PCI: System does not support PCI
[ 0.057542] PCI: System does not support PCI
[ 0.057542] NetLabel: Initializing
[ 0.057542] NetLabel: domain hash size = 128
[ 0.057542] NetLabel: protocols = UNLABELED CIPSOv4
[ 0.057542] NetLabel: unlabeled traffic allowed by default
[ 0.057542] Switching to clocksource xen
[ 0.061230] AppArmor: AppArmor Filesystem Enabled
[ 0.061253] pnp: PnP ACPI: disabled
[ 0.062521] NET: Registered protocol family 2
[ 0.065648] IP route cache hash table entries: 2048 (order: 2, 16384 bytes)
[ 0.065807] TCP established hash table entries: 8192 (order: 5, 131072 bytes)
[ 0.065861] TCP bind hash table entries: 8192 (order: 5, 131072 bytes)
[ 0.065881] TCP: Hash tables configured (established 8192 bind 8192)
[ 0.065885] TCP reno registered
[ 0.065889] UDP hash table entries: 128 (order: 0, 4096 bytes)
[ 0.065895] UDP-Lite hash table entries: 128 (order: 0, 4096 bytes)
[ 0.065934] NET: Registered protocol family 1
[ 0.065967] platform rtc_cmos: registered platform RTC device (no PNP device found)
[ 0.066094] Trying to unpack rootfs image as initramfs...
[ 0.072210] audit: initializing netlink socket (disabled)
[ 0.190742] type=2000 audit(1518793913.856:1): initialized
[ 0.209702] Freeing initrd memory: 12188k freed
[ 0.213726] VFS: Disk quotas dquot_6.5.2
[ 0.213770] Dquot-cache hash table entries: 512 (order 0, 4096 bytes)
[ 0.213950] hugetlbfs: disabling because there are no supported hugepage sizes
[ 0.214050] fuse init (API version 7.17)
[ 0.214104] msgmni has been set to 469
[ 0.214342] Block layer SCSI generic (bsg) driver version 0.4 loaded (major 253)
[ 0.214362] io scheduler noop registered (default)
[ 0.214367] io scheduler deadline registered
[ 0.214386] io scheduler cfq registered
[ 0.214439] pci_hotplug: PCI Hot Plug PCI Core version: 0.5
[ 0.214454] pciehp: PCI Express Hot Plug Controller Driver version: 0.4
[ 0.214460] acpiphp: ACPI Hot Plug PCI Controller Driver version: 0.5
[ 0.214672] Serial: 8250/16550 driver, 32 ports, IRQ sharing enabled
[ 0.215413] Linux agpgart interface v0.103
[ 0.216235] brd: module loaded
[ 0.216618] loop: module loaded
[ 0.217745] blkfront device/vbd/51714 num-ring-pages 1 nr_ents 32.
[ 0.218925] blkfront device/vbd/51713 num-ring-pages 1 nr_ents 32.
[ 0.219299] Fixed MDIO Bus: probed
[ 0.219322] tun: Universal TUN/TAP device driver, 1.6
[ 0.219329] tun: (C) 1999-2004 Max Krasnyansky <maxk@qualcomm.com>
[ 0.219390] PPP generic driver version 2.4.2
[ 0.219430] Initialising Xen virtual ethernet driver.
[ 0.222394] ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
[ 0.222413] ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver
[ 0.222423] uhci_hcd: USB Universal Host Controller Interface driver
[ 0.222459] usbcore: registered new interface driver libusual
[ 0.222477] i8042: PNP: No PS/2 controller found. Probing ports directly.
[ 1.223457] i8042: No controller found
[ 1.223625] mousedev: PS/2 mouse device common for all mice
[ 1.263475] rtc_cmos rtc_cmos: rtc core: registered rtc_cmos as rtc0
[ 1.263520] rtc_cmos: probe of rtc_cmos failed with error -38
[ 1.263601] device-mapper: uevent: version 1.0.3
[ 1.263654] device-mapper: ioctl: 4.22.0-ioctl (2011-10-19) initialised: dm-devel@redhat.com
[ 1.263664] EFI Variables Facility v0.08 2004-May-17
[ 1.263886] TCP cubic registered
[ 1.263952] NET: Registered protocol family 10
[ 1.264555] NET: Registered protocol family 17
[ 1.264564] Registering the dns_resolver key type
[ 1.264668] registered taskstats version 1
[ 1.273569] blkfront: xvda2: flush diskcache: enabled
[ 1.274605] blkfront: xvda1: flush diskcache: enabled
[ 1.368068] Magic number: 1:252:3141
[ 1.368107] /build/linux-JvAKgs/linux-3.2.0/drivers/rtc/hctosys.c: unable to open rtc device (rtc0)
[ 1.368118] BIOS EDD facility v0.16 2004-Jun-25, 0 devices found
[ 1.368121] EDD information not available.
[ 1.368534] Freeing unused kernel memory: 932k freed
[ 1.368687] Write protecting the kernel read-only data: 12288k
[ 1.373188] Freeing unused kernel memory: 1556k freed
[ 1.373812] Freeing unused kernel memory: 1172k freed
Loading, please wait...
Begin: Loading essential drivers ... done.
[ 1.402645] udevd[82]: starting version 175
Begin: Running /scripts/init-premount ... done.
Begin: Mounting root file system ... Begin: Running /scripts/local-top ... done.
Begin: Running /scripts/local-premount ... done.
[ 1.506696] JBD2: Unrecognised features on journal
[ 1.506706] EXT4-fs (xvda2): error loading journal
mount: mounting /dev/xvda2 on /root failed: Invalid argument
Begin: Running /scripts/local-bottom ... done.
done.
Begin: Running /scripts/init-bottom ... mount: mounting /dev on /root/dev failed: No such file or directory
done.
mount: mounting /sys on /root/sys failed: No such file or directory
mount: mounting /proc on /root/proc failed: No such file or directory
Target filesystem doesn't have requested /sbin/init.
No init found. Try passing init= bootarg.
BusyBox v1.18.5 (Ubuntu 1:1.18.5-1ubuntu4) built-in shell (ash)
Enter 'help' for a list of built-in commands.
(initramfs)
Посмотрим, что это за образ:
(initramfs) uname -a
Linux (none) 3.2.0-126-virtual #169-Ubuntu SMP Fri Mar 31 14:47:56 UTC 2017 x86_64 GNU/Linux
(initramfs)
Запуск маленькой ОС (Mini-OS)
Из предыдущей части стало ясно, что для работы необходимо создать домен (инстанс). Создается он с помощью утилиты xl на основе файла конфигурации.
Но если мы посмотрим на файл конфигурации
#
# Configuration file for the Xen instance test, created
# by xen-tools 4.7 on Fri Feb 16 18:09:35 2018.
#
#
# Kernel + memory size
#
bootloader = '/usr/lib/xen-4.8/bin/pygrub'
vcpus = '1'
memory = '256'
#
# Disk device(s).
#
root = '/dev/xvda2 ro'
disk = [
'file:/srv/xen/test/domains/test/disk.img,xvda2,w',
'file:/srv/xen/test/domains/test/swap.img,xvda1,w',
]
#
# Physical volumes
#
#
# Hostname
#
name = 'test'
#
# Networking
#
dhcp = 'dhcp'
vif = [ 'mac=00:16:3E:8E:3C:E0' ]
#
# Behaviour
#
on_poweroff = 'destroy'
on_reboot = 'restart'
on_crash = 'restart'
То увидим, что там необходимо наличие образов диска, присутствует сеть и так далее. Скорее всего, это слишком сложно для наших целей. Но Xen поддерживает и другие ОС. Базовой является так называемая Mini-OS . Причем она предназначена для тех, кто хочет портировать свои операционные системы на XEN, то есть это как раз наш вариант.
Код на git-е лежит отдельно от XEN.
Давайте сразу проделаем инструкции из README
make
sudo xl create -c domain_config
Получим вывод
Parsing config from domain_config
Xen Minimal OS (pv)!
start_info: 0x7d000(VA)
nr_pages: 0x2000
shared_inf: 0xdee73000(MA)
pt_base: 0x80000(VA)
nr_pt_frames: 0x5
mfn_list: 0x6d000(VA)
mod_start: 0x0(VA)
mod_len: 0
flags: 0x0
cmd_line:
stack: 0x2c6a0-0x4c6a0
MM: Init
_text: 0(VA)
_etext: 0x18484(VA)
_erodata: 0x1f000(VA)
_edata: 0x1f252(VA)
stack start: 0x2c6a0(VA)
_end: 0x6cfd8(VA)
start_pfn: 85
max_pfn: 2000
Mapping memory range 0x85000 - 0x2000000
setting 0-0x1f000 readonly
skipped 1000
MM: Initialise page allocator for 93000(93000)-2000000(2000000)
Adding memory range 94000-2000000
MM: done
Demand map pfns at 100000000000-108000000000.
Initialising timer interface
Initialising console ... done.
gnttab_table mapped at 0x100000000000.
Initialising scheduler
Thread "Idle": pointer: 0x0x96078, stack: 0x0xa0000
Thread "xenstore": pointer: 0x0x960d8, stack: 0x0xb0000
xenbus initialised on irq 1
Thread "shutdown": pointer: 0x0x96138, stack: 0x0xc0000
kernel.c: dummy main: par=0
Выход из консоли, как обычно, ctrl + ]
Проверим что у нас запустилась какая-то машина
sudo xl list
Name ID Mem VCPUs State Time(s)
Domain-0 0 15455 8 r----- 5117.7
Mini-OS 2 32 1 -b---- 0.0
Для возврата в консоль
sudo xl console Mini-OS
Для удаление домена
sudo xl destroy Mini-OS
Ну и посмотрим на содержимое domain_config в корне проекта
# -*- mode: python; -*-
#============================================================================
# Python configuration setup for 'xm create'.
# This script sets the parameters used when a domain is created using 'xm create'.
# You use a separate script for each domain you want to create, or
# you can set the parameters for the domain on the xm command line.
#============================================================================
#----------------------------------------------------------------------------
# Kernel image file.
kernel = "mini-os.gz"
# Initial memory allocation (in megabytes) for the new domain.
memory = 32
# A name for your domain. All domains must have different names.
name = "Mini-OS"
on_crash = 'destroy'
Видно, что можно указывать не диски, а напрямую образ с помощью параметра kernel, а также не указывать настройки сети и другие параметры, которые нам не нужны на первом этапе.
Портирование Embox на Xen
Создание конфигурационного файла
Для начала для запуска домена с Embox сделаем конфигурационный файл по образу и подобию Mini-OS.
name = "embox"
memory = 256
kernel = "/tmp/xen_embox"
Добавление архитектуры в Embox
В Embox есть диплом Антона Козлова (antonkozlov) о переносе его на новую платформу “Портирование операционной системы с модульным HAL в пользовательский режим”. Точнее, диплом был не о переносе Embox, а об организации операционных систем вообще, и Embox использовался в качестве подопытного кролика. Конкретно про Xen есть курсовая Андрея Голикова “Портирование операционной системы Embox на платформу Xen”. Чтобы не пересказывать диплом и курсовую, ограничусь несколькими важными и специфическими для Xen частями.
Всю необходимую информацию, как вы наверное догадались, можно получить из Mini-OS.
Одно из первых, с чего начинается портирование, это карта памяти (нет, не SD-карточка, о которой вы, возможно, подумали, а о разных регионах памяти — memory map), традиционно описываемая в lds-скрипте. Линкеру нужно указать, где же должен лежать код программы, данные, точка входа в программу и так далее. В нашем случае к этому прибавляется ещё и то, что перед загрузкой образа должны считываться некоторые особенности из секций “.note.Xen”, точнее заголовок в ELF PT_NOTE формате. Карта памяти вместе с особенностями описана тут.
В линкер-скрипт Embox для Xen добавлено следующее
PHDRS {
xen PT_NOTE;
}
SECTIONS {
.note : {
*(.note)
} :xen
}
Ну и ещё добавился ассемблерный файл, содержащий наполнение для этого заголовка
#include <xen/elfnote.h>
.section ".note", "a"
#define ELFNOTE(type, desc) .p2align 2; .long 1f - 0f; .long 3f - 2f; .long type; 0: .asciz "Xen"; 1: .p2align 2; 2: desc; 3: .p2align 2;
ELFNOTE(XEN_ELFNOTE_XEN_VERSION, .asciz "xen-3.0")
ELFNOTE(XEN_ELFNOTE_LOADER, .asciz "generic")
ELFNOTE(XEN_ELFNOTE_PAE_MODE, .asciz "yes")
ELFNOTE(XEN_ELFNOTE_VIRT_BASE, .long 0x100000)
ELFNOTE(XEN_ELFNOTE_PADDR_OFFSET, .long 0x100000)
ELFNOTE(XEN_ELFNOTE_HYPERCALL_PAGE, .long 0x100000)
Кроме этого, есть ещё пара структур, необходимых для взаимодействия между образом и Xen struct start_info и struct shared_info.
struct start_info нужна, если мы хотим получать информацию о машине при старте образа, то есть без неё, в принципе, можно обойтись.
*
* `incontents 200 startofday_shared Start-of-day shared data structure
* Xen/kernel shared data -- pointer provided in start_info.
*
* This structure is defined to be both smaller than a page, and the
* only data on the shared page, but may vary in actual size even within
* compatible Xen versions; guests should not rely on the size
* of this structure remaining constant.
*/
struct shared_info {
struct vcpu_info vcpu_info[XEN_LEGACY_MAX_VCPUS];
/*
* A domain can create "event channels" on which it can send and receive
* asynchronous event notifications. There are three classes of event that
* are delivered by this mechanism:
* 1. Bi-directional inter- and intra-domain connections. Domains must
* arrange out-of-band to set up a connection (usually by allocating
* an unbound 'listener' port and avertising that via a storage service
* such as xenstore).
* 2. Physical interrupts. A domain with suitable hardware-access
* privileges can bind an event-channel port to a physical interrupt
* source.
* 3. Virtual interrupts ('events'). A domain can bind an event-channel
* port to a virtual interrupt source, such as the virtual-timer
* device or the emergency console.
*
* Event channels are addressed by a "port index". Each channel is
* associated with two bits of information:
* 1. PENDING -- notifies the domain that there is a pending notification
* to be processed. This bit is cleared by the guest.
* 2. MASK -- if this bit is clear then a 0->1 transition of PENDING
* will cause an asynchronous upcall to be scheduled. This bit is only
* updated by the guest. It is read-only within Xen. If a channel
* becomes pending while the channel is masked then the 'edge' is lost
* (i.e., when the channel is unmasked, the guest must manually handle
* pending notifications as no upcall will be scheduled by Xen).
*
* To expedite scanning of pending notifications, any 0->1 pending
* transition on an unmasked channel causes a corresponding bit in a
* per-vcpu selector word to be set. Each bit in the selector covers a
* 'C long' in the PENDING bitfield array.
*/
xen_ulong_t evtchn_pending[sizeof(xen_ulong_t) * 8];
xen_ulong_t evtchn_mask[sizeof(xen_ulong_t) * 8];
/*
* Wallclock time: updated only by control software. Guests should base
* their gettimeofday() syscall on this wallclock-base value.
*/
uint32_t wc_version; /* Version counter: see vcpu_time_info_t. */
uint32_t wc_sec; /* Secs 00:00:00 UTC, Jan 1, 1970. */
uint32_t wc_nsec; /* Nsecs 00:00:00 UTC, Jan 1, 1970. */
struct arch_shared_info arch;
};
struct shared_info
, как видно, содержит информацию о взаимодействии, то есть через нее мы будем получать события: прерывания, пришли данные и т. д.Располагается эта структура со смещением 4кб
И наш линкер-скрипт выглядит так
SECTIONS {
.text : {
. = ALIGN(0x1000);
hypercall_page = .;
. += 0x1000;
xen_shared_info = .;
. += 0x1000;
_traps_text_start = .;
*(.traps.*)
_traps_text_end = .;
*(.text)
*(.text.*)
}
}
Собственно, на этом хотелось бы закончить, поскольку разбор всего кода явно выходит за рамки одной статьи. Скажу лишь, что на сегодняшний момент студентами реализован запуск машины с отладочным выводом.
Для того чтобы в этом убедиться, достаточно сделать в Embox следующее;
make confload-xen/debug
make
./scripts/xen/run_xen
Создастся домен, и пойдет вывод самого Embox. Выход из консоли, как всегда, ctrl + ] и потом не забудьте удалить машину:
sudo destroy embox
.Естественно, сначала нужно установить окружение. Но для простоты, уже мы, а не студенты, добавили возможность поставить всё через vagrand. Это описано на wiki. Вам потребуется только VirtualBox и vagrand. Ставить нужно не под Xen-ом.
В конце хотелось бы вновь затронуть тему обучения в IT. Как я уже писал в статье: “Как поймать программиста на списывании и стоит ли этим заниматься“, у нас в проекте мы пропагандируем погружение студентов в реальный проект. В этом случае студенты получают куда больше опыта, чем при выполнении учебных задач, пусть и довольно масштабных. И главное они не только обучаются какому-то конкретному языку, а учатся создавать продукты, решать задачи самостоятельно и попросту уметь мыслить. В качестве примера я сошлюсь на автора идеи про идеальную облачную платформу, Антона Козлова (antonkozlov), ведь именно на его диплом я ссылался ранее. По итогам обучения он смог не только писать код, но и предлагать собственные идеи, реализовывать их, разбивать задачу на составляющие и обучать студентов. Подобных примеров у нас много.
P.S.: Видео с той конференции доступно тут.