Wstęp

Specyfika odpluskwiania jądra

  • Diagnostyka błędów w jądrze systemu jest trudna, ze względu na jego złożoność i wielowątkowość. Błędy mogą ujawniać się tylko przy określonym przeplocie fragmentów kodu, dlatego mogą być bardzo trudne do odtworzenia. Dodatkową przeszkodą bywa sytuacja w której system na skutek wystąpienia błędu zupełnie odmawia współpracy, uniemożliwiając zebranie i analizę wskazówek, które mogą nas naprowadzić na przyczynę awarii.
  • Ze względu na fakt, że jądro jest zestawem różnych funkcjonalności niepowiązanych z (nie będących) żadnym konkretnym procesem, ciężko jest go uruchomić pod debuggerem i śledzić jego wykonywanie. Jest to jednak do pewnego stopnia możliwe, w dalszej części prezentacji postaramy się dostarczyć informacje o służących do tego narzędziach.

Wsparcie dla odpluskwiania w jądrze

Sekcja "Kernel Hacking" konfiguratora jądra

Magic SysRq key - CONFIG_MAGIC_SYSRQ

Klawisz SysRq (Alt + PrintScreen) pozwala na utrzymanie kontroli nad systemem nawet wtedy, gdy się zawiesi i nie reaguje na żadne komendy (jednak nadal działa jądro). Kontrola ta polega na naciskaniu określonych klawiszy mając jednocześnie wciśnięty SysRq. Możemy w ten sposób:

  • r - przełączyć klawiaturę w tryb surowy. Przydatne np. przy kłopotach z X-serwerem, jeśli pozostawi klawiaturę w stanie bliżej nieokreślonym.
  • k - zabić wszystkie procesy na bieżącej konsoli. Jest to wywołanie sekwencji “secure attention key” - w skrócie SAK.
  • s - zsynchronizować wszystkie dyski, zapisać zawartości buforów dysków.
  • u - odmontować i zamontować z powrotem wszystkie dyski (w trybie read-only). Pozwala to oszczędzić sporo czasu na sprawdzaniu dysków przy późniejszym restarcie systemu
  • b - zrestartować system. Najlepiej po wcześniejszym wykonaniu ‘k’, ‘s’ i ‘u’ (w przeciwnym razie jest to opcja bardzo niebezpieczna).
  • p - wypisać na ekran zawartość wszystkich rejestrów procesora
  • t - wypisać aktualną listę wszystkich zadań
  • m - wypisać informację o pamięci
  • h - wyświetlić pomoc

Kernel Debugging - CONFIG_DEBUG_KERNEL

Udostępnia dalsze opcje, ale sama nic nie włącza.

Detect Soft Lockups - CONFIG_DETECT_SOFTLOCKUP

Włącza opcję wykrywania w jądrze zastojów - gdy system działa w pętli w trybie jądra więcej niż 10 sekund nie oddając procesora innym zadaniom. Po wykryciu blokady wypisywany jest stos wywołań i nic więcej się nie stanie - system pozostanie zablokowany. Pomijalny wpływ na szybkość działania jądra.

Collect Scheduler Statistics - CONFIG_SCHEDSTATS

Jeśli wyrazimy zgodę, do schedulera zostanie dopisany dodatkowy kod, co spowoduje, że będą zbierane informacje o jego zachowaniu. Zostaną one zapisane w /proc/schedstat. Raczej nie potrzebujemy tej opcji jeżeli nie debugujemy samego schedulera. Nieduży narzut czasowy przy pracy jądra.

Debug slab memory allocations - CONFIG_DEBUG_SLAB

Spowoduje, że jądro będzie wykonywało dodatkowe sprawdzenia przy alokacji pamięci - każdy bajt jest ustawiany na 0xa5 przed przekazaniem go procesowi, a przy zwalnianiu ustawiany jest na 0x6b. Takie “zatruwanie” (”poisoning”) pamięci pozwala łatwiej wykrywać błędy, np. podczas analizy informacji wygenerowanych przez oops. Dodatkowo każdy blok przydzielanej pamięci jest oznaczany na początku i na końcu kontrolnymi wartościami. Jeśli zmienią się one, jądro natychmiast reaguje na przekroczenie przydzielonego obszaru.

