dlsym()
függvény
miért nem működik már az ELF
állományokra?Jelen pillanatban csak egyetlen mű foglalkozik az operációs rendszerek felépítésével a FreeBSD szemszögéből, név szerint a Marshall Kirk McKusick és George V. Neville-Neil által írt “The Design and Implementation of the FreeBSD Operating System” című könyv (ISBN 0-201-70245-2), amely a FreeBSD 5.X változatára koncentrál.
Emellett a UNIX® típusú rendszerek használatával kapcsolatos ismeret remekül alkalmazható a FreeBSD esetén is.
A témához tartozó többi könyvet a kézikönyv Az operációs rendszerek belső működésével foglalkozó irodalomjegyzékben találhatjuk meg.
Pontosabb tanácsokat akkor kapunk, ha elolvassuk a FreeBSD fejlesztéséről szóló cikket. Nagyon is számítunk mindenki segítségére!
Jelenleg három aktív és félig aktív ág van a FreeBSD CVS repositoryjában. (A korábbi ágakat már csak nagyon ritkán módosítják, ezért is csak három aktív fejlesztési ágon fejlesztenek):
RELENG_7 avagy 7-STABLE
RELENG_8 avagy 8-STABLE
HEAD avagy -CURRENT avagy 9-CURRENT
A HEAD nem olyan ág, mint a másik kettő. Ez egyszerűen csak “a jelenlegi, még el nem ágaztatott fejlesztési irány” jelentéssel bír, amire pedig sokszor röviden csak -CURRENT néven hivatkoznak.
Jelen pillanatban a -CURRENT a
9.X fejlesztési
irányát képviseli; az
6-STABLE ág, a
RELENG_6
, 2005 novemberében, a
7-STABLE ág, a
RELENG_7
, 2008 februárjában,
míg a 8-STABLE ág, a
RELENG_8
, 2009 novemberében
vált le a “-CURRENT”
ágból.
Olvassuk el a kiadások készítéséről szóló cikket.
Mert alapvetően ez lenne a cél: ahogy a neve is sugallja, a rendszer újrafordítása, vagyis a make world parancs feladata a rendszerben található összes bináris újrafordítása, aminek eredményeképpen egy tiszta és összefüggő környezetet kapunk (ezért is tart ilyen sokáig).
Ha a make world vagy a make install parancs futtatása előtt megadjuk a DESTDIR környezeti változót, akkor a frissen létrehozott binárisok az általa mutatott könyvtárba fognak kerülni pontosan úgy, ahogy az eredeti rendszer. Az osztott könyvtárak bizonyos módosításai és egyes programok fordítása azonban könnyen térdre kényszerítheti a make world futását.
18.6. Miért nem forgó (“round robin”) névfeloldással lehet elérni a CVSup szervereket és így megosztani köztük a terhelést?
Habár a CVSup tükrözések óránként frissítik magukat a központi CVSup szerverről, maga a frissítés azonban bármikor megtörténhet. Ennek következményeképpen egyes szervereken frissebb kód található, miközben a többin még az egy órával ezelőtti állapot szerepel. Ha a cvsup.FreeBSD.org forgó névfeloldással működne, akkor a felhasználók mindig egy véletlenszerűen választott CVSup szervert kapnának, és ezért a CVSup egymás utáni futtatásakor könnyen előfordulhatna, hogy a rendszer régebbi forrásait kapjuk vissza.
Igen, ezt a CTM használatával anélkül is megtudjuk tenni, hogy le kellene töltenünk az egész forrásfát.
Az újabb BSD alapú rendszerekben a
split(1) parancsnak már van egy
-b
paramétere, amellyel
tetszőleges méretűre fel tudunk darabolni
állományokat.
Íme erre egy példa a /usr/src/release/Makefile állományból:
ZIPNSPLIT= gzip --no-name -9 -c | split -b 1392k -
Erre vonatkozóan vessünk egy pillantást a FreeBSD továbbfejlesztéséről szóló cikkre.
Köszönjük, hogy gondolt ránk!
Frank Durda IV
(<[email protected]>
)
válasza:
Dióhéjban úgy tudnám ezt elmagyarázni, hogy van néhány I/O port, amelyet lekérdezve a PnP kártya képes válaszolni, hogy elérhető-e. Ezért a PnP eszközök keresése azzal kezdődik, hogy a rendszer felteszi a kérdést, van-e PnP kártya a számítógépben. Erre aztán a különböző kártyák a típusuk megjelölésével válaszolnak, amelyet ugyanezen az I/O porton kell visszaolvasni, így ha már legalább egy bitet beállít valaki, akkor folytatható a keresés. Ezután a keresést végző kódrész letiltja az X alatti (a Microsoft® és az Intel® által kiosztott) azonosítóval rendelkező kártyákat, majd ismét megnézi, hogy valaki továbbra is válaszol-e. Amennyiben a válasz 0, az arra utal, hogy már nincs aktív kártya az X azonosító felett. Ezt követően a rendszer megpróbálkozik az X alatti azonosítók lekérdezésével. Végül folytatja az X alatti keresést az X -(korlát / 4) feletti azonosítók letiltásával, majd megismétli az iménti kérdést. Ezzel a félig-meddig bináris keresési módszerrel aztán képes 264 lépésnél jóval kevesebből felderíteni a rendszerünkben megtalálható PnP kártyákat.
Az azonosítók két 32 bit hosszúságú mezőből (ezért írtunk az előbb 264 lépést) és egy 8 bites ellenőrzőösszegből állnak. Az első 32 bit a gyártót azonosítja. Ugyan soha nem vallják be, de úgy tűnik, hogy még ugyanannak a gyártónak is lehetnek eltérő gyártóazonosítóval rendelkező kártyái. A gyártók számára fenntartott 32 bites mező ezért valamennyire túlzás.
A második 32 bit lehet a kártya sorozatszáma vagy bárki más, amely alapján egyértelműen beazonosítható. A gyártó ugyanazzal a 32 bites értékkel nem gyárthat egy másik kártyát, csak abban az esetben, ha a másik 32 bit is eltér. Ennek köszönhetően egy gépen belül még az azonos típusú kártyák is el fognak térni 64 biten.
Az iménti 32 bites csoportok nem lehetnek teljesen nullák, ezért lehetséges, hogy a bináris keresés során a válaszban legalább egy bit mindig aktív lesz.
Miután a rendszer sikeresen beazonosította a rendelkezésre álló kártyákat, egyenként újra elindítja ezeket (ugyanazon az I/O porton keresztül), és megpróbálja kitalálni, hogy az adott eszközöknek milyen erőforrásokra van szüksége, milyen megszakítást akarnak használni stb. Az összes kártyától lekérdezi ezeket az információkat.
Az így megszerzett információkat aztán még kiegészíti a merevlemezen vagy az MLB BIOS-ban található ECU állományok tartalmával. Az ECU és az MLB BIOS PnP támogatása általában viszont nem valódi, és az ilyen eszközök igazából nem is állítanak be semmit maguktól. A BIOS és az ECU átvizsgálása azonban segít a felderítést végző rutinnak értesíteni a tényleges PnP eszközöket, hogy ne foglaljanak el olyan erőforrásokat, amelyeket a rendszer nem tud áthelyezni.
Ezután a PnP eszközöket a kód még egyszer végigjárja és átadja nekik a működésükhöz szükséges I/O, DMA, IRQ és memóracímek hozzárendeléseit. Az eszközök ekkor a megadott helyeken elérhetővé válnak és úgy is maradnak a rendszer következő indításáig, de igazából semmi sem rögzíti ezeket.
Talán túlságosan is egyszerűsítettem a fentieket, de szerintem már ennyi is elegendő az alapok megértéséhez.
A Microsoft néhány elsődleges nyomtatási állapotot jelző portot átrakott PnP-re, azzal a címszóval, hogy egyik kártya sem kódolta át ezeket a címeket az ellenkező I/O ciklusok számára. Találtam is egy eredeti IBM nyomtatókártyát, amely valóban át tudta írni az állapotjelző portot a PnP kezdeti változataiban, de arra a Microsoft csak annyit mondott, hogy “fogós”. Ezért a nyomtatási állapotot jelző portot a címek beállítására használja, illetve még a 0x800-as portot és egy harmadik I/O portot valahol a 0x200 és a 0x3ff környékén.
2003 februárja óta a FreeBSD képes dinamikusan és önműködően futás közben lefoglalni főeszközazonosítókat a meghajtóknak (lásd devfs(5)), ezért erre tulajdonképpen már nincs szükség.
A könyvtárak más fajta kiosztására vonatkozóan annyit tudok válaszolni, hogy a jelenleg is alkalmazott sémát az 1983-ban megalkotott változata óta változatlanul használjuk. Eredetileg a gyors állományrendszerhez készítettem, de soha nem ragaszkodtam hozzá. Remekül megoldja a cilindercsoportok betelésének problémáját, azonban sokan megjegyezték már, hogy a find(1) esetén gyengén működik. A legtöbb állományrendszert mélységi bejárással hozzák létre, így a könyvtárak szétszóródnak a cilindercsoportok közt és ezzel a későbbi mélységi keresések számára a lehető legrosszabb helyzetet alakítják ki. Ha valaki például tudja előre a létrehozni kívánt könyvtárak számát, akkor ezt úgy lehet megoldani, ha a művelet során (összes / cilindercsoportok) mennyiségű könyvtárat hozunk létre az egyes cilindercsoportokban. Ennek meghatározására nyilvánvalóan lehet adni valamilyen heurisztikát. Már egy kisebb előre rögzített szám, mint például a 10 kiválasztása is legalább egy nagyságrendnyi javulást jelent. Ha szeretnénk megkülönböztetni az állományrendszerek visszaállítását a hagyományos működéstől (amire a jelenlegi algoritmus sokkal érzékenyebb), akkor érdemes tizes csoportokba összefogni a könyvtárakat, feltéve, hogy 10 másodpercen belül hoztuk létre ezeket. Mindenesetre elmondható, hogy ezzel nyugodtan lehet kísérletezni.
Kirk McKusick <[email protected]>
, 1998 szeptembere
Általában így néz ki a rendszermag összeomlása:
Fatal trap 12: page fault while in kernel mode fault virtual address = 0x40 fault code = supervisor read, page not present instruction pointer = 0x8:0xf014a7e5 stack pointer = 0x10:0xf4ed6f24 frame pointer = 0x10:0xf4ed6f28 code segment = base 0x0, limit 0xfffff, type 0x1b = DPL 0, pres 1, def32 1, gran 1 processor eflags = interrupt enabled, resume, IOPL = 0 current process = 80 (mount) interrupt mask = trap number = 12 panic: page fault
Amikor egy ilyen üzenetet látunk, akkor nem elegendő újra előcsalni a hibát és beküldeni. Az utasításszámláló (“instruction pointer”) értéke ugyan nagyon fontos, de sajnos konfigurációk szerint eltérhet. Más szóval úgy fogalmazhatnék, hogy ennek az értéke a használatban levő rendszermag értékétől függően változhat. Ha a GENERIC rendszermagot használjuk valamelyik kiadásból, akkor viszont már elképzelhető, hogy valaki más is le tudja nyomozni a hibát okozó függvényt. Ha viszont egy saját beállításokkal rendelkező rendszermagot használunk, akkor egyedül csak mi vagyunk képesek megmondani a hiba pontos helyét.
Ezért a javaslatom a következő:
Jegyezzük le az utasításszámláló értékét. A 0x8: rész ebben az esetben annyira nem fontos, egyedül csak a 0xf0xxxxxx részre van szükségünk.
A rendszer újraindításakor írjuk be a következőt:
% nm -n
/a.hibát.okozó.rendszermag | grep f0xxxxxx
ahol az f0xxxxxx az utasításszámláló értéke. Könnyen előfordulhat, hogy ilyenkor még nem találunk egyezést, mivel a rendszermag szimbólumtáblájában csak az egyes függvények belépési pontjai találhatóak, és ha az utasításszámláló általában valamelyikük belsejébe mutat, nem az elejükre. Ha tehát nem még látunk semmit, akkor egyszerűen hagyjuk el az utolsó számjegyet és próbálkozzunk így:
% nm -n
/a.hibát.okozó.rendszermag | grep f0xxxxx
Ha még ez sem hoz eredményt, akkor vágjunk le a végéről egy újabb számjegyet. Egészen addig csináljuk, amíg nem kapunk valami értékelhető eredményt. Ilyennek tekintjük például azokat a függvényeket, amelyek a hibát okozhatták. Ez ugyan egy nem annyira pontos felderítési eszköz, viszont még ez is jobb a semminél.
A legjobb viszont mégis az, amikor sikerül lementeni a hiba bekövetkezésekor a memória tartalmát, majd a kgdb(1) használatával előbányászni belőle egy hívási láncot.
Ehhez többnyire a következő módszer javasolt:
A rendszermag konfigurációs állományába (/usr/src/sys/arch/conf/RENDSZERMAGKONFIG) vegyük fel a következő sort:
makeoptions DEBUG=-g # A rendszermag fordítása gdb(1) szimbólumokkal
Lépjünk be a /usr/src könyvtárba:
# cd /usr/src
Fordítsuk le a rendszermagot:
# make buildkernel KERNCONF=RENDSZERMAGKONFIG
Várjuk meg, amíg a make(1) befejezi a fordítást.
# make installkernel KERNCONF=RENDSZERMAGKONFIG
Indítsuk újra a gépet.
Megjegyzés: A KERNCONF használata nélkül a GENERIC rendszermag fordul és telepítődik.
A make(1) programnak a folyamat végeredményeként két rendszermagot kell készítenie: a /usr/obj/usr/src/sys/RENDSZERMAGKONFIG/kernel és a /usr/obj/usr/src/sys/RENDSZERMAGKONFIG/kernel.debug. Ezek közül a kernel /boot/kernel/kernel néven mentődik el, miközben a kernel.debug használható nyomonkövetésre a kgdb(1) programmal.
A rendszer csak akkor fogja elmenteni
összeomláskor a memória tartalmát,
ha az /etc/rc.conf
állományban beállítjuk a
dumpdev
értékét a
lapozóállományt tároló
partícióra (vagy az AUTO
értékre). Ennek hatására az
rc(8) szkriptek a dumpon(8) paranccsal
képesek engedélyezni a memória
lementését. A dumpon(8)
természetesen manuálisan is
elindítható. Az összeomlást
követően a memória lementett
tartalmához a savecore(8) programmal
férhetünk hozzá. Amikor viszont az
/etc/rc.conf állományban
megadjuk a dumpdev
értékét, az rc(8) szkriptek
maguktól lefuttatják a savecore(8)
parancsot és átrakják a mentést
a /var/crash
könyvtárba.
Megjegyzés: A FreeBSD által létrehozott memóriamentések mérete általában a számítógépünkben levő fizikai memória mennyiségével egyezik meg. Tehát ha 512 MB RAM van a gépünkben, akkor egy 512 MB méretű mentést fogunk kapni. Ezért gondoskodjunk róla, hogy a /var/crash könyvtárban mindig legyen elegendő hely az állomány tárolásához. A savecore(8) kézzel is lefuttathazó, és ilyenkor a memóriát akár egy másik könyvtárba is menthetjük. A mentés méretét options MAXMEM=N beállítással is korlátozhatjuk, ahol az N értéke a rendszermag által használható memória mérete KB-okban. Például, ha 1 GB RAM van a gépünkben, de a rendszermag által használható memóriát lekorlátozzuk 128 MB-ra, akkor a mentés mérete sem 1 GB lesz, hanem csak 128 MB.
Ahogy sikerült hozzájutnunk a memóriamentéshez, azonnal is kérhetünk a kgdb(1) használatával egy hívási láncot belőle:
% kgdb /usr/obj/usr/sys/RENDSZERMAGKONFIG/kernel.debug /var/crash/vmcore.0 (kgdb) backtrace
Előfordulhat, hogy ilyenkor több oldalnyi információ özönlik hirtelen a képernyőre, ezért javasolt ezeket lementeni a script(1) programmal. A nyomkövetési szimbólumokat is tartalmazó rendszermag esetén még akár azt a sort is megkapjuk a rendszermagon belül, ahol a hiba történt. A hívási láncot általában alulról felfelé kell olvasni, és ebből deríthető, hogy pontosan milyen események is vezettek az összeomláshoz. A kgdb(1) használatával még a különböző változók és struktúrák értékeit is meg tudjuk vizsgálni, így még többet megtudhatunk a rendszer állapotáról az összeomlás pillanatában.
Tipp: Ha az iméntiek mentén nagyon fellelkesültünk volna és van egy másik számítógépünk is, akkor a kgdb(1) akár távoli nyomkövetésre is beállítható, aminek köszönhetően a kgdb(1) használatával az egyik rendszeren meg tudjuk állítani a másikon futó rendszermagot, ellenőrizhetjük a viselkedését, akárcsak bármelyik más felhasználói program esetében.
Ha netalán engedélyeztük volna a DDB beállítást, és a rendszermag beleáll a nyomkövetőbe, akkor a rendszert mi magunk is össze tudjuk omlasztani (és így a memóriát elmenteni) a ddb parancssorában a panic parancs kiadásával. Ilyenkor a nyomkövető általában még egyszer megáll az összeomláskor. Ekkor a continue paranccsal fejeztethetjük be a memória lementését.
Az ELF állományokhoz tartozó
segédprogramok alapértelmezés szerint nem
teszik láthatóvá a dinamikus linker
számára a végrehajtható
állományban definiált
szimbólumokat. Ennek eredményeképpen a
dlsym()
a dlopen(NULL,
flags)
függvénytől kapott
információk alapján nem találja
meg a keresett szimbólumokat.
Ha szükségünk lenne ilyen
keresésekre a dlsym()
használata során a program
végrehajtható állományán
belül, akkor az adott programot a
--export-dynamic
opció
megadásával kell linkelni (lásd
ld(1)).
Az i386 platformon a rendszermag címtere alapértelmezés szerint 1 GB (PAE esetén 2 GB). Ha komolyabb hálózati forgalmat bonyolító szerverünk van (például egy nagyobb FTP vagy HTTP szerver) vagy rendszerükön használni akarjuk a ZFS állományrendszert, akkor könnyen kifuthatunk a címtérből.
A címtér méretének megváltoztatásához vegyük fel a következő sort a rendszermag konfigurációs állományába, majd fordítsuk újra a rendszermagot:
options KVA_PAGES=N
Az N megfelelő értékének megállapításához osszuk el a beállítani kívánt címtér (MB-okban megadott) méretét néggyel. (Tehát például 2 GB esetén ez 512 lesz.)
Ha kérdése van a FreeBSD-vel kapcsolatban, a következő
címre írhat (angolul): <[email protected]>.
Ha ezzel a dokumentummal kapcsolatban van kérdése,
kérjük erre a címre írjon: <[email protected]>.