Zmiany dotyczące modułów
1. Makra: module_init() i module_exit()
Mocno zalecane stało się stosowanie makr:
module_init(nazwa_funkcji_inicjującej_moduł)
module_exit(nazwa_funkcji_czyszczącej_moduł)
Mają one służyć do ustawiania funkcji inicjującej i czyszczącej modułu.
W jądrze 2.4 także można było z nich korzystać. Istniała jednak także możliwość zdefiniowania funkcji inicjującej o nazwie:
init_module() oraz czyszczącą o nazwie: cleanup_module().
Przy tak dobranym nazewnictwie, funkcji tych nie trzeba było ustawiać za pomocą powyższych makr.
2. Makro: MODULE_LICENSE()
Zalecane w jądrze 2.6 stało się też stosowanie makra : MODULE_LICENSE(nazwa_licencji),
którym ustawiamy licencje na jakiej tworzony jest dany moduł (makro to pojawiło się, tak naprawdę,
już w wersji jądra 2.4.10 ale nie było go w wersjach wcześniejszych jądra 2.4).
Słowo "zalecane" w punktach 1. i 2. oznacza, że tak naprawdę niezastosowanie się do reguł podanych w tych punktach wcale nie musi
spowodować niezaładowania danego modułu.
Pojawią się jednak ostrzeżenia, a podczas późniejszego korzystania mogą wystąpić nieoczekiwane błędy.
3. Nowe rozszerzenie
Moduły mają teraz nowe rozszerzenie:
.ko zamiast .o.
4. Parametry modułu
Do tej pory, aby zadeklarować jakąś zmienną jako parametr modułu należało używać makra:
MODULE_PARM(nazwa, typ),
gdzie nazwa oznaczała nazwę zmiennej (i parametru), zaś typ, typ tej zmiennej (i parametru).
W jądrze 2.6 aby móc deklarować parametry należy jawnie dołączyć do modułu plik:
linux/moduleparam.h,
a sama deklaracja powinna wyglądać na jeden z następujących sposobów:
a) najprostszy rodzaj deklaracji:
module_param(nazwa, typ, prawa),
gdzie pola nazwa i typ mają standardowe znaczenie, zaś pole prawa (tu i w następnych podpunktach)
oznacza prawa dostępu do tej zmiennej z zewnątrz modułu. Implementacja i wykorzystanie tego pola budzi, jak na razie,
pewne wątpliwości. Najbezpieczniejsze jest ustawienie go (dotyczy to także tego pola w następnych podpunktach) na zero.
b) gdy chcemy, by nazwa parametru widoczna z zewnątrz modułu (nazwa)
różniła się od nazwy zmiennej (zmienna) widocznej wewnątrz modułu:
module_param_named(nazwa, zmienna, typ, prawa).
c) gdy parametrem ma być string:
module_param_string(nazwa, string, dlugosc, prawa),
gdzie pole długość oznacza długość stringa nazwa i zazwyczaj jest ustawiane na sizeof(string).
d) gdy parametrem ma być tablica elementów określonego typu typ
module_param_array(nazwa, typ, liczba, prawa),
gdzie liczba jest zewnętrznym parametrem modułu i będzie ustawiona na liczbę podanych parametrów.
Przekazując do modułu wiele parametrów, piszemy je oddzielone przecinkami.
5. Aliasy do modułu
W jądrze 2.6 możemy wewnątrz modułu zdefiniować aliasy, pod jakimi moduł będzie
widziany z zewnątrz. Robimy to pisząc w kodzie modułu
MODULE_ALIAS(nazwa_aliasu).
6. Licznik odwołań do modułu
Zadaniem licznika odwołań do modułu jest dbanie o to, by moduł nie był usunięty,
gdy są jeszcze do niego jakieś odwołania z zewnątrz.
Licznik odwołań do modułu w jądrze 2.4 był zarządzany z wewnątrz tego modułu. Służyły do tego makra:
MOD_INC_USE_COUNT
MOD_DEC_USE_COUNT,
Należało umieścić je w kodzie odpowiednich funkcji, których wywołanie wiązało się z "odwołaniem do modułu"/"zwolnieniem modułu".
Czasami dodatkowo pojawiała się możliwość skorzystania z pola owner: (w strukturze file_operations)
i ustawienia go na THIS_MODULE, co powodowało, że licznik był zarządzany automatycznie.
Nie zawsze jednak dało się zastosować rozwiązanie nr dwa, zaś pierwsze rozwiązanie było dość niefortunne
i obciążające dla twórców modułów, którzy sami musieli dbać o licznik odwołań .
Mogło się to wiązać z licznymi błędami, zwłaszcza na etapie testowania danego modułu.
Dlatego też w jądrze 2.6 wprowadzono w tej kwestii zmianę.
Obecnie manipulować licznikiem referencji do modułu możemy jedynie z zewnątrz
modułu. Aby mieć możliwość skorzystania z jakiegokolwiek zasobu udostępnianego
przez moduł musimy najpierw w naszym kodzie (nie kodzie modułu !) wywołać funkcję:
int try_module_get(&moduł).
Następną czynnością powinno być sprawdzenie wartości zwróconej przez funkcję
(zero oznacza niepowodzenie (które może być spowodowane np. "unloadowaniem"
w danym momencie modułu, z którego chcemy wyeksportować np. zmienną)). W przypadku powodzenia możemu już bez przeszkód
korzystać z udostępnianych przez moduł symboli. Aby zwrócić referencję (zmniejszyć licznik) powinniśmy wywołać funkcję:
module_put().
Tak naprawdę, zmiana związana z licznikiem odwołań nie rozwiązuje problemu błędów (np. błędów na etapie testowania programów
odwołujących się do danego modułu), tylko ten problem delikatnie przesuwa w trochę inne miejsce.
Zamiast błędów związanych z zarządzaniem licznikiem z wewnątrz modułu, mogą się bowiem teraz pojawić inne.
Istnieje na przykład możliwość, że obcy program, odwołujący się do naszego modułu wywoła try_module_get(),
a zapomni wywołać module_put() lub po prostu zostanie jakoś niestandardowo przerwany. Wówczas licznik referencji do modułu
wciąż bedzie niezerowy, gdy tak naprawdę nikt już z tego modułu nie będzie korzystał.
Świadomi tego programiści jądra linuxa dostarczają więc dodatkowego makro:
CONFIG_MODULE_FORCE_UNLOAD,
którego umieszczenie w kodzie modułu daje poźniej możliwość wymuszenia "unloadowania" danego modułu.
Istnienie tego makra jest możliwe dzięki zmianie w sposobie zarządzania licznikiem odwołań do modułu.
Przy obecnej implementacji jądro wie bowiem kto jeszcze z danego modułu korzysta i kogo trzeba będzie o usunięciu danego modułu
"poinformować".
Makro to może być bardzo pomocne przy testowaniu programów i jest odpowiednikiem np. funkcji ioctl() z jądra 2.4,
z flagą oznaczającą zmniejszenie licznika (taka funkcja nie powinna być oczywiście udostępniana użytkownikowi ale w jądrze 2.4
na etapie testowania mogła okazać się bardzo pomocna).
7. Eksportowanie symboli
Zanim wyksportujemy do naszego modułu jakiś symbol z innego modułu musimy oczywiście użyć
try_module_get(&moduł_z_którego_chcemy_eksportować). Ponadto, inaczej niż to było w jądrze 2.4,
tutaj musimy jawnie podać, które symbole chcemy udostępniać innym (używając makra EXPORT_SYMBOL(nazwa_symbolu));
domyślnie bowiem żadne symbole nie są udostępniane. Oczywiście cała ta dywagacja dotyczy tylko
i wyłącznie zmiennych nieoznaczonych jako static, które eksportowane być nie mogą.
Dokumentacja