Ha kíváncsiak vagyunk a Linux
emuláció működésére,
olvassuk el ezt a szakaszt. Az itt ismertettek leginkább
Terry Lambert (<tlambert@primenet.com>
) FreeBSD chat levelezési lista
címére írt levele nyomán kerülnek
bemutatásra (Az üzenet azonosítója:
<199906020108.SAA07001@usr09.primenet.com>).
A FreeBSD rendelkezik egy ún. “végrehajtási osztály betöltővel” (execution class loader). Ez lényegében a execve(2) rendszerhívás alatt meghúzódó absztrakciós réteg.
A FreeBSD-nek a #! karaktersorozat hatására parancsértelmezők vagy a hozzájuk tartozó szkriptek betöltésére utasító biztonsági betöltő helyett van egy listája az alkalmas betöltőkről.
A UNIX® rendszerek a hagyományok szerint egyetlen betöltővel rendelkeznek, ami először megvizsgálja a betölteni kívánt állomány bűvös számát (ami általában az első 4 vagy 8 byte) és ez alapján eldönti, hogy az adott formátum támogatott-e. Amennyiben ez így van, meghívja a betöltőt.
Ha a bináris típusa nem ismert a rendszer számára, akkor az execve(2) hívás hibával tér vissza, és a parancsértelmező próbálja meg a saját parancsaiként értelmezni.
Eddig ez volt az alapértelmezés, “akármilyen parancsértelmezőnk is volt”.
Később az sh(1) kódjába bekerült egy aprócska okosítás, amivel megnézte az állomány első két karakterét, és ha az :\n volt, akkor a futtatáshoz maga helyett a csh(1) parancsértelmezőt hívta meg (ezt állítólag először a SCO csinálta).
A FreeBSD viszont végignézi a betöltők teljes listáját, amiben a sor végén szerepel egy általános #! formátumú betöltő. Ez az állomány futtatásához használatos értelmezők kódját keresi, és ha egyet sem sikerül azonosítania, akkor a /bin/sh programot indítja el.
A Linux ABI támogatását a FreeBSD úgy oldja meg, hogy először észleli az ELF bináris bűvös számát (ekkor még nem tesz különbséget a FreeBSD, Solaris™, Linux vagy más ELF típusú binárisokat használó operációs rendszerek közt).
Ezután az ELF formátum betöltője az ELF állomány megjegyzéseket tároló szakaszában bélyegek (brand) után kutat, ami SVR4 és Solaris ELF binárisok esetén nem létezik.
A Linux binárisokat működésükhöz a brandelf(1) segítségével Linux típusúnak kell megbélyegezni:
# brandelf -t Linux állomány
Miután ezt megcsináltuk, az ELF betöltő észre fogja venni az állomány Linux típusát.
Mikor az ELF betöltő észleli, hogy az állomány Linux típusú, kicseréli egy mutató értékét a proc struktúrában. Minden rendszerhívás ezen a mutatón keresztül érhető el (a hagyományos UNIX rendszerekben ez a rendszerhívásokat tartalmazó sysent[] struktúratömb). Emellett a frissen elindított program szoftveres megszakításait tartalmazó tömbjéhez beállítja a speciális jelzések kezelését, valamint a Linux modul által végzett néhány további (kisebb) javítást.
A Linux rendszerhívásokat tartalmazó tömb többek közt tartalmazza a sysent[] bejegyzések egy listáját, amelyek címei a rendszermag Linux moduljára mutatnak.
Amikor a Linux bináris hív egy rendszerhívást, a hozzátartozó szoftveres megszakítás kódja a proc struktúrából a neki megfelelő rendszerhívás kódját hivatkozza, így FreeBSD rendszerhívás belépési pontja helyett a Linuxét kapja meg.
Ráadásul Linux módban a
különböző állományok
hivatkozásai is
átirányítódnak.
Ez lényegében olyan, mint amit az
állományrendszerek
csatlakoztatásánál a union
beállítás csinál (ami
nem egyezik meg az
unionfs állományrendszerrel!).
Ilyenkor az állományokat először a
/compat/linux/eredeti-hely
könyvtárában keresi, és
majd ha ott nem találja, csak akkor
kezdi el keresni az
/eredeti-hely
ponton. Ezzel oldhatjuk meg, hogy más binárisok
futtatását igénylő binárisok is
képesek legyenek rendesen működni
(például így az egész linuxos
eszköztár tud futni a Linux ABI-n keresztül).
Egyúttal arra is utal, hogy ha a Linux binárisok
számára nem áll rendelkezésre a
megfelelő bináris, akkor FreeBSD binárisokat is
el tudnak indítani. Ha a uname(1) programot pedig
bemásoljuk a /compat/linux
könyvtáron belülre, akkor a Linux
binárisok képtelenek lesznek megmondani, hogy nem
Linux alatt futnak.
Így lényegében egy Linux magot találunk a FreeBSD rendszermagjában. A benne megtalálható különböző szolgáltatásokat megvalósító függvények: az állományműveletek, a virtuális memória kezelése, a jelzések küldése és System V típusú folyamatok közti kommunikáció stb. megegyeznek a FreeBSD és a Linux hívásai esetén egyaránt. Egyetlen eltérés, hogy a FreeBSD binárisok a FreeBSD segédfüggvényein (glue function), a Linux binárisok pedig a Linux segédfüggvényein keresztül férnek hozzájuk (a legelső operációs rendszerek tulajdonképpen csak a saját segédfüggvényeiket tartalmazták: a hívást kezdeményező program proc struktúrájában a függvények dinamikusan beállított címe helyett egy globális sysent[] struktúratömbben tárolták a meghívható függvényeket).
Melyik közülük a FreeBSD natív ABI-ja? Ez teljesen lényegtelen. Alapvetően az egyetlen különbség csupán annyi (pillanatnyilag, de ez a jövőben még változhat, valószínűleg hamarosan), hogy a FreeBSD segédfüggvényei statikusan megtalálhatóak a rendszermagban, míg a Linux segédfüggvényei egyaránt elérhetőek modulból vagy statikus linkeléssel.
Na igen, de akkor ez most emuláció? Nem. Ez egy ABI, nem emuláció. Itt szó sincs emulátorról (ahogy szimulátorról sincs).
De akkor mégis miért hívják ezt sokszor “Linux emulációnak”? Hát hogy nehezebb legyen eladni a FreeBSD-t! Komolyra fordítva a szót: ennek a kezdeti változata akkoriban született meg, amikor erre még nem volt rendes szó. Nem mondhattuk, hogy a FreeBSD befordítás vagy egy modul betöltése nélkül képes lett volna Linux binárisokat futtatni, ezért valamilyen módon meg kellett neveznünk az ilyenkor betöltött kódot -- ebből lett “a Linux emulátor”.
Ha kérdése van a FreeBSD-vel kapcsolatban, a következő
címre írhat (angolul): <freebsd-questions@FreeBSD.org>.
Ha ezzel a dokumentummal kapcsolatban van kérdése,
kérjük erre a címre írjon: <gabor@FreeBSD.org>.