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