2009-01-15 14 views
61

Se carico un modulo del kernel ed elenco i moduli caricati con lsmod, posso ottenere il "conteggio degli usi" del modulo (numero di altri moduli con un riferimento al modulo). C'è un modo per capire cosa sta utilizzando un modulo, però?C'è un modo per capire cosa sta usando un modulo del kernel di Linux?

Il problema è che un modulo che sto sviluppando insiste che il suo conteggio di utilizzo è 1 e quindi non posso usare rmmod per scaricarlo, ma la sua colonna "per" è vuota. Ciò significa che ogni volta che voglio ricompilare e ricaricare il modulo, devo riavviare la macchina (o, almeno, non riesco a capire qualsiasi altro modo per scaricarlo).

+0

"che cosa" in quali termini? quale codice? quale modulo? quale utente? quale programma? pero 'ho la sensazione che questo non sia un problema di programmazione :) interessante in ogni caso –

+1

Beh, questo è relativo alla programmazione, dal momento che sto chiedendo perché sto scrivendo un modulo del kernel. – mipadi

+0

chiarire la domanda per mostrare il problema di programmazione che si sta tentando di risolvere. –

risposta

1

Si potrebbe provare lsof o fuser.

+2

Hai provato davvero? –

+0

Inizialmente ci ho pensato, ma non funziona. – mipadi

+0

'lsof' mi ha aiutato a capirlo. +1 –

4

Tutto ciò che si ottiene è un elenco di quali moduli dipendono da quali altri moduli (la colonna Used by in lsmod). Non è possibile scrivere un programma per raccontare il motivo per cui il modulo è stato caricato, se è ancora necessaria per qualsiasi cosa, o che cosa potrebbe rompersi se si scarica e tutto ciò che dipende da esso.

6

Si dice sul Linux Kernel Module Programming Guide che il conteggio di utilizzo di un modulo è controllato dalle funzioni try_module_get e try_module_put. Forse puoi trovare dove sono chiamate queste funzioni per il tuo modulo.

40

In realtà, sembra esserci un modo per elencare i processi che richiedono un modulo/driver - tuttavia, non l'ho visto pubblicizzato (al di fuori della documentazione del kernel di Linux), quindi annoterò qui i miei appunti:

Prima di tutto, molte grazie per @haggai_e 's risposta; il puntatore alle funzioni try_module_get e try_module_put in quanto i responsabili della gestione del conteggio degli usi (refcount) è stata la chiave che mi ha permesso di rintracciare la procedura.

Guardando oltre per questo online, in qualche modo sono incappato nel post Linux-Kernel Archive: [PATCH 1/2] tracing: Reduce overhead of module tracepoints; che alla fine indicava una funzione presente nel kernel, conosciuta come (credo) "tracciamento"; la documentazione per questo è nella directory Documentation/trace - Linux kernel source tree. In particolare, due file spiegano il sistema di tracciamento, events.txt e ftrace.txt.

Tuttavia, è disponibile anche un breve "tracing mini-HOWTO" su un sistema Linux in esecuzione in /sys/kernel/debug/tracing/README (vedere anche I'm really really tired of people saying that there's no documentation…); si noti che nell'albero dei sorgenti del kernel, questo file è effettivamente generato dal file kernel/trace/trace.c. Ho provato questo su Ubuntu natty, e notare che dal /sys è di proprietà di root, è necessario utilizzare sudo a leggere questo file, come in sudo cat o

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

... e questo vale anche per quasi tutti altre operazioni sotto /sys che verranno descritte qui.


Prima di tutto, ecco un semplice codice minimo modulo/driver (che ho messo insieme le risorse di cui), che crea semplicemente un nodo /proc/testmod-sample file, che restituisce la stringa "Questo è testmod."Quando è in corso la lettura, questo è 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"); 

Questo modulo può essere costruito con il seguente Makefile (appena lo hanno collocato nella stessa directory di testmod.c, e quindi eseguire make in quello stesso directory):.

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 

Quando questo modulo/driver è costruito, l'output è un file oggetto del kernel, testmod.ko


A questo punto, è possibile preparare la traccia dell'evento relativa a try_module_get e try_module_put; quelli sono 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 

noti che sul mio sistema, l'analisi è abilitata di default:

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

... tuttavia, il modulo di tracciamento (in particolare) non è:

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

Ora, dovremmo prima creare un filtro, che reagirà sugli eventi module_get, module_put, ma solo per il modulo testmod. Per fare questo, dobbiamo prima verificare il formato della manifestazione:

$ 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 

Qui possiamo vedere che c'è un campo chiamato name, che contiene il nome del driver, che siamo in grado di filtrare contro. Per creare un filtro, abbiamo semplicemente echo la stringa di filtro nel file corrispondente:

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

Qui, prima nota che, dal momento che dobbiamo chiamare sudo, dobbiamo avvolgere l'intero echo reindirizzamento come un comando argomento di una sudo -ed bash. In secondo luogo, si noti che poiché abbiamo scritto al "genitore" module/filter, non gli eventi specifici (che sarebbero module/module_put/filter ecc.), Questo filtro verrà applicato a tutti gli eventi elencati come "figli" della directory module.

Infine, attivare l'analisi per il modulo:

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

