Strona główna
  2004.05 Tworzenie modułów ...

2004.05 Tworzenie modułów jądra – ten pierwszy raz [Programowanie], Informatyka, ►Artykuły, Linux+PL, ...

[ Pobierz całość w formacie PDF ]
dla programistów
Tworzenie modułów
jądra – ten pierwszy raz
Marek Sawerwain
stało się mniej popularne.
Zdecydowana większość pro-
gramistów tworzy oprogra-
mowanie przy pomocy wygodnych śro-
dowisk pracy, które ukrywają większość
szczegółów systemu. Na szczęście auto-
rzy sterowników czy ogólnie programiści
systemowi zawsze będą mieli zadania do
realizacji – ktoś musi napisać niezbędny
kod niskiego poziomu do obsługi jakie-
goś urządzenia.
Programowanie systemowe, czyli
m.in. tworzenie sterowników, wymaga
oczywiście doskonałej znajomości sprzę-
tu, ale podstawy tworzenia modułów dla
jądra Linuksa każdy może opanować bez
większych problemów. Jest to dość cenna
umiejętność, gdyż procedury działają-
ce na poziomie jądra w niektórych przy-
padkach mogą osiągać lepszą wydajność,
a nie każdy moduł musi być przecież od
razu sterownikiem do jakiegoś urządze-
nia podłączonego do komputera.
W tym artykule chciałbym pokazać
prosty moduł, który w katalogu
/proc
umieści plik z informacjami o proce-
sorze. Oczywiście, jądro Linux oferuje
nam takie informacje (w pliku
cpuinfo)
,
ale zrobienie tego samodzielnie da nam
kilka bezcennych doświadczeń.
Na samym początku naszej pracy od
razu należy ustalić, dla jakiego typu jądra
będziemy pisać moduły. Poszczególne
rodziny jąder, takie jak 2.0.x, 2.2.x, 2.4.x
oraz najnowsza 2.6.x (starsze typy jądra
odeszły już do lamusa), dość znacz-
nie się między sobą różnią. Ponieważ
w momencie pisania tego artykułu naj-
większą popularność ma jądro 2.4.x, to
moduły omawiane w tym artykule są
przeznaczone właśnie dla tej rodziny.
raczej trudne i wymagające dużych umie-
jętności. Najprostsze moduły są jednak
bardzo łatwe do napisania i wbrew
pozorom, nie trzeba doskonale oriento-
wać się w zawiłościach jądra.
Listing 1 zawiera przykład takiego
modułu, który wyświetla nieśmiertelne
„Hello World!”. W odniesieniu do typo-
wych programów w języku C, mamy tu
kilka różnic.
Pierwsza to naturalnie brak funk-
cji
main
. Zamiast niej są dwie specjalne
funkcje, których deklaracja jest koniecz-
na. Pierwsza (
int init_module();
) jest
wywoływana w momencie załadowa-
nia modułu przez jądro. Jak łatwo zgad-
nąć, powinny być w niej zawarte wszyst-
kie wstępne czynności. Odwrotna w dzia-
łaniu jest funkcja
void cleanup_module();
.
Jej zadaniem jest usunięcie np. przydzie-
lonej pamięci, czyli mówiąc nieco kolo-
kwialnie, posprzątanie po pracy modułu.
Istotnym elementem jest licencja. Jak
wiadomo, twórcy jądra Linuksa są bardzo
wrażliwi na tym punkcie, więc podczas
pisania modułów wymagane jest określe-
nie licencji, na jakiej udostępniamy nasz
moduł. W naszym przypadku będzie to
oczywiście licencja
GPL
, dlatego dodaje-
my do kodu dodatkowe makro w nastę-
pującej postaci:
MODULE_LICENSE(„GPL”);
.
Na płycie CD/DVD
Na płycie CD/DVD znajdują
się pliki źródłowe napisanego
sterownika, jak również
wszystkie listingi.
Listing 1.
Hello World! jako moduł jądra
Linuksa.
#define
MODULE
#define
__KERNEL__
#include
<linux/module.h>
#include
<linux/kernel.h>
MODULE_LICENSE
(
„GPL”
);
int
init_module
()
{
O autorze
Autor zajmuje się tworzeniem
oprogramowania dla WIN32
i Linuksa. Zainteresowania: teoria
języków programowania oraz
dobra literatura. Kontakt
z autorem:
autorzy@linux.com.pl.
printk
(
„<module1> Witaj
S
Świecie!!!
\n

);
return
0
;
void
cleanup_module
()
{ }
Początki są łatwe
Tworzenie większego oprogramowania,
działającego na poziomie jądra, to zadanie
60
maj 2004
P
rogramowanie systemowe
tworzenie modułów jądra Linux
dla programistów
Rysunek 1.
Wyniki polecenia
lsmod
plik wykonywalny (bądź biblioteka
dynamiczna), czyli efekt pracy programu
konsolidującego, tzw. linkera, który łączy
poszczególne pliki obiektów (pliki o roz-
szerzeniu
*.o
) w ostateczny plik binarny.
W przypadku modułu jądra sytuacja jest
odmienna. Wystarczają nam tylko same
pliki obiektowe – nie dokonujemy konso-
lidacji pliku wykonywalnego.
Kompilacja przykładu z Listingu 1
przedstawia się następująco:
jest, aby podać cel
all
, a w jego nagłów-
ku wymienić wszystkie pliki obiektowe.
Kompilacji dokona sam
Make
– użyje do
tego tylko opcji „-c” oraz dodatkowych
opcji podanych w zmiennej
CFLAGS
.
Plik
makefile
znajdujący się na płycie
CD/DVD jest nieco bogatszy, gdyż zawie-
ra dwa dodatkowe cele ładujące wszyst-
kie moduły do jądra (polecenie:
make
load
)
oraz usuwający moduły z pamięci
(polecenie:
make unload
).
Brak tego makra spowoduje, że podczas
ładowania modułu (poleceniem
insmod
)
otrzymamy komunikat o niezgodności
licencji, chociaż będzie on pracował
poprawnie.
Jądro Linuksa w przypadku modu-
łów oferuje jeszcze dwa inne makra:
MODULE_AUTHOR
, gdzie podajemy autora
modułu, oraz
MODULE_DESCRIPTION
, które-
go przeznaczeniem jest podanie krótkie-
go opisu modułu.
Kolejną różnicą jest brak typowych
funkcji bibliotecznych. Jak widać, nie włą-
czamy do naszego modułu pliku nagłów-
kowego
stdio.h
. Nie jest to problemem,
gdyż jądro oferuje kilka funkcji będą-
cych odpowiednikami typowych funkcji
bibliotecznych. Podstawowymi przykła-
dami są
printk
oraz
sprintf
. Jądro posia-
da również własne odpowiedniki takich
funkcji, jak
memset
czy
strcpy
. Nagłówki
tych funkcji zawiera plik
string.h
.
Bardzo ważne są także dwie definicje
preprocesora:
#define MODULE
oraz
#define
__KERNEL__
. Ich definicja musi nastą-
pić przed włączeniem pozostałych plików
nagłówkowych. Definicje można także
zdefiniować opcją „-D” podczas wywoły-
wania polecenia kompilatora –
gcc
.
Zadanie naszego modułu jest try-
wialne – instrukcją
printk
(odpowied-
nikiem
printf)
wyświetlamy komunikat
tekstowy.
Po załadowaniu modułu możemy nie
zobaczyć na ekranie naszego komunikatu,
gdyż zostanie on przesłany do logu jądra,
a ten możemy przejrzeć wydając polece-
nie
dmesg
(lepiej
dmesg | less
– będziemy
mogli przeglądać log strona po stronie kla-
wiszami [
PageUp
] i [P
ageDown
]). Niektóre
dystrybucje są jednak tak skonfigurowane,
iż komunikaty przekazane przez
printk

kierowane na konsolę.
gcc -c module1.c -D__KERNEL
S
-I/usr/src/linux/include
Deklaracja parametrów
jądra
Bardzo często do modułu w momencie
ładowania przekazywane są dodatkowe
parametry. np.:
Istotne jest wskazanie położenia plików
nagłówkowych jądra. Kilka informacji na
ten temat zawiera ramka
Źródła jądra w
systemie
. Po wykonaniu tego polecenia
otrzymamy plik
module1.o
, gotowy do
załadowania poleceniem
insmod
. Robimy
to w następujący sposób:
insmod ne io=0x260
Opis dodatkowych parametrów jest dość
prosty. Korzystamy z makra
MODULE_PAR
o dwóch argumentach. W pierwszym
parametrze podajmy zmienną, gdzie
będzie przechowywana wartość, nato-
miast w drugim określamy typ parametru.
Nazwa tego parametru jest odczytywana
z nazwy zmiennej, więc trzeba w tym
miejscu podać nazwę znaczącą, która
będzie charakteryzować przeznaczenie
parametru. Jeśli to nie wystarczy, to za
insmod ./module1.o
Wskazanie, że chodzi o plik znajdujący się
w katalogu bieżącym - ./ - jest konieczne,
ponieważ nasz moduł nie został jeszcze
zainstalowany w domyślnym katalogu, w
którym system przechowuje moduły.
Załadowany moduł usuwamy pole-
ceniem:
rmmod module1
Źródła jądra w systemie
Do poprawnej i bezproblemowej kompila-
cji naszych modułów potrzebne są nam
źródła jądra, a dokładniej pliki nagłów-
kowe. Stało się tradycją, że kompletne
źródła jądra są umieszczane w katalogu
/usr/src/
(w skrypcie makeile z Listin-
gu 2 powołujemy się na ten katalog).
W wielu dystrybucjach pliki nagłówkowe
są jednak umieszczane w standardowym
katalogu /usr/include. Dość często są
to pliki pochodzące z innej wersji jądra
niż jest zainstalowana w systemie. Po
kompilacji modułów z innymi plikami
nagłówkowymi, podczas próby ładowa-
nia otrzymamy komunikat o niezgodności
wersji. Rozwiązanie jest bardzo proste.
Wystarczy skasować katalogi
linux
,
asm
oraz
scsi
z
/usr/include
. Jeśli zależy nam
na obecności tych katalogów, to zamiast
kopiowania plików najlepiej utworzyć
odniesienia symboliczne. Znajdując się
w katalogu
/usr/include
, gdy tworzymy
dowiązanie
linux
, wydajemy polecenie
ln
w następującej postaci:
ln -s /usr/src/linux/include/linux
S
linux
W przypadku kilkunastu modułów najle-
piej przygotować odpowiedni skrypt dla
programu
Make
. Przykład takiego pliku,
kompilującego wszystkie moduły z tego
artykułu (ich kod źródłowy jak zawsze
znajduje się na płycie CD/DVD), przed-
stawia Listing 2.
Struktura skryptu
makefile
nie jest
skomplikowana. Definiujemy dokładnie
trzy zmienne:
CFLAGS
zawiera dodatko-
we opcje przeznaczone dla kompilatora,
CC
to zmienna zawierająca polecenie kom-
pilatora, a zmienna
OBJS
określa wszystkie
nazwy plików obiektowych, które maja
zostać utworzenie przez skrypt. Oczywi-
ście muszą istnieć odpowiednie pliki źró-
dłowe.
Skrypty dla programu
Make
zazwy-
czaj posiadają wiele reguł, np. jak otrzy-
mać z pliku źródłowego plik obiektu.
Wiele podstawowych reguł jest wbu-
dowanych do
Make’a
, więc nie musimy
definiować elementarnych reguł kompila-
cji kodu źródłowego do modułu. Ważne
Kompilacja oraz ładowanie
modułu
Gdy tworzymy standardowe oprogramo-
wanie, to przystępując do kompilacji naj-
częściej oczekujemy, że wynikiem będzie
www.linux.com.pl
61
 dla programistów
Listing 2.
Skrypt
makeile
odpowiedzialny za kompilację
przykładów
__pde=create_proc_read_entry(file_
S
proc_name, 0, NULL, procfile_read, NULL);
Jej wynikiem jest wskaźnik umieszczony
w naszej zmiennej. Pierwszy parametr
tej funkcji to nazwa pliku. Następnie
określamy tryb dostępu do pliku – zero
zapewni nam możliwość odczytywa-
nia zawartości pliku. Kolejny argument
to punkt umocowania naszego pliku
– wartość
NULL
oznacza katalog główny
systemu
proc
. Później podajemy proce-
durę odpowiedzialną za przygotowanie
danych. Ostatni argument to wskaźnik
void*
– możemy poprzez ten argument
przekazać dowolne dane, jeśli zachodzi
taka potrzeba.
Gdy użytkownik spróbuje odczy-
tać wartość naszego pliku, to oczywi-
ście zostanie wywołana funkcji
procfi-
le_read
. Dysponuje ona szeregiem para-
metrów. Najważniejszy dla nas parametr
to
page
. Jak widać z listingu, fun-
kcją
sprintf
przepisujemy przyszłą
zawartość pliku do tej właśnie zmien-
nej.
KERNEL_DIR=/usr/src/linux
CFLAGS=-D__KERNEL -I$(KERNEL_DIR)
S
-Wall
CC=gcc
OBJS=module1.o module2.o
S
module3.o mod_cpu.o
all: $(OBJS)
clean:
rm -f $(OBJS)
Rysunek 2.
Testowanie modułu z Listingu 4
i zarejestrować własny plik w systemie
proc
. Czynności, które wykonuje nasz
kod, warto pokazać za pomocą prostego
schematu blokowego. Rysunek 4 przed-
stawia taki schemat. Różni się on tym od
typowego schematu, że zamiast wbloków
START i STOP mamy tu takie czynności,
jak załadowanie modułu oraz usunięcie
modułu.
Dotychczas nie wspominałem, w jaki
sposób możemy uzyskać informacje
o procesorze. Wykorzystamy do tego celu
instrukcję
cpuid
. Z jej pomocą możemy
zdobyć wiele informacji, jednak dla swo-
istej „politycznej poprawności”, na począ-
tek sprawdzimy, czy nasz system obsłu-
guje taką instrukcję. Jest ona obecna we
wszystkich wydanych procesorach zgod-
nych z Intelem na przestrzeni ostatnich
kilkunastu lat.
Osoby, które chcą dokładniej przyj-
rzeć się sposobom wykrywania proces-
ora, odsyłam do kodu źródłowego
jądra – plik
arch/i386/kernel/setup.c
.
W artykule zostały jednak zastoso-
wane inne funkcje, których kod
pochodzi z programu
MPlayer 1.0.3
,
a dokładniej z plików
cpudetect.c
i
cpu-
detect.h
.
Przykład typowej funkcji
has_cpuid
,
która sprawdza obecność
cpuid
, zawiera
Listing 5. Cały test, jak widać, został zapi-
sany przy pomocy assemblera. Ogólna
idea testu jest bardzo prosta – wystarczy
spróbować zmienić 21 bit rejestru flag
(
EFLAGS
). Jeśli możemy zmienić ten bit, to
oznacza to, że mamy dostęp do instruk-
cji
cpuid
.
pomocą makra
MODULE_PARM_DESC
istnieje
możliwość dodania opisu określonego
parametru.
Listing 3 zawiera kod źródłowy
modułu definiującego jeden argument
par1
typu
string
(wszystkie typy parame-
trów zostały zebrane w Tabeli 1).
Argumenty mogą posiadać wartości
domyślne. Są one przypisane w momen-
cie deklaracji zmiennej. W przykła-
dzie wartością domyślną jest tekst
”de-
fault value”
. Zostanie on oczywiście
zastąpiony w momencie określenia war-
tości argumentu podczas ładowania
modułu:
Podczas usuwania modułu konieczną
operacją jest usunięcie wpisu z syste-
mu
proc
. Używamy w tym celu funkcji
remove_proc_entry
. Pierwszy argument
to nazwa naszego pliku, a drugim jest
wskazanie na katalog rodzicielski. Pod-
czas wywoływania podaliśmy wartość
NULL
, wskazując, że mamy na myśli kata-
log główny.
Gdy nie usuniemy naszego wpisu,
a dowolny program spróbuje odczytać
nasz plik, zakończy się to jego przerwa-
niem i zrzutem pamięci, czyli utworze-
niem ulubionego pliku wszystkich pro-
gramistów –
core
.
insmod ./module2 par1=”nowa wartość”
Nowy plik w katalogu /
proc
Zadaniem naszego modułu jest dostar-
czenie informacji o procesorze. Natu-
ralnym rozwiązaniem jest umieszczenie
tych informacji w katalogu
/proc
. Listing
4 zawiera fragmenty modułu tworzące-
go przykładowy plik w systemie
proc
– brakuje tylko pełnej definicji funkcji
proc_calc_metrics
, ale powrócimy do
niej w dalszej części.
Wszystkie funkcje zawiązane z zarzą-
dzaniem katalogiem
/proc
znajdują się w
pliku
proc_fs.h
. Obejmują one tworze-
nie katalogów oraz plików do odczy-
tu i zapisu. Nas interesuje utworzenie
pliku (zawierającego tekst) wyłącznie do
odczytu.
Rejestrację nowego pliku wykonuje-
my w funkcji
init_module
, ale zanim to
zrobimy, należy utworzyć zmienną glo-
balną reprezentującą ten plik. Definiuje-
my ją w taki sposób:
Zadanie główne –
informacje o procesorze
Po tych wstępnych informacjach, przy-
szedł czas na realizację głównego zada-
nia. Posiadamy już wystarczającą ilość
informacji o tym, jak utworzyć moduł
Tabela 1.
Typy parametrów przekazywanych do modułów
Oznaczenie
Deinicja
b
pojedynczy bajt (unsigned char)
h
krótka liczba całkowita (short int)
i
liczba całkowita (int)
l
liczba całkowita długa (long)
struct proc_dir_entry* __pde;
s
ciąg znaków
Rejestracja pliku to wywołanie tylko
jednej funkcji:
n1-n2[bhils]
tablica o co najmniej n1 elementach, jednak nie dłuższa niż n2
elementy
62
maj 2004
 tworzenie modułów jądra Linux
dla programistów
Listing 3.
Deklaracja nowego parametru
modułu
#define
MODULE
#define
__KERNEL__
#include
<linux/module.h>
#include
<linux/kernel.h>
MODULE_LICENSE
(
„GPL”
);
char
*
par1
=
”default value”
;
MODULE_PARM
(
par1,
„s”
);
int
init_module
()
{
void
cleanup_module
()
{ }
Na Listingu 5 znajduje się również
druga funkcja:
do_cpuid
. O ile zadaniem
pierwszej było sprawdzenie, czy mamy
dostęp do
cpuid
, to druga, w zależ-
ności od stanu rejestru
eax
, wykonuje
instrukcję
cpuid
, a otrzymane informa-
cje umieszcza w tablicy wskazanej przez
argument
p
naszej funkcji.
Rysunek 4.
Schemat działania modułu
procesora. Należy do argumentu
eax
załadować zero, a następnie wywo-
łać instrukcję
do_cpuid
. W podanej
tablicy zostaną umieszczone fragmen-
ty (po cztery litery) nazwy producenta.
W przypadku Intela będzie to napis
„GenuineIntel”
, a dla AMD –
„Authen-
ticAMD”
. Procesory pozostałych firm,
takich jak choćby Transmeta, także
zawierają swoją sygnaturę, więc można
z pomocą instrukcji
cpuid
ustalić pro-
ducenta.
Kod wpisujący do zmiennej
page
nazwę producenta oraz tzw. maksymal-
ną wartość poziomu, dla której genero-
wane są odpowiedzi instrukcji
cpuid
, jest
następujący:
Ściąganie informacji
Dysponując funkcjami opisanymi
w poprzednim punkcie, jesteśmy gotowi
do odczytania informacji o procesorze.
Na Listingu 6 znajdują się fragmenty
głównej funkcji
procfile_read
.
Wszystkie informacje o procesorze
są otrzymywane w wywołaniu funk-
cji
procfile_read
, gdy ktoś odczyta
nasz plik. Oprócz wykorzystywania
has_cpuid
oraz
do_cpuid
, korzystamy
z dwóch dodatkowych funkcji. O jednej
z nich (
proc_calc_metrics
) już wspomi-
nałem, a druga (
proc_sprintf
) to odpo-
wiednik
sprintf
, działający poprawnie
w środowisku jądra. Funkcje te są nam
potrzebne, aby poprawnie pisać do
zmiennej
page
.
Jako pierwszą cenną informację
spróbujmy uzyskać nazwę producenta
Listing 4.
Utworzenie nowego pliku w systemie plików
/proc
#define
MODULE
#define
__KERNEL__
#include
<linux/module.h>
#include
<linux/kernel.h>
#include
<linux/proc_fs.h>
MODULE_LICENSE
(
„GPL”
);
char
*
file_proc_name
=
”module3_info”
;
char
*
par1
=
”default value”
;
MODULE_PARM
(
par1,
„s”
);
struct
proc_dir_entry
*
__pde
;
int
proc_calc_metrics
(
char
*
page,
char
**
start, off_t off,
int
count,
int
*
eof,
int
len
)
{...}
int
procfile_read
(
char
*
page,
char
**
start, off_t off,
int
count,
int
*
eof,
void
*
data
)
{
Rysunek 3.
Moduł
mod_cpu
w akcji
– podstawowe informacje uzyskane przez
cpuid
int
len
;
len
=
sprintf
(
page,
”Hello, %s”,
par1
);
return
proc_calc_metrics
(
page, start, off, count, eof, len
);
int
init_module
()
{
__pde
=
create_proc_read_entry
(
file_proc_name,
0,
NULL, procfile_read, NULL
);
return
0
;
void
cleanup_module
()
{
remove_proc_entry
(
file_proc_name, NULL
);
}
www.linux.com.pl
63
printk
(
„<module2> Parametr
S
par1=[%s]
\n
”,
par1
);
return
0
;
}
 dla programistów
Listing 5.
Test, czy procesor udostępnia
instrukcję cpuid
Listing 6.
Fragmenty funkcji
procfile_read
, która przygotowuje dane o procesorze
int
procfile_read
(
char
*
page,
char
**
start, off_t off,
int
count,
int
*
eof,
void
*
data
)
{
int
has_cpuid(){
int
a, c;
int
len
=
0
;
unsigned int
regs
[
4
],
regs2
[
4
];
proc_sprintf
(
page,
&
off,
&
len,
”CPUID info v1.0
\n

);
if
(
has_cpuid
())
{
proc_sprintf
(
page,
&
off,
&
len,
”instrukcja cpuid jest dostępna
\n

);
do_cpuid
(
0x00000000,
regs
);
...
if
(
regs
[
0
]>=
0x00000001
)
{
unsigned
cl_size
;
do_cpuid
(
0x00000001,
regs2
);
cpuType
=(
regs2
[
0
] >>
8
)&
0xf
;
if
(
cpuType
==
0xf
)
cpuType
=
8
+((
regs2
[
0
]>>
20
)&
255
);
cpuStepping
=
regs2
[
0
] &
0xf
;
hasTSC
= (
regs2
[
3
] & (
1
<<
8
)) >>
8
;
hasMMX
= (
regs2
[
3
] & (
1
<<
23
)) >>
23
;
...
cl_size
= ((
regs2
[
1
] >>
8
) &
0xFF
)*
8
;
if
(
cl_size
)
{
cl_size
=
cl_size
;
proc_sprintf
(
page,
&
off,
&
len,
”Wielkość linijki cache’u (w bajtach):
%u
\n
”,
cl_size
);
__asm__ __volatile__ (
„pushf\n\t”
„popl %0\n\t”
„movl %0, %1\n\t”
„xorl $0x200000, %0\n\t”
„push %0\n\t”
„popf\n\t”
„pushf\n\t”
„popl %0\n\t”
: „=a” (a), „=c” (c)
:
: „cc”
);
return
(a!=c);
}
void
do_cpuid(
unsigned int
ax,
unsigned int
*p){
__asm __volatile__ (
„movl %%ebx, %%esi\n\t”
„cpuid\n\t”
„xchgl %%ebx, %%esi”
: „=a” (p[0]), „=S” (p[1]),
„=c” (p[2]),
„=d” (p[3])
}
...
}
do_cpuid
(
0x80000000,
regs
);
if
(
regs
[
0
]>=
0x80000001
)
{
proc_sprintf
(
page,
&
off,
&
len,
”rozszerzony poziom cpuid: %d
\n
”,
S
regs
[
0
]&
0x7FFFFFFF
);
do_cpuid
(
0x80000001,
regs2
);
hasMMX
|= (
regs2
[
3
] & (
1
<<
23
)) >>
23
;
...
}
if
(
regs
[
0
]>=
0x80000006
)
{
do_cpuid
(
0x80000006,
regs2
);
proc_sprintf
(
page,
&
off,
&
len,
”rozszerzone informacje o cache’u %d
\n
”,
S
regs2
[
2
]&
0x7FFFFFFF
);
cl_size
=
regs2
[
2
] &
0xFF
;
proc_sprintf
(
page,
&
off,
&
len,
”Wielkosc linijki cache’u (w bajtach):
S
%u
\n
”,
cl_size
);
: „0” (ax));
}
unsigned char r[4];
do_cpuid(0x00000000, r);
proc_sprintf(page,&off,&len,
Gdy chcemy uzyskać dalsze informacje,
jest to uzależnione od maksymalnej war-
tości umieszczonej w tablicy
r
pod pierw-
szym (zerowym) elementem. W naszym
module wyświetlamy informacje o tym,
czy są dostępne dodatkowe instrukcje
typu MMX czy SSE. Informacje te uzy-
skamy wywołując
do_cpuid
w następu-
jący sposób:
}
proc_sprintf
(
page,
&
off,
&
len,
”Zestawy instrukcji:
\n
MMX ...
\n
”,
hasMMX, ... ,has3DNowExt
);
}
else
{
proc_sprintf
(
page,
&
off,
&
len,
”instrukcja cpuid nie jest dostępna
\n

);
}
proc_sprintf
(
page,
&
off,
&
len,
”---
\n

);
return
proc_calc_metrics
(
page, start, off, count, eof, len
);
}
do_cpuid(0x00000001, r);
Po wywołaniu tej instrukcji w reje-
strze
edx
znajdą się dodatkowe informa-
cje, jaki typ instrukcji jest obsługiwany.
W naszym przypadku zawartość reje-
stru
edx
to ostatni element tablicy.
Sprawdzenie, jakie rodzaje instrukcji są
dostępne, to odpowiednie manipulowa-
nie bitami.
Załóżmy, że chcemy sprawdzić, czy
procesor oferuje nam dostęp do instruk-
cji SSE. Informacje o dostępności SSE
zawiera bit o numerze 25. Linijka kodu,
która wpisze do zmiennej
hasSSE
odpo-
wiednie wartości: jeden, jeśli instruk-
cje SSE są dostępne, a zero, jeśli nie, jest
następująca:
jest to zawartość rejestru
edx
). Gdy bit
będzie ustawiony, to wynikiem ope-
racji bitowego „i” będzie oczywiście
jedynka na 25 pozycji. Ponieważ my
chcemy uzyskać normalną liczbę, zero
bądź jeden, wystarczy, jeśli z powro-
tem przesuniemy nasz bit o 25 pozycji
w lewo na początek. W ten sposób
uzyskujemy informację, czy mamy
dostęp do instrukcji SSE. W analogiczny
sposób sprawdzamy, czy mamy dostęp
do instrukcji SSE2 – sprawdzamy 26
hasSSE = (r[3] & (1 << 25 )) >> 25;
Aby nie kodować mozolnie liczby
o ustawionym 25 bicie, wykorzystujemy
przesuniecie bitowe w lewo. Przesu-
wamy jedynkę. Następnie wykonujemy
bitową operację „i” na trzecim elemen-
cie naszej tablicy (dla przypomnienia
64
maj 2004
  [ Pobierz całość w formacie PDF ]
  • zanotowane.pl
  • doc.pisz.pl
  • pdf.pisz.pl
  • lily-lou.xlx.pl


  • zanotowane.pl
  • doc.pisz.pl
  • pdf.pisz.pl
  • rafalsal.opx.pl
  •  Linki
     : Strona pocz±tkowa
     : 2015 05 FLUGZEUG CLASSIC, FLUGZEUG CLASSIC - 2015
     : 2001.05 Szkoła konstruktorów, Elektronika, Szkoła konstruktorów, Szkola konstruktorow
     : 2.05 Ancient Aliens - Aliens and the Third Reich, Ancient Aliens
     : 2015 05 CLAUSEWITZ, 2015 CLAUSEWITZ
     : 2012 AON - Bibliografia publikacji pracowników w 2011r, 002-05 WOJSKO POLSKIE OD 01.01.1990, AON - Bibliografia publikacji pracowników
     : 2006 AON - Bibliografia publikacji pracowników w 2004r, 002-05 WOJSKO POLSKIE OD 01.01.1990, AON - Bibliografia publikacji pracowników
     : 2009 AON - Bibliografia publikacji pracowników w 2007r, 002-05 WOJSKO POLSKIE OD 01.01.1990, AON - Bibliografia publikacji pracowników
     : 2010 AON - Bibliografia publikacji pracowników w 2008r, 002-05 WOJSKO POLSKIE OD 01.01.1990, AON - Bibliografia publikacji pracowników
     : 2011 AON - Bibliografia publikacji pracowników w 2010r, 002-05 WOJSKO POLSKIE OD 01.01.1990, AON - Bibliografia publikacji pracowników
     : 2004 2 IPN - Pamięć i Sprawiedliwosc, IPN - PAMIĘĆ I SPRAWIEDLIWOŚĆ
     . : : .
    Copyright (c) 2008 To, co jest dla mnie dobre, a to, czego chcę, to często dwie różne rzeczy. | Designed by Elegant WPT