it-swarm.com.de

Gibt es eine Möglichkeit herauszufinden, was ein Linux-Kernel-Modul verwendet?

Wenn ich ein Kernel-Modul lade und die geladenen Module mit lsmod aufliste, kann ich den "use count" des Moduls erhalten (Anzahl der anderen Module mit einem Verweis auf das Modul). Gibt es eine Möglichkeit, herauszufinden, was ein Modul verwendet?

Das Problem ist, dass ein Modul, das ich gerade entwickle, darauf besteht, dass es eine Nutzungsanzahl von 1 hat. Daher kann ich rmmod nicht zum Entladen verwenden, aber die Spalte "by" ist leer. Das bedeutet, dass ich jedes Mal, wenn ich das Modul neu kompilieren und laden möchte, den Computer neu starten muss (oder zumindest keine andere Möglichkeit finde, es zu entladen).

64
mipadi

Tatsächlich scheint es eine Möglichkeit zu geben, Prozesse aufzulisten, die ein Modul/einen Treiber beanspruchen. Ich habe jedoch keine Werbung dafür gesehen (außerhalb der Linux-Kerneldokumentation). Deshalb schreibe ich hier meine Notizen auf:

Zunächst vielen Dank für die Antwort von @ haggai_e ; Der Zeiger auf die Funktionen try_module_get und try_module_put als Verantwortliche für die Verwaltung der Nutzungsanzahl (refcount) war der Schlüssel, mit dem ich die Prozedur aufspüren konnte.

Bei der Online-Suche stieß ich irgendwie auf den Beitrag Linux-Kernel-Archiv: [PATCH 1/2] -Tracing: Reduzierung des Overheads von Modul-Tracepoints ; was schließlich auf eine im Kernel vorhandene Einrichtung hinwies, die als (ich vermute) "Tracing" bekannt ist; Die Dokumentation dazu befindet sich im Verzeichnis Documentation/trace - Linux Kernel Source Tree . Insbesondere erläutern zwei Dateien die Tracefunktion: events.txt und ftrace.txt .

Aber es gibt auch ein kurzes "Tracing Mini-HOWTO" auf einem laufenden Linux-System in /sys/kernel/debug/tracing/README (siehe auch Ich habe es wirklich satt, dass Leute sagen, dass es keine Dokumentation gibt ... ); Beachten Sie, dass diese Datei im Kernel-Quelltextbaum tatsächlich von der Datei kernel/trace/trace.c generiert wird. Ich habe dies unter Ubuntu natty getestet und beachte, dass /sys im Besitz von root ist. Daher musst du Sudo verwenden, um diese Datei zu lesen, wie in Sudo cat oder

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

... und das gilt für so ziemlich alle anderen Operationen unter /sys, die hier beschrieben werden.


Hier ist zunächst ein einfacher minimaler Modul-/Treibercode (den ich aus den angegebenen Ressourcen zusammengestellt habe), der einfach einen /proc/testmod-sample-Dateiknoten erstellt, der die Zeichenfolge "This is testmod" zurückgibt. wenn es gelesen wird; Das ist 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");

Dieses Modul kann mit folgendem Makefile erstellt werden (muss nur im selben Verzeichnis wie testmod.c abgelegt werden und dann make in demselben Verzeichnis ausführen):

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

Wenn dieses Modul/dieser Treiber erstellt wird, ist die Ausgabe eine Kernelobjektdatei, testmod.ko.


Zu diesem Zeitpunkt können wir die Ereignisablaufverfolgung für try_module_get und try_module_put vorbereiten. diese sind in /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

Beachten Sie, dass auf meinem System die Ablaufverfolgung standardmäßig aktiviert ist:

$ Sudo cat /sys/kernel/debug/tracing/tracing_enabled
1

... die Modulverfolgung (speziell) ist jedoch nicht:

$ Sudo cat /sys/kernel/debug/tracing/events/module/enable
0

Jetzt sollten wir zuerst einen Filter erstellen, der auf die Ereignisse module_get, module_put usw. reagiert, jedoch nur für das Modul testmod. Dazu müssen wir zuerst das Format des Ereignisses überprüfen:

$ 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

Hier sehen wir, dass es ein Feld namens name gibt, das den Treibernamen enthält, nach dem wir filtern können. Um einen Filter zu erstellen, echoen wir einfach die Filterzeichenfolge in die entsprechende Datei:

Sudo bash -c "echo name == testmod > /sys/kernel/debug/tracing/events/module/filter"

Beachten Sie hier zunächst, dass wir, da wir Sudo aufrufen müssen, die gesamte Umleitung echo als Argumentbefehl eines Sudo- ed bash umbrechen müssen. Zweitens ist zu beachten, dass dieser Filter auf alle Ereignisse angewendet wird, die als "Kinder" des Verzeichnisses module aufgeführt sind, da wir in das übergeordnete Verzeichnis module/filter geschrieben haben und nicht in die spezifischen Ereignisse (die module/module_put/filter usw. wären).

Schließlich aktivieren wir das Tracing für das Modul:

Sudo bash -c "echo 1 > /sys/kernel/debug/tracing/events/module/enable"