Znacznie spowalnia działanie jądra, jeśli często są wykonywane operacje przydzielania i zwalniania dużych ilości pamięci.

Mutex debugging, deadlock detection - CONFIG_DEBUG_MUTEXES

Wykrywanie i raportowanie prób wyłamania mutex’ów, oraz związanych z semaforami blokad.

Spinlock debugging - CONFIG_DEBUG_SPINLOCK

Wykrywanie prób użycia niezainicjalizowanych spinlocków, oraz innych często popełnianych błędów.

Sleep-inside-spinlock checking - CONFIG_DEBUG_SPINLOCK_SLEEP

Jądro zacznie zgłaszać uwagi, gdy proces będąc wewnątrz spinlocka wywoła funkcję, która potencjalnie może spowodować zaśnięcie.

Compile the kernel with debug info - CONFIG_DEBUG_INFO

Obraz jądra po kompilacji będzie zawierał informacje dla debuggera (w rezultacie obraz będzie znacznie większy). Opcja ta bardzo nas interesuje, gdyż zamierzamy uruchomić jądro pod debuggerem. Warto też włączyć opcję CONFIG_FRAME_POINTER.

Check for stack overflows - CONFIG_DEBUG_STACKOVERFLOW

Opcja ta spowoduje wypisywanie komunikatów jeżeli stos wywołań funkcji przekroczy określony limit.

Stack utilization instrumentation - CONFIG_DEBUG_STACK_USAGE

Udostępnia statystyki zużycia pamięci przez jądro. Do obejrzenia przy użyciu SysRq-T, SysRq-P.

printk()

Umożliwia debugowanie jądra standardową metodą - poprzez wypisywanie w dogodnych momentach informacji. Funkcja printk() różni się od printf() dwiema cechami:

  • jest dostępna w trybie jądra,
  • komunikatom można przyporządkowywać stopień ważności, tzw. loglevel. Priorytet ten określamy przy pomocy jednego z makr, zdefiniowanych w pliku nagłówkowym <linux/kernel.h>.

Jak używać

Przykład wywołania:

printk(KERN_DEBUG "Here I am: %s:%i\n", __FILE__, __LINE__);

Makra opisujące priorytet są rozwijane do stringa i doklejane do komunikatu - stąd brak przecinka między pierwszymi dwoma parametrami.

KERN_EMERG “<0>” /* system is unusable */
KERN_ALERT “<1>” /* action must be taken immediately */
KERN_CRIT “<2>” /* critical conditions */
KERN_ERR “<3>” /* error conditions */
KERN_WARNING “<4>” /* warning conditions */
KERN_NOTICE “<5>” /* normal but significant condition */
KERN_INFO “<6>” /* informational */
KERN_DEBUG “<7>” /* debug-level messages */

Jeżeli nie podamy parametru loglevel, priorytet zostanie ustawiony na DEFAULT_MESSAGE_LOGLEVEL, w jądrze 2.6.17 równe KERN_WARNING.

Jak działa printk()

Funkcja printk() zapisuje komunikaty w buforze cyklicznym o długości __LOG_BUF_LEN, definiowanej przy kompilacji jądra. Jest to wartość pomiędzy 4KB a 1MB. Oznacza to, że starsze wiadomości nieodczytane z bufora zostają nadpisane. Następnie budzi któryś z procesów oczekujących na wiadomości - czytających z pliku /proc/kmsg lub uśpionych po wywołaniu funkcji systemowej syslog. Takim procesem jest np. demon klogd opisany poniżej.

demony klogd, syslogd

  • klogd i syslogd zbierają komunikaty od systemu operacyjnego i zapisują je na dysku i/lub wypisują na konsolę, zależnie od konfiguracji.
  • Demon klogd odbiera komunikaty od jądra systemu, odczytując je z pliku /proc/kmsg (można to zmienić przekazując inną ścieżkę w parametrach - patrz man klogd). Następnie przekazuje je do syslogd, który zajmuje się przetwarzaniem wszystkich komunikatów w systemie. Pojawiają się one na konsoli, jeżeli mają priorytet mniejszy niż wartość zmiennej console_loglevel. Domyślny loglevel możemy zdefiniować wywołując klogd -c <wartosc miedzy 0 a 7>. Komunikaty od jądra zazwyczaj zapisywane są w pliku /var/log/messages.
  • Jeżeli demony klogd i syslogd nie są uruchomione, dane z /proc/kmsg możemy sami odczytać korzystając z komendy dmesg.
  • Plik konfiguracyjny dla syslogd: /etc/syslog.conf. Można w nim zdefiniować, gdzie będą przechowywane pliki dziennika dla konkretnych programów.

