The Daily Newsstand · Free, Always
Monday, October 26, 2026

SPICE протокол: Обзор, узкие места и три идеи, которые могли бы ускорить его в 2 раза (но я не буду их реализовывать)

Translate

Эта статья основана на нескольких годах работы над модернизацией SPICE: изучении исходного кода, анализе узких мест и попытках понять логику архитектурных решений. Это концентрированный опыт, а не пересказ документации.

История протокола здесь не для полноты изложения. Она объясняет, почему SPICE устроен именно так и почему одни ограничения можно снять, а другие приходится принимать как данность.

Что это

SPICE (Simple Protocol for Independent Computing Environments) — протокол прикладного уровня для удалённого доступа к рабочему столу виртуальной машины. Клиент подключается к ВМ на гипервизоре и работает с ней так, будто она стоит на столе: видит экран, слышит звук, управляет клавиатурой и мышью, пробрасывает USB‑устройства.

Главная идея — не передавать экран целиком, а передавать только изменения, причём в виде команд отрисовки, а не готовых кадров. Если на экране ничего не меняется, SPICE молчит. Если изменилась одна иконка — передаёт команду «нарисуй иконку здесь». Клиент сам отрисовывает результат. Это снижает нагрузку на сеть и делает работу отзывчивой даже на небыстрых каналах.

Второе отличие — серверная часть SPICE работает на уровне гипервизора, а не в гостевой ОС. Гостевые агенты общаются с ней через символьные устройства, а не через сеть. Это принципиальное архитектурное решение, которое даёт безопасность и устойчивость (подробнее — в разделе «Как работает»).

SPICE — не транспорт в чистом виде, а протоколов прикладного уровня для конкретного сценария. Он работает поверх TCP. И он не передаёт файлы: это задача других протоколов, например FASP или COMET.

История

Кто создал

Протокол разработан компанией Qumranet — израильским стартапом, который также стоял у истоков KVM, технологии виртуализации в ядре Linux. Qumranet не просто разработала KVM, но и фактически продвинула её в ядро GNU/Linux, принеся виртуализацию в массы. SPICE создавался вместе с KVM как часть одной экосистемы.

Изначально SPICE был коммерческим закрытым продуктом для корпоративного сегмента: компаниям нужен был быстрый и надёжный протокол удалённого доступа к виртуальным машинам, который не уступал бы Citrix и VMware. В сентябре 2008 года Red Hat купила Qumranet примерно за 107 миллионов долларов — вместе с KVM и SPICE. Через год, в 2009-м, SPICE открыли как проект с открытым исходным кодом.

Ключевые вехи

  • 2008 — Qumranet разрабатывает SPICE; Red Hat покупает компанию.

  • 2009 — SPICE открыт как проект с открытым исходным кодом.

  • 2010 — SPICE включён в Red Hat Enterprise Virtualization.

  • 2012 — поддержка SPICE появляется в Proxmox VE.

  • 2016 — SPICE становится частью oVirt, открытой платформы виртуализации.

  • ~2019 — серверную часть начинают переписывать с C на C++ (переходный период с ошибками).

  • Сейчас — SPICE используется в RHEV, oVirt, Proxmox VE и других открытых гипервизорах, но развитие замедлилось.

Честно о состоянии: в последние годы SPICE развивается медленно. Драйверы QXL для современных ОС деградировали: они не раскрывают полный набор команд, отправляя всё изображение как скриншот. Серверную часть переписали с C на C++ в переходный период, и там остались ошибки. Для работы и модификации рекомендуется брать C‑версию — она стабильнее, и в ней исправлены ошибки, которых нет в C++‑версии. Исходный код доступен на GitLab freedesktop.org. Подробнее — в «Заметках из практики».

Как работает

Три компонента

  • Гостевые агенты. Работают внутри виртуальной машины. Каждый агент отвечает за свой канал: vdagent — за буфер обмена и разрешение экрана, usbredir — за USB, и так далее. Агенты общаются с серверной частью не по сети, а через символьные устройства.

  • Серверная часть. Работает на уровне гипервизора, внутри процесса QEMU, отдельно для каждой виртуальной машины. Принимает данные от агентов, от QXL и отправляет их клиенту.

  • Клиент SPICE. Приложение на устройстве пользователя. Принимает видеопоток, декодирует его, отправляет на сервер сигналы ввода.

