I v tomto díle budeme stále věrni minulému tématu a tím jsou "problémy" se zaváděním a odstraňováním modulů.
Jmenný prostor je v celém jádru pouze jeden a proto je nutné dbát na rozumné pojmenování funkcí, proměnných a maker (pro všechny tři věci budu dále používat pojem symbol) použitých ve Vašem ovladači. Veškeré veřejně přístupné symboly jsou uloženy v tzv. Kernel symbol table. Rozumná doporučení pro pojmenování můžeme shrnout takto:
Není mnoho doporučení jak správně nazvat Vaše symboly, ale pokud se jich budete držet předejdete mnoha konfliktům a ušetříte si čas.
Níže uvedená makra jsou definována v hlavičkoveém souboru
linux/module.h.
V případě, že Váš modul nemá žádný symbol, který má být veřejný je dobré
zapsat makro EXPORT_NO_SYMBOLS kdekoli ve zdrojovém
souboru. Z důvodu portability doporučejeme toto makro uvádět ve funkci
init_module() (což je jediné místo kde toto makro pracuje správně
ve starších verzích jádra).
static int __init
init_module(void)
{
...
EXPORT_NO_SYMBOLS;
...
}
Pokud máme několik symbolů v modulu, které chceme zpřístupnit celému jádru (i ostatním modulům) je nutné tento symbol tzv. exportovat. K tomuto účelu nám slouží dvě výše uvedená makra, která se liší pouze v tom, že první exportuje symbol s verzí a druhé bez verze. My zatím budeme používat exportování symbolů bez verzí a k problematice symbolů s verzemi se dostaneme v některém z dalších dílů.
Aby tato makra mohla fungovat, je nutné definovat makro
EXPORT_SYMTAB ještě před použitím hlavičkového souboru
linux/module.h a nejlépe na úrovni argumentu překladače
-DEXPORT_SYMTAB.
První část našeho příkladu bude kratičký modul, který bude exportovat jeden
symbol bez verze. Tím symbolem bude funkce int lz_get_value(),
která vrací obsah proměnné value. Pro větší rozmanitost je tato
proměnná také parametrem modulu a tudíž můžeme její hodnotu ovlivnit při
zavádění. Následující ukázku je taktéž přiložena k článku jako soubor
lzsym.c.
#ifdef MODULE
#include <linux/module.h>
MODULE_DESCRIPTION("Exported symbol example");
MODULE_AUTHOR("Robert V0jta ");
MODULE_LICENSE("GPL");
MODULE_PARM(value, "i");
MODULE_PARM_DESC(value, "Exported symbol value");
#endif /* MODULE */
#include <linux/init.h>
static int value = 0;
static int __init
init_module(void)
{
printk("<1>symbol module started\n");
return 0;
}
static void __exit
cleanup_module(void)
{
printk("<1>Symbol module unloaded\n");
}
int
lz_get_value(void)
{
return value;
}
#ifdef EXPORT_SYMTAB
EXPORT_SYMBOL_NOVERS (lz_get_value);
#endif /* EXPORT_SYMBOL */
Jak sami vidíte není to vůbec nic složitého. Teď potřebujeme hlavičkový
soubor (lzsym.h), který bude deklarovat námi exportovaný symbol
lz_get_value pro použití v jiných modulech. Tento symbol bude
definován v případě existence makra __KERNEL__, protože může
obsahovat i jiné deklarace použitelné v uživatelských aplikacích.
#ifndef LZ_SYMBOL_H #define LZ_SYMBOL_H #ifdef __KERNEL__ extern int lz_get_value(void); #endif /* __KERNEL__ */ #endif /* LZ_SYMBOL_H */
A teď se dostáváme k poslední části našeho příkladu a tím je modul, který
náš exportovaný symbol použije. Není to nic složitého, pouze při zavádění
vypíše vrácenou hodnotu onoho symbolu (lzsymuse.c).
#ifdef MODULE
#include <linux/module.h>
MODULE_DESCRIPTION("LZ Symbol usage");
MODULE_AUTHOR("Robert V0jta ");
MODULE_LICENSE("GPL");
#endif /* MODULE */
#include <linux/init.h>
#include "lzsym.h"
static int __init
init_module(void)
{
printk("<1>LZ symbol value is %d\n", lz_get_value());
return 0;
}
static void __exit
cleanup_module(void)
{
printk("<1>LZ symbol usage modul unloading\n");
}
Jak sami vidíte, jedinou nutností bylo použití hlavičkového souboru
lzsym.h v kterém je deklarace našeho symbolu
lz_get_value.
Ještě nám zbývá uvést ukázkový Makefile pro tento příklad.
MODULES = lzsym.o lzsymuse.o CC = gcc CFLAGS = -O2 -DMODULE -D__KERNEL__ all: $(MODULES) clean: rm -f $(MODULES) lzsym.o: lzsym.c $(CC) $(CFLAGS) -DEXPORT_SYMTAB -o $@ -c $< lzsymuse.o: lzsymuse.c $(CC) $(CFLAGS) -o $@ -c $<
Za domácí úlohu si zkuste zrušit definici makra EXPORT_SYMTAB
z argumentu překladače a uvidíte co to udělá.
Nyní si moduly zkompilujeme.
[echelon:~/linuxzone/ldd/4/v0jta-ldd-4]make
gcc -O2 -DMODULE -D__KERNEL__ -DEXPORT_SYMTAB -o lzsym.o \
-c lzsym.c
gcc -O2 -DMODULE -D__KERNEL__ -o lzsymuse.o -c lzsymuse.c
[echelon:~/linuxzone/ldd/4/v0jta-ldd-4]
A postupně zavedeme v pořadí lzsym.o
a lzsymuse.o.
[root@echelon v0jta-ldd-4]# insmod ./lzsym.o value=111 symbol module started [root@echelon v0jta-ldd-4]# insmod ./lzsymuse.o LZ symbol value is 111 [root@echelon v0jta-ldd-4]#
Teď si oba moduly zkuste odstranit v opačném pořadí než jste je zaváděli,
tedy lzsymuse a poté lzsym. Pokud máte, zkuste si
zavést modul lzsymuse.o a uvidíte notoricky známou hlášku ...
[root@echelon v0jta-ldd-4]# insmod ./lzsymuse.o ./lzsymuse.o: unresolved symbol lz_get_value [root@echelon v0jta-ldd-4]#
... o neexistujícím symbolu, protože modul, který tento symbol exportuje
(lzsym) není zaveden. Existuje zde možnost jak automaticky
zavádět potřebné moduly do jádra, ale tu si probereme v některém z dalších
dílů. Teď si zaveďte oba moduly ve správném pořadí a zkuste z jádra odstranit
modul lzsym.
[root@echelon v0jta-ldd-4]# rmmod lzsym lzsym: Device or resource busy [root@echelon v0jta-ldd-4]#
100% se Vám to nepodaří z jednoho důvodu - v jádru jsou stále aktivní
moduly, které potřebují ke svému běhu symboly exportované z našeho moduly
a proto ho jádro nemůže odstranit. Tuto informace můžete získat i jinou cestou
a tím je příkaz lsmod, který Vám ukáže závislost modulů.
[root@echelon v0jta-ldd-4]# lsmod | grep lzsym lzsymuse 896 0 (unused) lzsym 984 0 [lzsymuse] [root@echelon v0jta-ldd-4]#
Zde vidíte, že modul lzsym je používán modulem
lzsymuse a proto ho jádro nemůže odstranit.
Zmiňujeme-li problémy s odstraňováním modulu z jádra, musíme se také
zastavit u dalších tří maker, které jsou definována v hlavičkovém souboru
linux/module.h a jsou jimi:
MOD_INC_USE_COUNT
Při použití tohoto makra se počítadlo použití modulu zvýší o jedničku.
MOD_DEC_USE_COUNT
Při použití tohoto makra se počítadlo použití modulu sníží o jedničku.
MOD_IN_USE
Toto počítadlo vrátí hodnotu true v případě, že modul je
používán, resp. pokud je počítadlo použití větší jak nula.
Dnešní jádra si sami zjišťují jestli je modul používán či nikoli a proto není nutné využívat těchto maker. Kdo chce ale psát portabilní kód (pro starší jádra) musí tyto makra použít, protože starší jádra touto funkcí nedisponují.
Představme si modul pro zvukovou kartu, který po zavedení není používán
a proto ho můžeme bez problémů odstranit. Tento modul se začne používat
v případě, že si například pustíme XMMS a začneme si přehrávat MP3. Poté
nám XMMS zavolá funkci open na zařízení
/dev/dsp (či jiné). Dále se nám zavolá funkce open
implementovaná v našem ovladači, která zavolá makro
MOD_INC_USE_COUNT a modul je již používán. Během ukončování XMMS
se zavolá funkce close, kterou také náš modul implementuje a v ní
je zavoláno makro MOD_DEC_USE_COUNT. V tomto případě už není náš
modul používán a můžeme ho bez problémů odstranit.
Mohu zjistit kolikrát je náš modul používán? Ano, můžete a to pomocí
příkazu lsmod, který ve třetím sloupečku (Used) ukazuje počítadlo
použití u daného modulu. Právě teď poslouchám MP3 a když se podívám na
zvukovou kartu, vidím že je opravdu použita a modul neodstraním dokud
neukončím přehrávání MP3, neboli dokud nepřestanu používat dané zařízení.
i810_audio 24712 1 (autoclean) ac97_codec 13320 0 (autoclean) [i810_audio] soundcore 6500 2 (autoclean) [i810_audio]
Tento sloupeček souvisí s automatickým zaváděním modulů, které jsem zmiňoval výše a bude podrobněji rozebrán v některém z dalších dílů.
V případě, že budete používat tyto makra je nutná obezřetnost z jednoho
prostého důvodu - jestliže kvůli nějaké akci zvýšíte počítadlo použití modulu
je nutné po ukončení dané akce počítadlo zase snížit. Jinak se Vám počítadlo
nevrátí zpět na nulu a modul z jádra neodstraníte. Podobné případy mohou
vzniknout při ladění modulu kdy Vaše funkce nejsou bezproblémové a nastane-li
havárie Vašeho modulu, počítadlo zůstane větší jako nula a modul zase
neodstraníte. Ladíte-li, doporučuji předefinovat makra
MOD_INC_USE_COUNT a MOD_DEC_USE_COUNT například na
{}.
Teď už umíte exportovat symboly, používat je v jiných modulech a také jste zase o něco chytřejší co se problematiky zavádění a odstraňování modulů týče. K exportování symbolů, jejich smyslu a použití se vrátíme ještě v příštím díle a posuneme se o malinko dále.
Zjistil jsem, že při psaní článků docela často zaměňuji pojmy
deklarace a definice. Vynasnažím se, aby od
příštího dílu byla vždy použita správná terminologie. Omlouvám se.
Použité ukázky v dnešním díle naleznete zde.
Zdroj: Linuxzone.cz
Autor: Robert V0jta, 15. 11. 2002, 00:00
Sekce: Programování, Komentářů: 2
Průměrné hodnocení: 2,9