web-dev-qa-db-ja.com

Linuxカーネルモジュールを使用しているものを把握する方法はありますか?

カーネルモジュールをロードし、ロードされたモジュールをlsmodでリストすると、モジュールの「使用カウント」(モジュールへの参照を持つ他のモジュールの数)を取得できます。 whatがモジュールを使用していることを把握する方法はありますか?

問題は、私が開発しているモジュールはその使用回数が1であると主張しているため、rmmodを使用してアンロードできませんが、その「by」列は空です。つまり、モジュールを再コンパイルして再ロードするたびに、マシンを再起動する必要があります(または、少なくとも、他の方法でアンロードすることはできません)。

69
mipadi

実際には、モジュール/ドライバーを要求するプロセスをリストする方法があるようです-しかし、私はそれを宣伝していません(Linuxカーネルのドキュメント以外)ので、ここにメモを書き留めます:

まず、 @ haggai_e の回答に感謝します。使用カウント(refcount)の管理を担当する関数としての関数try_module_getおよびtry_module_putへのポインターは、プロシージャを追跡できるようにするキーでした。

このオンラインをさらに探して、私は何らかの形で投稿を偶然見つけました Linux-Kernel Archive:[PATCH 1/2]トレース:モジュールトレースポイントのオーバーヘッドを減らす ;これは最終的に、カーネルに存在する「トレース」と呼ばれる機能を指していました。このドキュメントは、ディレクトリ Documentation/trace-Linux kernel source tree にあります。特に、トレース機能を説明する2つのファイル events.txtftrace.txt があります。

しかし、/sys/kernel/debug/tracing/READMEで実行中のLinuxシステムには、短い「トレースmini-HOWTO」もあります( ドキュメントがないと言っている人には本当にうんざりしています... )。カーネルソースツリーでは、このファイルは実際にはファイル kernel/trace/trace.c によって生成されることに注意してください。 Ubuntu nattyでこれをテストしましたが、/sysはrootが所有しているため、Sudo catのようにSudoを使用してこのファイルを読み取る必要があります。

Sudo less /sys/kernel/debug/tracing/README

...それは、ここで説明する/sysの下の他のほとんどすべての操作に当てはまります。


まず、これは単純な最小限のモジュール/ドライバーコードです(参照されたリソースからまとめました)。これは、文字列「This is testmod」を返す/proc/testmod-sampleファイルノードを作成するだけです。読まれているとき;これは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_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ドライバーに対して取得することはほとんどすべてです。refcountは、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ファイルを読み取ることに言及していることに注意してください。ただし、1つの問題は、このファイルが「パイプ」されることを意図していないことです(したがって、このtraceファイルでtail -fを実行しないでください)。しかし、代わりに、各操作の後にtraceを再読み取りする必要があります。最初のinsmodの後、catから同じ出力を取得し、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)で見つけることができません。プロセス名として空の<...>を取得します。したがって、この場合、trace_pipeからの実行中の出力を(teeを介して)ログに記録することをお勧めします。また、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

...「フル」であっても。

最後に、フィルターを実装しなかった場合、実行中のシステムでallモジュール呼び出しのログを取得することに注意してください。 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

...これにより、かなりのオーバーヘッドが追加されます(ログデータの量と、生成に必要な処理時間の両方で)。


これを見ながら、私は Ftrace PDFによるLinuxカーネルのデバッグ につまずきました。これはツールを指します trace-cmd 。これは上記とほとんど同じですが、より簡単なコマンドラインインターフェイス。 KernelShark ;と呼ばれるtrace-cmdの「フロントエンドリーダー」GUIもあります。これらは両方ともSudo apt-get install trace-cmd kernelsharkを介してDebian/Ubuntuリポジトリにもあります。これらのツールは、上記の手順の代わりになる可能性があります。

最後に、上記のtestmodの例は複数のクレームのコンテキストでの使用を実際に示していませんが、コーディングしているUSBモジュールがpulseaudioによって繰り返しクレームされていることを発見するために同じトレース手順を使用しましたUSBデバイスが差し込まれたらすぐに-そのような手順はそのようなユースケースで機能するようです。

47
sdaau

Linux Kernel Module Programming Guide には、モジュールの使用回数は関数try_module_getおよびtry_module_put。おそらく、これらの関数がモジュールで呼び出される場所を見つけることができます。

7
haggai_e

取得できるのは、どのモジュールが他のどのモジュールに依存しているかのリストです(Used by列(lsmod)。モジュールがなぜロードされたのか、それがまだ何かのために必要であるのか、それをアンロードするとそれに依存するすべてのものが壊れるのかを伝えるプログラムを書くことはできません。

4
Norman Ramsey

--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
$
3
JonahB 9L

lsofまたはfuserを試すことができます。

1
jedihawk

kgdbを試して、ブレークポイントをモジュールに設定します

0
river