Wykorzystanie funkcji printk() w jądrze
Uwagi od tłumacza!!
Tekst ten został przetłumaczony z informacji znalezionych na ten temat, nie był stosowany przez osoby tłumaczące. Jeśli przetestowałeś tą metodę i masz jakieś konkretne spostrzeżenia podziel się nimi z Nami.
Pierwszy hack kernela
Tutaj, udowodnisz że hackowanie jądra nie jest magią. Pamiętaj, w momencie tym uruchomiłeś twoje nowe jądro. Teraz będziemy je "hackować". Dodamy funkcję printk, która wyświetli wiadomości pojawiające się w czasie ładowania.
Najpierw, otwórz plik init/main.c ulubionym edytorem. Jeśli używasz w nim kontroli kodu, sprawdź najpierw plik dla pewności. W BitKeeper, użyj polecenia bk edit init/main.c. W CVS, użyj polecenia cvs co init/main.c. Zlokalizuj funkcję calibrate_delay(void) w tym pliku.
Pierwsze linie deklarują kilka zmiennych, i wyglądają mniej więcej tak:
unsigned long ticks, loopbit;
int lps_precision = LPS_PREC;
Teraz, wpisz następującą linią tuż pod nimi
printk("*** I am a kernel hacker! ***\n")
Przekompiluj jądro korzystając z polecenia make bzImage lub make zImage, uruchom LILO lub ybin lub cokolwiek czego potrzebujesz i załaduj nowe jądro. Nie musisz uruchamiać make clean. Podczas ładowania jądra obserwuj ekran - powinieneś zobaczyć nową wiadomość. Jeśli przewinęła się szybko i zniknęła z ekranu zanim zdążyłeś przeczytać, zaloguj się i wpisz:
$ dmesg | less
Powinieneś zobaczyć swoją wiadomość blisko początku wyjścia.
Gratuluje! Właśnie zhackowałeś jądro. Funkcja printk jest jednym z głównych narzędzi kernel hackera. Będziesz jej ciągle używać.
Więcej sztuczek z wykorzystaniem printk
printk, jest na wiele sposobów kernelowym odpowiednikiem funkcji printf ( standardowa funkcja języka C ). Składnie obu funkcji są najczęściej takie same. Użyjesz jej określonych argumentów w źródłach kernela dużo częściej niż robisz to w kodach użytkownika. Szczególnie użytecznymi argumentami są "%x" lub "%p". Argument "%x" nakazuje wyświetlenie wartości heksadecymalnej, która bazuje na kodzie szesnastkowym i zazwyczaj niesie bardziej użyteczne informacje od wartości dziesiętnej, bazującej na kodzie dziesiętnym. Spowodowane jest to tym, że logiczną jednostką zapisu informacji w komputerze jest bajt ( 8 bitów ), a bajt mieści się w dwóch członach w reprezentacji heksadecymalnej. Więc wartość szesnastkową:
0x12345678
Reprezentują te cztery bity ( zignorowano starszy bity pamięci)
0x12
0x34
0x56
0x78
Jak widzisz, łatwo jest wydzielić wartość heksadecymalną na dwie niezależne wartości bitowe - po prostu przeczytaj dwa człony w jednym czasie. Część "0x" mówi tylko, że następujące liczby bazują na kodzie szesnastkowym, nie dziesiętnym. (Zauważ: tylko przedpotowe i nieistotne urządzenia są nie ośmiobitowe. Nie dbamy o bajty, które nie mają długości 8 bitów.)
Argument "%p" nakazuje wyświetlenie wartości jako wskaźnik lub adres pamięci. Przełącznik ten będzie zależał od urządzenia, ale zawsze ma wartość szesnastkową.
Następnym twoim zadaniem będzie wyświetlenie adresu zmiennej. Pod twoim pierwszym printk ( kawałek wyżej, plikinit/main.c przyp. tłumacz ), dodaj to:
prinkt("Adres loops_per_fiffy %p\n", &loops_per_jiffy);
Przekompiluj i załaduj jądro. Teraz wiesz w jakim adresie wirtualnym pamięci przechowywana jest zmienna loops_per_jiffy. Możesz się tego także dowiedzieć z pliku System.map w katalogu źródłowym jądra:
$ grep loops_per_jiffy System.map
Następnie, zamierzamy nauczyć się czegoś na temat poziomów logowania. Możesz określić każdą funkcję printk by mieć określony poziom ważności wiadomości. W zależności od obecnego poziomu logowania, niektóre wiadomości zostaną wyświetlone na konsoli (zazwyczaj na monitorze), a niektóre nie. Dodaj to za twoim funkcjami printk ( init/main.c przyp. tłumacz ):
printk(KERN_DEBUG"***This is a debug message only***\n");
Jeśli twój poziom logowanie jest ustawiony normalnie, nie zobaczysz tej wiadomości na monitorze w czasie ładowania. Kiedy skończy się ładowanie, zaloguj się i spójrz na wiadomości z kernela:
$ dmesg | less
Pomiędzy innymi wiadomościami powinieneś zobaczyć:
*** This is a debug message only ***
Jest to pomocne, teraz możesz przeglądać wyjście tylko wtedy kiedy chcesz, zamiast mieć go wyświetlonego na ekranie ( prawdopodobnie gdy piszesz ). Definicja różnych KERN_* poziomów zawarta jest w pliku include/linux/kernel.h . Zazwyczaj będziesz potrzebować tylko KERN_INFO i KERN_DEBUG.
Każdorazowo kiedy chcesz dowiedzieć się co dzieje się w kernelu, dobrą metodą na początek jest włożenie w najważniejszych miejscach kilku funkcji printk. Uważaj żebyś nie wyświetlił za dużo informacji - może to zwolnić jądro do poziomu nieużywalnego. W niektórych miejscach dodanie funkcji printk może spowodować zniszczenie lub błąd albo jego naprawienie. Aby dokładnie dowiedzieć się co robi printk, zacznij czytać plik kernel/printk.c.
Miłej zabawy z prinkt!