Символьные устройства

Связь между гостевыми агентами и серверной частью — ключевая особенность SPICE. Агенты не открывают сетевые сокеты. Вместо этого они общаются через символьные устройства — виртуальные последовательные порты (virtio‑serial). Эти порты пробрасываются в обработчик spicevmc внутри QEMU, и уже оттуда данные попадают в серверную часть SPICE.

Символьные устройства работают по принципу блочных устройств в Linux: их можно пробросить через файл настройки виртуальной машины или через параметры QEMU напрямую. Количество таких устройств не ограничено — QEMU позволяет создавать их столько, сколько нужно. Обработчик spicevmc разработан теми же авторами, что и сам SPICE.

Цепочка выглядит так:

гостевой агент → виртуальный последовательный порт → обработчик spicevmc → серверная часть SPICE в QEMU → TCP → клиент

Сеть «начинается» только на гипервизоре, в гостевой ОС её нет.

Архитектура SPICE: гостевые агенты общаются через символьные устройства с серверной частью внутри QEMU, а клиент получает данные по одному TCP‑соединению с мультиплексированием каналов.

Почему серверная часть на гипервизоре

Это ключевое архитектурное решение, и оно даёт три преимущества.

  • Безопасность. В «обычной» архитектуре гостевой агент сам открывает сокет и отправляет данные клиенту. Если виртуальная машина скомпрометирована, злоумышленник получает прямой доступ к сетевому соединению прямо из гостя. В SPICE этого нет: серверная часть живёт в процессе QEMU, гость до неё не дотягивается. Даже если ВМ полностью захвачена, атакующий видит только виртуальный последовательный порт, а реальный TCP‑сокет — снаружи, на хосте.

  • Устойчивость. Если агент живёт в гостевой ОС, пользователь или процесс может его завершить — соединение упадёт. Нужна отдельная сторожевая служба, которая перезапускает агента, и это лишняя поверхность для атаки. В SPICE серверная часть в процессе QEMU не зависит от того, что происходит в госте.

  • Независимость от гостевой ОС. Агентов в госте даже необязательно устанавливать: базовые функции работают и без них. Серверная часть сама знает, как обращаться с символьными устройствами, и гостевая ОС не влияет на архитектуру соединения.

Мультиплексирование каналов

Внутри серверной части SPICE поделён на виртуальные каналы: display, inputs, cursor, audio, usb, record, playback, smartcard и другие. Это не отдельные TCP‑соединения, как иногда думают, а виртуальные каналы, которые мультиплексируются в одно TCP‑соединение и демультиплексируются на клиенте.

У каждого канала три части: агентская, серверная и клиентская. Первая работает с гостевым агентом через символьное устройство, вторая — внутри серверной части, третья — с клиентом. Это разбиение реализовано на уровне классов в C++ версии.

Каналы не блокируют друг друга: если изображение «задумалось», звук и ввод продолжают работать. Это важно для отзывчивости — курсор должен двигаться плавно, даже если картинка на экране грузится.

Важное уточнение: документация spice‑space.org в некоторых местах упрощена или устарела. В частности, она может создать впечатление, что каждый канал использует отдельный сокет, — на самом деле транспорт один, а каналы — это объекты внутри серверной части.

QXL — сердце SPICE

QXL — главная особенность SPICE. Это виртуальная видеокарта, которая позволяет получить ускорение OpenGL без проброса настоящей видеокарты. Реализация QXL находится прямо в QEMU, тесно связана с гипервизором и работает в паре с серверной частью SPICE.

Как это работает. QXL берёт команды отрисовки из гостевой ОС и отправляет их из QEMU напрямую в серверную часть SPICE. Там эти команды преобразуются в атомарные примитивы через Cairo, и уже эти примитивы уходят клиенту по сети. Клиент не получает готовую картинку — он отрисовывает её сам из команд.

В чём выигрыш. Команды сжимаются лучше, чем готовые кадры: одна команда «нарисуй прямоугольник» занимает байты, а тот же прямоугольник в кадре — килобайты. Плюс команды, которые перекрывают друг друга, отбрасываются — это называется отсечение перекрытий (occlusion culling). В сумме это даёт заметную экономию трафика и ускорение без графического процессора на клиенте.

