Как мы портировали программно-аппаратное решение из SPARC Solaris в AMD64 Linux и виртуализировали все это

от автора

Эта статья для тех, кто все еще сильно любит древние Unix, но уже понимает, что срочно нужно мигрировать на Linux…

image

Началась эта история с того, что наш уважаемый заказчик решил уменьшить риски эксплуатации оборудования 10-ти летнего возраста, сэкономить на лицензиях и перейти с Unix на Linux, а заодно и виртуализировать это программно-аппаратное решение. Не то, чтобы заказчик не любил Solaris и Unix, просто сама возможность виртуализировать серверное приложение, жестко привязанное к специфической на сегодняшний день архитектуре SPARC и «седеющей» операционной системе Solaris, выглядела для заказчика очень привлекательной. Отдельным пунктом стоял вопрос замены специализированной карты с PCI интерфейсом на доступное «виртуализированное» решение. Мы решили взяться за такую интересную задачу.

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

  1. «привязанный» к системным вызовам ядра Solaris, но гибко настраиваемый, многопоточный Web сервер (демон) с внешним Web API
  2. код, привязанный к Си-шному API специализированной карты в слоте PCI
  3. Unix — вызовы IPC к программе PGP
  4. код, привязанный к дюжине специализированных Unix библиотек

И вот этот код и программно-аппаратное решение нужно было «виртуализировать» и заставить работать в Linux.

image

Отступление от темы

Сейчас уже понимаю, что портирование этого проекта было крайне рискованное занятие…

Задачу «в лоб» не решить, поэтому мы решили разбить задачу на части.

Отдельно хотелось сказать, что код демона оказался endian — нейтральный, что несколько упростило задачу.

В процессе поиска оптимального пути портирования этого кода мы исследовали техническое руководство IBM по портированию Solaris — приложения в Linux.

Первое, что хотелось выяснить, это насколько трудоемко окажется портировать вызовы ядра Solaris в относительно POSIX — совместимый Linux? Solaris, являясь POSIX сертифицированной ОС, имеет в своем ядре весьма специфические функции работы с потоками. Естественно, есть и те, что не имеют аналогов в ядре Linux:

  • thr_suspend();
  • thr_continue();

Функционал демона не только полностью полагался на эти, но также и на другие функции ядра Solaris: thr_keycreate(), thr_self(), thr_getspecific() и другие. В руководстве IBM прямо говорилось о том, что такой код при портировании в Linux придется переписать. А это значило, что тысячи строк многопоточного низкоуровневого кода «C с классами» нужно было проанализировать и переписать. Неужели нет другого пути? И нам показалось, что мы его нашло! В поиске рабочего «костыля» для портирования кода для Solaris «как есть» в Linux мы наткнулись на старый проект Compaq Solaris-compatible Thread Library (ScTL). Ура, у нас есть «прослойка», которая ляжет между портированным демоном и ядром Linux! Но не тут то было…

Сначала этот проект не хотел собираться, так как изначально почившая в обозе компания Compaq задумывала проект для Tru64 UNIX, потом портировала проект в Linux Intel x86 и другие платформы. Методом проб и ошибок после исправлений исходников ScTL нам удалось собрать требуемые нам библиотеки для нового ядра Linux на AMD64. Демон на Linux увидел «родные» для него и специфичные для Solaris функции и даже делал вид, что может работать, но все этим и закончилось. В процессе работы с ScTL возникали исключения времени исполнения в самом коде ScTL и демон «падал». Разбираться в таком коде 13-ти летней «свежести» времени не было, мы стали искать альтернативу. Выбор оказался очевиден, хотелось максимальной совместимости с C++11 и STL, поэтому решили остановиться на std::thread и не связываться со стандартом POSIX для потоков напрямую. Поскольку демон был web-сервером, нам крайне желательно было перейти с низкоуровневых сокетов на Boost::Asio. Сравнивая решения мы решили использовать относительно простой, но уже пригодный к использованию в сторонних проектах (лицензия MIT) проект на GitHub. В прикладных классах для обеспечения асинхронности из C++11 нам пригодились std::async и, конечно, лямбды. Итак, web-сервер мы заменили и он проходил отдельные тесты.

image

Следующая проблема — решить как «виртуализировать» специализированную карту расширения с PCI интерфейсом? Схожее по функциональности оборудование имелось на рынке, но это также аппаратное решение, хотя и с интерфейсом USB. Так как используемый заказчиком гипервизор позволял подключать USB оборудование в виртуальную систему, мы пришли к решению, в котором аппаратное решение с USB интерфейсом подключалось в виртуальную среду и становилось доступно в Linux нашему приложению. Все это потребовало значительной переработки связанной с PCI — картой кода, написания нового кода для работы с USB оборудованием и последующего тестирования.

image

Третья проблема была — PGP. Ее свободный аналог GnuPG не хотел работать в Linux по требуемому сценарию как в Solaris и нам пришлось переписать IPC на вызовы библиотеки gpgme, в результате повысилась надежность, демон прекрасно заработал с родными «C-шными» вызовами и стал корректно обрабатывать ошибки в нештатных ситуациях.

Оставалась четвертая проблема — зависимость от Unix библиотек. Но после решения предыдущих задач эти библиотеки не казались такой уж большой проблемы. Мы построили таблицу соответствия пакетов Linux (аналоги) пакетам из Solaris, затем выяснили, какие пакеты содержат в себе нужные нам библиотеки и для каких целей и как их хочет использовать наш демон. В итоге нашлись все требуемые свободные аналоги, сборка и тесты показали, что Unix-подобие ОС Linux позволяет упростить портирование кодовой базы.

В заключение хочется рассказать о двух моментах: используемая нами система сборки и целевые дистрибутивы Linux. В оригинале с кодом для Solaris нам достался Makefile. Он был неплохим решением 20 лет назад, но нам хотелось гибкости при сборке и CMake дал нам то, что мы искали: настраивать работу с Boost, линковать с разными версиями библиотек, учитывать изменения в ABI у g++. Сейчас CMake позволяет нам удобным образом генерировать файлы для Debug и Release сборок. С целевыми дистрибутивами ситуация обстояла так, что Заказчик хотел использовать Red Hat Enterprise Linux, у нас же на предприятии использовался Ubuntu. Дело закончилось статической компиляцией Release сборки с минимальными зависимостями (привет любителям .Net и Java), хотя размер исполняемого файла несколько «подрос».

В итоге мы портировали Solaris демон в Linux, но при этом мы выбросили практически весь системный код, «вросший корнями» в Solaris UNIX, написали новый код для «виртуализации» PCI карты в подобное по функционалу USB устройство и сделали отдельные места в коде более надежными. Что немаловажно, наш новый демон для Linux читает файлы конфигурации, скопированные из Solaris и корректно настраивает свою работу по ним, сохраняя 100% совместимость со спецификацией API как Web сервис.

image

Благодаря стандарту C++11 (а на подходе C++17), Boost, CMake и современным гипервизорам техническое задание, казавшееся поначалу невыполнимым, оказалось вполне себе исполнимым и даже увлекательным.
ссылка на оригинал статьи https://habrahabr.ru/post/325304/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *