Lekcja 07- Moduły

Wstęp

Temat modułów został poruszony w lekcji o kernelu, w tej lekcji chciałbym przedstawić sposoby użycia modułów. Lekcja ta ma raczej zasygnalizować Wam ten aspekt ponieważ sądzę, że póki co wasze systemy będą dosyć niewielkie i nie będzie problemem wkompilowywanie wszystkiego do kernela. Podkreślę też na wstępnie, że nie będziemy się tutaj zajmować implementacją żadnego modułu.

Dla przypomnienia. Czym jest moduł? Moduł to komponent, który może zostać załadowany do kernela jak i odładowany(nie wiem czy to najlepsze słowo w tym przypadku) z niego. Skompilowany moduł ma rozszerzeni .ko co jest skrótem od kernel object. Moduły mogą mieć zależności między sobą co może niekiedy utrudniać pracę z nimi.

Mogło się również nasunąć pytanie: „ej, ale mówiłeś, że ten kernel to monotlityczny jest czy coś takiego? W sensie, że to jedna całość jest, to skąd moduły?”. Zgadza się tak mówiłem i podtrzymuję to zdanie. Moduł po załadowaniu staje się częścią kernela. Jaki to ma wpływ na system? Taki jak wspomniano w lekcji o kernelu, jeśli w tym module będzie jakiś błąd to może nam się cały system zawiesić.

Trzeba też mieć na uwadze, że moduły są budowane dla konkretnej wersji kernel. W ogólności możemy zmusić system aby załadował moduł, który był zbudowany dla innej wersji kernela niż używana wersja, ale nie jest to zalecana operacja.

Konfiguracja kernela

W tej lekcji zaczniemy trochę nietypowo bo nie zaczniemy od omówienia tematyki, a od konfiguracji Linuksa. Tak będzie prościej.

Do prezentacji pracy z modułami wykorzystamy trochę zmodyfikowany przykład z poprzedniej lekcji. Przejdź do swoich źródeł kernela i otwórz menuconfig. Opcje, które ostatnio ustawiałeś jako wkompilowane w kernel ustaw teraz jako moduły. Dla przypomnienia były to opcje:

  1. CONFIG_IIO
  2. CONFIG_I2C_MUX
  3. CONFIG_INV_MPU6050_I2C

Jeśli używasz RPi4 to pozostałe opcje, które zmieniałeś czyli:

  1. CONFIG_I2C_CHARDEV
  2. CONFIG_I2C_BCM2835
  3. CONFIG_I2C_BRCMSTB

pozostaw dalej jako wkompilowane w kernel dla uproszczenia.

Gdy już zmienisz ustawienia Linuksa przebuduj go, a następnie wgraj obraz kernela i device-tree do odpowiedniego miejsca na karcie pamięci. Nie przekładaj jeszcze karty do płytki.

Instalacja modułów

Podczas budowania kernela powstaje wiele plików. Do tej pory nas interesowały jedynie dwa- obraz kernela oraz device-tree. Tym razem interesują nas również zbudowane moduły. Ich już trochę jest i raczej nikomu nie chciałoby się kopiować każdego z nich oddzielnie. Zestaw Makefile’i kernelowych dostarcza rozwiązanie tego problemu, wystarczy, że wykonasz następującą komendę:

# BBB
sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- \
    INSTALL_MOD_PATH=/media/user/rootfs modules_install
# RPi4
sudo make ARCH=arm64 CROSS_COMPILE=aarch-64linux-gnu- \
    INSTALL_MOD_PATH=/media/user/rootfs modules_install

Powyższa komenda instaluje zbudowane moduły pod ścieżką wskazaną przez INSTALL_MOD_PATH w katalogu lib/module/wersja_linuksa. Pamiętaj aby zawsze podawać tę ścieżkę inaczej moduły zostaną zainstalowane na twoim komputerze.

I to wszystko co musimy zainstalować na karcie, żeby mieć pewność, że wszystko się poprawnie zapisało wykonaj komendy:

sync
sudo umount /media/user/*

Teraz możesz przełożyć kartę do swojej płytki i ją uruchomić.

Zaznaczę jeszcze, że gdy używamy buildsystemu takiego jak Buildroot i będziemy budowali za jego pomocą kernel to moduły zostaną załadowane automatycznie.

Dodatkowo warto też zwrócić uwagę, że w systemie mogą się znajdować moduły dla różnych wersji Linuksa.

Ładowanie modułów

Zacznijmy może od tego co się zmieniło w systemie. Przejdź do katalogu w którym znajduje się reprezentacja twojego urządzenia:

# BBB
cd /sys/bus/i2c/device/2-0068
# RPi4
cd /sys/bus/i2c/device/1-0068

Wykonaj komendę ls i czego nie widzisz? Tak brakuje nam katalogu iio:device0, w którym są dostępne wszystkie dane pochodzące od akcelerometra. Ale mamy przecież sterownik do MPU6050 na karcie pamięci więc ogarniemy tak żeby był ten katalog.

Przejdź do katalogu w którym znajduje się moduł do obsługi MPU6050:

cd /lib/modules/wersja_linuksa/kernel/drivers/iio/imu/inv_mpu6050/

Mamy tam dwa moduły, my docelowo chcemy załadować inv-mpu6050-i2c.ko. To ten moduł umożliwia nam obsługę MPU6050 za pomocą magistrali I2C. Spróbujmy go zatem załadować w najprostszy sposób za pomocą programu insmod:

insmod inv-mpu6050-i2c.ko

No i coś nie poszło, pojawiło się kilka błędów:

[  371.873163] inv_mpu6050_i2c: Unknown symbol inv_mpu_core_probe (err -2)
[  371.880003] inv_mpu6050_i2c: Unknown symbol i2c_mux_alloc (err -2)
[  371.886235] inv_mpu6050_i2c: Unknown symbol inv_mpu_pmops (err -2)
[  371.892499] inv_mpu6050_i2c: Unknown symbol i2c_mux_del_adapters (err -2)
[  371.899346] inv_mpu6050_i2c: Unknown symbol i2c_mux_add_adapter (err -2)
[  371.908395] inv_mpu6050_i2c: Unknown symbol inv_mpu_core_probe (err -2)
[  371.915109] inv_mpu6050_i2c: Unknown symbol i2c_mux_alloc (err -2)
[  371.921432] inv_mpu6050_i2c: Unknown symbol inv_mpu_pmops (err -2)
[  371.927649] inv_mpu6050_i2c: Unknown symbol i2c_mux_del_adapters (err -2)
[  371.934502] inv_mpu6050_i2c: Unknown symbol i2c_mux_add_adapter (err -2)
insmod: can't insert 'inv-mpu6050-i2c.ko': unknown symbol in module, or unknown parameter

Te problemy wynikają z faktu, że moduł inv-mpu6050-i2c posiada zależności w innych modułach. Musimy je więc jakoś załadować. Tylko, które dokładnie musimy? I czy trzeba je wszystkie po kolei ładować insmodem?

Na szczęście istnieje rozwiązanie tych dwóch problemów i nie musimy ani wiedzieć co musimy po kolei załadować i nie musimy też wydawać miliona komend, wystarczy dokładnie jedna komenda! Naszym rozwiązaniem problemu jest program modprobe. Wykonaj następującą komendę:

modprobe inv-mpu6050-i2c.ko

I tym razem sukces! Sprawdź teraz czy katalog z plikami akcelerometra istnieje:

# BBB
ls /sys/bus/i2c/devices/2-0068/iio\:device0/
# RPi4
ls /sys/bus/i2c/devices/1-0068/iio\:device0/

Powinna wyświetlić się zawartość znana Tobie z poprzedniej lekcji. Czyli sukces.

Jak chcesz zobaczyć jeszcze jedną sztuczkę to możesz zrestartować swoją płytkę i załaduj sterownik do MPU6050 będąc w dowolnym katalogu:

cd /
modprobe inv-mpu6050-i2c.ko

I to też się udało! W dociekliwych głowach być może pojawiło się pytanie: „Ale skąd ten modprobe wie co ma załadować i czemu nie muszę mu podawać dokładnej ścieżki do modułu?”.

Istnieje plik modules.dep znajdujący się w /lib/modules/wersja_linuksa. To w tym pliku znajduje się lista modułów i zależności pomiędzy nimi. Plik ten jest generowany podczas instalacji modułów za pomocą programu depmod. Jeśli miałbyś kiedyś potrzebę wygenerowania zależności pomiędzy modułami to możesz wykonać komendę:

depmod -b /ścieżka/do/rootfs -w wersja_linuksa

W naszym przypadku by to wyglądało tak:

depmod -b /media/user/rootfs -w 5.14.1

Sprawdzanie listy załadowanych modułów

Sprawdzanie listy załadowanych modułów jest bardzo proste. Służy do tego komenda lsmod. Po wykonaniu tej komendy powinieneś zobaczyć takie wyjście:

Module                  Size  Used by    Not tainted
inv_mpu6050_i2c        16384  0 
inv_mpu6050            28672  2 inv_mpu6050_i2c
industrialio_triggered_buffer    16384  1 inv_mpu6050
kfifo_buf              16384  1 industrialio_triggered_buffer
i2c_mux                16384  1 inv_mpu6050_i2c
industrialio           61440  3 inv_mpu6050,industrialio_triggered_buffer,kfifo_buf

lsmod pokazuje nazwy załadowanych modułów, ich rozmiary oraz przez ile i jakie moduły są używane. „Not tainted” oznacza, że wszystko jest zrobione tak jak Linus nakazał czyli wszystkie modułu używają licencji GPL oraz używane moduły zostały zbudowane dla obecnie używanej wersji kernela. W przypadku Linuksa słowo tainted czyli skażony będzie oznaczać, że coś jest nie tak albo, że coś nie może być sprawdzone.

Usuwanie modułu

Gdy już nie chcemy używać danego modułu możemy go usunąć poleceniem rmmod. Zwróć uwagę, że nie możemy usunąć modułu, który jest używany przez inny moduł. Czyli w naszym przykładzie możemy wykonać operacje:

rmmod inv_mpu6050_i2c

ponieważ ten moduł nie jest używany przez żaden inny, ale nie możemy wykonać np. tego:

rmmod industrialio

ponieważ ten moduł jest używany przez aż 3 inne moduły.

Możesz również użyć polecenia modprobe do usunięcia modułu:

modprobe -r inv_mpu6050_i2c

To polecenie usunie pojedynczy moduł, jeśli chciałbyś usunąć dany moduł wraz z modułami, które go używają wykonaj polecenie:

modprobe --remove-dependencies industrialio

Ten wpis został opublikowany w kategorii Kurs budowania Linuksa. Dodaj zakładkę do bezpośredniego odnośnika.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *