有没有办法找出使用 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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 16:53:32  来源:igfitidea点击:

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

linuxkernelkernel-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 rmmodto 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 lsofor fuser.

你可以试试lsoffuser

回答by Norman Ramsey

All you get are a list of which modules depend on which other modules (the Used bycolumn 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 bylsmod 中的列)。您无法编写程序来说明为什么加载模块,是否仍然需要任何东西,或者如果卸载它以及依赖它的所有东西可能会破坏什么。

回答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_getand try_module_put. Perhaps you can find where these functions are called for your module.

它在Linux Kernel Module Programming Guide上说,模块的使用计数由函数try_module_gettry_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_getand try_module_putas 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.txtftrace.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 /sysis owned by root, you have to use sudoto read this file, as in sudo cator

但是,在运行的 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 /syswhich 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-samplefile 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 makein 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_getand 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_putetc events, but only for the testmodmodule. To do that, we should first check the format of the event:

现在,我们应该首先做一个过滤器,将在反应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

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 echothe 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 echoredirection 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/filteretc), this filter will be applied to all events listed as "children" of moduledirectory.

在这里,首先要注意,由于我们必须调用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_pipeis 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_pipeis 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 testmoddriver - 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_pipewith 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/traceinstead of trace_pipeas here. However, one problem is that this file is not meant to be "piped" (so you shouldn't run a tail -fon this tracefile); but instead you should re-read the traceafter each operation. After the first insmod, we would obtain the same output from cat-ing both traceand trace_pipe; however, after the rmmod, reading the tracefile would give:

在这里,请注意,大多数示例指的是读取文件/sys/kernel/debug/tracing/trace而不是trace_pipe此处。然而,一个问题是这个文件并不意味着要“管道化”(所以你不应该tail -f在这个trace文件上运行);但相反,您应该trace在每次操作后重新阅读。在第一个之后insmod,我们将从cat-ing both traceand获得相同的输出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 insmodhad 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_pipein this case. Also, note that in order to clear/reset/erase the tracefile, 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 traceis 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 grepand such, as those use the binfmt_miscmodule:

最后,请注意,如果我们没有实现过滤器,我们将获得正在运行的系统上所有模块调用的日志 - 这将记录任何调用(也包括后台)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-cmdcalled 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 testmodexample 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 pulseaudioas 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 并为您的模块设置断点