Programowanie Systemów Wbudowanych Jadro systemu Linux Iwona Kochańska Katedra Systemów Elektroniki Morskiej WETI PG March 20, 2017
Elementy wbudowanego OS Linux Toolchain kompilator i inne narzdzia do tworzenia kodu dla urzadzeń Bootloader inicjalizuje platform sprzętowa oraz ładuje jadro systemu Kernel serce systemu, zarzadza zasobami i komunikuje się ze sprzętem Root filesystem GUT Intel 2015/16 2/51 biblioteki i programy uruchamiane w systemie
Uruchamianie systemu Linux GUT Intel 2015/16 3/51
Jadro systemu Linux Jadro (Kernel) - część systemu operacyjnego odpowiedzialna za zarzadzanie zasobami i komunikację ze sprzętem zwykle dopasowane do konkretnej konfiguracji sprzętu drzewa urzadzeń umożliwiaja tworzenie generycznego jadra, dopasowanego do platformy sprzętowej poprzez dtb 1991 - Linus Torvalds rozpoczał prace nad systemem operacyjnym Linux dla komputerów Intel 386 i 486 Inspiracja - Minix OS napisany przez Andrew S. Tanenbaum (1987) Różnica między Linux a Minix: 32-bitowe jadro wykorzystujace pamięć wirtualna kod otwarty (open source, później - licencja GPL 2) GUT Intel 2015/16 4/51
Przy okazji... ważna ksiażka! GUT Intel 2015/16 5/51
Co robi jadro? Linus Torvalds napisał jadro systemu operacyjnego, wykorzystujac komponenty projektu GNU (toolchain, biblioteka C, podstawowe narzędzia linii poleceń) Jadro może pracować wraz: przestrzenia użytkownika GNU (GNU user space) -> GNU/Linux dla komputerów CP i serwerów przestrzenia użytkownika Android -> OS dla urzadzeń mobilnych mała przestrzenia użytkownika Busybox -> kompaktowy system wbudowany Główne zadania jadra: zarzadzanie zasobami, interfejs ze sprzętem, dostarczanie API z użytecznym poziomem abstrakcji dla programów w przestrzeni użytkownika GUT Intel 2015/16 6/51
Co robi jadro? Aplikacje uruchamiane sa w przestrzeni użytkownika z niskim poziomem praw do CPU. Moga wywoływać funkcje biblioteczne. Biblioteka C - interfejs między przestrzenia użytkownika (user space) a przestrzenia jadra (kernel space). Tłumaczy funkcje poziomu użytkownika (user level functions) w wywołania systemowe (kernel system calls). Interfejs wywołań systemowych używa pułapki (trap) lub przerwań programowych (software interrupt) do przełaczania CPU między przestrzenia użytkownika a przestrzenia jadra Przestrzeń j adra - wysoki poziom praw dostępu do całej przestrzeni adresowej i rejestrów CPU GUT Intel 2015/16 7/51
Co robi jadro? System call handler przekazuje wywołania do podsystemów jadra: scheduling calls -> scheduler, filesystem calls -> filesystem code, Niektóre wywołania wymagaja pobrania danych z urzadzeń dołaczonych do systemu - sa przekazywane do sterownika urzadzenia Urzadzenie może samo wywołać funkcję jadra generujac przerwanie Przerwanie może być obsłużone tylko przez sterownik (nigdy przez aplikację w przestrzeni użytkownika!) Wszystko co robi aplikacja jest robione przez jadro systemu. GUT Intel 2015/16 8/51
Wersje jadra Nowa wersja jadra - raz na 8-12 tygodni Numerowanie wersji: przed lipcem 2011: 3 liczby,2 np.: 2.6.39. Środkowa liczba wskazuje, czy jest to wersja rozwojowa (l. nieparzysta) czy stabilna (l. parzysta) lipiec 2011: zmiana numeru z 2.6.39 na 3.0. Rezygnacja ze środkowej liczby. kwiecień 2015: wersja 4 GUT Intel 2015/16 9/51
Wersje jadra Drzewo rozwoju kodu jadra: $ g i t clone g i t : / / g i t. k e r n e l. org / pub / scm / l i n u x / k e r n e l / g i t / t o r v a l d s / l i n u x. g i t Cykl rozwoju kodu: 2 tygodnie akceptacji łatek do nowej wersji faza stabilizacji - publikacja wersji-kandydatów (numerowanych jako -rc1, -rc2, itd.) użytkownicy testuja wersje-kandydatów i zgłaszaja raporty błędów i poprawek po naprawieniu wszystkich błędów nowa wersja jadra jest publikowana Przegl ad wszystkich wersji: http://kernelnewbies.org/linuxversions. GUT Intel 2015/16 10/51
Wersja stabilna jadra O wersje stabline jadra dba Greg Kroah-Hartman. wersje naprawcze (bug fix releases) jadra stabilnego numerowane sa trzecia liczba:3.18.1, 3.18.2, itd. Przed wersja 3 były to numery 4-liczbowe (2.6.29.1, 2.6.39.2) Niektóre jadra sa oznaczone jako long term (utrzymywane przez 2 lata lub dłużej) Każdego roku publikowane jest przynajmniej jedno jadro długoterminowe (4.1, 3.18, 3.14, 3.12, 3.10, 3.4, 3.2, 2.6.32). j adro 2.6.32 jest utrzymywane od 5 lat (aktulanie wersja 2.6.32.68). GUT Intel 2015/16 11/51
Wsparcie sprzedawców sprzętu Sam Linux obsługuje waski zbiór platform sprzętowych wsparcie niezależnych projektów open source: Linaro, Yocto Project wsparcie firm dostarczajacych rozwiazań z wbudowanym OS Linux wsparcie dostawców SoC (jadro działajace na SoC) GUT Intel 2015/16 12/51
Licencje Kod źródłowy Linux: licencja GPL v2 (GNU General Public License) Założenia GPL: wolność uruchamiania programu w dowolnym celu wolność analizowania, jak program działa i dostosowywania go do swoich potrzeb wolność rozpowszechniania niezmodyfikowanej kopii programu wolność udoskonalania programu i publicznego rozpowszechniania własnych ulepszeń, dzięki czemu może z nich skorzystać cała społeczność GUT Intel 2015/16 13/51
Licencje Tekst licencji - w pliku COPYING. Rozpoczyna się adnotacja, że uruchamianie wywołań jadra z przestrzeni użytkownika przez interfej wywołań systemowych nie jest pochodna pracy jadra, więc nie podlega licencji. Licencja GPL gwarantuje, że programista systemu wbudowanego zawsze ma dostęp do kodu źródłowego jadra GUT Intel 2015/16 14/51
Licencje Problem z licencja: moduły jadra Moduł jadra (kernel module) - fragment kodu dynamicznie linkowany z jadrem podczas jego pracy (linked at runtime), rozszerzajacy funkcjonalność jadra licencja GPL nie rozróżnia linkowania statycznego i dynamicznego akceptowalna praktyka: GPL nie musi obejmować modułow jadra (zapis w makro MODULE_LICENSE) dyskusja o GPL vs moduły jadra: http://yarchive.net/comp/linux/gpl_modules.html. GUT Intel 2015/16 15/51
Budowa jadra - źródła Pobranie źródeł jadra: git lub tarball. Przykład: $ g i t clone g i t : / / g i t. k e r n e l. org / pub / scm / l i n u x / k e r n e l / g i t / s t a b l e / l i n u x s t a b l e. g i t l i n u x $ cd l i n u x $ g i t checkout v4. 1. 1 0 GUT Intel 2015/16 16/51
Budowa jadra - źródła Źródła zorganizowane sa w katalogi: arch: pliki specyficzne dla architektur procesora Documentation drivers: sterowniki fs: kod systemu plików include: pliki nagłówkowe jadra init: kod rozruchowy jadra kernel: podstawowe funkcje: scheduling, liczniki, zarzadzanie moca, debugger mm: zarzadzanie pamięcia net: protokoły sieciowe scripts: skrypty, np. kompilator drzewa urzadzeń (dtc) tools: narzędzia, np. do pomiaru wydajności, perf GUT Intel 2015/16 17/51
Konfiguracja jadra Jadro może być różnie skonfigurowane: od małego, dedykowanego (np. do obsługi termostatu) po duże i złożone (np. do obsługi telefonu komórkowego) W każdej wersji jadra - wiele opcji konfiguracji Kconfig - narzędzie konfiguracyjne Kbuild - narzędzie do kompilacji i budowy jadra Dokumentacja narzędzi: Documentation/kbuild/. Kconfig/Kbuild s a używane również w innych projektach: crosstool-ng, U-Boot, Barebox, BusyBox. GUT Intel 2015/16 18/51
Konfiguracja jadra Opcje konfiguracji zorganizowane sa w sposób hierarchiczny jako pliki Kconfig Dokumentacja: Documentation/kbuild/kconfig-language.txt. Przykład głównego menu (plik Kconfig): Ostatnia linia: odwołanie do innego pliku Kconfig, zależnego od architektury sprzętu GUT Intel 2015/16 19/51
Konfiguracja jadra Wybór architektury: ARCH=[architecture], w przeciwnym wypadku wybrana zostanie architektura maszyny lokalnej Wartość ARCH - jeden z podkatalogów katalogu arch (wyjatek: ARCH=i386 i ARCH=x86_64 maja te same źródła arch/x86/kconfig). Wyglad menu głównego może być różny w zależności od ARCH GUT Intel 2015/16 20/51
Konfiguracja jadra Plik Kconfig składa się z: wielu menu, oznaczonych słowami kluczowymi: menu, menu title, endmenu pozycji menu (config) GUT Intel 2015/16 21/51
Konfiguracja jadra Informacje o konfiguracji przechowywane sa w pliku.config (plik ukryty, widoczny dla komendy ls -a) nazwy zmiennych w pliku.config rozpoczynaja się od przedrostka CONFIG_ DEVMEM is enabled -> CONFIG_DEVMEM=y # # A u t o m a t i c a l l y generated f i l e ; DO NOT EDIT. # Linux / arm 4.9.13 Kernel C o n f i g u r a t i o n # CONFIG_ARM=y CONFIG_ARM_HAS_SG_CHAIN=y CONFIG_MIGHT_HAVE_PCI=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y CONFIG_HAVE_PROC_CPU=y... GUT Intel 2015/16 22/51
Konfiguracja jadra Typy danych: bool: y lub not defined. tristate: opcja może być zrealizowana jako moduł jadra (m), wbudowana w jadro (y) lub not defined int: liczba calkowita w systemie dziesiętnym hex: liczba całkowita bez znaku w systemie szestastkowym string: łańcuch znaków. GUT Intel 2015/16 23/51
Konfiguracja jadra Zależności (dependencies) między pozycjami menu sa wyrażone poprzez depends on: Jeśli CONFIG_MTD nie zostało ustawione jako enabled, bieżaca opcja nie zostanie wyświetlona w menu. GUT Intel 2015/16 24/51
Konfiguracja jadra Odrócona zależność: słowo select ustawia inne opcje, jeśli bieżaca jest enabled (przydatne podczas definiowania opcji zależnych od architektury) GUT Intel 2015/16 25/51
Konfiguracja jadra Narzędzia do czytania plików Kconfig i generowania pliku.config: Menuconfig xconfig gconfig. Każde z nich uruchamia się poleceniem make, podajac informację o architekturze platformy: $ make ARCH=arm menuconfig GUT Intel 2015/16 26/51
Konfiguracja jadra - Raspberry Pi Najpierw konfiguracja cross-kompilatora na komputerze hosta! Źródła: g i t clone h t t p s : / / g i t h u b. com / r a s p b e r r y p i / t o o l s + dodanie ściezki do cross-kompilatora do zmiennej środowiskowej $PATH w pliku.bashrc Źródła jadra Linux: $ g i t clone depth=1 h t t p s : / / g i t h u b. com / r a s p b e r r y p i / l i n u x Następnie: cd l i n u x KERNEL=kernel7 make ARCH=arm CROSS_COMPILE=arm l i n u x gnueabihf bcm2709_defconfig GUT Intel 2015/16 27/51
Konfiguracja jadra - Raspberry Pi Konfiguracja z interfejsem graficznym: cd l i n u x KERNEL=kernel7 make ARCH=arm CROSS_COMPILE=arm l i n u x gnueabihf bcm2709_defconfig menuconfig Klawisz / - szukanie opcji GUT Intel 2015/16 28/51
Konfiguracja jadra Zbiór działajacych plików konfiguracyjnych: arch/$arch/configs, Podczas budowy j adra generowany jest plik nagłówkowy include/generated/autoconf.h, który zawiera sekcje #define dla każdej wartości opcji konfiguracyjnej GUT Intel 2015/16 29/51
Raspberry Pi - kompilacja Uruchomienie kompilacji: make ARCH=arm CROSS_COMPILE=arm l i n u x gnueabihf zimage modules lub: make ARCH=arm CROSS_COMPILE=arm l i n u x gnueabihf a l l dt Pliki wynikowe: jadro, moduły jadra, drzewo urzadzeń. GUT Intel 2015/16 30/51
Identyfikacja jadra Zapytanie o ostatnio zbudowana wersję jadra: $ make kernelversion 4.1.10 Ta sama informację można otrzymać podczas działania jadra za pomoca komendy uname Po zmianie domyślnej konfiguracji jadra należy ja nazwać poprzez ustawienie zmiennej CONFIG_LOCALVERSION (w General setup configuration menu). Zapytanie o wersję konfiguracji: $ make kernelrelease Ta sama informacja umieszczona jest na poczatku log-a jadra GUT Intel 2015/16 31/51
Moduły jadra (kernel modules) OS Linux dla Desktop: moduły jadra umożliwiaja dynamiczne linkowanie sterowników w zależności od dołaczonego sprzętu bez modułów jadra każdy sterownik musiałby być statycznie linkowany z kodem jadra -> znaczny wzrost rozmiaru jadra OS Linux dla systemów wbudowanych: konfiguracja sprzętu i jadra jest zwykle ustalona i znana moduły jadra generuja zależności między wersjami jadra i systemu plików -> potencjalne źródło problemów (np. podczas aktualizacji jednego z elementów) zwykle jadra systemów wbudowanych nie korzystaja z żadnych dodatkowych modułów GUT Intel 2015/16 32/51
Moduły jadra Kiedy stosowanie modułów jadra w systemie wbudowanym jest korzystne? kiedy rozruch musi być szybki - można odłożyć na poźniej ładowanie niektórych sterowników kiedy system jest rozbudowany - zbyt dużo sterowników, by linkować je z jadrem statycznie (np. interfejs USB obsługujacy szerokie spektrum urzadzeń) GUT Intel 2015/16 33/51
Kompilacja jadra Kbuild - narzędzie do budowy jadra. Jest to zbiór skryptów make, które: odczytuja informację o konfiguracji z pliku.config, rozwiazuj a zależności kompiluja wszystkie niezbędne elementy do kompilacji samego jadra (dtb, moduły jadra) GUT Intel 2015/16 34/51
Kompilacja jadra Zależności (dependencies) zapisane sa w plikach makefiles w ścieżce bieżacej każdego budowanego elementu. Przykład: obj-y - element kompilowany bezwarunkowo obj-$(config_param) - element kompilowany zależnie od parametru (y, m lub not defined). GUT Intel 2015/16 35/51
Kompilacja obrazu jadra Czego oczekuje program rozruchowy? U-Boot: plik uimage, nowsze wersje moga używać zimage za pomoca komendy bootz platformy x86: plik bzimage większość pozostałych programów rozruchowych: plik zimage Przykład budowy pliku zimage: Opcja -j 4: 4 zadania moga być wykonywane jednocześnie GUT Intel 2015/16 36/51
Kompilacja obrazu jadra Platformy ARM: Linux 3.7 - to samo jadro może działać dla róznych platform ARM Jadro wybiera właściwa platformę sprzętowa np. odczytujac informację z drzewa urzadzeń Lokalizacja pamięci fizycznej może być różna w zależności od platformy (a więc i adres jadra) LOADADDR = adres odczytany z pliku mach-[your SoC]/Makefile.boot (wartość zreladdr-y). Kompilacja jadra: GUT Intel 2015/16 37/51
Kompilacja obrazu jadra W wyniku kompilacji jadra powstaja dwa pliki: vmlinux - jadro jako plik ELF. Jeśli jadro było kompilowane z właczon a opcja debugowania (CONFIG_DEBUG_INFO=y), plik ten zawiera symbole pomocne dla debuggerów (np. kgdb) System.map - tablica symboli Większość programów rozruchowych nie obsługuje kodu ELF. Obraz przetwarzany jest dalej w surowy plik binarny. GUT Intel 2015/16 38/51
Kompilacja drzewa urzadzeń dtbs - narzędzie do kompilacji i budowy drzewa urzadzeń. Uruchomienie: target dla polecenia make Przepis dla dtbs: plik arch/$arch/boot/dts/makefile plik z kodem źródłowym Pliki.dtb generowane sa w tej samej ścieżce co źródła GUT Intel 2015/16 39/51
Kompilacja modułów jadra Moduły moga być kompilowane niezależnie od jadra. Target modules dla make: Skompilowane moduły maja rozszerzenie.ko i znajduja się w tej samej ścieżce co źródła Target modules_install dla make - instalacja modułów jadra Domyślna lokalizacja modułów ( /lib/modules) może być zmieniona za pomoca opcji INSTALL_MOD_PATH. GUT Intel 2015/16 40/51
Czyszczenie źródeł jadra Opcje dla polecenia make: clean: usuwa pliki objektów i większość plików pośrednich mrproper: usuwa wszystkie pliki pośrednie, włacznie z plikiem.config file distclean: jak mrproper oraz usuwa pliki zapasowe narzędzi kompilacji GUT Intel 2015/16 41/51
Uruchomienie jadra Proces uruchamiania jadra jest zależny od platformy sprzętowej Przykład - uruchomienie j adra Linuxa na platformie BeagleBone Black z poziomu U-Boot: GUT Intel 2015/16 42/51
Uruchomienie jadra - kernel panic Kernel panic - pojawienie się nierozwiazywalnego błędu wypisanie na konsoli informacji o błędzie i zawieszenie działania systemu Przykład: brak poprawnego systemu plików: [ 1.886379] Kernel panic not syncing: VFS: Unable to mount root fs on unknown block(0,0) [ 1 bład może być naprawiony przez użytkownika - system plików może być dostarczony np. na karcie SD GUT Intel 2015/16 43/51
Uruchomienie jadra - wczesna przestrzeń użytkownika (early user space) źródło: http://www.slideshare.net/shimosawa/linux-initialization-process-2 GUT Intel 2015/16 44/51
Uruchomienie jadra - wczesna przestrzeń użytkownika Proces PID 1 uruchamia kod init/main.c, kernel_init(). Jeśli jest dostepny ramdisk, PID 1 spróbuje uruchomić program /init (uruchomienie przestrzeni użytkownika) Jeśli nie uda się znaleźć i uruchomić /init, jadro spróbuje uruchomić prepare_namespace() w init/do_mounts.c. wymaga opcji root= w celu zdefiniowania urzadzenia blokowego (block device), z którego ma zostać zamontowany system plików root=/dev/<disk name><partition number> root=/dev/<disk name>p<partition number> Przykład: system plików z karty SD. Kolejność poleceń-prób zamontowania systemu plików root=/dev/mmcblk0p1/sbin/init /etc/init bin/sh GUT Intel 2015/16 45/51
Komunikaty jadra Wartość 0 - komunikat najważniejszy. Komunikaty można obejrzeć po wywołaniu plecenia dmesg GUT Intel 2015/16 46/51
Komunikaty jadra GUT Intel 2015/16 47/51
Komunikaty jadra Komunikaty sa zapisywane w buforze log_buf o rozmiarze 2^CONFIG_LOG_BUF_SHIFT Polecenie dmesg wypisuje na ekranie zawartość bufora Komunikat jest wyświetlany, jeśli ich poziom jest mniejszy od poziomu logowania w konsoli (wartość 7) Oznacza to, że komunikaty KERN_DEBUG nie sa zapisywane. Zmiana poziomu logowania konsoli: loglevel=<level> lub dmesg -n <level>. GUT Intel 2015/16 48/51
Linia poleceń jadra Kernel command line - łańcuchy znaków przekazywane do jadra: przez bootloader (np. zmienna bootargs U-Boot) zdefiniowane w drzewie urzadzeń jako część konfiguracji jadra w CONFIG_CMDLINE. Komunikaty: debug ustawienie poziomu logowania konsoli na najwyższy poziom (8) init= program init uruchamiany z zamontowanego systemu plików (domyślnie /sbin/init) lpj= ustawia loops_per_jiffy panic= jak się zachowa jadro w przypadku kernel panic jeśli >0 - liczba sekund przed ponownym uruchomieniem jeśli = 0 - system czeka w nieskończoność (domyślnie) jeśli <0 - system uruchamia się ponownie bez żadnego opóźnienia GUT Intel 2015/16 49/51
Linia poleceń jadra Komunikaty: quite ustawia poziom logowania konsoli na 1 rdinit= init program z ramdisk (domyślnie /init) ro montuje urzadzenie root-a jako read-only (bez wpływu na ramdisk) root= urzadzenie reprezentujace system plików root-a rootdelay= liczba sekund przed próba zamontowania urzadzenia roota (domyślnie 0) rootfstype= rodzaj systemu plików urzadzenia roota (wymagane dla jffs2) rootwait czeka na wykrycie urzadzenia roota (np. karty SD) rw montuje urzadzenie root-a jako read-write (domyślnie) GUT Intel 2015/16 50/51
Literatura Ch. Simmonds, Mastering Embedded Linux Programming, PACKT 2015 Linux Kernel Newbies, kernelnewbies.org Linux Weekly News, www.lwn.net GUT Intel 2015/16 51/51