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.
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.
15 votes
La question est assez claire pour moi, Norman : comment peut-il découvrir ce qui empêche rmmod de supprimer son module expérimental ? comment peut-il éviter de devoir redémarrer à chaque fois qu'il compile une nouvelle version ?