有没有办法找出使用 Linux 内核模块的内容?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/448999/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is there a way to figure out what is using a Linux kernel module?
提问by mipadi
If I load a kernel module and list the loaded modules with lsmod
, I can get the "use count" of the module (number of other modules with a reference to the module). Is there a way to figure out whatis using a module, though?
如果我加载一个内核模块并用 列出加载的模块lsmod
,我可以获得该模块的“使用计数”(引用该模块的其他模块的数量)。有没有办法找出什么是使用一个模块有关系吗?
The issue is that a module I am developing insists its use count is 1 and thus I cannot use rmmod
to unload it, but its "by" column is empty. This means that every time I want to re-compile and re-load the module, I have to reboot the machine (or, at least, I can't figure out any other way to unload it).
问题是我正在开发的一个模块坚持它的使用计数为 1,因此我不能rmmod
用来卸载它,但它的“by”列是空的。这意味着每次我想重新编译和重新加载模块时,我都必须重新启动机器(或者,至少,我想不出任何其他方法来卸载它)。
回答by jedihawk
You might try lsof
or fuser
.
你可以试试lsof
或fuser
。
回答by Norman Ramsey
All you get are a list of which modules depend on which other modules (the Used by
column in lsmod). You can't write a program to tell why the module was loaded, if it is still needed for anything, or what might break if you unload it and everything that depends on it.
您所得到的只是一个列表,其中列出了哪些模块依赖于哪些其他模块(Used by
lsmod 中的列)。您无法编写程序来说明为什么加载模块,是否仍然需要任何东西,或者如果卸载它以及依赖它的所有东西可能会破坏什么。
回答by haggai_e
It says on the Linux Kernel Module Programming Guidethat the use count of a module is controlled by the functions try_module_get
and try_module_put
. Perhaps you can find where these functions are called for your module.
它在Linux Kernel Module Programming Guide上说,模块的使用计数由函数try_module_get
和try_module_put
. 也许您可以找到为您的模块调用这些函数的位置。
回答by sdaau
Actually, there seems to be a way to list processes that claim a module/driver - however, I haven't seen it advertised (outside of Linux kernel documentation), so I'll jot down my notes here:
实际上,似乎有一种方法可以列出声明模块/驱动程序的进程——但是,我还没有看到它的广告(在 Linux 内核文档之外),所以我会在这里记下我的笔记:
First of all, many thanks for @haggai_e's answer; the pointer to the functions try_module_get
and try_module_put
as those responsible for managing the use count (refcount) was the key that allowed me to track down the procedure.
首先,非常感谢@haggai_e的回答;指向函数的指针try_module_get
以及try_module_put
负责管理使用计数 (refcount)的指针是让我能够追踪过程的关键。
Looking further for this online, I somehow stumbled upon the post Linux-Kernel Archive: [PATCH 1/2] tracing: Reduce overhead of module tracepoints; which finally pointed to a facility present in the kernel, known as (I guess) "tracing"; the documentation for this is in the directory Documentation/trace - Linux kernel source tree. In particular, two files explain the tracing facility, events.txtand ftrace.txt.
在网上进一步寻找这个,我不知何故偶然发现了Linux-Kernel Archive:[PATCH 1/2] 跟踪:减少模块跟踪点的开销;它最终指向内核中存在的一个工具,称为(我猜)“跟踪”;相关文档位于Documentation/trace - Linux kernel source tree 目录中。特别是,两个文件解释了跟踪工具,events.txt和ftrace.txt。
But, there is also a short "tracing mini-HOWTO" on a running Linux system in /sys/kernel/debug/tracing/README
(see also I'm really really tired of people saying that there's no documentation…); note that in the kernel source tree, this file is actually generated by the file kernel/trace/trace.c. I've tested this on Ubuntu natty
, and note that since /sys
is owned by root, you have to use sudo
to read this file, as in sudo cat
or
但是,在运行的 Linux 系统上还有一个简短的“跟踪迷你 HOWTO” /sys/kernel/debug/tracing/README
(另请参阅我真的很厌倦人们说没有文档......);请注意,在内核源代码树中,该文件实际上是由文件kernel/trace/trace.c 生成的。我已经在Ubuntu这个测试natty
,并注意因为/sys
所有者是root,你必须使用sudo
读取这个文件,如sudo cat
或
sudo less /sys/kernel/debug/tracing/README
... and that goes for pretty much all other operations under /sys
which will be described here.
... 这几乎适用于/sys
将在此处描述的所有其他操作。
First of all, here is a simple minimal module/driver code (which I put together from the referred resources), which simply creates a /proc/testmod-sample
file node, which returns the string "This is testmod." when it is being read; this is testmod.c
:
首先,这是一个简单的最小模块/驱动程序代码(我从引用的资源中组合而成),它只是创建一个/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");
This module can be built with the following Makefile
(just have it placed in the same directory as testmod.c
, and then run make
in that same directory):
可以使用以下内容构建此模块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
When this module/driver is built, the output is a kernel object file, testmod.ko
.
构建此模块/驱动程序时,输出是内核对象文件testmod.ko
.
At this point, we can prepare the event tracing related to try_module_get
and try_module_put
; those are in /sys/kernel/debug/tracing/events/module
:
此时,我们可以准备与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
Note that on my system, tracing is by default enabled:
请注意,在我的系统上,默认情况下启用跟踪:
$ sudo cat /sys/kernel/debug/tracing/tracing_enabled
1
... however, the module tracing (specifically) is not:
...但是,模块跟踪(特别是)不是:
$ sudo cat /sys/kernel/debug/tracing/events/module/enable
0
Now, we should first make a filter, that will react on the module_get
, module_put
etc events, but only for the testmod
module. To do that, we should first check the format of the event:
现在,我们应该首先做一个过滤器,将在反应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
Here we can see that there is a field called name
, which holds the driver name, which we can filter against. To create a filter, we simply echo
the filter string into the corresponding file:
在这里我们可以看到有一个名为 的字段name
,它保存驱动程序名称,我们可以对其进行过滤。要创建过滤器,我们只需echo
将过滤器字符串放入相应的文件中:
sudo bash -c "echo name == testmod > /sys/kernel/debug/tracing/events/module/filter"
Here, first note that since we have to call sudo
, we have to wrap the whole echo
redirection as an argument command of a sudo
-ed bash
. Second, note that since we wrote to the "parent" module/filter
, not the specific events (which would be module/module_put/filter
etc), this filter will be applied to all events listed as "children" of module
directory.
在这里,首先要注意,由于我们必须调用sudo
,我们必须将整个echo
重定向包装为 a sudo
-ed的参数命令bash
。其次,请注意,由于我们写入的是“父” module/filter
,而不是特定事件(可能是module/module_put/filter
等),因此此过滤器将应用于列为module
目录“子”的所有事件。
Finally, we enable tracing for module:
最后,我们启用模块跟踪:
sudo bash -c "echo 1 > /sys/kernel/debug/tracing/events/module/enable"
From this point on, we can read the trace log file; for me, reading the blocking, "piped" version of the trace file worked - like this:
从这点开始,我们可以读取跟踪日志文件;对我来说,阅读跟踪文件的阻塞“管道”版本有效 - 像这样:
sudo cat /sys/kernel/debug/tracing/trace_pipe | tee tracelog.txt
At this point, we will not see anything in the log - so it is time to load (and utilize, and remove) the driver (in a different terminal from where trace_pipe
is being read):
此时,我们不会在日志中看到任何内容 - 因此是时候加载(并使用和删除)驱动程序(在与trace_pipe
读取位置不同的终端中):
$ sudo insmod ./testmod.ko
$ cat /proc/testmod-sample
This is testmod.
$ sudo rmmod testmod
If we go back to the terminal where trace_pipe
is being read, we should see something like:
如果我们回到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
That is pretty much all we will obtain for our testmod
driver - the refcount changes only when the driver is loaded (insmod
) or unloaded (rmmod
), not when we do a read through cat
. So we can simply interrupt the read from trace_pipe
with CTRL+Cin that terminal; and to stop the tracing altogether:
这几乎是我们为testmod
驱动程序获得的全部内容- 引用计数仅在驱动程序加载 ( insmod
) 或卸载 ( rmmod
)时发生变化,而不是在我们读取时发生变化cat
。所以我们可以简单地在那个终端trace_pipe
中用CTRL+中断读取C;并完全停止跟踪:
sudo bash -c "echo 0 > /sys/kernel/debug/tracing/tracing_enabled"
Here, note that most examples refer to reading the file /sys/kernel/debug/tracing/trace
instead of trace_pipe
as here. However, one problem is that this file is not meant to be "piped" (so you shouldn't run a tail -f
on this trace
file); but instead you should re-read the trace
after each operation. After the first insmod
, we would obtain the same output from cat
-ing both trace
and trace_pipe
; however, after the rmmod
, reading the trace
file would give:
在这里,请注意,大多数示例指的是读取文件/sys/kernel/debug/tracing/trace
而不是trace_pipe
此处。然而,一个问题是这个文件并不意味着要“管道化”(所以你不应该tail -f
在这个trace
文件上运行);但相反,您应该trace
在每次操作后重新阅读。在第一个之后insmod
,我们将从cat
-ing both trace
and获得相同的输出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
... that is: at this point, the insmod
had already been exited for long, and so it doesn't exist anymore in the process list - and therefore cannot be found via the recorded process ID (PID) at the time - thus we get a blank <...>
as process name. Therefore, it is better to log (via tee
) a running output from trace_pipe
in this case. Also, note that in order to clear/reset/erase the trace
file, one simply writes a 0 to it:
...也就是说:此时,insmod
已经退出了很长时间,因此它不再存在于进程列表中 - 因此无法通过当时记录的进程 ID(PID)找到 - 因此我们获取空白<...>
作为进程名称。因此,在这种情况下最好记录(通过tee
)正在运行的输出trace_pipe
。另外,请注意,为了清除/重置/擦除trace
文件,只需向其写入 0:
sudo bash -c "echo 0 > /sys/kernel/debug/tracing/trace"
If this seems counterintuitive, note that trace
is a special file, and will always report a file size of zero anyways:
如果这看起来违反直觉,请注意这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
... even if it is "full".
......即使它是“满的”。
Finally, note that if we didn't implement a filter, we would have obtained a log of allmodule calls on the running system - which would log any call (also background) to grep
and such, as those use the binfmt_misc
module:
最后,请注意,如果我们没有实现过滤器,我们将获得正在运行的系统上所有模块调用的日志 - 这将记录任何调用(也包括后台)grep
等等,因为那些使用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
... which adds quite a bit of overhead (in both log data ammount, and processing time required to generate it).
...这增加了相当多的开销(在日志数据量和生成它所需的处理时间方面)。
While looking this up, I stumbled upon Debugging Linux Kernel by Ftrace PDF, which refers to a tool trace-cmd, which pretty much does the similar as above - but through an easier command line interface. There is also a "front-end reader" GUI for trace-cmd
called KernelShark; both of these are also in Debian/Ubuntu repositories via sudo apt-get install trace-cmd kernelshark
. These tools could be an alternative to the procedure described above.
在查找这个时,我偶然发现了 Ftrace PDF 调试 Linux 内核,它指的是一个工具trace-cmd,它几乎与上面类似 - 但通过更简单的命令行界面。还有一个trace-cmd
名为KernelShark的“前端阅读器”GUI ;这两个也都在 Debian/Ubuntu 存储库中,通过sudo apt-get install trace-cmd kernelshark
. 这些工具可以替代上述过程。
Finally, I'd just note that, while the above testmod
example doesn't really show use in context of multiple claims, I have used the same tracing procedure to discover that an USB module I'm coding, was repeatedly claimed by pulseaudio
as soon as the USB device was plugged in - so the procedure seems to work for such use cases.
最后,我只是指出,虽然上面的testmod
例子并没有真正表现出多重索赔的情况下使用,我已经使用了相同的跟踪过程来发现一个USB模块,我编码,多次被要求pulseaudio
尽快USB 设备已插入 - 因此该过程似乎适用于此类用例。
回答by JonahB 9L
If you use rmmod WITHOUT the --force option, it will tell you what is using a module. Example:
如果你使用没有 --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
$
回答by Locane
For anyone desperate to figure out why they can't reload modules, I was able to work around this problem by
对于任何渴望弄清楚为什么他们不能重新加载模块的人,我能够解决这个问题
- Getting the path of the currently used module using "modinfo"
- rm -rfing it
- Copying the new module I wanted to load to the path it was in
- Typing "modprobe DRIVER_NAME.ko".
- 使用“modinfo”获取当前使用的模块的路径
- rm -rfing 它
- 将我想加载的新模块复制到它所在的路径
- 输入“modprobe DRIVER_NAME.ko”。
回答by river
try kgdb and set breakpoint to your module
尝试 kgdb 并为您的模块设置断点