Автор: (c)Крис Касперски ака мыщъх
...Задачи, решаемые с помощью компьютера, нередко самим компьютером
и порождаются.
Пол Грэм
В последнее время много говорят о переносе UNIX-программ на Windows. Только так, и никак не наоборот. Но ведь существует большое количество Windows-программ, аналога которых на других платформах нет (прежде всего, это ваши собственные программы). Стоит ли их переносить на UNIX и если да, то как?
Абсолютно переносимого программного обеспечения не существует, как не существует абсолютного нуля. Понятие "переносимости" еще не означает, что портирование сводится к простой перекомпиляции. Всегда требуются дополнительные усилия по его адаптации. Иногда эти усилия настолько значительны, что проще переписать программу с нуля, чем гонять ее между платформами. Системно-ориентированные пакеты (FAR, Soft-ice) переносить вообще бессмысленно.
В любом случае вы должны полностью разобраться в исходных текстах, которые переносите. При доминирующем стиле кодирования интерфейс программы перемешан с "вычислительной" частью (спасибо визуальным средам разработки!) и разделить их не проще, чем сиамских близнецов (но разделять все же придется, потому что интерфейс в UNIX очень сильно другой). Типичный код нашпигован большим количеством системно-зависимых функций - вместо стандартных библиотечных функций преобладают вызовы API и MFC. Активно используется ассемблерные вставки и повсеместно - умолчания компилятора. Это в Багдаде char по умолчанию unsigned, но в других компиляторах он ведет себя совсем не так! Про "умолчанную" кратность выравнивая структур я и вовсе молчу. Хуже этого только нестандартные расширения компилятора и специфические особенности его поведения. Большинство программ, созданных современными "программистами", не компилируются MS VC, если написаны на BCC и соответственно, наоборот. До переноса на UNIX им также далеко, как их авторам до звания "программиста" (необязательно даже "почетного программиста", можно просто "стажера", путающего язык со средой разработки).
Считается, что перенос сокращает издержки на развитие и сопровождение проекта. Имея независимые версии для Windows и UNIX, вы вынуждены вносить исправления и гонять багов в обеих программах одновременно. Портабельный код этих недостатков лишен. Якобы. Скажите, когда-нибудь вы пробовали писать программу, компилируемую более чем одним компилятором? Матерились при этом? И правильно! Я бы тоже заматерился. Ограничения, налагаемые переносимым кодом, лишают нас многих "вкусностей" языка и значительно увеличивают трудоемкость разработки. Допустим, вы используете шаблоны (templates) и на MS VC все работает, но при переходе на другой компилятор программа разваливается к черту. А некоторые компиляторы не инициализируют статические экземпляры класса. Ну, не инициализируют и все тут! Забудьте о стандартах. Компиляторы все равно их не придерживаются. Чем же тогда руководствоваться? А ничем! Это уж как повезет/не повезет.
При каждом внесении изменений в программу прогоняйте ее через все целевые компиляторы. Код, специфичный для данной платформы, заботливо окружайте #ifdef или выносите в отдельный файл, ну и т.д. В конечном счете, вы получите все те же два независимых проекта, но тесно переплетенные друг с другом, причем внесение изменений в один из них дает непредсказуемый эффект в другом. Нет-нет, не подумайте! Я вовсе не противник переносимого кода, просто не понимаю тех, для кого переносимость - цель, а не средство. Никто не спорит, что такие проекты как Apache или GCC должны изначально разрабатываться как переносимые (процент системно-независимого кода в них очень велик), но вот мелкую утварь типа почтового клиента лучше затачивать под индивидуальную платформу, а при переходе на UNIX переписывать заново.
Если нужно быстро перенести программу - воспользуйтесь WINE или Willows. Это бесплатно распространяемые имитаторы Windows, оборачивающие UNIX-функции толстым слоем переходного кода, реализующего Win32 API и работающие на Windows 9x/NT/2000/XP, Linux, FreeBSD, Solaris, а Willows еще и на QNX (есть такой клон UNIX, управляющий истребителями, атомными реакторами и прочими mission-critical системами, кстати говоря, бесплатный и свободно умещающийся на одной дискетке).
Обратите внимание: не эмуляторы, а именно - имитаторы (WINE именно так и расшифровывается: "Wine Is Not Emulator" - это вам не эмулятор). Портируемая программа исполняется на "живом" процессоре, практически не теряя в скорости. Во всяком случае, реклама говорит именно так. А что реальная жизнь? При всей схожести между UNIX и Windows NT (их ядра наследуют общий набор концепций), они во многом различны. В UNIX есть замечательная функция fork, расщепляющая процесс напополам. В NT ее нет. CreateProcess/CreateThread - это фуфло. И вот почему. Накладные расходы на расщепление процесса fork'ом ничтожны, чего нельзя сказать о создании процесса/потока с нуля. Кстати говоря, с потоками в Linux сплошной напряг (внутреннее потоки представляют те же процессы, но только с "извращениями"). Всегда заменяйте CreateThread на fork, когда это только возможно (процессы, в отличие от потоков, исполняются в различных адресных пространствах и могут обмениваться данными только через IPC, например - проецируемые в память файлы). К тому же, средства синхронизации потоков в Windows и UNIX очень различны, а в Linux синхронизация не поддерживается вовсе и реализуется внешними библиотеками. Все это делает отображение Win32 API на UNIX-функции неоднозначным и выбор предпочтительного системного вызова в каждом конкретном случае должен определяться индивидуально. Человеком. Имитатор на это не способен и падения производительности не избежать (другое дело, что при современных аппаратных мощностях на производительность можно положить).
Конструктивно большинство имитаторов состоят из двух основных компонентов: бинарного интерфейса (Binary Interface) и библиотеки разработчика (Library). Некоторые имитаторы (например, Willows) включают еще и уровень абстрагирования от платформы (Platform-abstraction Layer), что упрощает их перенос на другие системы, но это уже детали реализации.
Бинарный интерфейс включает в себя Win32-загрузчик, "переваривающий" PE-файлы и с максимальной точностью воссоздающий привычное для них окружение. Необходимость в перекомпиляции при этом отпадает, однако совместимость остается на уровне слабого подобия левой руки. Реально удается запустить лишь небольшое количество офисных приложений типа Office, Acrobat, Photoshop и т.д. Системные утилиты, скорее всего, откажут в работе и тут на помощь приходит библиотека - заголовочные файлы плюс lib-файл. Адаптировав приложение, мы может компилировать его как в ELF (тогда необходимость иметь на машине установленный имитатор отпадает), либо в PE. Красота! (что такое красота? это женщина, использующая банан не по назначению).
В крайнем случае, можно воспользоваться полноценным эмулятором PC - VMWare или Win4Lin, однако полезность этого решения сомнительна. Дело даже не в аппаратных требованиях (я вполне успешно гоняю VMWare на P-III 733), а удобстве использования (точнее, его отсутствии). Достаточно сказать, что обмениваться данными с эмулятором придется через виртуальную локальную сеть, гоняя их в обе стороны в хвост и в гриву.
Рисунок 1. Windows-приложение, запущенное под WINE.
Для переноса игр и других графических приложений лучше всего подходит WineX, в настоящее время переименованный в Cedega - коммерческая версия имитатора WINE от компании Transgaming, ориентированная на DirectX, Direct3D, OpenGL и прочие технологии этого уровня. Работает в Linux, Mac, PlayStation 2, XBox и Next Gen. Хотите "поквакать" (Word почему-то упорно предлагает заменить это слово на "покакать") в Linux? Нет проблем! А еще можно "поДУМать" или погонять в NFS. Список поддерживаемых игр очень велик и счет идет на тысячи наименований.
Рисунок 2. QUAKE 3 на Linux.
Копания Mainsoft (та самая, у которой свистнули исходные тексты Windows 2000) выпустила замечательный продукт Visual MainWin, позволяющий писать код в Microsoft Visual Studio и тут же компилировать его под разные платформы (Windows, Linux, HP-UP, AXI, Solaris), причем количество поддерживаемых платформ планомерно растет.
Пакет состоит из нескольких частей - это и инспектор кода, позволяющий обнаружить системно-зависимые участки (пускай программист сам решает, как он будет их исправлять!), и препроцессор, подготавливающий исходный код к последующей трансляции GCC (или любым другим UNIX-компилятором) и, конечно же, обширная библиотека функций, реализующая: а) Windows-примитивы (SEH, DLL, процессы/потоки, средства их синхронизации, реестр, буфер обмена и поддержку национальных языков); б) графический и пользовательский интерфейс (GDI32, USER32); в) COM-модель (ActiveX, OLE, MIDL, DCOM); г) библиотеку времени исполнения (ALT, MFC, C Runtime library). Полный перечень поддерживаемых фич - на www.mainsoft.com/solutions/vmw5_wp.html.
Это коммерческий продукт, причем очень сильно коммерческий (лицензия на одного разработчика стоит свыше двух тысяч долларов), правда доступна 30-дневная полнофункциональная демо-версия, так что... решайте сами: иметь или не иметь.
Рисунок 3. Портирование приложений под Visual WinMain, интегрированного в Microsoft Visual Studio.
Рисунок 4. Так выглядит Visual WinMain.
MainWin, конечно, мощная штука, но иногда требуется софтина помельче. Основной камень преткновения - это, конечно же, MFC. В Microsoft Visual Studio все визуальные средства разработки построены именно на нем. И хотя исходные тексты MFC доступны, перенести его на UNIX намного сложнее, чем создать с нуля, сохранив иерархию классов и прототипы функций.
wxWindows - это бесплатная библиотека, практически полностью совместимая с MFC и работающая на всех UNIX-платформах, где есть GTK+, Motif или его бесплатный клон Lesstif. Единственное отличие заключается в том, что вместо префикса "C" здесь используется "wx", в результате чего CWnd превращается в wxWnd. Некоторые классы еще не реализованы (например, отсутствует CEditView) и когда они появятся - неизвестно. Это, конечно, неприятно, но и не смертельно. Без недостающих классов можно кое-как обойтись, заменив CEditVIew на wxTextCtrl; а операцию "перебивки" префиксов загнать в препроцессор или повесить на макрос. Самое главное - wxWindows прекрасно работает на Windows, а значит, один проект не распадется на два!
На сайте IBM есть замечательная статья по переносу MFC приложений на wxWindows (см. "Ссылки"), а на сайте самой wxWindows еще немного материалов на эту тему. Судя по баннерам, проекту покровительствуют весьма влиятельные компании - VMWare и Helpware, поэтому за его дальнейшую судьбу можно не волноваться.
Рисунок 5. Иерархия классов.
Рисунок 6. Иерархия классов wxWindows.
Рисунок 7. Оригинальное MFC-приложение.
Рисунок 8. ...то же приложение, портированное на UNIX с помощью wxWindows.
Множество полезных библиотек можно найти на www.sourceforge.net, например, библиотеку для работы с ini-файлами (не анализировать же ее с помощью Бизона!) - libini.lib. Все они бесплатны, распространяются в исходных текстах и легко подключаются к любому проекту. Никогда не бросайтесь писать никакой код, предварительно не поискав в Сети. Скорее всего, он написан до вас, так зачем же изобретать велосипед, когда есть готовые чертежи?
Класс | MFC-класс | wxWindows класс |
Document | Cdocument | wxDocument |
View | Cview | wxView |
Edit view | CeditView | отсутствует |
Template class | CMultiDocTemplate | wxDocTemplate |
MDI parent frame | CMDIFrameWnd | wxDocMDIParentFrame |
MDI child frame | CMDIChildWnd | wxDocMDIChildFrame |
Document manager | отсутствует | wxDocManager |
Таблица 1. Соответствие основных классов между MFC и wxWindows.
Багдад - великая фирма! Это она создала Turbo Pascal и Turbo Debugger (точнее, не создала, а спи... то есть купила). Это она создала Turbo Vision и определила облик интегрированной среды разработки. Скажу честно. Я не считаю Borland C++ хорошим компилятором (он как-то странно трактует ANSI Стандарт, да и оптимизирует хреново), Билдер я обхожу стороной, а от Дельфи меня натурально тошнит. Но это - личные впечатления. Мой любимый MS VC на UNIX'е оказывается в глубокой жопе (перенес требует больших денежных вложений и телодвижений), а на Багдаде - просто перкомпилируешь на Kylix'e и все!
Kylix - это DELPHI и BUILDER для Linux, распространяющийся по лицензии GPL (то есть, на халяву) и включающий в себя интегрированную среду разработки (экранный редактор, интерактивный отладчик, ну, в общем - кто не понял, тот в Багдаде не бывал) со всеми необходимыми библиотеки и слоями абстрагирования на борту. При условии, что программа не использует прямых вызовов Win32 API, перенос не представляет никакой проблемы (на самом деле, все намного сложнее и если это не чисто вычислительная задача типа бухгалтерии, без прямых вызовов ей никак не обойтись, достаточно захотеть прочитать сектор с CD-ROM диска).
Рисунок 9. Kylix в разгаре рабочего дня. для разнообразия - на китайском.
Рисунок 10. "КИЛИК (греч. "Kylix") - древнегреческий глиняный, реже металлический сосуд для питья вина: плоская чаша на подставке с двумя горизонтальными ручками" - выписка из энциклопедического словаря.
А вот что действительно приводит меня в возбуждение, так это Free Pascal (он же FPK Pascal) - бесплатный кросс-платформенный компилятор Паскаля (с исходниками!), поддерживающий Intel x86, Motorola 680x0, PowerPC и работающий практически на любой операционной платформе: Linux, FreeBSD, NetBSD, MacOSX/Darwin, MacOS classic, DOS, Win32, OS/2, BeOS, Solaris, QNX и Amiga. Синтаксически и семантически Free Pascal полностью совместим с TP 7.0 и практически полностью - с DELPHI версий 2 и 3. В дальнейшем планируется поддержка перекрытия функций и операторов. Вы еще не бьетесь в оргазме? Kylix и рядом не валялся. На платформе Linux он король, а за ее пределами кто?
Единственное, чего недостает Free Pascal - так это нормального IDE. Хотя, на мой мыщъх'ый взгляд, тот IDE, который есть - гораздо нормальнее MS VC и DELPHI, вместе взятых. Одно слово - консоль! При ближайшем рассмотрении выясняется другая замечательная вещь. Free Pascal - не совсем компилятор, точнее - совсем не компилятор! Это - транслятор Паскаля в Си. Формально его можно считать компилятором переднего плана (Front-End Compiler), состыкованного с GCC. Отсюда и приличное качество оптимизации и кросс-платформенность.
Рисунок 11. Интегрированная среда разработки Free Pascal'я - мой дом, моя нора! "Нора", а сказал, а не "дыра", урою этих GUI'шников, блин.
Смельчакам, отважившимся на самостоятельный перенос Windows-приложений, не обойтись без таблиц соответствий API функций системным вызовам, которые приводятся ниже. Разумеется, это не все функции, а только самые популярные из них (полный список занял бы несколько увесистых томов, для транспортировки которых пришлось бы обзавестись грузовиком):
Win32 | Linux |
CreateProcess | Fork()/execv() |
TerminateProcess | Kill |
ExitProcess() | exit() |
GetCommandLine | Argv[] |
GetCurrentProcessId | getpid |
KillTimer | alarm(0) |
SetEnvironmentVariable | putenv |
GetEnvironmentVariable | getenv |
GetExitCodeProcess | waitpid |
Таблица 2. Функции для работы с процессами.
Win32 | Linux |
_beginthread | pthread_attr_initpthread_attr_setstacksizepthread_create |
_endthread | pthread_exit |
TerminateThread | pthread_cancel |
GetCurrentThreadId | pthread_self |
TerminateThread((HANDLE *) threadId, 0); | pthread_cancel(threadId); |
WaitForSingleObject (); | pthread_join(); |
_endthread(); | pthread_exit(0); |
Sleep (50) | struct timespec timeOut,remains;timeOut.tv_sec = 0;timeOut.tv_nsec = 500000000; /* 50 milliseconds */ nanosleep(&timeOut, &remains); |
SleepEx (0,0) | sched_yield() |
Таблица 3. Функции для работы с потоками.
Win32 | Linux |
CreateFileMapingOpenFileMapping | mmapshmget |
UnmapViewOfFile | munmapshmdt |
MapViewOfFile | mmapshmat |
UnmapViewOfFile(token->location);CloseHandle(token->hFileMapping); | munmap(token->location, token->nSize);close(token->nFileDes); remove(token->pFileName); free(token->pFileName); |
Таблица 4. Функции для работы с файлами, проецируемыми в память.
Перенос Windows-приложений на UNIX намного проще, чем это кажется поначалу. К вашим услугам обширный инструментарий и огромное количество библиотек (большей частью бесплатных). Сосредоточьтесь на программном коде и забудьте о пустяках - пусть ими занимается машина (см. эпиграф), но не откладывайте это дело в дальний ящик и прекратите, наконец, игнорировать UNIX-платформу. Ее популярность - свершившийся факт. Так зачем терять рынок? Тем более, что конкурировать здесь пока не с кем. В UNIX до сих пор нет множества привычных Windows-приложений и утилит (систем распознавания текста, шестнадцатеричных редакторов и т.д.), поэтому даже плохенькая программа проглатывается публикой с энтузиазмом. Вы все еще ищете, во что вонзить свои когти?