Systemy Operacyjne – semestr drugi
Wykład pierwszy
„Rodowód” Linuksa
Inspiracją dla autora systemu Linux był system Minix napisany przez Andrew Tanenbauma, który bazował z kolei na systemie Unix. Termin Unix w obecnych czasach oznacza całą rodzinę systemów operacyjnych opartych na wspólnej filozofii, której r
ź ód e
ł m jest projekt systemu operacyjnego, jaki powstał w roku 1969 w Bell Labs, będących częścią amerykańskiego koncernu AT&T. System ten jest pochodną innego systemu operacyjnego Multics, nad którego powstaniem pracowano w trzech ośrodkach: w MIT, w AT&T i w General Electric. Firma AT&T po pewnym czasie wycofała się z prac, ale osoby zatrudnione przy tym projekcie pozosta y
ł jej pracownikami. Do ich grona nale e
ż li między
innymi Ken Thompson, Dennis Ritchie i Douglas McIlroy. To ta trójka odegra a
ł największą rolę w powstaniu pierwszej
wersji systemu Unix. Pracę nad nią rozpoczęli Ken Thompson i Dennis Ritchie, pisząc ca y ł kod w assemblerze
komputera PDP-7, który poznali tworząc wcześniej grę „Space Travel” dla tej platformy sprz t ę owej. Znaczący wkład do
projektu systemu wniósł Douglas McIlroy, opracowywuj c
ą np.: łącza nienazwane, które są jednym z r
ś odków
zapewniających komunikację między procesami, a tak e
ż wyznaczając regu y
ł tworzenia kodu, które dziś są częścią
in y
ż nierii oprogramowania. W roku 1973 kod r
ź ódłowy Uniksa został w większości przepisany w języku C, który
opracował Dennis Ritchie1, dzięki czemu system ten stał się a
ł twy w przenoszeniu na inne platformy sprzętowe2.
W roku 1980 roku AT&T zaczę o
ł sprzedawać wersję systemu Unix, którą nazwano System III. Wersja ta była rozprowadzana razem z kodem źródłowym, a więc wiele firm i innych instytucji, które nabyło ten system, mog o ł go
modyfikować wprowadzając nowe funkcjonalno c
ś i. Pod względem wprowadzonych do tego systemu innowacji
wyró n
ż iał się Uniwersytet Berkeley. Wszystkie wersje systemu Unix, które powsta y
ł w tej placówce by y
ł oznaczane
nazwą BSD (Berkeley System Distribution). Najwi k
ę szy wk a
ł d do prac nad Uniksem BSD wniósł Bill Joy. To
w Berkeley powstał s y
ł nny edytor vi, pow o
ł ka csh, a tak e
ż tu dodano do j d
ą ra podsystemy odpowiedzialne za obsługę
protoko u
ł TCP/IP i pamięć wirtualn .
ą Z czasem te funkcjonalności zostały włączone do dystrybucji rozpowszechnianej
przez AT&T (wersja System V). W chwili obecnej rozwój gał z
ę i BSD jest kontynuowany przez takie projekty, jak
FreeBSD, OpenBSD, NetBSD i DragonFly BSD. Aby uporządkować rozwój Uniksa opracowano kilka standardów, z których najważniejszymi są POSIX oraz SUS. Większość odmian Uniksa spe n
ł ia wymagania tych standardów.
W chwili obecnej niemal e
ż wszystkie odmiany Uniksa to systemy wielodostępne i wielozadaniowe, obs u ł gujące
wszystkie cechy nowych architektur sprzętowych. Jest jednak e
ż kilka cech, które odró n
ż iają go od innych systemów
operacyjnych. Przede wszystkim jest on systemem o stosunkowo prostej budowie (jego twórcy stosowali zasadę Keep It Small and Simple – KISS3). Jądro Uniksa implementuje niewielką liczbę wywo a
ł ń systemowych, podczas gdy w innych
systemach ta liczba dochodzi do dziesiątek tysięcy. Wywo a
ł nia systemowe w Uniksie zosta y
ł opracowane w myśl
zasady sformułowanej przez Douglasa McIlroy'a: „Rób jedną rzecz, ale rób ją dobrze”, co oznacza, e ż wykonują jedną
czynność, ale w sposób uniwersalny. Większość elementów w systemie Unix jest traktowana jako plik, co znacznie ułatwia zarządzanie urządzeniami i danymi. W ko c
ń u Unix ma mechanizmy, które pozwalają na tworzenie w krótkim
czasie nowych procesów, oraz dostarcza r
ś odków prostej komunikacji między nimi4. Unix był tak e
ż jednym
z pierwszych systemów operacyjnych, które zosta y
ł napisane w języku wysokiego poziomu, co uczyni o
ł go systemem
przenośnym.
Historia Linuksa
Linux5 jest systemem uniksopodobnym (ang. Unix-like), ale nie jest Uniksem. Pracę nad jądrem tego systemu rozpoczął Linus Benedict Torvalds w roku 1991, wzorując się na wspomnianym systemie Minix. Nie oznacza to jednak, e
ż kod przez niego napisany zawiera fragmenty kodu Miniksa lub oryginalnego Uniksa. Linux powstał „od zera”, ale jest zgodny z standardami POSIX i SUS. Pierwsza wersja kodu r
ź ódłowego Linuksa mia a
ł ponad 10 tysięcy wierszy
kodu. Bież c
ą e wersje liczą oko o
ł 5 milionów linii kodu i są tworzone przez obszerną grupę programistów, wspó p
ł racujących ze sobą przez Internet. Prace tej grupy koordynuje oczywiście Linus Torvalds. Kod r ź ódłowy Linuksa
dostępny jest na zasadach licencji GNU GPL v2.0, która gwarantuje, e
ż jest on wolnodostępny. Należy zauwa y
ż ć, e
ż
słowo Linux ma dwa znaczenia. W powyższym tekście oznacza o
ł ono po prostu jądro systemu operacyjnego. W drugim
znaczeniu, oznacza podstawową wersję systemu operacyjnego, która oprócz jądra zawiera również zestaw narzędzi do kompilacji programów w językach C i assembler, bibliotekę języka C i powłokę systemową. Wszystkie wymienione tu elementy, oprócz jądra, zosta y
ł stworzone przez fundację GNU, zało o
ż ną przez Richarda Stallmana. Istnieje wiele
odmian systemu Linux, nazywanych dystrybucjami, które oprócz wymienionych tu podstawowych narzędzi zawierają również inne oprogramowanie np. system X Window, będ c
ą y implementacją interfejsu graficznego u y
ż tkownika, oraz
1
Pozostałą część pozostawiono w assemblerze.
2
Dok a
ł dniej – a
ł twiej było go przenieść na inne platformy, ni
ż system napisany w ca o
ł c
ś i w assemblerze.
3
W a
ł ściwie ten skrót rozwijano jako "Keep It Simple, Stupid!". Osobiście wolę wersję a ł godniejszą :-)
4
Ostatnio pojawiają się g o
ł sy, e
ż te mechanizmy komunikacji stają się powoli przestarza e
ł .
5
W kwestii językowej: piszemy Linux, ale odmieniamy: Linuksa, Linuksowi, itd.
1
Systemy Operacyjne – semestr drugi
szereg innych aplikacji, niezbędnych u y
ż tkownikom. W dalszej części wyk a
ł du słowo „Linux” będzie oznaczało
wyłącznie j d
ą ro systemu operacyjnego. Pierwotnie Linux przeznaczony był wyłącznie na architekturę i386 (powstał na komputerze wyposażonym w procesor Intel 80386). Obecnie obs u
ł guje obs u
ł guje całą gamę mniej lub bardziej
popularnych platform sprzętowych, takich jak : AMD64, PowerPC, Sparc, Ultra Sparc, MIPS. Wspiera również architektury równoległe (SMP, ostatnio NUMA).
Zasada działania j d
ą ra
Jądro (ang. kernel) systemu (nazywane czasami rdzeniem (ang. core)) jest częścią systemu operacyjnego, która stale rezyduje w pamięci komputera, nadzoruje pracę sprzętu i aplikacji u y
ż tkownika, oraz dostarcza tym ostatnim
określonych us u
ł gi. Najczęściej jądro (monolityczne) zawiera takie elementy, jak: procedury obs u ł gi przerwa ,
ń
mechanizm szeregowania, mechanizm zarządzania pami c
ę ią i obs u
ł gi plików. Jądro pracuje w trybie
uprzywilejowanym, co oznacza, e
ż ma nieograniczony dostęp do wszystkich zasobów systemu komputerowego, w tym
do pamięci operacyjnej. Obszar pamięci zajmowany przez jądro nazywany jest przestrzenią adresową j d ą ra. Dosyć
często o czynnościach wykonywanych przez jądro mówimy, e
ż zachodzą w przestrzeni j d
ą ra. Procesy u y
ż tkownika
pracują w trybie procesora, w którym dostęp do zasobów systemu jest ograniczony. Obszary pami c ę i
przyporządkowane aplikacjom użytkownika nazywamy przestrzenią adresową użytkownika. Analogicznie jak ma to miejsce w przypadku j d
ą ra, o czynnościach, które są wykonywane przez aplikacje mówimy, e
ż są wykonywane
w przestrzeni użytkownika. Do komunikacji między procesami u y
ż tkownika, a jądrem s u
ł żą wywo a
ł nia systemowe.
Zazwyczaj aplikacje u y
ż tkownika nie wywo u
ł ją ich bezpośrednio, lecz korzystają z funkcji dostępnych w standardowej
bibliotece języka C (libc). Niektóre z tych funkcji zawierają sam kod wywo a
ł nia funkcji systemowej, czyli są swego
rodzaju „opakowaniami” na wywo a
ł nia systemowe, inne funkcje wykonują przed zainicjalizowaniem wywołania
systemowego dodatkowe czynno c
ś i, jeszcze inne mogą korzystać z większej liczby wywołań systemowych. Są również
funkcje, które nie korzystają w ogóle z wywołań systemowych. Jeśli jądro wykonuje za pośrednictwem wywołania systemowego jakąś usługę dla aplikacji u y
ż tkownika, to działa w kontek c
ś ie procesu, co oznacza, e
ż kod jądra
realizujący to wywo a
ł nie ma dostęp do informacji o procesie, który korzysta z tego wywo a
ł nia. W takiej sytuacji mo n
ż a
również powiedzieć, e
ż aplikacja wykonuje wywołanie systemowe w przestrzeni jądra, choć nie jest to określenie poprawne. J d
ą ro systemu jest oprogramowaniem sterowanym zdarzeniami. Zdarzenia pochodzące od sprzętu są sygnalizowane za pomocą przerwań. Te przerwania pojawiają się zazwyczaj asynchronicznie6. Z ka d ż ym z nich jest
związany pewien numer, na podstawie którego jądro odnajduje i wykonuje odpowiednią procedurę obs u ł gi tego
przerwania. Te procedury są wykonywane w kontek c
ś ie przerwania, co oznacza, e
ż nie są związane z a
ż dnym
konkretnym procesem. Pozwala to przyspieszyć ich działanie. Jądro ma również mo l
ż iwość blokowania wszystkich
przerwań lub tylko wybranych przerwa ,
ń na czas obsługi jednego z nich. Reasumując, procesor w systemie
komputerowym, który kontroluje Linux, albo wykonuje kod aplikacji, albo kod jądra w kontekście procesu, albo kod jądra w kontekście przerwania.
Porównanie z innymi systemami uniksowymi
Jądro sytemu Linux jest podobne do rdzeni innych systemów wzorowanych na Uniksie, ale istnieje również kilka znacz c
ą ych ró n
ż ic. Podobnie jak w większości Uniksów, jądro Linuksa jest monolityczne, ale udostępnia mo l ż iwość
korzystania z a
ł dowanych dynamicznie modu ó
ł w, które zazwyczaj są sterownikami urządze .
ń Linux obs u
ł guje również
symetryczne przetwarzanie wieloprocesorowe (SMP), co nie jest cechą wszystkich Uniksów. Podobnie jak Solaris i IRIX, Linux mo e
ż obs u
ł giwać od wersji 2.6 wywłaszczanie zadań jądra. Jądro Linuksa nie rozró n
ż ia w t
ą ków i procesów,
traktuje je niemal e
ż w ten sam sposób. Niektóre mechanizmy, które przez twórców Uniksa zosta y
ł uznane za mało
wydajne lub wręcz za zb d
ę ne (jak np. obsługa strumieni) nie zosta y
ł w ogóle zaimplementowane w jądrze Linuksa. Ze
względów wydajnościowych strony pamięci zajmowanej przez jądra Linuksa nie podlegają wymianie. Ta ostatnia cecha jest konsekwencją faktu, e
ż Linux jest tworzony przez ogromną społeczność programistów – w wyniku dyskusji i rozwa a
ż ń cz o
ł nkowie tej spo e
ł czność doszli do wniosku, e
ż taki mechanizm spowodowałby pogorszenie wydajności
systemu. Kolejne wersje jądra są oznaczane symbolami składającymi się z trzech7 liczb rozdzielonych kropkami.
Pierwsza liczba to g ó
ł wny numer wersji, druga to numer podwersji – jeśli jest parzysta, to jest to wersja stabilna, jeśli nieparzysta to wersja rozwojowa. Trzeci numer jest numerem rewizji i oznacza, e
ż w poprzedniej wersji jądra znaleziono
jakiś błąd i wydano poprawioną wersję8. Poszczególne dystrybucje Linuksa mogą dodawać inne liczby do tych przedstawionych wy e
ż j.
6
Istnieją równie
ż przerwania synchroniczne.
7
Pojawiła się również czwarta liczba (j d
ą ro 2.6.8.1). Jej wprowadzenie związane by o
ł z naprawieniem powa n
ż ego błędu,
który wykryto tu
ż po wypuszczeniu wersji 2.6.8. Po d u
ł giej przerwie wprowadzono ją ponownie.
8
Wraz z pojawieniem się jądra 2.6.0 proponowano zmianę znaczenia numeru rewizji, ale ta propozycja chyba zosta a ł
odrzucona.
2
Systemy Operacyjne – semestr drugi
Krótka charakterystyka metodologii programowania jądra
Ponieważ jądro systemu z definicji ma być optymalne pod względem wykorzystania pami c
ę i i szybkości dzia a
ł nia, to
przeglądając kod r
ź ód o
ł wy Linuksa mo e
ż my spotkać wiele fragmentów, które a
ł mią niektóre przyjęte kanony dobrze
napisanego oprogramowania (jak cho b
ć y u y
ż wanie instrukcji goto). Tak, jak w przypadku innych systemów
uniksopodobnych, większość kodu Linuksa jest napisana w języku C, a tylko niewielka częś , ć zale n
ż a od konkretnej
architektury sprzętowej w assemblerze9. Pisząc w a
ł sny fragment j d
ą ra, czy to w postaci modu u
ł , czy ingerując
w istniejący kod nale y
ż mieć świadomość pewnych ogranicze .
ń Przede wszystkim, pisz c
ą kod jądra nie mamy dostępu
do funkcji standardowej biblioteki C (libc, a konkretniej glibc). Jest to naturalną konsekwencją tego, e ż to jądro
stanowi wsparcie dla tej biblioteki, a nie odwrotnie. Na szczęście zostały zaimplementowane w jądrze funkcje, będące substytutami tych z biblioteki glibc. Zamiast funkcji printf jest funkcja printk, zamiast funkcji do manipulowania a
ł
c
ń uchami znaków, które są dostępne po włączeniu pliku string.h są podobne funkcje dost p ę ne po włączeniu pliku
linux/string.h. Uogólniając – jądro mo e
ż korzystać wyłącznie ze swoich plików nag ó
ł wkowych, niedozwolone jest
korzystanie z zewn t
ę rznych plików nag ó
ł wkowych. Kod jądra jest pisany z wykorzystaniem standardu ISO C99 języka
C, oraz z wykorzystaniem rozszerzeń GNU C. Oznacza to, e
ż mo e
ż być kompilowany prawie wył c
ą znie przez kompilator
C pochodzący z GCC (Gnu Compilers Collection). Do wspomnianych rozszerzeń nale y ż wykorzystywanie funkcji
rozwijanych w miejscu wywo a
ł nia (ang. inline functions), obecnie wprowadzone już do standardu j z ę yka C. Takie
funkcje są bardziej bezpieczne i eleganckie niż makra, a przy tym równie skuteczne. Funkcje rozwijane w miejscu wywołania są definiowane z u y
ż ciem słów kluczowych static i inline. Nale y
ż jednak pamiętać, aby nie nadu y
ż wać
takich funkcji, bo prowadzi to do niepotrzebnego zwiększenia objętości kodu wynikowego. Innym z wykorzystywanych rozszerzeń jest mo l
ż iwość wprowadzania wstawek assemblerowych do kodu napisanego w języku C. Te wstawki są stosowane wszędzie tam, gdzie wymagana jest optymalizacja szybkości dzia a
ł nia jądra. Kompilator gcc udostępnia
dwie dyrektywy wspomagaj c
ą e optymalizację kodu. Są to likely() i unlikely(). Służą one do oznaczania prawdopodobie s
ń twa wykonania kodu w instrukcjach warunkowych, np.: jako unlikely() mo e
ż my zaznaczyć warunki
związane z obs u
ł gą większości wyjątków. Pisz c
ą kod dzia a
ł jący w przestrzeni jądra musimy pami t
ę a ,
ć e
ż nie mamy
„siatki zabezpieczającej” w postaci ochrony pamięci. Jeśli będziemy l
ź e zarządzać pamięcią jądra, to możemy naruszyć
stabilność systemu operacyjnego. Jeśli chcemy u y
ż ć liczb i arytmetyki zmiennoprzecinkowej, to musimy sami
oprogramować FPU, jednak e
ż nigdy dotąd potrzeba u y
ż wania takiej arytmetyki w przestrzeni jądra nie zaistnia a
ł .
Rozmiar stosu jądra10 jest ograniczony i wynosi w przypadku architektur 32 bitowych 8KB11, a w przypadku 64
bitowych procesorów Alpha 16KB. Procesy u y
ż tkownika dysponują stosem znajdującym się w przestrzeni
u y
ż tkownika, który nie jest ograniczony12. Programując w jądrze musimy pamiętać o synchronizacji dostępu do zasobów wspó d
ł zielonych. Mamy do dyspozycji np. takie środki synchronizacji jak semafory i rygle pętlowe (ang. spin-lock), czyli semafory z aktywnym oczekiwaniem. Ostatnią wa n
ż ą rzeczą, o której nale y
ż pamiętać pisząc lub zmieniając
kod jądra, to przenośność. Nawet pisząc kod w j z
ę yku C mo e
ż my doświadczyć ró n
ż ych problemów, jak choćby
porządek bitów i bajtów w słowie, czy rozmiary poszczególnych typów danych.
Bardzo krótkie wprowadzenie do tworzenia modu ó
ł w jądra 2.613
Modu y
ł jądra są formą bibliotek współdzielonych, jednak e
ż nie są one dynamicznie łączone z procesami użytkownika
lecz z samym jądrem. Dzięki modu o
ł m mo e
ż my łatwo dodawać lub usuwać nową funkcjonalność do jądra, bez
konieczności restartowania systemu. Pisząc w a
ł sny moduł nale y
ż być świadomym tego, że błąd w module mo e
ż
spowodować powa n
ż e konsekwencje dla pracy systemu, a w niektórych przypadkach mo e
ż również doprowadzić do
uszkodzenia sprzętu.
Sposób pisania modułów dla jądra Linuksa 2.6 ró n
ż i się od tego, który obowiązywał we wcześniejszych wersjach.
Przede wszystkim, aby skompilować moduł nale y
ż mieć zainstalowany ca y
ł kod r
ź ód o
ł wy jądra w systemie. Kompilację
wykonujemy za pomocą programu narzędziowego „make”. W ramce poniżej przedstawiona została zawartość pliku konfiguracyjnego (Makefile) dla tego programu.
9
Dodajmy: w notacji AT&T.
10 Określenia co najmniej mylące. Chodzi o stos jądra, który jest przydzielany ka d ż emu procesowi użytkownika
i u y
ż wany je l
ś i ten proces zainicjalizuje wywołanie systemowe.
11 Trwa dyskusja, czy nie zmniejszyć tego rozmiaru do 4KB.
12 Dok a
ł dniej: po wykonaniu z poziomu pow o
ł ki tcsh polecenia limit otrzymuję informację, że stos w przestrzeni
u y
ż tkownika ma rozmiar 8MB, po wykonaniu polecenia unlimit (konto u y
ż tkownika nieuprzywilejowanego)
i ponownym wykonaniu limit okazuje si ,
ę e
ż rozmiar stosu jest nieograniczony (moja bież c
ą a dystrybucja Linuksa to
Mandriva Linux 2005 LE).
13 Całość rozdziału (wraz z przyk a
ł dami) bazuje na ksią c
ż e” Jonathan Corbet, Alessandro Rubini, Greg Kroah-Hartman,
"Linux Device Drivers"
3
Systemy Operacyjne – semestr drugi
Treść tego pliku zosta a
ł zapo y
ż czona z ksią k
ż i
# If KERNELRELEASE is defined, we've been invoked from the
"Linux Device Drivers" i uzupe n
ł iona o regułę
# kernel bulid system an can use its language.
„clean” usuwaj c
ą ą pliki powsta e
ł w wyniku
ifneq ($(KERNELRELEASE),)
kompilacji lub edycji pliku z kodem r
ź ódłowym
obj-m := first.o
modułu. Aby program „make” wykonał regułę
„clean” nale y
ż go wywołać następująco: „make
# Otherwise we were called directly from command
clean”. Wywołania programu bez parametru
# line; invoke the kernel bulid system.
spowoduje skompilowanie modułu o nazwie
„first”. Który moduł ma zostać skompilowany
else
okre l
ś a wiersz „obj-m := first.o” pliku
konfiguracyjnego.
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
clean:
rm -f *~
rm -rf .tmp_versions
rm -f first.ko
rm -f first.mod.c
rm -f first.mod.o
rm -f first.o
rm -f .first*
Moduł o nazwie „first” jest modułem typu „Hello World!”, który powinni stworzyć wszyscy początkujący programiści, chc c
ą y pisać w a
ł sne modu y
ł (kod nale y
ż umieścić w pliku o nazwie „first.c”):
#include<linux/init.h>
#include<linux/module.h>
static int __init first_init(void)
{
printk(KERN_ALERT"Welcome\n");
return 0;
}
static void __exit first_exit(void)
{
printk(KERN_ALERT"Good bye\n");
}
module_init(first_init);
module_exit(first_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Arkadiusz Chrobot <a.chrobot@tu.kielce.pl>");
MODULE_DESCRIPTION("Another \"Hello World!\" kernel module :-)");
MODULE_VERSION("1.0");
4
Systemy Operacyjne – semestr drugi
Pliki nagłówkowe „linux/init.h” i „linux/module.h” są dołączane do kodu ka d
ż ego modułu jądra. Zawierają między
innymi makra preprocesora, które zostaną opisane ni e
ż j. Funkcje o nazwach „first_init” i „first_exit” są odpowiedzialne
odpowiednio za wykonanie inicjalizacji tuż po załadowaniu modułu i deinicjalizację tuż przed usunięciem modu u ł
z pamięci operacyjnej. Obie funkcje zostały zdefiniowane z użyciem słowa kluczowego static, co oznacza, e ż są
dostępne wewnątrz przestrzeni nazw modu u
ł „first”, co zapobiega ewentualnemu konfliktowi nazw. Żadna z tych
funkcji nie przyjmuje parametrów wywołania. Funkcja „first_exit” nic również nie zwraca, natomiast „fist_init” zwraca wartość typu int. Jeśli u y
ż libyśmy terminologii znanej z języków obiektowych, to o tych funkcjach możemy myśleć jako o konstruktorze i destruktorze. W nagłówkach takich funkcji mogą, ale nie muszą występować znaczniki __init i __exit.
Pierwszy sygnalizuje, e
ż funkcja jest u y
ż wana wył c
ą znie podczas inicjalizacji modułu i po jej wykonaniu mo n
ż a zwolnić
pamięć na nią przydzieloną. Drugi znacznik sygnalizuje, e
ż funkcja jest u y
ż wana wyłącznie podczas sprzątania. Ten
znacznik ma znaczenie je l
ś i moduł jest włączany do kodu j d
ą ra na etapie kompilacji, lub gdy j d
ą ro jest tak
skonfigurowane, e
ż nie pozwala na usunięcie za a
ł dowanych modu ó
ł w. Makra module_init oraz module_exit przyjmują
jako parametry wywo a
ł nia adresy funkcji odpowiedzialnych za inicjalizację i sprz t
ą anie po module. Te makra
pozwalają programiście powiadomić kompilator, które funkcje będą odpowiedzialne za inicjalizację i sprzątanie po module. Cztery pozostałe makra nie są obowi z
ą kowe i pozwalają określić licencję na jakiej moduł jest
rozpowszechniany, autora modu u
ł (zalecane jest podanie jego adresu e-mail), opis modułu oraz jego wersj .
ę Wszystkie
te informacje są zapisane w postaci a
ł
c
ń uchów znaków, według konwencji obowiązującej w j z
ę yku C. Mając plik
wynikowy mo e
ż my je odczytać posługuj c
ą się programem „modinfo” np.: „modinfo first.ko”. Nieumieszczenie w kodzie
modu u
ł makra MODULE_LICENSE lub podanie nazwy innej niż któraś z wolnych licencji spowoduje wystąpienie komunikatu ostrzegawczego, mówiącego o tym, e
ż jądro zosta o
ł „ska o
ż ne” modułem o nieznanej lub niedopuszczalnej
licencji. Nazwy licencji, które nie generują takiego ostrze e
ż nia, to: „GPL”, „GPL v2”, „ “GPL additional rights”, „Dual
BSD/GPL”, „Dual MPL/GPL”. Modu y
ł na licencji niewolnej mogą jako parametr tego makra przekazać „Proprietary”.
W kodzie modu u
ł występują wywołania funkcji „printk”. Warto zwrócić uwagę na znacznik będący argumentem wywołania tej funkcji i poprzedzający w a
ł ściwy ła c
ń uch znaków. Okre l
ś a on poziom wa n
ż ości wypisywanego
komunikatu lub inaczej poziom logowania. Istnieje kilka takich znaczników: KERN_EMERG – sytuacja awaryjna, KERN_ALERT – problem wymagający natychmiastowej reakcji, KERN_CRIT – sytuacja krytyczna, KERN_ERR – błąd, KERN_WARNING – ostrze e
ż nie, KERN_NOTICE – sytuacja normalna, ale zaszło zdarzenie godne zauwa e ż nia,
KERN_INFO – informacja, KERN_DEBUG – komunikat diagnostyczny. Komunikaty od modu ó
ł w jądra zapisywane są
w pliku dziennika systemowego (zazwyczaj /var/log/messages), wypisywane na konsolę i mo n ż a je wyświetlić na ekran
za pomocą polecenia „dmesg”. Moduły jądra mo e
ż my a
ł dować lub usuwać będąc zalogowanymi jako użytkownik „root”
lub jako u y
ż tkownik posiadaj c
ą y takie uprawnienia (patrz polecenia „sudo” i „su”). Korzystając z polecenia „insmod”
mo e
ż my za a
ł dować modu ,
ł a poleceniem „rmmod” usunąć, np.: „insmod first.ko”, „rmmod first”. Informacje o za a
ł dowanych modu a
ł ch mo e
ż my uzyskać używaj c
ą polecenia „lsmod”, również będąc zalogowanymi jako zwyk y
ł
u y
ż tkownik. Istnieją również inne narzędzia do obsługi modułów, które nie będą tutaj opisywane (np. „modprobe”). Ze względu na dosyć częste zmiany w API jądra warto zapoznać się z działaniem makrodefinicji preprocesora LINUX_VERSION_CODE i KERNEL_VERSION. Pierwsza zwraca wersję r
ź ódeł jądra zainstalowanych w systemie
w postaci wartości typu int, a druga zamienia numer wersji jądra przekazany jej przez argumenty w postaci trzech liczb (np. KERNEL_VERSION(2,6,19)) na postać numeryczną. Dzięki nim mo e
ż my napisać odpowiednie instrukcje
warunkowe dla preprocesora, aby włączał bądź nie określone fragmenty kodu do kompilacji.
5