77

カーネル モジュールをロードし、ロードされたモジュールを で一覧表示すると、モジュールlsmodの「使用回数」(モジュールへの参照を持つ他のモジュールの数) を取得できます。しかし、モジュールを使用しているものを把握する方法はありますか?

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

4

7 に答える 7

50

実際、モジュール/ドライバーを要求するプロセスを一覧表示する方法があるようですが、(Linux カーネルのドキュメント以外で) 宣伝されているのを見たことがないので、ここにメモを書き留めておきます。

まず、@haggai_eの回答に感謝します。try_module_get関数へのポインターとtry_module_put、使用カウント (refcount) の管理を担当するものは、手順を追跡するための鍵でした。

これをオンラインでさらに調べたところ、どういうわけか投稿Linux-Kernel Archive: [PATCH 1/2] トレース: モジュール トレースポイントのオーバーヘッドの削減;に出くわしました。これは最終的に、「トレース」として知られる、カーネルに存在する機能を指していました。このドキュメントはDocumentation/trace - Linux kernel source treeディレクトリにあります。具体的には、2 つのファイルevents.txtftrace.txtでトレース機能について説明しています。

しかし、実行中の Linux システムに関する短い "tracing mini-HOWTO"もあります (ドキュメントがないと言う人には本当にうんざりです...</a>/sys/kernel/debug/tracing/READMEも参照してください)。カーネル ソース ツリーでは、このファイルは実際にはファイルkernel/trace/trace.cによって生成されることに注意してください。私はこれを Ubuntu でテストしましたが、はルートによって所有されているため、このファイルを読み取るには、またはのように使用する必要があることにnatty注意してください。/syssudosudo 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_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_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全体を-edの引数コマンドとしてラップする必要があることに注意してください。次に、特定のイベント (など) ではなく「親」に書き込んだため、このフィルターはディレクトリの「子」としてリストされているすべてのイベントに適用されることに注意してください。echosudobashmodule/filtermodule/module_put/filtermodule

最後に、モジュールのトレースを有効にします。

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_pipeCTRL+を使用して読み取りを中断するだけです。Cトレースを完全に停止するには:

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

/sys/kernel/debug/tracing/traceここで、ほとんどの例は、ここではなくファイルの読み取りを参照していることに注意してtrace_pipeください。ただし、1 つの問題は、このファイルが「パイプ」されることを意図していないことです (したがってtail -f、このtraceファイルに対して a を実行しないでください)。代わりにtrace、各操作の後に再読み込みする必要があります。最初の の後、と の両方を-inginsmodすると同じ出力が得られます。ただし、ファイルを読み取ると、次のようになります。cattracetrace_pipermmodtrace

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

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


これを調べているときに、Debugging Linux Kernel by Ftrace PDFに出くわしました。これはtrace-cmdツールを参照しており、上記とほとんど同じことを行いますが、より簡単なコマンドライン インターフェイスを使用します。KernelSharktrace-cmdと呼ばれる「フロントエンド リーダー」GUI もあります。これらは両方とも、 を介して Debian/Ubuntu リポジトリにもあります。これらのツールは、上記の手順の代わりになる可能性があります。sudo apt-get install trace-cmd kernelshark

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

于 2013-03-19T07:59:23.927 に答える
7

Linuxカーネルモジュールプログラミングガイドには、モジュールの使用回数は関数try_module_getとによって制御されると記載されていますmodule_put。おそらく、これらの関数がモジュールに対して呼び出される場所を見つけることができます。

詳細:https ://www.kernel.org/doc/htmldocs/kernel-hacking/routines-module-use-counters.html

于 2009-01-16T08:38:15.310 に答える
4

得られるのは、どのモジュールが他のどのモジュールに依存しているかのリストだけです ( Used bylsmod の列)。モジュールがロードされた理由、モジュールがまだ必要な場合、モジュールとそれに依存するすべてのものをアンロードすると壊れる可能性があることを示すプログラムを作成することはできません。

于 2009-01-16T01:22:28.297 に答える
2

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

于 2009-01-16T01:18:31.673 に答える
2

--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
$
于 2013-11-08T15:04:28.780 に答える
-4

モジュールをリロードできない理由を理解しようと必死になっている人のために、私はこの問題を回避することができました。

  • 「modinfo」を使用して現在使用されているモジュールのパスを取得する
  • rm -rfing
  • ロードしたい新しいモジュールをそれがあったパスにコピーする
  • 「modprobe DRIVER_NAME.ko」と入力しています。
于 2016-08-17T19:52:45.457 に答える