Poniżej przedstawiamy schemat przepływu komunikatów poprzez klogd i syslogd:

Schemat

oops

Co to jest Oops?

Oops wyzwalany jest, gdy w jądrze zaistnieje jakaś wyjątkowa sytuacja - np. próba odczytu spod nieprawidłowego adresu w pamięci. Jest to zrzut zawartości wszystkich rejestrów procesora oraz stosu jądra z momentu wystąpienia błędu. Wypisywany jest na konsolę oraz do cyklicznego bufora jądra i ma być pomocą dla programisty próbującego zdiagnozować przyczynę błędu. Drugą jego funkcją jest powstrzymanie jądra przed zupełną destabilizacją lub zniszczeniem danych - dlatego natychmiast są zabijane procesy oraz komponenty jądra odpowiedzialne za sytuację.

Przykładowy oops

Unable to handle kernel NULL pointer dereference at virtual address  00000014
*pde = 00000000
Oops: 0000
CPU:    0
EIP:    0010:[<c017d558>]
EFLAGS: 00210213
eax: 00000000   ebx: c6155c6c   ecx: 00000038   edx: 00000000
esi: c672f000   edi: c672f07c   ebp: 00000004   esp: c6155b0c
ds: 0018   es: 0018   ss: 0018
Process tar (pid: 2293, stackpage=c6155000)
Stack: c672f000 c672f07c 00000000 00000038 00000060 00000000 c6d7d2a0 c6c79018 
       00000001 c6155c6c 00000000 c6d7d2a0 c017eb4f c6155c6c 00000000 00000098 
       c017fc44 c672f000 00000084 00001020 00001000 c7129028 00000038 00000069 
Call Trace: [<c017eb4f>] [<c017fc44>] [<c0180115>] [<c018a1c8>] [<c017bb3a>] [<c018738f>] [<c0177a13>] 
       [<d0871044>] [<c0178274>] [<c0142e36>] [<c013c75f>] [<c013c7f8>] [<c0108f77>] [<c010002b>] 

Code: 8b 40 14 ff d0 89 c2 8b 06 83 c4 10 01 c2 89 16 8b 83 8c 01

Analiza oopsa

