81 votes

Y a-t-il un moyen de savoir ce qui utilise un module du noyau Linux ?

Si je charge un module du noyau et que je liste les modules chargés avec lsmod Si le module est utilisé, je peux obtenir le "nombre d'utilisations" du module (nombre d'autres modules faisant référence au module). Existe-t-il un moyen d'obtenir ce que utilise-t-il un module ?

Le problème est qu'un module que je suis en train de développer insiste sur le fait que son nombre d'utilisations est de 1 et que je ne peux donc pas utiliser rmmod pour le décharger, mais sa colonne "by" est vide. Cela signifie que chaque fois que je veux recompiler et recharger le module, je dois redémarrer la machine (ou, du moins, je ne trouve pas d'autre moyen de le décharger).

0 votes

"quoi" en quels termes ? quel code ? quel module ? quel utilisateur ? quel programme ? j'ai légèrement l'impression que ce n'est pas lié à la programmation :) intéressant néanmoins

2 votes

Eh bien, il es lié à la programmation, puisque je pose la question parce que j'écris un module de noyau.

0 votes

Veuillez clarifier la question pour montrer le problème de programmation que vous essayez de résoudre.

53voto

sdaau Points 6262

En fait, il semble y avoir un moyen de lister les processus qui réclament un module/pilote - cependant, je ne l'ai pas vu annoncé (en dehors de la documentation du noyau Linux), donc je vais noter mes notes ici :

Tout d'abord, merci beaucoup pour @haggai_e la réponse de l'entreprise ; le pointeur vers les fonctions try_module_get y try_module_put comme responsables de la gestion du compte d'utilisation (refcount) a été la clé qui m'a permis de retrouver la procédure.

En cherchant plus loin sur Internet, je suis tombé par hasard sur ce post Archives Linux-Kernel : [PATCH 1/2] tracing : Réduction de la surcharge des tracepoints des modules ; qui pointait finalement vers une fonctionnalité présente dans le noyau, connue sous le nom de (je suppose) "traçage" ; la documentation à ce sujet se trouve dans le répertoire Documentation/trace - Arbre des sources du noyau Linux . En particulier, deux fichiers expliquent la fonction de traçage, événements.txt y ftrace.txt .

Mais il y a aussi un court "mini-HOWTO de traçage" sur un système Linux en cours d'exécution dans /sys/kernel/debug/tracing/README (voir aussi Je suis vraiment fatigué des gens qui disent qu'il n'y a pas de documentation ) ; notez que dans l'arbre des sources du noyau, ce fichier est en fait généré par le fichier kernel/trace/trace.c . Je l'ai testé sur Ubuntu natty et notez que puisque /sys est détenu par Root, vous devez utiliser sudo pour lire ce fichier, comme dans sudo cat o

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

... et c'est valable pour presque toutes les autres opérations dans le cadre de l'UE. /sys qui sera décrit ici.


Tout d'abord, voici un simple code minimal de module/pilote (que j'ai assemblé à partir des ressources mentionnées), qui crée simplement un fichier /proc/testmod-sample qui renvoie la chaîne de caractères "This is testmod." lorsqu'il est lu ; c'est 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");

Ce module peut être construit avec les éléments suivants Makefile (il suffit de le placer dans le même répertoire que testmod.c puis exécutez make dans ce même répertoire) :

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

Lorsque ce module/pilote est construit, la sortie est un fichier objet du noyau, testmod.ko .


A ce stade, nous pouvons préparer le suivi des événements liés à try_module_get y try_module_put ; ceux-là sont dans /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

Notez que sur mon système, le traçage est activé par défaut :

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

... cependant, le traçage des modules (spécifiquement) ne l'est pas :

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

Maintenant, nous devrions d'abord faire un filtre, qui réagira sur le module_get , module_put etc., mais uniquement pour les événements testmod module. Pour ce faire, nous devons d'abord vérifier le format de l'événement :

$ 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

Ici, nous pouvons voir qu'il y a un champ appelé name qui contient le nom du conducteur, sur lequel nous pouvons filtrer. Pour créer un filtre, il suffit de echo la chaîne de filtre dans le fichier correspondant :

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

Ici, notez d'abord que puisque nous devons appeler sudo nous devons envelopper l'ensemble echo en tant qu'argument de commande d'une sudo -ed bash . Deuxièmement, notez que puisque nous avons écrit à la "mère" module/filter et non les événements spécifiques (qui seraient module/module_put/filter etc), ce filtre sera appliqué à tous les événements répertoriés comme "enfants" de module répertoire.

Enfin, nous activons le traçage pour le module :

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

A partir de ce point, nous pouvons lire le fichier de trace ; pour moi, la lecture de la version bloquée, "piped" version du fichier de trace a fonctionné - comme ceci :

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

À ce stade, nous ne verrons rien dans le journal - il est donc temps de charger (et d'utiliser, et de supprimer) le pilote (dans un terminal différent de celui où se trouve le pilote trace_pipe est en cours de lecture) :

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

Si nous retournons au terminal où trace_pipe est en cours de lecture, nous devrions voir quelque chose comme :

# 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

C'est à peu près tout ce que nous obtiendrons pour notre testmod driver - le refcount ne change que lorsque le driver est chargé ( insmod ) ou non chargé ( rmmod ), mais pas lorsque nous faisons une lecture à travers cat . Nous pouvons donc simplement interrompre la lecture de trace_pipe avec CTRL + C dans ce terminal ; et d'arrêter complètement le traçage :

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

Ici, notez que la plupart des exemples font référence à la lecture du fichier /sys/kernel/debug/tracing/trace au lieu de trace_pipe comme ici. Cependant, un problème est que ce fichier n'est pas destiné à être "pipé" (vous ne devez donc pas exécuter un fichier tail -f sur ce trace ) ; mais vous devriez plutôt relire le fichier trace après chaque opération. Après la première insmod nous obtiendrions le même résultat avec cat -les deux trace y trace_pipe Cependant, après le rmmod en lisant le trace donnerait :

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

... c'est-à-dire qu'à ce stade, les insmod avait déjà été quitté depuis longtemps, et il n'existe donc plus dans la liste des processus - et ne peut donc pas être trouvé via l'ID de processus (PID) enregistré à ce moment-là - nous obtenons donc un message vide. <...> comme nom de processus. Par conséquent, il est préférable d'enregistrer (via tee ) une sortie courante de trace_pipe dans ce cas. Notez également que pour effacer/remettre à zéro/effacer la mémoire de la trace on écrit simplement un 0 dans le fichier :

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

Si cela semble contre-intuitif, notez que trace est un fichier spécial, et rapportera toujours une taille de fichier de zéro de toute façon :

$ 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

... même si elle est "pleine".

Enfin, notons que si nous n'avions pas mis en place un filtre, nous aurions obtenu un journal de tous sur le système en cours d'exécution - ce qui permettrait d'enregistrer tout appel (même en arrière-plan) au module grep et autres, comme ceux qui utilisent le binfmt_misc module :

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

... ce qui ajoute un certain nombre de frais généraux (à la fois en termes de quantité de données de journal et de temps de traitement nécessaire pour les générer).


En cherchant, je suis tombé sur Débogage du noyau Linux par Ftrace PDF qui fait référence à un outil trace-cmd qui fait à peu près la même chose que ci-dessus, mais à travers une interface de ligne de commande plus facile. Il y a aussi une interface graphique "lecteur frontal" pour trace-cmd appelé KernelShark ; les deux sont aussi dans les dépôts Debian/Ubuntu via sudo apt-get install trace-cmd kernelshark . Ces outils pourraient constituer une alternative à la procédure décrite ci-dessus.

Enfin, j'aimerais simplement noter que, bien que les éléments ci-dessus testmod L'exemple ne montre pas vraiment l'utilisation dans le contexte de revendications multiples, j'ai utilisé la même procédure de traçage pour découvrir qu'un module USB que je code, a été revendiqué à plusieurs reprises par pulseaudio dès que le périphérique USB a été branché - la procédure semble donc fonctionner pour de tels cas d'utilisation.

5 votes

Merci pour le commentaire, @RichardHansen - la question est "Y a-t-il un moyen de déterminer ce que utilise un module" ; et vous pouvez voir dans la trace du module que par exemple rmmod-21354 ou tr-6232 (nom du processus - ID du processus) sont ceux qui font la module_put c'est-à-dire en changeant le refcount du module - c'est-à-dire que ces processus "utilisent" le module ; je dirais donc que cela répond exactement à la demande de l'OP... A la vôtre !

1 votes

Le lien "Je suis vraiment très fatigué" est cassé.

12voto

Johannes Schaub - litb Points 256113

Rmmod a un --force paramètre. Si vous connaissez les fonctions de votre module, et que vous avez un noyau configuré pour supporter le forçage du déchargement, cela peut fonctionner. C'est pour vous éviter d'avoir à redémarrer jusqu'à ce que vous ayez résolu le problème avec le ref-counting), mais cela ne vous montrera pas la cause du chargement de votre module. Je pense qu'il n'est pas possible de savoir pourquoi le module a été chargé en premier lieu (c'est-à-dire quel chemin de code exact). Peut-être dmesg a enregistré quelque chose d'utile ou vous pouvez ajouter un enregistrement utile dans le code de votre module.

8voto

haggai_e Points 2143

Il est dit sur le Guide de programmation du module du noyau Linux que le nombre d'utilisation d'un module est contrôlé par les fonctions try_module_get y module_put . Peut-être pouvez-vous trouver où ces fonctions sont appelées pour votre module.

Plus d'informations : https://www.kernel.org/doc/htmldocs/kernel-hacking/routines-module-use-counters.html

4voto

Norman Ramsey Points 115730

Tout ce que vous obtenez est une liste de quels modules dépendent de quels autres modules (les Used by dans lsmod). Vous ne pouvez pas écrire un programme pour savoir pourquoi le module a été chargé, s'il est encore nécessaire pour quoi que ce soit, ou ce qui pourrait se casser si vous le déchargez et tout ce qui en dépend.

2voto

JonahB 9L Points 1

Si vous utilisez rmmod SANS l'option --force, il vous dira ce qui utilise un module. Exemple :

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

5 votes

Eh bien, ce n'est pas correct en général : J'ai sur ma machine : $ lsmod | grep snd snd_seq 47263 1 snd_timer 19130 1 snd_seq snd_seq_device 5100 1 snd_seq ... donc snd_seq est revendiqué par quelque chose (refcount est 1), mais on ne peut pas dire pourquoi, car la colonne qui le suit est vide et donc aucun autre module ne le revendique spécifiquement (mais peut-être que si un module ftrace s le noyau déjà depuis le début du processus de démarrage, on pourrait le découvrir, je suppose).

6 votes

Cela ne fonctionne que dans le cas où lsmod affiche également quelque chose dans la colonne "utilisé par" ; rmmod n'a pas plus de logique que lsmod en termes d'affichage des dépendances.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X