Da questo punto in poi, siamo in grado di leggere il file di registro di traccia; per me, la lettura del blocco, "convogliato" versione del file di traccia ha funzionato - in questo modo:

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

A questo punto, non vedremo nulla nel registro - quindi è il momento di caricare (e utilizzare , e rimuovere) il driver (in un terminale diverso da dove trace_pipe si sta leggendo):

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

Se torniamo al terminale dove trace_pipe viene letto, dovremmo vedere qualcosa di simile:

# 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 

Questo è praticamente tutto quello che otterremo per il nostro driver testmod - il refcount cambia solo quando il driver è stato caricato(), o non scaricato (rmmod), non quando si effettua una lettura tramite cat. Quindi possiamo semplicemente interrompere la lettura da trace_pipe con CTRL + C in quel terminale; e per fermare il tracciamento del tutto:

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

Qui, si noti che la maggior parte degli esempi si riferiscono a leggere il file /sys/kernel/debug/tracing/trace anziché trace_pipe come qui. Tuttavia, un problema è che questo file non deve essere "collegato" (quindi non si dovrebbe eseguire un tail -f su questo file trace); ma invece dovresti rileggere lo trace dopo ogni operazione. Dopo il primo insmod, otterremmo lo stesso output da cat -in entrambi trace e trace_pipe; Tuttavia, dopo la rmmod, la lettura del file trace darebbe:

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

... cioè: a questo punto, il insmod erano già stati usciti per lungo tempo, e quindi non esiste più nel processo lista - e quindi non può essere trovato tramite l'ID di processo registrato (PID) al momento - così otteniamo un vuoto <...> come nome del processo. Pertanto, è meglio registrare (tramite tee) un output in esecuzione da trace_pipe in questo caso. Si noti inoltre che, al fine di cancellare/reset/cancellare il file trace, si scrive semplicemente un 0 ad esso:

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

Se questo sembra controintuitivo, notare che trace è un file speciale, e sarà sempre segnalare un file dimensione di zero comunque:

$ 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 

... anche se è "pieno".

Infine, ricordiamo che se non avessimo implementato con un filtro, avremmo ottenuto un registro di tutti i moduli invita il sistema in esecuzione - che sarebbe il login qualsiasi chiamata (anche di fondo) per grep e simili, come quelli utilizzare il modulo 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 

...che aggiunge un bel po 'di overhead (in entrambi i dati di registro e tempo di elaborazione necessario per generarlo).


Mentre cerca questo in su, sono incappato in Debugging Linux Kernel by Ftrace PDF, che si riferisce ad uno strumento trace-cmd, che praticamente fa il simile come sopra - ma attraverso un'interfaccia a riga di comando più facile. C'è anche una GUI "front-end reader" per trace-cmd chiamata KernelShark; entrambi questi sono anche nei repository Debian/Ubuntu via sudo apt-get install trace-cmd kernelshark. Questi strumenti potrebbero essere un'alternativa alla procedura descritta sopra.

Infine, vorrei solo notare che, mentre l'esempio precedente testmod non mostra realmente l'uso nel contesto di più attestazioni, ho usato la stessa procedura di tracciamento per scoprire che un modulo USB che sto codificando, è stato ripetutamente richiesto da pulseaudio non appena il dispositivo USB è stato collegato, quindi la procedura sembra funzionare per tali casi d'uso.

+5

Grazie per il commento, @RichardHansen - la domanda è "C'è un modo per capire _what_ sta usando un modulo"; e puoi vedere nella traccia del modulo che per esempio 'rmmod-21354', o' tr-6232' (nome processo - ID processo) sono quelli che eseguono 'module_put', cioè, cambiano il conto del modulo - che è, quei processi stanno "usando" il modulo; quindi direi che risponde esattamente a ciò che l'OP chiedeva ... Saluti! – sdaau

2

Se si utilizza rmmod SENZA l'opzione --force, verrà indicato cosa sta utilizzando un modulo. Esempio:

$ 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

Beh, non è corretto in generale: ho sulla mia macchina: '$ lsmod | grep snd snd_seq 47263 1 snd_timer 19130 1 snd_seq snd_seq_device 5100 1 snd_seq ... ' ; quindi 'snd_seq' è richiesto da qualcosa (refcount è 1), ma non si può dire perché, dato che la colonna dopo è vuota e quindi nessun altro modulo lo rivendica in modo specifico (ma forse se uno' ftrace's il kernel già dall'inizio del processo di avvio, si potrebbe scoprire, immagino). – sdaau

+0

Questo non sembra funzionare in tutti i casi. – Martin

+3

Questo funziona solo nella situazione in cui lsmod mostra anche qualcosa nella colonna "used by"; rmmod non ha più logica di lsmod in termini di visualizzazione delle dipendenze. – dannysauer

-2

Per chiunque disperato di capire perché non possono ricaricare i moduli, sono stato in grado di risolvere questo problema

  • Ottenere il percorso del modulo attualmente utilizzato utilizzando "modinfo"
  • rm -rfing si
  • Copiare il nuovo modulo ho voluto caricare al percorso era in
  • Digitando "modprobe DRIVER_NAME.ko".
+0

Questa risposta in realtà non risponde alla domanda posta. – kelnos

0

prova kgdb e impostare breakpoint al modulo

Problemi correlati