簡體   English   中英

有沒有辦法找出使用 Linux 內核模塊的內容?

[英]Is there a way to figure out what is using a Linux kernel module?

如果我加載內核模塊並使用lsmod列出加載的模塊,我可以獲得模塊的“使用計數”(引用該模塊的其他模塊的數量)。 有沒有辦法弄清楚什么是使用一個模塊有關系嗎?

問題是我正在開發的模塊堅持其使用計數為 1,因此我不能使用rmmod卸載它,但它的“by”列是空的。 這意味着每次我想重新編譯和重新加載模塊時,我都必須重新啟動機器(或者,至少,我想不出任何其他方法來卸載它)。

實際上,似乎有一種方法可以列出聲明模塊/驅動程序的進程——但是,我還沒有看到它的廣告(在 Linux 內核文檔之外),所以我會在這里記下我的筆記:

首先,非常感謝@haggai_e的回答; 指向函數try_module_gettry_module_put作為負責管理使用計數 (refcount) 的指針是讓我能夠追蹤過程的關鍵。

在網上進一步尋找這個,我不知何故偶然發現了Linux-Kernel Archive:[PATCH 1/2] 跟蹤:減少模塊跟蹤點的開銷 它最終指向內核中存在的一個工具,稱為(我猜)“跟蹤”; 相關文檔位於Documentation/trace - Linux kernel source tree 目錄中 特別是,兩個文件解釋了跟蹤工具, events.txtftrace.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_gettry_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_getmodule_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 tracetrace_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_getmodule_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
$

您可以嘗試lsoffuser

嘗試 kgdb 並為您的模塊設置斷點

對於任何渴望弄清楚為什么他們不能重新加載模塊的人,我能夠解決這個問題

  • 使用“modinfo”獲取當前使用的模塊的路徑
  • rm -rfing 它
  • 將我想加載的新模塊復制到它所在的路徑
  • 輸入“modprobe DRIVER_NAME.ko”。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM