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



powrót do strony głównej