FreeBSD-BG
Linux емулация под FreeBSD
 
 

1. Защо е необходимо

Поради това, че много софтуерни производители разпространяват своите продукти в бинарен вид, компилирани само за Linux, на FreeBSD се налага да компенсира някакси този проблем. Накратко прави се опит да се поддържа бинарна съвместимост с Linux приложенията. Това позволява на FreeBSD потребителите да използват почти 90% от Linux приложенията , например Linux версийте на Star Office Netscape , Adobe Acrobat, RealPlayer 5 и 7, VMWare, Oracle , WordPerfect, Doom , Quake и други . Има обаче някои специфични за Linux реализации които не се поддържат (все още) от FreeBSD и съответно няма да могат да бъдат стартирани под него . Например, ако Linux binary-то разчита прекалено много на Linux /proc файловата система (която е различна от /proc на FreeBSD) или пък ползва някакви i386-специфични системни повиквания като поддръжка на виртуален 8086 режим.

2.1 Инсталация

2.1.1 Инсталиране на Linux ABI

Бинарната съвместимост с Linux не е включена по подразбиране, най-лесния начин това да стане е като се зареди linux KLD object ("Kernel LoaDable object''), просто като се напише linux в командния ред, или пък ако искате да се зарежда всеки път автоматично тогава добавете linux_enable= "YES" в /etc/rc.conf .

Последното предизвиква изпълнението на следния код в скрипта /etc/rc.i386:

    # Стартиране на бинарна съвместимост с Linux при "поизкване".
#
case ${linux_enable} in
[Yy][Ee][Ss])
echo -n ' linux'; linux > /dev/null 2>&1
;;
esac

Командата kldstat(8) може да се използва за да се провери дали KLD е зареден :

    % kldstat
    Id Refs Address    Size       Name
     1    2 0xc0100000 16bdb8   kernel
     7    1 0xc24db000 d000       linux.ko

Ако по някаква причина не искате или не можете да заредите KLD, тогава можете компилирате поддръжката на бинарна съвместимост с Linux директно в ядрото, добавяйки options LINUX в конфигурационния файл на ядрото. След което прекомпилирайте и инсталирайте новото ядро .

2.1.2 Инсталиране на Linux Runtime Libraries

Това може да бъде направено инсталирайки linux_base port, или инсталирайки ги ръчно .

2.1.2.1 Инсталиране чрез linux_base Port

Това е по-лесния начин и става точно както се инсталира всеки друг порт от Ports collection . Порта съдържа избрани пакети от минимална инсталация на Red Hat Linux заедно с "Linux kernel module".

    # cd /usr/ports/emulators/linux_base
    # make install distclean

Сега би трябвало да имате работеща бинарна съвместимост с Linux . Някои програми обаче могат да "протестират" че откриват неподходящи версии на някои библиотеки, но по принцип това не е проблем .

2.1.2.2 Ръчно Инсталиране

Ако нямате инсталиран Ports Collection , тогава можете да ги инстлирате ръчно . Ще се нуждаете от Linux споделени библиотеки от които зависят приложенията , както и от runtime linker (програма която свързва динамично изпълнимия файл с библиотеките от които той зависи ) . Също така ще трябва да се създаде "shadow root" директория (скрита коренна директория ) , /compat/linux , за Linux библиотеките на вашата FreeBSD система. Всяка споделена библиотека необходима на Linux приложенията работещи под FreeBSD ще бъдат потърсени първо в тази директория. Така ,че ако Linux приложението трябва да зареди библиотеката , /lib/libc.so , FreeBSD ще се опита да зареди /compat/linux/lib/libc.so ,
и ако не съществува тогава ще опита /lib/libc.so . Споделените библиотеки трябва да бъдат инсталирани в /compat/linux/lib и по-точно в пътищата които ще ги търси Linux ld.so . Ако липсват някои споделени библиотеки необходими на Linux програмите които ще стартирате на вашата FreeBSD система, то те могат да бъдат добавени .

2.1.2.3 Инсталиране на допълнителни споделени библиотеки

Да приемем че имаме инсталиран linux_base порта и ващето Linux приложение стартирано под FreeBSD все още се "оплаква" че му липсват споделени библиотеки. Как да разберем обаче от кои точно се нуждае то и как да му ги осигурим ?

Ако имате достъп до Linux система , вижте кои библиотеки са необходими и директно ги изкопирайте от там. Например, да кажем ,че сме изтеглили бинарната Linux версия на Doom, и сме я сложили на Linux система до която имаме достъп. Проверяваме с ldd на кои споделени библиотеки разчита това приложение:

    % ldd linuxdoom
    libXt.so.3 (DLL Jump 3.1) => /usr/X11/lib/libXt.so.3.1.0
    libX11.so.3 (DLL Jump 3.1) => /usr/X11/lib/libX11.so.3.1.0
    libc.so.4 (DLL Jump 4.5pl26) => /lib/libc.so.4.6.29

Ще трябва да вземете всички файлове от последната колона и да ги поставите в /compat/linux , с имената от първата колона като символични връзки сочещи към тях . На FreeBSD система би изглеждало така:

    /compat/linux/usr/X11/lib/libXt.so.3.1.0
/compat/linux/usr/X11/lib/libXt.so.3 -> libXt.so.3.1.0
/compat/linux/usr/X11/lib/libX11.so.3.1.0
/compat/linux/usr/X11/lib/libX11.so.3 -> libX11.so.3.1.0
/compat/linux/lib/libc.so.4.6.29 /compat/linux/lib/libc.so.4 -> libc.so.4.6.29

Забележка: Ако вече имате Linux споделената библиотека със съвпадащ главен номер в първата колона от ldd output-а , тогава няма да е необходимо да се копират на вашата система файловете от последната колона , тази който имате би трябвало да работи. Можете да премахнете старата библиотека, след като вече имате симболична връзка
сочеща към новата. Примерно ако имате :

    /compat/linux/lib/libc.so.4.6.27
/compat/linux/lib/libc.so.4 -> libc.so.4.6.27

и откриете с ldd , че приложението изисква последната версия на споделената библиотека :

    libc.so.4 (DLL Jump 4.5pl26) -> libc.so.4.6.29

все пак ако имате библиотека с една или две версии по-стара (последната цифра) , то тогава може и да не копирате новата /lib/libc.so.4.6.29 , програмата би трябвало да работи и с малко по-старата версия. Но все пак може да предпочетете и новата версия, което би изглеждало така :

    /compat/linux/lib/libc.so.4.6.29
/compat/linux/lib/libc.so.4 -> libc.so.4.6.29

Забележка: Механизма на символичните връзки е необходим само на Linux приложението. FreeBSD runtime linker-а се грижи относно съвпадането на главния номер на версията. Само за сведение: /compat/linux е връзка към /usr/compat.

2.1.3 Инсталиране на Linux ELF Binaries

Голяма част от Linux приложенията могат да се намерят в секциите "linux" и "emulators" на Ports collection (/usr/ports), и съответно да се изтеглят, компилират и инсталират, ако нямате инсталиран Ports collection на ващата система, тогава чрез /stand/sysinstall можете да изтеглите пакетите (prеcompiled packages) от някои FTP mirror и те автоматично ще бъдат инсталирани. А пък за разработката на такива погледнете linux-devtools и linuxthreads в секция "devel". ELF binaries понякога се нуждаят от маркиране или типизиране. При опит да се изпълни нетипизирано (unbranded) ELF binary, ще се изведе съобщение за грешка (ядрото незнае как да го третира):

    % ./my-linux-elf-binary
    ELF binary type not known
    Abort

С други думи казано, на ядрото на FreeBSD му се налага да направи разлика между различните типове ELF binary-та така ,че да процедира по съответния начин с тях . Ако Linux ELF binary-то не е "маркирано" (типизирано) тогава , трябва да се помогне на ядрото на FreeBSD , за да разбере че това наистина е Linux ELF binary, и трябва да се изпълнява като такова. Маркираме го с помоща на brandelf(1)
( маркира ELF binary за специфично ABI- Application Binary Interface ) :

    % brandelf -t Linux my-linux-elf-binary

За в бъдеще това все по-рядко ще се налага, понеже binary-тата идват маркирани .
Други примери за инстралиране и стартиране на Linux приложения под FreeBSD :
Mathematica , Oracle , SAP R/3 (4.6B - IDES)

2.2 Проблеми

2.2.1 DNS проблеми

Ако при използването на Linux binaries под FreeBSD се получава следното съобщение:

    resolv+: "bind" is an invalid keyword resolv+:
"hosts" is an invalid keyword

Проверете дали имате конфигуриран файла /compat/linux/etc/host.conf :

    order hosts, bind
multi on

Когато обаче няма /compat/linux/etc/host.conf , Linux приложенията намират /etc/host.conf на FreeBSD и се "оплакват" от несъвместимия FreeBSD синтаксис. Проверете също и /compat/linux/etc/nsswitch.conf , защото Linux приложенията ползващи версия 2 на glibc за реда на резолвинга ше търсят него. Най-общо казано при подобни проблеми със съвместимостта си представяме ,че /compat/linux/ е GNU/Linux система и се процедира аналогино все едно, че работим в момента на такава (е само няма linux kernel :).

3. Реализация

Накратко да проследим как стартират и се изпълнявани приложенията в системата.

Във FreeBSD има абстракция наречена "execution class loader'', реализирана в системното повикване execve(2) . Това означава, че FreeBSD ядрото прави разлика между няколко loaders (за приложения), това са обектните (или бинарни) файлови формати които се разпознават от системата и един специален "#!" loader. Ако някой процес "извика" execve(), ядрото на FreeBSD ще се опита да определи обектния му файлов формат по магическия номер (запис в началото на файла, обикновено първите 4 или 8 байта) за да провери дали това е разбираем за системата изпълним двoичен файл. Обектните файлови формати които FreeBSD може да зарежда (т.е. разполага със съответните loaders) и изпълнява са: a.out , elf, shell, gzip .

Идеята за gzip loader е изпълнимите файлове да бъдат компресирани (с алгоритъма на gzip), и когато бъде стартиран такъв файл ядрото на FreeBSD ще го декомпресира преди да го зареди в паметта и изпълни (нямам представа дали наистина това работи). Специалният loader "#!" е точно shell loader- а. Той позволява тези файлове които не са обектни все пак да бъдат изпълнявани. Идеята е, че по този начин може да се каже на ядрото кой обектен файл да зареди и изпълни стартирайки необектен файл имащ "#!" поредица в началото си. Примерно за shell скриптовете това е "#!/bin/sh". Всъщност всичко зависи от посочения път защото ядрото в случая ще изпълни /bin/sh и ще добави истинското име на изпълнимия файл в командния ред на "интепретатора" ( в случая /bin/sh ). Практически може да се укаже всичко, дори би трябвало да може да се изпълняват MIPS асемблерски файлове в SPIM симулатора просто като се добави "#!spim на първия ред на вашия асемблерски файл. Форматът a.out все по-рядко се среща. Файлове в този формат могат да бъдат изпълнявани на FreeBSD система, но формата е почти отхвърлен, така, че го забравяме:). Наследи го elf (статията няма за цел да обяснява разликата между двата формата, за детайлно описание на a.out и elf форматите вижте съответните man страници). Така, да започнем интересната част, а това е когато ядрото на FreeBSD установи, че си има работа с някой elf (горски духове, самодиви и други *диви :). Сценариите са два ( с разклонения поради това,че имаме няколко типа elf ):

  1. elf файла е свързан динамично - в този случай името на runtime-loader-a (или dynamic linker-a) е закодирано в самия файл. Loader-ите са: за FreeBSD е "/usr/libexec/ld-elf.so.1", за Linux - /lib/ld-linux.so.2 Така, че ядрото на FreeBSD се ориентира по този запис за да определи типа elf и да приложи съответния elf loader (съответното ABI).
  2. elf файла е свързан статично - в този случай ядрото на FreeBSD разчита на информацията за типа elf записана в специален сектор от файла (man brandelf(1) ). Ако файла не е маркиран, няма как да се определи типа му и съответно какво ABI ползва, за това ще трябва да го маркираме ръчно с brandelf(1)
    		# brandelf -t Linux file