Дополнительно QXL поддерживает работу с несколькими мониторами, что делает его пригодным для профессиональных сценариев.

Два потока

SPICE старается не распыляться по потокам: чем меньше потоков, тем меньше нагрузка на планировщик ОС на хосте, и виртуальные машины работают быстрее. Сейчас у SPICE два потока: один обслуживает канал экрана и курсора, другой — все остальные каналы. Остальные каналы работают в асинхронном режиме.

У этого решения есть обратная сторона: если нагрузить один из «остальных» каналов (например, передать большой файл через буфер обмена, который висит на главном канале), картинка на время передачи может подвиснуть. Это архитектурное ограничение, а не ошибка.

Почему TCP

SPICE использует TCP. Это осознанный выбор, и у него несколько причин:

  • Готовый протокол с гарантией доставки. TCP даёт надёжность, порядок и управление перегрузкой «из коробки» — не нужно реализовывать всё это заново.

  • Дружелюбие к сетевым сценариям. Виртуальных машин в сети может быть много; TCP с его состояниями и демультиплексированием по портам хорошо для этого подходит.

  • Совместимость с корпоративными сетями. В компаниях UDP часто заблокирован или инспектируется. TCP пропускают почти везде.

  • Требование QXL. QXL отправляет команды отрисовки, и если пропустить одну команду, получится не просто потеря кадра, а артефакт — вся отрисовка после неё может «поехать». TCP гарантирует порядок и доставку, а значит, и целостность картинки.

Важно: SPICE требует гипервизора с поддержкой SPICE — KVM/QEMU, Proxmox VE, Red Hat Virtualization, oVirt. На «голом железе» или в среде, где нет такого гипервизора, он не заработает. Это не универсальный протокол удалённого доступа, а специализированный инструмент для виртуализации.

Сравнение с RDP

Критерий

SPICE

RDP

Разработчик

Qumranet / Red Hat

Microsoft

Открытый исходный код

Да

Нет (спецификации открыты)

Основная среда

Linux / KVM

Windows

Серверная часть

На гипервизоре, вне гостя

В гостевой ОС

Транспорт

TCP, одно соединение

TCP + UDP (8.0+)

Проброс USB

Да, полноценно

Да, с ограничениями

Сложность настройки

Выше

Ниже

Требует гипервизора

Да

Нет

Где применяется

  • VDI‑инфраструктуры. Пользователи подключаются к виртуальным рабочим столам с тонких клиентов или ноутбуков.

  • Тестовые стенды. Разработчики и инженеры работают с ВМ разных конфигураций — не переключая физические машины.

Ограничения

  • Не для передачи файлов. SPICE передаёт то, что видно на экране, а не файлы между ВМ. Для файлов — FASP, COMET и другие протоколы передачи файлов.

  • Требует гипервизора. Не работает на «голом железе» и в средах без KVM/QEMU.

  • Сложнее в настройке, чем RDP. Нужно настроить гипервизор, серверную часть SPICE и клиент — три компонента вместо одного.

  • Зависит от сети. При высоких задержках или потерях качество картинки падает — это неизбежно для интерактивного протокола.

  • Шифрование слабее, чем у RDP. Обычно SPICE используют внутри доверенной сети или заворачивают в туннель.

  • Один поток на все «остальные» каналы. Нагрузка на любой из них (например, на буфер обмена) может временно «подвесить» картинку.

  • Деградация драйверов QXL. Современные драйверы не раскрывают полный набор команд, отправляя всё изображение как скриншот. Это снижает эффективность протокола.

  • Медленное развитие. Проект развивается слабо; серверную часть переписали на C++ в переходный период, и там остались ошибки.

Заметки из практики

Раздел основан на опыте работы с исходным кодом SPICE. Это не документация и не систематическое исследование — скорее наблюдения, которые будут полезны тем, кто собирает SPICE, форкает его или добавляет новые каналы.

C или C++: какую версию брать

Серверная часть SPICE изначально была написана на C с использованием GObject. Позже её начали переписывать на C++, но этот переход остался незавершённым: часть кода на C++, часть на C, и в C++‑версии остались ошибки, которых в C‑версии не было или которые там уже исправили.

