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

73

Wenn ich ein Kernelmodul lade und die geladenen Module mit aufliste lsmod, kann ich die "Verwendungsanzahl" des Moduls erhalten (Anzahl anderer Module mit Bezug auf das Modul). Gibt es eine Möglichkeit herauszufinden, was ein Modul verwendet?

Das Problem ist, dass ein Modul, das ich entwickle, darauf besteht, dass seine Verwendungsanzahl 1 ist und ich es daher nicht rmmodzum Entladen verwenden kann, aber seine "by" -Spalte leer ist. Dies 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 zum Entladen finden kann).

Mipadi
quelle
"was" in welchen Begriffen? Welcher Code? Welches Modul? Welcher Benutzer? welches Programm? obwohl ich leicht das Gefühl habe, dass dies nicht mit der Programmierung zu
tun hat
1
Nun, es hängt mit der Programmierung zusammen, da ich frage, weil ich ein Kernelmodul schreibe.
Mipadi
Bitte klären Sie die Frage, um das Programmierproblem anzuzeigen, das Sie lösen möchten.
Norman Ramsey
10
Die Frage ist mir ziemlich klar, Norman: Wie kann er herausfinden, was rmmod davon abhält, sein experimentelles Modul zu entfernen?; Wie kann vermieden werden, dass jedes Mal neu gestartet werden muss, wenn eine neue Version kompiliert wird?
Stirb in Sente am

Antworten:

50

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), daher schreibe ich meine Notizen hier auf:

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

Als ich online weiter danach suchte , stieß ich irgendwie auf das Post- Linux-Kernel-Archiv: [PATCH 1/2] Tracing: Reduzieren Sie den Overhead von Modul-Tracepoints ; was schließlich auf eine im Kernel vorhandene Einrichtung hinwies, die als (ich denke) "Tracing" bekannt ist; Die Dokumentation dazu befindet sich im Verzeichnis Documentation / trace - Linux-Kernel-Quellbaum . Insbesondere erläutern zwei Dateien die Ablaufverfolgungsfunktion events.txt und ftrace.txt .

Es gibt aber 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-Quellbaum tatsächlich von der Datei kernel / trace / trace.c generiert wird . Ich habe dies unter Ubuntu getestet nattyund /sysfestgestellt, dass sudoSie diese Datei wie in sudo catoder verwenden müssen , da sie Root gehört

sudo less /sys/kernel/debug/tracing/README

... und das gilt für so ziemlich alle anderen Operationen, unter /sysdenen hier beschrieben wird.


Hier ist zunächst ein einfacher minimaler Modul- / Treibercode (den ich aus den angegebenen Ressourcen zusammengestellt habe), der einfach einen /proc/testmod-sampleDateiknoten 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 wie folgt erstellt werden Makefile(lassen Sie es einfach im selben Verzeichnis wie platzieren testmod.cund dann makeim selben 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 Kernel-Objektdatei testmod.ko.


An diesem Punkt können wir die Ereignisverfolgung in Bezug auf try_module_getund vorbereiten try_module_put. die 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

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

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

Nun sollten wir zunächst einen Filter machen, die auf den reagieren module_get, module_putusw. Ereignissen, sondern nur für das testmodModul. Dazu sollten 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 gibt name, das den Treibernamen enthält, nach dem wir filtern können. Um einen Filter zu erstellen, fügen wir einfach echodie Filterzeichenfolge in die entsprechende Datei ein:

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

Beachten Sie hier zunächst, dass wir, da wir aufrufen sudomüssen, die gesamte echoUmleitung als Argumentbefehl eines sudo-ed umschließen müssen bash. Zweitens ist zu beachten, dass dieser Filter auf alle Ereignisse angewendet wird, die als "Kinder" des Verzeichnisses aufgeführt sind, da wir an das "übergeordnete Element" geschrieben haben module/filter, nicht an die spezifischen Ereignisse (die module/module_put/filterusw. wären ) module.

Schließlich aktivieren wir die Ablaufverfolgung 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 wie folgt funktioniert:

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

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

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

Wenn wir zu dem Terminal zurückkehren, in trace_pipedem gelesen wird, sollten wir Folgendes sehen:

# 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 testmodTreiber erhalten - der Refcount ändert sich nur, wenn der Treiber geladen ( insmod) oder entladen ( rmmod) wird, nicht wenn wir ihn durchlesen cat. Wir können also einfach den Lesevorgang trace_pipemit CTRL+ Cin diesem Terminal unterbrechen . und um die Verfolgung insgesamt zu stoppen:

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

Beachten Sie hier, dass sich die meisten Beispiele auf das Lesen der Datei beziehen, /sys/kernel/debug/tracing/traceanstatt trace_pipewie hier. Ein Problem ist jedoch, dass diese Datei nicht als "Piping" gedacht ist (Sie sollten also keine Datei tail -ffür diese traceDatei ausführen ). Stattdessen sollten Sie die tracenach jeder Operation erneut lesen . Nach dem ersten insmodwürden wir die gleiche Ausgabe von cat-ing sowohl traceals auch erhalten trace_pipe; Nach dem würde das rmmodLesen der traceDatei 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 das insmodschon lange beendet und existiert daher nicht mehr in der Prozessliste - und kann daher zu diesem Zeitpunkt nicht über die aufgezeichnete Prozess-ID (PID) gefunden werden - also wir Holen Sie sich ein Leerzeichen <...>als Prozessname. Daher ist es in diesem Fall besser, teeeine laufende Ausgabe (über ) zu protokollieren trace_pipe. Beachten Sie außerdem, dass zum Löschen / Zurücksetzen / Löschen der traceDatei einfach eine 0 geschrieben wird:

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

Wenn dies nicht traceintuitiv erscheint, beachten Sie, dass es sich um eine spezielle Datei handelt, die ohnehin immer eine Dateigröße von Null meldet:

$ 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 aller Modulaufrufe auf dem laufenden System erhalten hätten, das jeden Aufruf (auch Hintergrund) an grepund solche protokollieren würde , wie diejenigen, die das binfmt_miscModul 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 einen erheblichen Mehraufwand verursacht (sowohl bei der Menge der Protokolldaten als auch bei der Verarbeitungszeit, die zum Generieren erforderlich ist).


Während ich dies nachgeschlagen habe , bin ich auf das Debuggen des Linux-Kernels von Ftrace PDF gestoßen , das sich auf ein Tool trace- cmd bezieht , das so ziemlich das Gleiche wie oben tut - aber über eine einfachere Befehlszeilenschnittstelle. Es gibt auch eine "Front-End-Reader" -GUI mit demtrace-cmd Namen KernelShark . Beide befinden sich auch in Debian / Ubuntu-Repositories über sudo apt-get install trace-cmd kernelshark. Diese Tools könnten eine Alternative zu dem oben beschriebenen Verfahren sein.

Abschließend möchte ich nur darauf hinweisen, dass das obige testmodBeispiel zwar nicht wirklich die Verwendung im Zusammenhang mit mehreren Ansprüchen zeigt, ich jedoch das gleiche Ablaufverfolgungsverfahren verwendet habe, um festzustellen, dass ein von mir codiertes USB-Modul wiederholt von beansprucht wurde pulseaudio, sobald Das USB-Gerät wurde angeschlossen - daher scheint das Verfahren für solche Anwendungsfälle zu funktionieren.

sdaau
quelle
5
Vielen Dank für den Kommentar, @RichardHansen - die Frage ist "Gibt es eine Möglichkeit herauszufinden, was ein Modul verwendet"; und Sie können in der Modulablaufverfolgung sehen, dass zum Beispiel rmmod-21354oder tr-6232(Prozessname - Prozess-ID) diejenigen module_putsind, die die Refcount des Moduls ändern, dh diese Prozesse "verwenden" das Modul; Ich würde also argumentieren, dass es genau das beantwortet, wonach das OP gefragt hat ... Prost!
Sdaau
Der Link "Ich bin wirklich sehr, sehr müde" ist defekt
Hi-Angel
4

Sie erhalten Used bylediglich eine Liste der Module, die von welchen anderen Modulen abhängen (die Spalte in lsmod). Sie können kein Programm schreiben, um festzustellen, warum das Modul geladen wurde, ob es noch für irgendetwas benötigt wird oder was möglicherweise kaputt geht, wenn Sie es entladen und alles, was davon abhängt.

Norman Ramsey
quelle
3

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
$
JonahB 9L
quelle
5
Nun, das ist im Allgemeinen nicht richtig: Ich habe auf meiner Maschine : $ lsmod | grep snd snd_seq 47263 1 snd_timer 19130 1 snd_seq snd_seq_device 5100 1 snd_seq ... ; so snd_seqwird von etwas behauptet (refcount ist 1), aber man kann nicht sagen warum, da die Spalte danach leer ist und daher kein anderes Modul dies spezifisch behauptet (aber vielleicht, wenn man ftraceden Kernel bereits vom Beginn des Startvorgangs an hat, eine könnte herausfinden, denke ich).
Sdaau
5
Dies funktioniert nur in der Situation, in der lsmod auch etwas in der Spalte "verwendet von" anzeigt. rmmod hat in Bezug auf die Anzeige von Abhängigkeiten nicht mehr Logik als lsmod.
Dannysauer
2

Sie könnten versuchen lsofoder fuser.

jedihawk
quelle
Wie genau kann ich lsof dafür verwenden?
Vadim Kotov
1
Es ist keine universelle Lösung, aber falls der Treiber ein Gerät /devdarin erstellt, kann von aufgelistet werden lsof.
Sergey Manucharian
0

Versuchen Sie es mit kgdb und setzen Sie den Haltepunkt auf Ihr Modul

Fluss
quelle
-3

Für alle, die unbedingt herausfinden möchten, warum sie keine Module neu laden können, konnte ich dieses Problem umgehen, indem ich sie umging

  • Abrufen des Pfads des aktuell verwendeten Moduls mit "modinfo"
  • rm-rfing es
  • Kopieren des neuen Moduls, das ich laden wollte, in den Pfad, in dem es sich befand
  • Geben Sie "modprobe DRIVER_NAME.ko" ein.
Locane
quelle
Diese Antwort beantwortet die gestellte Frage nicht wirklich.
Kelnos