Ab diesem Zeitpunkt können wir die Trace-Protokolldatei lesen. Für mich hat das Lesen der blockierenden "piped" -Version der Trace-Datei funktioniert - wie folgt:

Sudo cat /sys/kernel/debug/tracing/trace_pipe | tee tracelog.txt

Zu diesem Zeitpunkt wird im Protokoll nichts angezeigt. Es ist also an der Zeit, den Treiber zu laden (und zu verwenden und zu entfernen) (in einem anderen Terminal als dem, in dem trace_pipe gelesen wird):

$ Sudo insmod ./testmod.ko
$ cat /proc/testmod-sample 
This is testmod.
$ Sudo rmmod testmod

Wenn wir zu dem Terminal zurückkehren, in dem trace_pipe gelesen wird, sollten wir etwas sehen wie:

# 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

Das ist so ziemlich alles, was wir für unseren testmod-Treiber erhalten - die Anzahl ändert sich nur, wenn der Treiber geladen (insmod) oder entladen (rmmod) wird, nicht, wenn wir cat durchlesen. So können wir einfach das Lesen von trace_pipe mit unterbrechen CTRL+C in diesem Terminal; und um die Verfolgung ganz zu stoppen:

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

Beachten Sie, dass sich die meisten Beispiele auf das Lesen der Datei /sys/kernel/debug/tracing/trace anstelle von trace_pipe beziehen. Ein Problem besteht jedoch darin, dass diese Datei nicht als "Pipe" -Datei gedacht ist (Sie sollten daher keinen tail -f für diese trace -Datei ausführen). Stattdessen sollten Sie trace nach jeder Operation erneut lesen. Nach dem ersten insmod würden wir die gleiche Ausgabe von cat erhalten, und zwar sowohl trace als auch trace_pipe. Nach dem rmmod würde das Lesen der trace-Datei jedoch Folgendes ergeben:

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

... das heißt: zu diesem Zeitpunkt war die insmod schon lange beendet und existiert daher nicht mehr in der Prozessliste - und kann daher über die aufgezeichnete Prozess-ID (PID) zu diesem Zeitpunkt nicht gefunden werden - also wir erhalten einen leeren <...> als Prozessnamen. Daher ist es in diesem Fall besser, eine laufende Ausgabe von trace_pipe (über tee) zu protokollieren. Beachten Sie auch, dass zum Löschen/Zurücksetzen/Löschen der Datei trace einfach eine 0 in diese geschrieben wird:

Sudo bash -c "echo 0 > /sys/kernel/debug/tracing/trace"

Wenn dies nicht intuitiv erscheint, beachten Sie, dass trace eine spezielle Datei ist und trotzdem immer eine Dateigröße von Null ausgibt:

$ 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

... auch wenn es "voll" ist.

Beachten Sie schließlich, dass wir, wenn wir keinen Filter implementiert hätten, ein Protokoll von allen Modulaufrufen auf dem laufenden System erhalten hätten - das jeden Aufruf (auch Hintergrund) von protokollieren würde grep und solche, die das Modul binfmt_misc verwenden:

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

... was zu einem erheblichen Mehraufwand führt (sowohl bei der Menge der Protokolldaten als auch bei der Verarbeitungszeit, die für deren Generierung erforderlich ist).


Beim Nachschlagen stieß ich auf Debugging Linux Kernel von Ftrace PDF , das auf ein Tool verweist trace-cmd , das so ziemlich das Gleiche wie oben macht - aber durch ein einfachere Befehlszeilenschnittstelle. Es gibt auch eine "Front-End-Reader" -GUI für trace-cmd namens KernelShark ; beide befinden sich auch in Debian/Ubuntu-Repositories über Sudo apt-get install trace-cmd kernelshark. Diese Tools könnten eine Alternative zum oben beschriebenen Verfahren sein.

Abschließend möchte ich nur bemerken, dass das obige Beispiel testmod zwar nicht wirklich die Verwendung im Zusammenhang mit mehreren Behauptungen zeigt, ich jedoch das gleiche Ablaufverfolgungsverfahren verwendet habe, um festzustellen, dass ein USB-Modul, das ich codiere, wiederholt von pulseaudio als beansprucht wurde Sobald das USB-Gerät eingesteckt war - so scheint das Verfahren für solche Anwendungsfälle zu funktionieren.

47
sdaau

Im Linux Kernel Module-Programmierhandbuch heißt es, dass die Verwendungsanzahl eines Moduls von den Funktionen try_module_get und try_module_put gesteuert wird. Vielleicht können Sie herausfinden, wo diese Funktionen für Ihr Modul aufgerufen werden.

6
haggai_e

Sie erhalten nur eine Liste, welche Module von welchen anderen Modulen abhängen (die Used by-Spalte in lsmod). Sie können kein Programm schreiben, um herauszufinden, warum das Modul geladen wurde, ob es noch für irgendetwas benötigt wird oder was kaputt gehen könnte, wenn Sie es entladen und was davon abhängt.

4
Norman Ramsey

Wenn Sie rmmod OHNE die Option --force verwenden, erfahren Sie, was ein Modul verwendet. Beispiel:

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

Sie könnten lsof oder fuser versuchen.

1
jedihawk

probiere kgdb und setze einen Haltepunkt für dein Modul

0
river