[英]Is there a way to figure out what is using a Linux kernel module?
如果我加載內核模塊並使用lsmod
列出加載的模塊,我可以獲得模塊的“使用計數”(引用該模塊的其他模塊的數量)。 有沒有辦法弄清楚什么是使用一個模塊有關系嗎?
問題是我正在開發的模塊堅持其使用計數為 1,因此我不能使用rmmod
卸載它,但它的“by”列是空的。 這意味着每次我想重新編譯和重新加載模塊時,我都必須重新啟動機器(或者,至少,我想不出任何其他方法來卸載它)。
實際上,似乎有一種方法可以列出聲明模塊/驅動程序的進程——但是,我還沒有看到它的廣告(在 Linux 內核文檔之外),所以我會在這里記下我的筆記:
首先,非常感謝@haggai_e的回答; 指向函數try_module_get
和try_module_put
作為負責管理使用計數 (refcount) 的指針是讓我能夠追蹤過程的關鍵。
在網上進一步尋找這個,我不知何故偶然發現了Linux-Kernel Archive:[PATCH 1/2] 跟蹤:減少模塊跟蹤點的開銷; 它最終指向內核中存在的一個工具,稱為(我猜)“跟蹤”; 相關文檔位於Documentation/trace - Linux kernel source tree 目錄中。 特別是,兩個文件解釋了跟蹤工具, events.txt和ftrace.txt 。
但是,在/sys/kernel/debug/tracing/README
運行的 Linux 系統上還有一個簡短的“跟蹤迷你 HOWTO”(另請參閱我真的很厭倦人們說沒有文檔…… ); 請注意,在內核源代碼樹中,該文件實際上是由文件kernel/trace/trace.c 生成的。 我已經在 Ubuntu natty
上對此進行了測試,請注意,由於/sys
歸 root 所有,因此您必須使用sudo
來讀取此文件,如sudo cat
或
sudo less /sys/kernel/debug/tracing/README
... 這幾乎適用於/sys
下的所有其他操作,將在此處進行描述。
首先,這是一個簡單的最小模塊/驅動程序代碼(我從引用的資源中整理出來的),它只是創建一個/proc/testmod-sample
文件節點,它返回字符串“This is testmod”。 正在閱讀時; 這是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");
可以使用以下Makefile
構建此模塊(只需將其與testmod.c
放在同一目錄中,然后在同一目錄中運行make
):
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
構建此模塊/驅動程序時,輸出是內核對象文件testmod.ko
。
此時,我們可以准備try_module_get
和try_module_put
相關的事件追蹤; 這些在/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
請注意,在我的系統上,默認情況下啟用跟蹤:
$ sudo cat /sys/kernel/debug/tracing/tracing_enabled
1
...但是,模塊跟蹤(特別是)不是:
$ sudo cat /sys/kernel/debug/tracing/events/module/enable
0
現在,我們應該首先制作一個過濾器,它將對module_get
、 module_put
等事件做出反應,但僅適用於testmod
模塊。 為此,我們應該首先檢查事件的格式:
$ 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
在這里我們可以看到有一個名為name
的字段,它保存驅動程序名稱,我們可以對其進行過濾。 要創建過濾器,我們只需將過濾器字符串echo
顯到相應的文件中:
sudo bash -c "echo name == testmod > /sys/kernel/debug/tracing/events/module/filter"
在這里,首先請注意,由於我們必須調用sudo
,因此我們必須將整個echo
重定向包裝為sudo
-ed bash
的參數命令。 其次,請注意,由於我們寫入的是“父” module/filter
,而不是特定事件(即module/module_put/filter
等),因此此過濾器將應用於列為module
目錄“子項”的所有事件。
最后,我們啟用模塊跟蹤:
sudo bash -c "echo 1 > /sys/kernel/debug/tracing/events/module/enable"
從這點開始,我們可以讀取跟蹤日志文件; 對我來說,閱讀跟蹤文件的阻塞“管道”版本有效 - 像這樣:
sudo cat /sys/kernel/debug/tracing/trace_pipe | tee tracelog.txt
此時,我們不會在日志中看到任何內容 - 因此是時候加載(並利用和刪除)驅動程序(在與讀取trace_pipe
不同的終端中):
$ sudo insmod ./testmod.ko
$ cat /proc/testmod-sample
This is testmod.
$ sudo rmmod testmod
如果我們回到正在讀取trace_pipe
的終端,我們應該看到如下內容:
# 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
這幾乎是我們將為testmod
驅動程序獲得的testmod
- 引用計數僅在驅動程序加載 ( insmod
) 或卸載 ( rmmod
) 時發生變化,而不是在我們讀取cat
。 因此,我們可以在該終端中使用CTRL + C簡單地中斷從trace_pipe
的讀取; 並完全停止跟蹤:
sudo bash -c "echo 0 > /sys/kernel/debug/tracing/tracing_enabled"
在這里,請注意,大多數示例指的是讀取文件/sys/kernel/debug/tracing/trace
而不是這里的trace_pipe
。 然而,一個問題是這個文件並不意味着要“管道化”(所以你不應該在這個trace
文件上運行一個tail -f
); 但相反,您應該在每次操作后重新讀取trace
。 在第一個insmod
,我們將從cat
-ing trace
和trace_pipe
獲得相同的輸出; 但是,在rmmod
,讀取trace
文件會給出:
<...>-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
...也就是說:此時, insmod
已經退出很長時間,因此它不再存在於進程列表中 - 因此無法通過當時記錄的進程ID(PID)找到 - 因此我們得到一個空白的<...>
作為進程名稱。 因此,在這種情況下,最好記錄(通過tee
)來自trace_pipe
的運行輸出。 另外,請注意,為了清除/重置/擦除trace
文件,只需向其寫入 0:
sudo bash -c "echo 0 > /sys/kernel/debug/tracing/trace"
如果這看起來違反直覺,請注意trace
是一個特殊文件,並且始終報告文件大小為零:
$ 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
......即使它是“滿的”。
最后,請注意,如果我們沒有實現過濾器,我們將獲得正在運行的系統上所有模塊調用的日志——它會記錄對grep
等的任何調用(也包括后台),例如那些使用binfmt_misc
模塊的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
...這增加了相當多的開銷(在日志數據量和生成它所需的處理時間方面)。
在查找這個時,我偶然發現了 Ftrace PDF 調試 Linux 內核,它指的是一個工具trace-cmd ,它幾乎與上面類似 - 但通過更簡單的命令行界面。 還有一個名為KernelShark 的trace-cmd
的“前端閱讀器”GUI; 這兩個也通過sudo apt-get install trace-cmd kernelshark
在 Debian/Ubuntu 存儲庫中。 這些工具可以替代上述過程。
最后,我只是注意到,雖然上面的testmod
示例並未真正顯示在多個聲明的上下文中的使用,但我使用相同的跟蹤程序發現我正在編碼的 USB 模塊被pulseaudio
反復聲明為一旦插入 USB 設備 - 因此該過程似乎適用於此類用例。
它在Linux Kernel Module Programming Guide上說, 模塊的使用計數由函數try_module_get
和module_put
。 也許您可以找到為您的模塊調用這些函數的位置。
更多信息: https : //www.kernel.org/doc/htmldocs/kernel-hacking/routines-module-use-counters.html
所有你得到的是其中的模塊依賴於它的其他模塊(該列表Used by
在lsmod的列)。 您無法編寫程序來說明為什么加載模塊,是否仍然需要任何東西,或者如果卸載它以及依賴它的所有東西可能會破壞什么。
如果你使用沒有 --force 選項的 rmmod,它會告訴你什么正在使用模塊。 示例:
$ 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
$
您可以嘗試lsof
或fuser
。
嘗試 kgdb 並為您的模塊設置斷點
對於任何渴望弄清楚為什么他們不能重新加載模塊的人,我能夠解決這個問題
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.