Понастоящем във FreeBSD има реализация на няколко типа ABI ( Application Binary Interface ): FreeBSD ABI(естествено:), Linux ABI и SRV4 ABI (Linux ABI се поддържа от FreeBSD 2.2 и нагоре, а от FreeBSD 4.1 е включена подръжка и на SRV4 ABI), за справка man brandelf(1) . Значи съществува възможността същите тези компилирани на дадена система приложения да се изпълняват и на други системи разполагащи с необходимото ABI. Но да проследим реализацията на Linux ABI във FreeBSD. Тя се базира на код към ядрото на FreeBSD наречен "Linux kernel module"и неговата задача е да реализира Linux syscall interface за Linux приложенията използвайки FreeBSD syscalls (системни повиквания или internals). Например ако някое Linux приложение генерира системното повикване fork(), то ще бъде прихванато и обработено от "Linux kernel module"и ще му се осигури Linux-съвместим fork() чрез FreeBSD syscalls. Та значи изпълнимите elf файлове са "белязани", и това се използва при създаването на процеса с цел системните повиквания които той ще генерира да бъдат насочени към "правилния обработчик". На практика това означава, че Linux binary ще създаде процес който ще използва "Linux kernel module" за обработката на системните повиквания, a FreeBSD binary ще създаде процес който ползва директно FreeBSD системни повиквания. Освен това "Linux kernel module"при стартирането на Linux binaries прави и една магия с пътя преиндексирайки го с /compat/linux, поради това, че proc файловите системи на Linux и FreeBSD са различни, а в случая това приложение се нуждае от Linux-съвместима такава. Монтирането на Linux-compatible proc в /compat/linux удовлетворява това изискване (man linprocfs ). Първо, се прави опит да се открие файла в директорията /compat/linux/original_path , ако няма успех тогава се търси в /original_path . Например ако бъде генерирано системното повикване open() от някое Linux binary, това означава, че файла ще се търси първо в йерархията /compat/linux/original_path и после в /original_path . Така се подсигурява, че могат да работят успешно binaries които изискват други binaries и също така означава, че Linux binaries могат да зареждат и изпълняват FreeBSD binaries, ако няма налични под същото име Linux binaries. Можем да поставим uname(1) в /compat/linux за да се предотврати "оплакването" на някои Linux binaries, че системата "не изглежда да е" Linux. По подобен начин се процедира и с реализацията на SRV4 ABI във FreeBSD. Технически погледнато е възможно реализирането на което и да е ABI. В случая с FreeBSD (а пък и който и да е друг UNIX вариант) е най-лесно да се имплементира поддръжка на ABI на ниво ядро на сродните UNIX OS ( Linux и FreeBSD са доста близки по отношение на функциите осигурявани от ядрото). Вероятно е възможна реализацията на ниво ядро и на Win32 или MacOS ABI ( MacOS X се брои за UNIX ), обаче хич няма да е лесно поради дълбоките различия. Но пък за подкарването на Win32 приложения можете да използвате WINE. Той работи под Linux и под FreeBSD , а освен това се разпространява и във вид на сорс код и просто може да бъде компилиран на дадената система. WINE обаче работи в т.н. user-space (не бихте искали WINE да бъде реализиран като модул към ядрото, нали ;^). WmWare се разпространява само в бинарен формат за Linux, обаче може да стартира и под FreeBSD благодарение на поддръжката на Linux ABI. WmWare също работи в user-space. С него може да се подкарват (инсталират) цяла Операционна система , а не само нейните приложения както е при WINE.

Кратък FAQ

Кое е истинското ABI на FreeBSD? Ами така да се каже няма значение ... Като цяло, единствената разлика е, че FreeBSD ABI е compiled-in в ядрото (няма как иначе:), а поддръжката на Linux ABI може да бъде compiled-in или достъпнa през модул на FreeBSD ядрото (всичко работи се в kernel-space)

Дали това наистина е емулация ? Получава се малко игра на думи, но технически правилния отговор е "реализация на ABI" и то на ниво ядро (т.е. интерфейса между приложението и ОС).

Тогава защо понякога се нарича "Linux емулация''...? Защото исторически погледнато това е било реализирано по време когато не е имало дума различна от тази за да се опише какво наистина става. Казвайки,че FreeBSD изпълнява Linux binaries няма да е истина , ако не е компилиран съответния код в ядрото или не е зареден като модул, и за това е имало нужда от дума която да описва какво ще бъде зареждано - и от тук идва "Linux емулация" .

Други ресурси:

System V Application Binary Interface
SCO Developer Specifications
ABI specification for UNIX SVR4 on the Intel architecture
OpenGL® Application Binary Interface for Linux
Intel(R) Itanium(tm) Processor-specific Application Binary Interface (ABI)
Linux+FreeBSD mini HOWTO - The Penguin and the FBSD daemon play ice-hockey on one machine

Превод (с доста промени:) на posting в един FreeBSD maillist, и глава 22 на FreeBSD Handbook.Последната част на тази глава се базира на този posting, но е доста "набързо" стъкмена (нe, че не е вярна:) за това се постарах да обобщя нещата доколкото е възможно.