Pokud načtím modul jádra a zobrazí seznam načtených modulů s lsmod
, mohu získat "počet použití" modulu (počet dalších modulů s odkazem na modul). Existuje však způsob, jak zjistit co používá modul?
Problém je v tom, že modul, který vyvíjím, trvá na tom, že počet jeho použití je 1, a proto nemohu použít rmmod
k jeho uvolnění, ale sloupec „by“ je prázdný. To znamená, že pokaždé, když chci re-kompilovat a znovu načíst modul, musím restartovat počítač (nebo alespoň nemohu přijít na jiný způsob, jak ho vyložit).
Ve skutečnosti se zdá, že existuje způsob, jak vypsat procesy, které si nárokují modul/ovladač - ale neviděl jsem inzerovaný (mimo dokumentaci jádra Linuxu), takže si zapíšu své poznámky zde:
Za prvé, mnohokrát děkujeme za @ haggai_e odpověď; ukazatel na funkce try_module_get
a try_module_put
jako osoby odpovědné za správu počtu použití (refcount) byl klíč, který mi umožnil sledovat postup.
Podíváme-li se dále na tento online, nějak jsem narazil na post Linux-Kernel Archive: [PATCH 1/2] trasování: Snižte režii sledovacích bodů modul ; který nakonec ukázal na zařízení přítomné v jádře, označované jako „trasování“; dokumentace je v adresáři Documentation/trace - zdrojový strom jádra Linux . Zejména dva soubory vysvětlují sledovací zařízení, events.txt a ftrace.txt .
V systému Linux v systému /sys/kernel/debug/tracing/README
je však také krátký "trasovací mini-HOWTO" (viz také jsem opravdu unavený z lidí, kteří říkají, že neexistuje žádná dokumentace ... ); Všimněte si, že ve zdrojovém stromu jádra je tento soubor vlastně generován souborem kernel/trace/trace.c . Testovali jsme to na Ubuntu natty
a všimněte si, že protože /sys
je vlastněn rootem, musíte použít Sudo
pro čtení tohoto souboru, jako v Sudo cat
nebo
Sudo less /sys/kernel/debug/tracing/README
... a to platí téměř pro všechny ostatní operace pod /sys
, které budou popsány zde.
Především zde je jednoduchý minimální modul/kód ovladače (který jsem dal dohromady z uvedených zdrojů), který jednoduše vytvoří souborový uzel /proc/testmod-sample
, který vrací řetězec "To je testmod." při čtení; toto je testmod.c
:
/*
https://github.com/spotify/linux/blob/master/samples/tracepoints/tracepoint-sample.c
https://www.linux.com/learn/linux-training/37985-the-kernel-newbie-corner-kernel-debugging-using-proc-qsequenceq-files-part-1
*/
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h> // for sequence files
struct proc_dir_entry *pentry_sample;
char *defaultOutput = "This is testmod.";
static int my_show(struct seq_file *m, void *v)
{
seq_printf(m, "%s\n", defaultOutput);
return 0;
}
static int my_open(struct inode *inode, struct file *file)
{
return single_open(file, my_show, NULL);
}
static const struct file_operations mark_ops = {
.owner = THIS_MODULE,
.open = my_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int __init sample_init(void)
{
printk(KERN_ALERT "sample init\n");
pentry_sample = proc_create(
"testmod-sample", 0444, NULL, &mark_ops);
if (!pentry_sample)
return -EPERM;
return 0;
}
static void __exit sample_exit(void)
{
printk(KERN_ALERT "sample exit\n");
remove_proc_entry("testmod-sample", NULL);
}
module_init(sample_init);
module_exit(sample_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mathieu Desnoyers et al.");
MODULE_DESCRIPTION("based on Tracepoint sample");
Tento modul může být sestaven s následujícími Makefile
NAME _ (stačí jej umístit do stejného adresáře jako testmod.c
a pak spustit make
v tomtéž adresáři):
CONFIG_MODULE_FORCE_UNLOAD=y
# for oprofile
DEBUG_INFO=y
EXTRA_CFLAGS=-g -O0
obj-m += testmod.o
# mind the tab characters needed at start here:
all:
make -C /lib/modules/$(Shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(Shell uname -r)/build M=$(PWD) clean
Když je tento modul/ovladač vytvořen, výstup je soubor jádra, testmod.ko
.
V tomto okamžiku můžeme připravit trasování událostí týkající se try_module_get
a try_module_put
; ty jsou v /sys/kernel/debug/tracing/events/module
:
$ Sudo ls /sys/kernel/debug/tracing/events/module
enable filter module_free module_get module_load module_put module_request
V mém systému je ve výchozím nastavení povoleno sledování:
$ Sudo cat /sys/kernel/debug/tracing/tracing_enabled
1
... ale sledování modulu (konkrétně) není:
$ Sudo cat /sys/kernel/debug/tracing/events/module/enable
0
Nyní bychom měli nejprve vytvořit filtr, který bude reagovat na události module_get
, module_put
atd., Ale pouze na modul testmod
name__. Nejprve bychom měli zkontrolovat formát události:
$ Sudo cat /sys/kernel/debug/tracing/events/module/module_put/format
name: module_put
ID: 312
format:
...
field:__data_loc char[] name; offset:20; size:4; signed:1;
print fmt: "%s call_site=%pf refcnt=%d", __get_str(name), (void *)REC->ip, REC->refcnt
Zde vidíme, že existuje pole nazvané name
name__, které obsahuje jméno ovladače, proti kterému můžeme filtrovat. Chcete-li vytvořit filtr, jednoduše echo
řetězec filtru do odpovídajícího souboru:
Sudo bash -c "echo name == testmod > /sys/kernel/debug/tracing/events/module/filter"
Zde si nejprve povšimněte, že protože musíme volat Sudo
name__, musíme celý příkaz echo
zabalit jako příkaz argumentu Sudo
name __- ed bash
name__. Za druhé, všimněte si, že jelikož jsme psali "rodiči" module/filter
, ne specifickým událostem (které by byly module/module_put/filter
atd.), Tento filtr bude použit na všechny události uvedené jako "děti" v adresáři module
name__.
Nakonec povolíme sledování modulu:
Sudo bash -c "echo 1 > /sys/kernel/debug/tracing/events/module/enable"
Od tohoto okamžiku můžeme číst soubor protokolu trasování; pro mě, čtení blokování, "piped" verze souboru trasování pracoval - takto:
Sudo cat /sys/kernel/debug/tracing/trace_pipe | tee tracelog.txt
V tomto okamžiku se v logu nic nezobrazí - je tedy na čase načíst (a využít a odebrat) ovladač (v jiném terminálu, ze kterého se čte trace_pipe
):
$ Sudo insmod ./testmod.ko
$ cat /proc/testmod-sample
This is testmod.
$ Sudo rmmod testmod
Pokud se vrátíme do terminálu, kde se čte trace_pipe
, měli bychom vidět něco jako:
# tracer: nop
#
# TASK-PID CPU# TIMESTAMP FUNCTION
# | | | | |
insmod-21137 [001] 28038.101509: module_load: testmod
insmod-21137 [001] 28038.103904: module_put: testmod call_site=sys_init_module refcnt=2
rmmod-21354 [000] 28080.244448: module_free: testmod
To je skoro vše, co pro náš ovladač testmod
získáme - refcount se změní pouze v případě, že je ovladač načten (insmod
name__) nebo unloaded (rmmod
name__), ne když čteme pomocí cat
name__. Můžeme tedy jednoduše přerušit čtení z trace_pipe
pomocí CTRL+C v tomto terminálu; a zastavit stopování úplně:
Sudo bash -c "echo 0 > /sys/kernel/debug/tracing/tracing_enabled"
Všimněte si, že většina příkladů odkazuje na čtení souboru /sys/kernel/debug/tracing/trace
místo trace_pipe
jako zde. Jedním problémem však je, že tento soubor nemá být "piped" (takže byste neměli spouštět tail -f
v tomto souboru trace
name__); ale místo toho byste měli po každé operaci znovu přečíst trace
name__. Po prvním insmod
bychom získali stejný výstup z cat
name trace
a trace_pipe
; po rmmod
by však čtení souboru trace
dalo:
<...>-21137 [001] 28038.101509: module_load: testmod
<...>-21137 [001] 28038.103904: module_put: testmod call_site=sys_init_module refcnt=2
rmmod-21354 [000] 28080.244448: module_free: testmod
... to je: v tomto okamžiku insmod
byl již ukončen na dlouhou dobu, a tak již neexistuje v seznamu procesů - a proto nemůže být nalezen přes zaznamenaný proces ID (PID) v té době - tedy dostaneme prázdný název <...>
jako název procesu. Proto je lepší v tomto případě logovat (prostřednictvím tee
name__) běžící výstup z trace_pipe
. Všimněte si také, že pro vymazání/reset/vymazání souboru trace
stačí na něj zapsat 0:
Sudo bash -c "echo 0 > /sys/kernel/debug/tracing/trace"
Pokud se to jeví jako neintuitivní, poznamenejte si, že trace
je speciální soubor a vždy bude vykazovat velikost souboru nula:
$ Sudo ls -la /sys/kernel/debug/tracing/trace
-rw-r--r-- 1 root root 0 2013-03-19 06:39 /sys/kernel/debug/tracing/trace
... i když je "plná".
Nakonec si všimněte, že kdybychom neimplementovali filtr, dostali bychom log všechny volání modulů na běžícím systému - což by zaprotokolovalo jakýkoliv hovor (také pozadí) do grep
a takové jako ty, které používají modul binfmt_misc
:
...
tr-6232 [001] 25149.815373: module_put: binfmt_misc call_site=search_binary_handler refcnt=133194
..
grep-6231 [001] 25149.816923: module_put: binfmt_misc call_site=search_binary_handler refcnt=133196
..
cut-6233 [000] 25149.817842: module_put: binfmt_misc call_site=search_binary_handler refcnt=129669
..
Sudo-6234 [001] 25150.289519: module_put: binfmt_misc call_site=search_binary_handler refcnt=133198
..
tail-6235 [000] 25150.316002: module_put: binfmt_misc call_site=search_binary_handler refcnt=129671
... který přidává docela trochu režie (v obou log data ammount, a doba zpracování potřebná pro jeho generování).
Zatímco jsem to hledal, narazil jsem na Debugging Linux Kernel by Ftrace PDF , což se týká nástroje trace-cmd , který se do značné míry podobá výše uvedenému, ale prostřednictvím jednodušší rozhraní příkazového řádku. K dispozici je také GUI "front-end reader" pro trace-cmd
nazvané KernelShark ; oba jsou také v Debianu/Ubuntu repository přes Sudo apt-get install trace-cmd kernelshark
. Tyto nástroje by mohly být alternativou výše popsaného postupu.
Nakonec bych si všiml, že zatímco výše uvedený testmod
příklad ve skutečnosti nevykazuje použití v kontextu více nároků, použil jsem stejný postup sledování, abych zjistil, že modul USB, který kóduji, byl opakovaně deklarován pulseaudio
jako jakmile je zařízení USB připojeno - tak se zdá, že postup pro takové případy použití funguje.
Říká se v Linux Kernel Module Programming Guide , že počet použití modulu je řízen funkcemi try_module_get
a try_module_put
. Možná zjistíte, kde jsou tyto funkce volány pro váš modul.
Vše, co dostanete, je seznam modulů, které závisí na tom, které další moduly (sloupec Used by
v lsmod). Nemůžete napsat program, abyste zjistili, proč byl modul načten, pokud je stále potřeba pro cokoliv, nebo co by se mohlo rozbít, pokud ho vyložíte a vše, co na něm záleží.
Pokud použijete možnost rmmod BEZ možnosti --force, řekne vám, co používá modul. Příklad:
$ lsmod | grep firewire
firewire_ohci 24695 0
firewire_core 50151 1 firewire_ohci
crc_itu_t 1717 1 firewire_core
$ Sudo modprobe -r firewire-core
FATAL: Module firewire_core is in use.
$ Sudo rmmod firewire_core
ERROR: Module firewire_core is in use by firewire_ohci
$ Sudo modprobe -r firewire-ohci
$ Sudo modprobe -r firewire-core
$ lsmod | grep firewire
$
Můžete zkusit soubor lsof
nebo fuser
.
zkuste kgdb a nastavte breakpoint na váš modul