2009-01-15 16 views
61

Si cargo un módulo kernel y listamos los 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). ¿Hay alguna manera de averiguar que está usando un módulo?¿Hay alguna manera de averiguar qué está usando un módulo kernel de Linux?

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

+0

"qué" en la que los términos? ¿qué código? ¿qué módulo? que usuario? ¿que programa? Aunque tengo la sensación de que esto no está relacionado con la programación :) interesante, sin embargo, –

+1

Bueno, está relacionado con la programación, ya que estoy preguntando porque estoy escribiendo un módulo kernel. – mipadi

+0

aclare la pregunta para mostrar el problema de programación que está tratando de resolver. –

Respuesta

1

Puede probar lsof o fuser.

+2

¿De hecho has intentado esto? –

+0

Pensé en eso inicialmente, pero no funciona. – mipadi

+0

'lsof' me ayudó a resolverlo. +1 –

4

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.

6

Dice en el Linux Kernel Module Programming Guide 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.

40

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 como los responsables de gestionar el conteo de uso (refcount) fue la clave que me permitió rastrear el procedimiento.

Buscando más a fondo en línea, de alguna manera encontré la publicación Linux-Kernel Archive: [PATCH 1/2] tracing: Reduce overhead of module tracepoints; 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 - Linux kernel source tree. En particular, dos archivos explican la función de rastreo, events.txt y ftrace.txt.

Pero también hay un breve "mini-HOW de rastreo" en un sistema Linux en ejecución en /sys/kernel/debug/tracing/README (vea también I'm really really tired of people saying that there's no documentation…); 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, usted tiene que utilizar sudo para leer este archivo, como en sudo cat o

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

... y eso va para casi todo otras operaciones bajo /sys que se describirán aquí.


En primer lugar, aquí es un simple código mínimo módulo/controlador (que he creado a partir de los recursos mencionados), que simplemente crea un nodo /proc/testmod-sample archivo, que devuelve la cadena "Esto es testmod."Cuando se está leyendo, lo que 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 puede ser construido con el siguiente Makefile (solo tiene que colocar en el mismo directorio que testmod.c y ejecute 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 este módulo/controlador está integrado, la salida es un archivo de objeto de núcleo, testmod.ko


En este punto, podemos preparar el seguimiento de eventos relacionado con try_module_get y try_module_put; ellos 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 seguimiento está activado por defecto:

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

... Sin embargo, el módulo de rastreo (en concreto) no es:

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

Ahora, primero debemos hacer un filtro, que reaccionará en los eventos module_get, module_put, etc., pero solo para el módulo testmod. Para ello, lo primero que debe comprobar el formato de la prueba:

$ 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 tiene el nombre del controlador, que podemos filtrar en contra. Para crear un filtro, simplemente echo la cadena de filtro en el archivo correspondiente:

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

Aquí, primera nota que ya que tenemos que llamar sudo, tenemos que envolver todo el echo redirección como un comando de argumentos de una sudo -ed bash. En segundo lugar, tenga en cuenta que, dado que escribimos al "padre" module/filter, no a 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 module.

Por último, habilitar el seguimiento para el módulo:

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

partir de este punto, podemos leer el archivo de registro de seguimiento; para mí, la lectura del bloqueo, "hilo" versión del archivo de rastreo trabajó - como esto:

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

En este punto, no vamos a ver nada en el registro - por lo que es tiempo para cargar (y utilizar y eliminar) el conductor (en un terminal diferente de donde se está leyendo trace_pipe):

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

Si volvemos a la terminal donde se está leyendo 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(), no cuando leemos cat. Entonces podemos simplemente interrumpir la lectura de trace_pipe con CTRL + C en ese terminal; y para detener el rastreo 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 la lectura del archivo en lugar de /sys/kernel/debug/tracing/tracetrace_pipe como en este caso. 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 trace); pero en su lugar, debe volver a leer el trace después de cada operación. Después de la primera insmod, obtendríamos la misma salida de cat -tanto trace como trace_pipe; Sin embargo, después de la rmmod, leyendo el archivo 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 se había salido por mucho tiempo, por lo que ya no existe en el proceso lista - y, por lo tanto, no se puede encontrar a través del ID del proceso registrado (PID) en ese momento - así obtenemos un <...> en blanco como nombre del proceso. Por lo tanto, es mejor registrar (a través de tee) una salida en ejecución de trace_pipe en este caso. Además, tenga en cuenta que con el fin de borrar/reiniciar/borrar el archivo trace, uno simplemente escribe un 0 a él:

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

Si esto parece contrario a la intuición, tenga en cuenta que trace es un archivo especial, y siempre informar de un archivo tamaño 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".

Por último, tenga en cuenta que si no ha aplicado un filtro, habríamos obtenido un registro de todos los módulos de llama en el sistema actual - que ingrese cualquier llamada (también de fondo) a grep y tal, como las utilice 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 mira esto, yo nos topamos con Debugging Linux Kernel by Ftrace PDF, que se refiere a una herramienta trace-cmd, que prácticamente hace lo similar a la anterior - pero a través de una interfaz de línea de comandos fácil. También hay una GUI de "lector front-end" para trace-cmd llamada KernelShark; ambos están también en los repositorios de Debian/Ubuntu a través del sudo apt-get install trace-cmd kernelshark. Estas herramientas podrían ser una alternativa al procedimiento descrito anteriormente.

Por último, me gustaría señalar que, aunque el ejemplo anterior testmod no muestra realmente el uso en contexto de varias notificaciones, he utilizado el mismo procedimiento de localización para descubrir que un módulo USB que estoy codificando fue repetidamente reclamado por pulseaudio tan pronto como se enchufa el dispositivo USB, por lo que el procedimiento parece funcionar para tales casos de uso.

+5

Gracias por el comentario, @RichardHansen - la pregunta es "¿Hay alguna manera de averiguar _qué_ está usando un módulo"; y puede ver en el rastro del módulo que, por ejemplo, 'rmmod-21354', o' tr-6232' (nombre del proceso - ID del proceso) son los que hacen '' module_put', es decir, cambiar el refcount del módulo, eso es decir, esos procesos están "usando" el módulo; entonces yo diría que responde exactamente lo que pidió el OP ... ¡Saludos! – sdaau

2

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

Bueno, eso no es correcto en general: tengo en mi máquina: '$ lsmod | grep snd snd_seq 47263 1 snd_timer 19130 1 snd_seq snd_seq_device 5100 1 snd_seq ... ' ; así 'snd_seq' es reclamado por algo (refcount es 1), pero uno no puede decir por qué, ya que la columna está vacía y ningún otro módulo lo reclama específicamente (pero tal vez si' ftrace's the kernel ya desde el principio del proceso de arranque, uno podría descubrirlo, supongo). – sdaau

+0

Esto no parece funcionar en todos los casos. – Martin

+3

Esto solo funciona en la situación donde lsmod también muestra algo en la columna "usado por"; rmmod no tiene más lógica que lsmod en términos de visualización de dependencias. – dannysauer

-2

Para cualquier persona desesperada por averiguar por qué no pueden recargar módulos, yo era capaz de solucionar este problema mediante la

  • Conseguir el camino del módulo utilizado actualmente usando "modinfo"
  • rm -rfing que
  • Copiar el nuevo módulo quería cargar a la trayectoria fue en
  • Typing "modprobe DRIVER_NAME.ko".
+0

Esta respuesta en realidad no responde la pregunta. – kelnos

0

kgdb oportunidad y establecer punto de interrupción a su módulo

Cuestiones relacionadas