lsmod - ¿Hay alguna manera de descubrir qué está usando un módulo de kernel de Linux?
insmod linux (7)
Si cargo un módulo kernel y lsmod
módulos cargados con lsmod
, puedo obtener el "conteo de uso" del módulo (número de otros módulos con una referencia al módulo). Sin embargo, ¿hay alguna manera de averiguar qué está usando un módulo?
El problema es que un módulo que estoy desarrollando insiste en que su recuento de uso es 1 y, por lo tanto, no puedo usar rmmod
para descargarlo, pero su columna "por" está vacía. Esto significa que cada vez que quiero volver a compilar y volver a cargar el módulo, tengo que reiniciar la máquina (o, al menos, no puedo encontrar ninguna otra forma de descargarlo).
prueba kgdb y establece un punto de interrupción para tu módulo
En la Guía de programación del módulo Linux Kernel, dice que el conteo de uso de un módulo está controlado por las funciones try_module_get
y try_module_put
. Quizás pueda encontrar dónde se llaman estas funciones para su módulo.
Puede probar lsof
o fuser
.
Todo lo que obtiene es una lista de qué módulos dependen de qué otros módulos (la columna Used by
en lsmod). No puede escribir un programa para decir por qué se cargó el módulo, si todavía se necesita para algo, o qué podría romperse si lo descarga y todo lo que depende de él.
Si usa rmmod SIN la opción --force, le dirá qué está usando un módulo. Ejemplo:
$ 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
$
Para cualquiera que esté desesperado por descubrir por qué no pueden volver a cargar los módulos, pude solucionar este problema
- Obteniendo la ruta del módulo actualmente utilizado usando "modinfo"
- rm -rfing it
- Copiando el nuevo módulo que quería cargar en la ruta en la que estaba
- Escribiendo "modprobe DRIVER_NAME.ko".
En realidad, parece haber una manera de enumerar procesos que reclaman un módulo / controlador; sin embargo, no lo he visto anunciado (fuera de la documentación del kernel de Linux), así que anotaré mis notas aquí:
Antes que nada, muchas gracias por la respuesta de @haggai_e ; el puntero a las funciones try_module_get
y try_module_put
ya que los responsables de administrar el conteo de uso (refcount) fue la clave que me permitió rastrear el procedimiento.
Buscando más en línea en esto, de alguna manera me topé con el seguimiento de Linux-Kernel Archive: [PATCH 1/2]: reducir la sobrecarga de los puntos de seguimiento del módulo ; que finalmente apuntó a una instalación presente en el kernel, conocida como (supongo) "rastreo"; la documentación para esto está en el directorio Documentation / trace - árbol de código fuente del kernel de Linux . En particular, dos archivos explican la función de rastreo, events.txt y ftrace.txt .
Pero también hay un breve "mini-HOWTO de rastreo" en un sistema Linux en ejecución en /sys/kernel/debug/tracing/README
(ver también , estoy realmente cansado de que la gente diga que no hay documentación ... ); tenga en cuenta que en el árbol fuente del kernel, este archivo es realmente generado por el archivo kernel / trace / trace.c . He probado esto en Ubuntu natty
, y tenga en cuenta que dado que /sys
es propiedad de root, debe usar sudo
para leer este archivo, como en sudo cat
o
sudo less /sys/kernel/debug/tracing/README
... y eso vale para casi todas las demás operaciones en /sys
que se describirán aquí.
En primer lugar, aquí hay un código mínimo simple de módulo / controlador (que /proc/testmod-sample
de los recursos referidos), que simplemente crea un nodo de archivo /proc/testmod-sample
, que devuelve la cadena "This is testmod". cuando está siendo leído; esto es 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");
Este módulo se puede construir con el siguiente Makefile
(solo tiene que colocarlo en el mismo directorio que testmod.c
, y luego ejecutar make
en ese mismo directorio):
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
Cuando se testmod.ko
este módulo / controlador, el resultado es un archivo de objeto kernel, testmod.ko
.
En este punto, podemos preparar el seguimiento de eventos relacionado con try_module_get
y try_module_put
; esos están en /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
Tenga en cuenta que en mi sistema, el rastreo está habilitado por defecto:
$ sudo cat /sys/kernel/debug/tracing/tracing_enabled
1
... sin embargo, el seguimiento de módulos (específicamente) no es:
$ sudo cat /sys/kernel/debug/tracing/events/module/enable
0
Ahora, primero debemos hacer un filtro, que reaccionará en los module_get
, module_put
, etc., pero solo para el módulo testmod
. Para hacer eso, primero debemos verificar el formato del evento:
$ 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
Aquí podemos ver que hay un campo llamado name
, que contiene el nombre del controlador, con el que podemos filtrar. Para crear un filtro, simplemente hacemos echo
la cadena de filtro en el archivo correspondiente:
sudo bash -c "echo name == testmod > /sys/kernel/debug/tracing/events/module/filter"
Aquí, primero note que dado que tenemos que llamar a sudo
, tenemos que envolver toda echo
redirección de echo
como un comando de argumento de un bash
sudo
-ed. En segundo lugar, tenga en cuenta que, dado que escribimos en el module/filter
"principal", no en los eventos específicos (que serían module/module_put/filter
etc.), este filtro se aplicará a todos los eventos enumerados como "hijos" del directorio del module
.
Finalmente, habilitamos el rastreo para el módulo:
sudo bash -c "echo 1 > /sys/kernel/debug/tracing/events/module/enable"
A partir de este momento, podemos leer el archivo de registro de seguimiento; para mí, leer la versión de bloqueo, "entubada" del archivo de rastreo funcionó - de esta manera:
sudo cat /sys/kernel/debug/tracing/trace_pipe | tee tracelog.txt
En este punto, no veremos nada en el registro, por lo que es hora de cargar (y utilizar, y eliminar) el controlador (en un terminal diferente desde donde se lee la trace_pipe
):
$ sudo insmod ./testmod.ko
$ cat /proc/testmod-sample
This is testmod.
$ sudo rmmod testmod
Si volvemos a la terminal donde se lee trace_pipe
, deberíamos ver algo como:
# 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
Eso es casi todo lo que obtendremos para nuestro controlador testmod
: el recuento cambia solo cuando el controlador está cargado ( insmod
) o descargado ( rmmod
), no cuando hacemos un cat
lectura. Entonces podemos simplemente interrumpir la lectura de trace_pipe
con CTRL + C en esa terminal; y para detener el rastreo por completo:
sudo bash -c "echo 0 > /sys/kernel/debug/tracing/tracing_enabled"
Aquí, tenga en cuenta que la mayoría de los ejemplos se refieren a leer el archivo /sys/kernel/debug/tracing/trace
lugar de trace_pipe
como aquí. Sin embargo, un problema es que este archivo no está destinado a ser "canalizado" (por lo que no debe ejecutar un tail -f
en este archivo de trace
); pero en su lugar, debe volver a leer la trace
después de cada operación. Después de la primera insmod
, obtendríamos el mismo resultado de cat
-ing trace
y trace_pipe
; Sin embargo, después del rmmod
, leer el archivo de trace
daría:
<...>-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
... es decir: en este punto, la insmod
ya había sido abandonada por mucho tiempo, por lo que ya no existe en la lista de procesos y, por lo tanto, no se puede encontrar a través del ID del proceso registrado (PID) en ese momento, por lo tanto obtenemos un <...>
como nombre del proceso en blanco. Por lo tanto, es mejor registrar (a través de tee
) un resultado en ejecución desde trace_pipe
en este caso. Además, tenga en cuenta que para borrar / restablecer / borrar el archivo de trace
, uno simplemente le escribe un 0:
sudo bash -c "echo 0 > /sys/kernel/debug/tracing/trace"
Si esto parece contrario a la intuición, tenga en cuenta que el trace
es un archivo especial, y siempre informará un tamaño de archivo de cero de todos modos:
$ 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
... incluso si está "lleno".
Finalmente, tenga en cuenta que si no implementamos un filtro, obtendríamos un registro de todas las llamadas de módulo en el sistema en ejecución, lo que registraría cualquier llamada (también de fondo) a grep
y tal, como aquellos que usan el módulo 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
... lo cual agrega bastante sobrecarga (tanto en la cantidad de datos de registro como en el tiempo de procesamiento requerido para generarla).
Mientras buscaba esto, me encontré con Debugging Linux Kernel por Ftrace PDF , que hace referencia a una herramienta trace-cmd , que hace más o menos lo mismo que antes, pero a través de una interfaz de línea de comandos más sencilla. También hay una GUI de "lector front-end" para trace-cmd
llamada KernelShark ; ambos están también en repositorios Debian / Ubuntu a través de sudo apt-get install trace-cmd kernelshark
. Estas herramientas podrían ser una alternativa al procedimiento descrito anteriormente.
Finalmente, me gustaría señalar que, si bien el ejemplo anterior de testmod
no muestra realmente el uso en contexto de múltiples reclamos, he usado el mismo procedimiento de rastreo para descubrir que un módulo USB que estoy codificando, fue reclamado repetidamente por pulseaudio
como tan pronto como el dispositivo USB se conectó, por lo que el procedimiento parece funcionar para tales casos de uso.