当前位置:  开发笔记 > 编程语言 > 正文

有没有办法弄清楚什么是使用Linux内核模块?

如何解决《有没有办法弄清楚什么是使用Linux内核模块?》经验,为你挑选了2个好方法。

如果我加载内核模块并列出已加载的模块lsmod,我可以得到模块的"使用计数"(其他模块的数量与模块的引用).有没有办法弄清楚什么是使用模块?

问题是我正在开发的模块坚持它的使用计数为1,因此我不能用rmmod它来卸载它,但它的"by"列是空的.这意味着每次我想重新编译和重新加载模块时,我都必须重启机器(或者,至少,我无法找出任何其他方法来卸载它).



1> sdaau..:

实际上,似乎有一种方法可以列出声称模块/驱动程序的进程 - 但是,我没有看到它被广告(在Linux内核文档之外),所以我会在这里记下我的注释:

首先,非常感谢@haggai_e的回答; 指向函数的指针try_module_gettry_module_put负责管理使用计数(refcount)的指针是允许我追踪程序的关键.

在网上进一步观察,我不知何故偶然发现Linux-Kernel Archive:[PATCH 1/2]跟踪:减少模块跟踪点的开销 ; 最后指向内核中存在的工具,称为(我猜)"跟踪"; 此文档位于Documentation/trace - Linux内核源代码树目录中.特别是,两个文件解释了跟踪工具,events.txt和ftrace.txt.

但是,在运行的Linux系统上还有一个简短的"跟踪迷你HOWTO" /sys/kernel/debug/tracing/README(另见我真的厌倦了人们说没有文档...); 请注意,在内核源代码树中,此文件实际上是由文件kernel/trace/trace.c生成的.我已经在Ubuntu上测试了这个natty,并注意,因为/sysroot由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 
#include 
#include 
#include  // 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_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驱动程序获取的全部内容- 仅当驱动程序加载(insmod)或卸载(rmmod)时才会更改引用计数,而不是在我们执行读取操作时更改cat.所以我们可以简单地在该终端trace_pipe中用CTRL+ 中断读取C; 并完全停止跟踪:

sudo bash -c "echo 0 > /sys/kernel/debug/tracing/tracing_enabled"

请注意,大多数示例都是指读取文件/sys/kernel/debug/tracing/trace而不是trace_pipe此处.但是,一个问题是这个文件不是"管道"(所以你不应该tail -f在这个trace文件上运行); 但相反,你应该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模块:

...
  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

...它增加了相当多的开销(在日志数据ammount和生成它所需的处理时间).


在查看此内容时,我偶然发现了Ftrace PDF调试Linux内核,它引用了一个工具trace-cmd,它几乎与上面类似 - 但是通过一个更简单的命令行界面.还有一个trace-cmd名为KernelShark的"前端阅读器"GUI ; 这两个也都在Debian/Ubuntu存储库中sudo apt-get install trace-cmd kernelshark.这些工具可以替代上述程序.

最后,我只是注意到,虽然上面的testmod示例并没有真正显示在多个声明的上下文中使用,但我使用相同的跟踪过程来发现我正在编码的USB模块,pulseaudio一旦被重复声明USB设备已插入 - 因此该过程似乎适用于此类用例.


感谢@RichardHansen的评论 - 问题是"有没有办法找出_what_正在使用模块"; 并且您可以在模块跟踪中看到例如`rmmod-21354`或`tr-6232`(进程名称 - 进程ID)是执行`module_put`的那个,即更改模块的引用计数 - 是,那些过程是"使用"模块; 所以我认为它完全回答了OP所要求的......干杯!

2> haggai_e..:

它在Linux内核模块编程指南中说,模块的使用计数由函数try_module_get和控制try_module_put.也许您可以找到为模块调用这些函数的位置.

推荐阅读
我我檬檬我我186
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有