Linux Kernel II - Solutions for TV and IoT - ADB · komendy insmod bądź modprobe, ... w obrębie...
Transcript of Linux Kernel II - Solutions for TV and IoT - ADB · komendy insmod bądź modprobe, ... w obrębie...
„Hello kernel” - jak napisaćpierwszy moduł
Linux Kernel II
page 2
Przypomnienie (I)
• Moduły uruchamiane są i działają w przestrzeni Kernela (Kernel space),
• Moduły piszemy w języku C,• Moduły działają inaczej niż aplikacje: służą rozszerzaniu
podstawowych funkcjonalności jądra systemu Linux,• Popełnianie błędów programistycznych może owocować
poważniejszymi problemami.
Linux Kernel - Hello World module
page 3
Przypomnienie (II)
Przykładowy i uproszczony schemat architektury aplikacji:
Linux Kernel - Hello World module
Hardware Driver Kernel Syscall Library App
page 4
Wywołania systemowe (syscalls)
• Podstawowe instrukcje systemu Linux dostępne z poziomu użytkownika,
• Zwykle obudowane bibliotekami systemowymi, nie są wywoływane wprost,
• Każda funkcja standardowych bibliotek języka C może być zastąpiona wywołaniem, bądź serią wywołań systemowych,
• Przykładowo:
int printf(const char *format, ...); [std C lib function]
ssize_t write(int fd, const void *buf, size_t count); [syscall]
Linux Kernel - Hello World module
page 5
• Najprostsza odpowiedź: NIE!• Dlaczego?
• Bo... Kernel nie zapewnia wzparcia dla C++,• Bo... nie ma innych sterowników w C++,• Bo… C jest wystarczające, aby napisać każdy sterownik.
• Trudniejsza odpowiedź: TAK! Zawsze możemy przepisać Kernel w języku C++ ;)
• Więcej informacji:• http://www.tux.org/lkml/#s15-3
Czy moduły możemy pisać w C++? (pytanie z poprzedniego wykładu)
Linux Kernel - Hello World module
page 6
Cykl życia modułu
Linux Kernel - Hello World module
Wczytaj moduł(Insert)
Wywołaj funkcję inicjującą(Init function)
Czekaj na polecenie(request)
Wywołaj funkcję czyszczącą(Cleanup)
Remove module
komenda insmod
Komenda rmmod
Kolejka poleceń(request queue)
page 7
• Czego potrzebujemy?• Kompilatora GCC, który musi być odowiedni dla
posiadanej wersji Linuxa, zwykle jest on dostarczany wraz z “toolchainem”,
• Skompilowanego, bądź odpowiednio przygotowanego kodu źródłowego systemu Linux,
• Konfiguracji Kernela (plik .config),• Listy symboli używanych w Kernelu (plik
Module.symvers).
Pierwszy moduł: przygotowanie (I)
Linux Kernel - Hello World module
page 8
• Jeśli posiadamy skompilowane źródła systemu Linux w odpowiedniej wersji, to właśnie zaoszczędziliśmy sobie nieco pracy,
• Jeśli posiadamy prekompilowany system Linux (np. Arch Linux, Ubuntu czy Raspbian), to:
• Używamy przeglądarki Google, aby wyszukać instrukcję “odtworzenia” źródeł systemu Linux,
• Wszystkie instrukcje są do siebie podobne i sprowadzają się do pozyskania plików “.config” oraz “Module.symvers”,
• Większość popularnych systemów posiada łatwo dostępną instrukcję.
Pierwszy moduł: przygotowanie (II)
Linux Kernel - Hello World module
page 9
• Przykładowa instrukcja: https://github.com/notro/rpi-source/wiki• Powyższa instrukcja w skrócie:
• Aktualizujemy kompilator GCC, jeśli jest taka potrzeba,• Pobieramy skrypt i uruchamiamy go, a wtedy:
• Skrypt aktualizuje firmware RPI (także Kernel), pobiera źródła systemu Linux w najnowszej wersji do katalogu domowego,
• Odzyskuje konfigurację systemu Linux z /proc/config.gz, bądź generuje domyślną: “make bcmrpi_defconfig”,
• Pobiera plik “Module.symvers” (!),• Wykonuje “make modules_prepare”.
Przykład: przygotowanie systemu Raspbian (III)
Linux Kernel - Hello World module
page 10
• W pliku .config przechowywane są informacje o obecnej konfiguracji systemu Linux,
• Aby skonfigurować Kernel użyj:
make <configuration> [ls -l arch/arm/configs]
make mrpropermake menuconfigmake oldconfig
Plik .config
Linux Kernel - Hello World module
page 11
• przechowuje dane o eksportowanych przez Kernel symboli,• Symbole to funkcje widoczne dla innych modułów,
Budowa pliku:0x5d9a4bf2 – CRC,ipv6_chk_custom_prefix – nazwa funkcji (symbol)net/ipv6/ipv6 – moduł w którym symbol jest umiejscowiony.
Plik Module.symvers
Linux Kernel - Hello World module
page 12
#include <linux/init.h>#include <linux/module.h>#include <linux/sched.h>
MODULE_LICENSE("Dual BSD/GPL");
static int __init hello_init(void) { printk(KERN_ALERT "Hello kernel with process=%s (PID=%i, parent=%s)\n", current->comm, current->pid, current->parent->comm); return 0;}static void __exit hello_exit(void) { printk(KERN_ALERT "Goodbye kernel world.\n");}
module_init(hello_init);module_exit(hello_exit);
Moduł “Hello Kernel”
Linux Kernel - Hello World module
page 13
• Linux/module.h• Plik potrzebny wszystkim dynamicznie ładowanym modułom,• Zawiera wiele definicji między innymi makra MODULE_LICENSE,
• Linux/init.h• Plik nagłówkowy zawierający definicje potrzebne to inicjalizacji
modułów, m.in. makra module_init, module_exit, __init, __exit,• Linux/sched.h
• Plik nagłówkowy zawierający m.in. definicje dostarczające dane o obecnym procesie,
• Current→comm, current→parent, current→pid.
Pliki nagłówkowe
Linux Kernel - Hello World module
page 14
• Możliwe licencje znaleźć można w pliku nagłówkowym linux/module.h,
• Dostępne licencje: GPL, GPL v2, Dual BSD/GPL, Dual MPL/GPL, Proprietary i inne...,
• Niektórych funkcji można używać jedynie w modułach oznaczonych konkretnymi licencjami,
• Moduły nie zawierające definicji licencji traktowane są jako “proprietary”,
• Kiedy moduł oznaczony jest jako “proprietary”, Kernel staje się “skażony” (ang. “tainted”),
• “Skażone” Kernele nie są wspierane przez brać linuxową...
Makro MODULE_LICENSE
Linux Kernel - Hello World module
page 15
static int __init hello_init(void) { printk(KERN_ALERT "Hello kernel with process=%s (PID=%i, parent=%s)\n", current->comm, current->pid, current->parent->comm); return 0;}
• Hello_init: funkcja statyczna, zwracająca wartość decymalną (integer), nie przyjmująca argumentów,
• __init: znacznik mówiący Kernelowi, że funkcja jest używana tylko podczas inicjalizacji modułu (potem jest usuwana z pamięci),
• Printk: funkcja pisząca, podobna do funkcji “printf” w przestrzeni użytkownika (userspace),
• KERN_ALERT: poziom logowania, jest to zwykły string,• “current”: struktura zawierająca informacje o obecnym procesie.
Funkcja init
Linux Kernel - Hello World module
page 16
static void __exit hello_exit(void) { printk(KERN_ALERT "Goodbye kernel world.\n");}
• Hello_exit: funkcja statyczna, nie zwracająca oraz nie przyjmująca wartości,
• __exit: znacznik mówiący Kernelowi, że funkcja jest używana jedynie podczas kończenia pracy modułu,
• Jeśli funkcja oznaczona jako __exit zostanie użyta w innym kontakście, zwrócony zostanie błąd.
Funkcja exit
Linux Kernel - Hello World module
page 17
module_init(hello_init);module_exit(hello_exit);
• Nazwy funkcji użyte jako argumenty makr module_init i module_exit są uruchamiane odpowiednio:
• W czasie inicjalizacji pracy modułu użyta zostanie funkcja “hello_init”,
• W czasie kończenia pracy modułu użyta zostanie funkcja “hello_exit”,
• Przykładowy wynik końcowy:
Makra module_init i module_exit
Linux Kernel - Hello World module
page 18
KERN_VER := $(shell uname -r)PWD := $(shell pwd)
KERNELDIR ?= /lib/modules/$(KERN_VER)/build
obj-m := hello.o
all: clean compile
compile: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean: $(MAKE) -C $(KERNELDIR) M=$(PWD) clean
Kompilacja modułu: plik makefile
Linux Kernel - Hello World module
page 19
• Dlaczego pliki makefile modułów Kernela wyglądają inaczej niż tradycyjne?
• Ponieważ plik makefile kompilowanego modułu staje się częścią procesu kompilowania całego Kernela,
• Ta linijka jest odpowiedzialna za dołączenie świeżo napisanego modułu do procesu kompilacji Kernela:
obj-m := hello.o
• Za wywołanie procesu kompilacji odpowiada komenda $(MAKE):
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
Kompilacja modułu: wyjaśnienie
Linux Kernel - Hello World module
page 20
• Moduły możemy budować na dwa sposoby:• Jako wbudowane (build-in), ładowane automatycznie podczas startu
systeu Linux,• Jako zewnętrzne (loadable), ładowane przed użytkownika za pomocą
komendy insmod bądź modprobe,• Aby zbudować moduł jako wbudowany należy w pliku “.config”:
• Oznaczyć moduł jako “y”,np. CONFIG_HELLO_WORLD=y
• Aby zbudować moduł jako zewnętrzny należy w pliku “.config”:• Oznaczyć moduł jako “m”,
np. CONFIG_HELLO_WORLD=m• Dlatego też, jeśli w pliku makefile napiszemy:
• Obj-y := hello.o, nasz moduł będzie wbudowany,• Obj-m := hello.o, nasz moduł będzie zewnętrzyny.
Kompilacja modułu: informacje dodatkowe (I)
Linux Kernel - Hello World module
page 21
• Budowanie modułu złożonego z plików hello1.c and hello2.c:
obj-m := hello.ohello-objs := hello1.o hello2.o
• Nietypowa komenda “$(MAKE)”:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
Odpowiada ona za wywołanie dodatkowego procesu budowania – dziecka – w obrębie nadrzędnego polecenia “make”.
Kompilacja modułu: informacje dodatkowe (II)
Linux Kernel - Hello World module
page 22
• insmod – aby wczytać I uruchomić moduł,• rmmod – aby zakończyć pracę modułu,• lsmod – aby wyświetlić załadowane moduły,• Polecenie: dmesg | tail wyświetli wszystkie polecenia “printk”
użyte w module.
Dynamiczne uruchamianie i kończenie pracy modułów
Linux Kernel - Hello World module
page 23
• Kernel Symbol Table to po prostu Tablica Symboli Kernela,• Tablica symboli odnosi się bezpośrednio to wspomnianego wczesniej
pliku Module.symvers,• Co należy zrobić, aby napisaną przez nas funkcję dodać do tablicy
symboli Kernela?Należy wyeksportować ją za pomocą makra: EXPORT_SYMBOL(),
• Po użyciu makra EXPORT_SYMBOL Kernel umieści naszą funkcję w globalnej tablicy symboli i stanie się ona widoczna dla wszystkich modułów w Kernelu.
“Kernel symbol table”
Linux Kernel - Hello World module
page 24
• Kiedy zobaczymy taki błąd:Error inserting './hello.ko': -1 “Invalid module format”
• Znaczy to, że coś poszło nie tak... Aby przyjrzeć się bliżej problemowi wpisujemy:
“cat /var/log/messages”, bądź “dmesg” i sprawdzamy ostatnie wpisy,
• Najczęstsze błędy:1. hello: version magic '<this>' should be '<that>'
Zła wersja kernela: należy poszukać DOKŁADNIE takiego samego Kernela, bądź pogrzebać w /linux/module.h (czasem trudne).
Potencjalne problemy i pozbywanie się ich (I)
Linux Kernel - Hello World module
page 25
2. hello: no symbol version for module_layoutŹródła systemu Linux nie są kompletne. Brakuje w nich pliku “Module.symvers”: należy pobrać plik od twórcy, bądź skompilować źródła systemu Linux, które posiadamy.
3. W przypadku każdego problemu najprostszym, rozwiązaniem jest przeszukanie internetu. Kernel posiada znakomitą dokumentację! :)
Potencjalne problemy i pozbywanie się ich (II)
Linux Kernel - Hello World module
page 26
• Aby skompilować swój moduł potrzebujesz odpowiednio przygotowane źródła systemu Linux: konfiguracja oraz stworzenie tablicy symboli są niezbędne!
• Moduły możemy budować jako integralną część Linuxa (build-in) bądź jako zewnętrzne pliki ładowane dynamicznie (loadable),
• Proces budowania modułu jest częścią systemu budowania CAŁEGO systemu Linux,
• Pliki nagłówkowe systemu Linux dostarczają wielu ciekawych informacji,
• Częste sięganie do dokumentacji systemu Linux może zaoszczędzić wielu nerwów,
• Programowanie w systemie Linux wymaga wprawy i szczególnie na początku, może sprawiać problem.
Podsumowanie
Linux Kernel - Hello World module
Linux Kernel – Wprowadzenie
Koniec