Пример — утечка памяти в канале WebDAV: в C‑версии её исправили, а в C++‑версию исправление не попало (подробнее — ниже). Именно поэтому для работы и модификации рекомендуется брать C‑версию: она стабильнее, лучше отлажена, и в ней меньше сюрпризов.

Исходный код обеих версий доступен на GitLab freedesktop.org: gitlab.freedesktop.org/spice/spice. В репозитории можно переключаться между ветками и тегами, чтобы работать с той или иной версией.

Сложность добавления канала

В C++ версии серверной части добавить новый канал — это большая работа. Приходится проходить по всем уровням: от функции, которую вызывает QEMU, до реализации классов канала. У каналов есть родительские классы (RedChannel, RedChannelClient и другие), и при наследовании возникает запутанная зависимость: не укажешь что‑то — канал не заведётся или отдельная его часть не сможет работать нормально. Много мелочей, которые нужно искать самому.

С гостевой частью проще, а вот клиентская часть канала — мрак. У каждого канала есть конвейер (pipe) — динамический буфер для приёма и отправки сообщений. Инициализация сложная: если сделать что‑то не так, клиент падает с ошибкой сегментации из‑за несозданного RedChannelClientPriv.

Утечка памяти при передаче данных

Когда отправляется сообщение, создаётся новый элемент конвейера (pipe_item), который выделяется динамически. Система по идее не имеет узкого места в виде буферов в пользовательском пространстве, но использует умные указатели — и при передаче большого объёма данных появляется утечка.

В старых записях от разработчиков (ещё в C‑версии) указано, что утечка была именно в этом месте: не выставлялись лимиты на создание элементов конвейера, и при передаче файлов всё «давилось», а излишки висели в памяти. Утечка примерно равна размеру файла. При повторной передаче того же файла утечки уже не было — почему, точно не известно, возможно, срабатывал пул буферов.

В C++ версии эта проблема осталась в канале WebDAV: исправление, сделанное в C‑версии, туда не попало. Это ещё один аргумент в пользу C‑версии.

Деградация драйверов QXL

Из‑за замедления развития SPICE современные драйверы QXL перестали раскрывать возможности видеокарты: они отправляют всё изображение как скриншот, не поддерживая полный набор команд. Это сильно снижает эффективность протокола.

Обходной путь — использовать старые драйверы для Windows 7. Они работают лучше современных.

QXL и проброшенная видеокарта

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

Направления развития

Ниже — направления, которые выглядят перспективно. Это не план разработки, а аналитика: что можно было бы сделать, чтобы SPICE вышел на другой уровень.

1. Адаптивность поверх TCP

SPICE использует TCP с его реактивным управлением перегрузкой: при потере скорость падает. Идея — добавить механизм упреждения перегрузки поверх TCP, как это делают FASP и COMET: измерять задержку и подстраивать скорость заранее. TCP остаётся транспортом с гарантией доставки, но логика адаптивности переносится на уровень приложения.

Технически это можно сделать через дополнительную прослойку — например, поверх веб‑сокета или собственного потокового слоя (RedStream) над TCP, — и добавить логику адаптивности в клиентскую и серверную части. RedStream очень хороший вариант, это небольшая абстракция сокетов и соединения в целом. Имеет очень простые функции и логику для работы, рекомендую работать именно на этом уровне, можно очень легко и быстро менять поведение всего протокола на уровне транспорта. Это позволит работать сразу со всеми каналами, если, конечно, не требуется обратного.

2. Несколько параллельных TCP‑соединений

Более простой путь к ускорению — добавить второе TCP‑соединение параллельно первому и распределять каналы между ними. Это даёт примерно двукратный выигрыш по пропускной способности, если узкое место — одно соединение, а не канал в целом.

Подход известен как многопоточный TCP (multi‑stream TCP). Реализовать его можно на уровне веб‑сокета или собственного потокового слоя — так же, как в первом направлении. Каналы (или часть каналов) можно распределить между соединениями, а на клиенте собирать их в один поток. Даёт гарантированный прирост, GridFTP и Zettar ZX уже доказали это. В реальных условиях, TCP утилизирует 20–30% канала, при каждом добавленном соединении можно смело прибавлять 20%, если нет упора в скорость сети.