Surowe dane wypisane w oopsie są bardzo trudne do zanalizowania, dlatego używa się narzędzia ksymoops, które potrafi przetworzyć je na zrozumiałe symbole i adresy. Programowi temu należy przekazać następujące dane:

  • treść oryginalnego Oopsa (zazwyczaj można znaleźć ją w pliku wygenerowanym przez syslogd - np. /var/log/messages)
  • plik vmlinux - wykonywalny plik zawierający jądro Linuksa (zazwyczaj znajduje się w /usr/src/linux/
  • plik z symbolami jądra i modułów - /proc/ksyms
  • lista załadowanych modułów z momentu wystąpienia błędu - /proc/modules (plik ten, oraz /proc/ksyms są aktualne tylko pod warunkiem, że zostały przekazane do ksymoops zaraz po wystąpieniu oopsa - w przeciwnym razie mogły się zmienić, jeśli były ładowane lub odłączane jakieś moduły jądra)
  • System.map - (domyślnie w /usr/src/linux/System.map)

Manual dla ksymoops

Fragment tego, co może nam wygenerować ksymoops

Jak widać, pojawiają się tu nawet ludzkie nazwy funkcji ze stosu wywołań:

...
Process tar (pid: 2293, stackpage=c6155000)
...

>>EIP; c017d558 <create_virtual_node+298/490>   <=====
Trace; c017eb4f <ip_check_balance+34f/ae0>
Trace; c017fc44 <reiserfs_kfree+14/50>
Trace; c0180115 <fix_nodes+115/450>
Trace; c018a1c8 <reiserfs_insert_item+88/110>
Trace; c017bb3a <reiserfs_new_inode+3da/500>
Trace; c018738f <pathrelse+1f/30>
Trace; c0177a13 <reiserfs_lookup+73/d0>
Trace; d0871044 <END_OF_CODE+9a77/???
Trace; c0178274 <reiserfs_mkdir+d4/1d0>
Trace; c0142e36 <d_alloc+16/160>
Trace; c013c75f <vfs_mkdir+7f/b0>
Trace; c013c7f8 <sys_mkdir+68/b0>
Trace; c0108f77 <system_call+33/38>
Trace; c010002b <startup_32+2b/139>

Code;  c017d558 <create_virtual_node+298/490>
00000000 <_EIP>:
Code;  c017d558 <create_virtual_node+298/490>   <=====
   0:   8b 40 14                  mov    0x14(%eax),%eax   <=====
Code;  c017d55b <create_virtual_node+29b/490>
   3:   ff d0                     call   *%eax

strace, ltrace

strace i ltrace są programami, które śledzą odwołania danego procesu odpowiednio - do funkcji systemowych (strace) oraz funkcji bibliotecznych (ltrace). Zarejestrowane jest każde wywołanie funkcji systemowej, a także sygnały jakie proces otrzymuje od systemu. Program, który będzie śledzony przez strace/ltrace nie musi być skompilowany z informacjami dla debuggera (-g). Wynik działania strace może sotać wypisany na stderr lub zapisany do pliku którego adres podajemy wraz z opcją -o.

Podstawy gdb

  • gdb uruchamiamy poleceniem gdb <plik wykonywalny>. Plik powinien być skompilowany z opcją -g - zawierać informacje potrzebne debuggerowi.
  • Nastepnie uruchamiamy nasz program poleceniem run (lub r) <lista argumentów>.
  • Być może najpierw chcielibyśmy ustalić, gdzie program ma się zatrzymać: break (lub b) <nazwa funkcji>, lub b <numer linii>.
  • Do następnej funkcji przejdziemy poleceniem n (next).
  • s (step) - step into - program wykonuje się po jednej linijce kodu.
  • bt (backtrace) - wyświetlimy stos wywołań programu
  • p (print) <nazwa zmiennej> - dowiemy się, jaką aktualnie ma wartość
  • d <numer breakpointa> - usuniemy breakpoint. Bez parametru usuniemy wszystkie breakpointy.
  • c (continue) - kontynuujemy wykonanie programu.
  • return <wyrazenie> - spowoduje zakonczenie funkcji.
  • help <komenda> - HELP!!!

Możemy również przypisać zmiennym wartości (print <zmienna>=<wartosc>).

kgdb

kgdb jest łatką na jądro Linuksa pozwalającą uruchomić je zdalnie pod debuggerem gdb.

Debugowanie odbywa się przy użyciu dwóch maszyn. Na jednej uruchamiany jest debugger, a na drugiej uruchamiany jest system operacyjny ze spatchowanym jądrem. Łatka zawiera komponent gdb, który pozwala na komunikację z debuggerem na drugiej maszynie poprzez port szeregowy lub ethernet. Modyfikuje również reakcję na błędy - jądro zamiast panikować, przekazuje kontrolę debuggerowi, co pozwala na zanalizowanie problemu.

Strona domowa projektu

Instalacja

Wersję kgdb przeznaczoną dla jądra 2.6.17.13 na razie możemy zdobyć pobierając najświeższe pliki z repozytorium CVS:

 cvs -z3 -d:pserver:anonymous@kgdb.cvs.sourceforge.net:/cvsroot/kgdb co -P kgdb-2

Musimy nałożyć łatkę na jądro Linuksa. W tym celu pobieramy źródła jądra:

wget http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.17.13.tar.bz2
tar -xjf linux-2.6.17.13.tar.bz2
cd linux-2.6.17.13/

Następnie wykonujemy

patch -b -p1 <dla każdego pliku .patch znajdującego się w katalogu <gdzieśtam>/kgdb-2>

Lub używamy programu quilt zgodnie z instrukcją podaną w README kgdb-2/.

Wykonujemy make menuconfig i wybieramy opcje, które nas interesują (kernel debug info!), a przede wszystkim opcje zalecone w README:

Kernel hacking ->
        KGDB: kernel debugging with remote gdb ->
                KGDB: Thread analysis
                KGDB: Console messages through gdb
Device drivers ->
        Character devices ->
                Serial drivers ->
                        KGDB: On generic serial port (8250)

Następnie wykonujemy make bzImage, a uzyskany obraz jądra kopiujemy do katalogu /boot na maszynie testowej. Dodajemy odpowiedni wpis do menu.lst dla GRUBa, dopisując kgdbwait do polecenia ładującego nasz obraz. To sprawi, że jądro po załadowaniu będzie czekać na połączenie od gdb z drugej maszyny. Szczegóły we wspomnianym README.

KGDB można używać także na VMware, tutaj znajdują się odpowiednie wskazówki dla osób używających VMWare Server pod Linuksem i pod Windows.

Użycie

Po rozpakowaniu obrazu jądra maszyna testowa zawiesza się w oczekiwaniu na połączenie ze zdalnego gdb. Na maszynie development, w katalogu ze zrodlami jądra włączamy gdb vmlinux, a następnie wpisujemy target remote <adres portu szeregowego, z ktorym polaczana jest druga maszyba>. Gdb ma teraz dostep do kodu wykonywanego na maszynie testowej.

KDB

KDB jest debuggerem wbudowanym w jądro i pozwala monitorować struktury danych oraz pamięć systemu w czasie jego działania.

Strona domowa projektu

Możliwości:

  • wypisywanie zawartości struktur danych systemu według podanego adresu
  • możliwość wprowadzania zmian w zawartości struktur
  • kontrola działania jądra - wykonywanie kodu po jednym kroku (niestety tylko asemblera)
  • dostęp do stosu wywołań danego procesu

KDB jest łatką na jądro - a więc instalacja polega na nałożeniu tej łatki, a następnie wybraniu odpowiednich opcji związanych z działaniem KDB w dziale Kernel hacking konfiguratora [ CONFIG_KDB ].

Debugger domyślnie jest włączony i uaktywnia się przy wystąpieniu kernel panic, lub po dojściu do wcześniej ustalonych breakpointów. Opcja kdb dodana w lilo spowoduje zatrzymanie jądra na początku inicjalizacji systemu, co pozwala na ustalenie breakpointów.

UML

Co to jest UML?

Strona domowa projektu

  • User-Mode Linux (UML) jest popularnym, uniwersalnym i w pełni funkcjonalnym wirtualnym systemem linuksowym działającym na linuksowym hoście. UML ma liczne zastosowania. Może służyć do wygodnego testowania aplikacji bez narażania systemu hosta na uszkodzenie. Zwykły użytkownik może przetestować w UML nowe wersje jądra bez obaw o błędy spowodowane nowymi i niesprawdzonymi poprawkami. Administratorzy testują w UML konfiguracje systemów. W jednym hoście można skonfigurować wiele wersji UML’a i zasymulować całą sieć. W naszym przypadku najważniejsza jest możliwość debugowania jądra hosta.
  • User-Mode Linux nie jest ani emulatorem, ani interfejsem API(jakim jest Wine). Jego zasada działania jest podobna do virtualnych maszyn takich jak VMware, Virtual PC. Jednak i system virtualny, i system hosta są Linuksami oraz posiadają niemal identyczną strukturę, co powoduje że komunikacja pomiędzy nimi jest bardzo wydajna, a warstwa minimalna abstrakcji/translacji powoduje niewielkie obciążenia sięgające zaledwie 20% mocy obliczeniowej, która jest potrzebna aplikacji.
  • Dodatkowo maszyna UML dostarcza szereg urządzeń, dostępne na platformie na której jest instalowany SO (nie koniecznie dostępnych na hoście).

Jak to działa?

  • Sposób działania UML’a jest podobny do linuxa, tzn. uruchamia swój scheduler i w razie potrzeby jakiejś struktury danych odwołuje się do głównego scheduler-a hosta. W podobny sposób UML uruchamia swój własny wirtualny system. Alokuje niezbędną pamięć spośród istniejącej fizycznej przestrzeni adresowej głównego linuxa. Następnie mapuje przydzieloną pamięć na swoją wirtualną pamięć i określa tam swoją wirtualną przestrzeń adresową. Jeśli będzie istniała potrzeba konfiguracji pliku wymiany (swap) to zostanie podłączony plik wymiany.
  • Normalnie, jądro Linuksa zarządza całym dostępnym sprzętem (video card, keyboard, hard drives, itd) co powoduje że każdy proces prosi o dostęp do danego urządzenia.
+-----------+-----------+----+
| Process 1 | Process 2 | ...|
+-----------+-----------+----+
|       Linux Kernel         |
+----------------------------+
|         Hardware           |
+----------------------------+

Różnica pomiędzy jądrem UML-a a jąrdem Linuksa jest takaż że jest on traktowany jak zwykły proces, a wiec nie zarządza sprzętem, tylko o niego prosi jądro hosta.

            +----------------+
            | Process 2 | ...|
+-----------+----------------+
| Process 1 | User-Mode Linux|
+----------------------------+
|       Linux Kernel         |
+----------------------------+
|         Hardware           |
+----------------------------+

Debugowanie jądra przy pomocy UML-a

  • W pierwszej kolejności trzeba zbudować UML-a ze źródła jądra. W tym celu najleży pobrać jądro w wersji 2.2.15 lub nowszej. W przypadku kernel-a wersji 2.6 pakiet UML jest dodawany opcjonalnie, niestety w starszych wersjach trzeba dodatkowo pobrać patch-a UML. Dokładniejsze informacje związane z instalacją i konfiguracją można znaleźć tutaj.
  • Kolejny krok polega na odpowiednim skonfigurowaniu pobranego kodu. Ponizej przedstawiam menu konfiguracyjne w którym trzeba uruchomić zakładke UML, oraz hacking.

  • Po skonfigurowaniu i skompilowaniu, jesteśmy gotowi do boot-owania jądra.

Debugowanie UML przy pomocy gdb

Ponieważ UML jest traktowany przez gdb jak zwykły proces, więc możemy używać tych samych poleceń i narzędzi co zostały przedstawione w rozdziale Podstawy gdb. Poniżej został przdstawiony przykład debugowania UML przy pomocy gdb.

Debugowanie modułów

Istnieją dwie podstawowe metody debudowania modułów.

Pierwsza jest oparta na gdb

Metoda ta polega na zlokalizowaniu danego modułu i podanie debugerowi jego adresu, który zastał przydzielony przez UML.

Kiedy mamy zbudowany UML, nalezy zainstalować moduły, oraz umieścić je w odpowiednik katalodu, by były widoczne dla gdb. Cała czynność sprowadz się do zaledwie kilku poleceń, które zostały przedstawione poniżej:

1 make modules_install INSTALL_MOD_PATH=mods ARCH=um
2 UML# mkdir -p /lib/modules/`uname -r` 
3 host% scp -r mods/lib/modules/2.6.18-rc3-mm2/kernel/ root@UML:/lib/modules/2.6.18-rc3-mm2 

W lini trzeciej powyższego kodu, nie powinniśmy wpisywać uname -r zamiast 2.6.18-rc3-mm2, gdyż grozi to rozszerzeniem modułów hosta.

Po wykonaniu poniższego polecenia, możemy sprawdzić czy udało nam się załadować moduły w odpowiednie miejsce, z którego będą pobierane prze gdb.

UML# lsmod
Module                  Size  Used by
loop                   12328  0 

Gdyz mamy już zbudowane moduły, które się mieszczą w katalogu root@UML:/lib/modules/2.6.18-rc3-mm2, możemy przystąpić do debudowania, gdzie poprzez pierwsze polecenie p modules wczytujemy dostępne moduły. Cała trudność debugowania modułów przy pomocy gdb, poleda na tym, że jeżeli chcemy wybrać odpowiedzni moduł, musimy podać jego adres w pamieci, który został przydzielony dynamicznie przez UML-a. Przykład debugowania modułów UML-a przy pomocy gdb.

(gdb) p modules
$1 = {next = 0x3502cea4, prev = 0x3502cea4}

(gdb) p *((struct module *)0x3502cea0)
$3 = {state = MODULE_STATE_LIVE, list = {next = 0x81e0dec, prev = 0x81e0dec}, 
  name = "loop", '\0' <repeats 55 times>, mkobj = {kobj = {
      k_name = 0x3502ceec "loop", name = "loop", '\0' <repeats 15 times>, 
      kref = {refcount = {counter = 2}}, entry = {next = 0x81e0ac8, 
        prev = 0x34a486c8}, parent = 0x81e0ad0, kset = 0x81e0ac0, ktype = 0x0, 
      dentry = 0x3297277c, poll = {lock = {raw_lock = {<No data fields>}}, 
        task_list = {next = 0x3502cf1c, prev = 0x3502cf1c}}}, 
    mod = 0x3502cea0}, param_attrs = 0x0, modinfo_attrs = 0x9598620, 
  version = 0x0, srcversion = 0x0, syms = 0x3502bc20, num_syms = 2, 
  crcs = 0x0, gpl_syms = 0x0, num_gpl_syms = 0, gpl_crcs = 0x0, 
  unused_syms = 0x0, num_unused_syms = 0, unused_crcs = 0x0, 
  unused_gpl_syms = 0x0, num_unused_gpl_syms = 0, unused_gpl_crcs = 0x0, 
  gpl_future_syms = 0x0, num_gpl_future_syms = 0, gpl_future_crcs = 0x0, 
  num_exentries = 0, extable = 0x0, init = 0x3502f000, module_init = 0x0, 
  module_core = 0x3502a000, init_size = 0, core_size = 12328, 
  init_text_size = 0, core_text_size = 6468, unwind_info = 0x0, 
  arch = {<No data fields>}, unsafe = 0, license_gplok = 1, ref = {{count = {
        counter = 0}}}, modules_which_use_me = {next = 0x3502cfe0, 
    prev = 0x3502cfe0}, waiter = 0x94cf7c0, exit = 0x3502b8b6, 
  symtab = 0x3502bc30, num_symtab = 166, strtab = 0x3502c690 "", 
  sect_attrs = 0x346e14d8, percpu = 0x0, args = 0x94c3698 ""}

(gdb) p &((struct module *)0).list
$4 = (struct list_head *) 0x4
W drugim sposobie debugowania jądra, używamy umlgdb

Pierwsz metoda jest dość uciążliwa, gdyż musimy podać debugerowi adresy modułów jakie przydzielił im dynamicznie UML. Jednak istnieje wygodniejsze narzędzie napisane przez Chandan Kudige, które jest skryptem umlgdb.

Podsumowanie

  • Zalety UML-a
    • Jeśli UML padnie, to twój system nadal będzie działać
    • Można postaić jądro UML bez uprawniń administratora
    • Możesz debugować UML jak każdy inny proces
    • Można używać UML-a do testowania nowych aplikacji bez obawy przed zniszczeniem czegokolwiek
    • Można kompilować i sprawdzać funkcjonalność nowych wersji jądra
    • Można eksperymentować z różnymi dystrybucjami linuxa oraz z różnymi wersjami jąder.
    • UML zawiera cechę współdzielenia obrazu głównego systemu plików. Jeśli chcemy korzystać z kilku emulacji, bazując na jednym obrazie systemu macierzystego, to dzięki tej właściwości zaoszczędzimy sporo miejsca na dysku twardym. UML będzie w poszczególnych swoich sesjach zapisywał tylko różnice dzielące wersje obrazu macierzystego (głównego).
    • UML emuluje po prostu obraz systemu plików. Można nawet powiedzieć, że nie emuluje do końca systemu operacyjnego
    • Szybkość działania
    • UML to potężne narzędzie w rękach administratora - dzięki niemu przetestuje on nowe ustawienia, bezpieczeństwo lub wydajność sieci lub serwerów.
 
odpluskwianie_jadra.txt · ostatnio zmienione: 2006/11/30 00:10 przez 83.24.211.206
 

Autorzy:

Valid XHTML 1.0 Valid CSS Driven by DokuWiki