Важно: Это относительно простой и хороший способ гарантированно получить прирост скорости. При правильной организации, это даёт дополнительный параллелизм, ведь два соединения способны работать независимо. Самый простой способ — перенести часть виртуальных каналов SPICE на второе TCP соединение. Для этого их можно отсортировать по важности, например, вынести Display и Cursor в отдельное TCP соединение, тогда они будут обрабатываться на личном потоке и будут иметь личное соединение — меньше задержек, максимальный параллелизм, меньше потерь (общее кол‑во данных на соединении будет меньше и случайные неизбежные потери будут происходить реже, это позволит TCP поддерживать большую скорость). Остальные каналы будут жить в другом соединении и потоке. Выигрыш крайне высок:

  • 1. Чистая многопоточность. Потенциально, конкуренция есть, внутри malloc(), при создании RedPipeItem. SPICE является не процессом ОС, а потоком который запускает QEMU. В большинстве реализаций malloc(), выделяются отдельные большие сегменты памяти для каждого потока, поэтому каждый работает со своим участком. Если QEMU создаст много потоков и аллокатор памяти начнёт делиться сегментами с разными потоками, то будут задействованы атомарные операции и блокировки. Любые операции ожидания или барьеры памяти выстраивают микро‑последовательность, что приводит к замедлению. Большая часть скорости многопоточной программы зависит от логики которая требует последовательного выполнения. Это редкая и маловероятная ситуация, но это даёт дополнительную почву для оптимизаций.

  • 2. Меньше задержек. Каждый из потоков работает в своём темпе. Буферы у сокетов не пересекаются, нет блокировки из‑за потери PING или другого второстепенного сообщения.

  • 3. Выше пропускная способность. Посмотрите Zettar ZX, GridFTP или Multi‑stream TCP.

Если пойти по пути распределения между несколькими соединениями, то можно сделать динамическое кол‑во соединений. Это сильная гибкость и дополнительные настройки, но требует правильной архитектуры. Возможно, стоит использовать гибридный подход, выделить от 1 до N соединений для Display и Cursor каналов и от 1 до N соединений для других каналов. Всё зависит от вашего воображения, возможна более тонкая разбивка каналов или более хитрые схемы.

Второй вариант имеет смысл в паре с первым. Адаптивность позволит получить больше из каждого соединения. Скорее всего, самый оптимальный вариант для реальных VDI‑сред — два соединения + личная адаптивность у каждого из них. Дружелюбие всё ещё сохраняется, но скорость работы сильно возрастает. Это только один из вариантов, всё зависит от желания и фантазии.

3. Унифицированный канал (UniChannel)

Идея — добавить ещё один канал, внутри которого можно строить свои виртуальные каналы и играть по своим правилам. Это позволило бы вынести в отдельную подсистему сложные сценарии (например, передачу файлов или что‑то ещё), не трогая всю архитектуру SPICE. Внутри UniChannel можно было бы сделать свои потоки, свой протокол, свою логику — и всё это в рамках одного канала.

По сути, это способ расширять SPICE без переписывания его ядра: новый канал становится «песочницей», где можно экспериментировать, не рискуя сломать остальные каналы.

4. Комбинация всех подходов

Самое интересное — совместить всё: несколько параллельных TCP‑соединений дают пропускной способности, адаптивность поверх TCP даёт устойчивость на плохих каналах, а UniChannel даёт пространство для расширения и новых подсистем.

Такие изменения могли бы вывести SPICE на другой уровень — в сфере протоколов доставки рабочего стола ему было бы сложно найти равных. Но это требует значительных усилий и координации.

Заключение

SPICE — зрелый, но недооценённый протокол. Его архитектура с серверной частью на гипервизоре и мультиплексированием каналов остаётся элегантной и безопасной. Однако деградация драйверов QXL и медленное развитие проекта не позволяют ему раскрыть весь потенциал.

Три идеи, описанные выше — адаптивность поверх TCP, несколько параллельных соединений и UniChannel — могли бы ускорить SPICE в разы и раскрыть его. Реализовывать их я не планирую, но если кто‑то возьмётся — буду рад помочь советом.

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.