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.
"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 –
Beh, questo è relativo alla programmazione, dal momento che sto chiedendo perché sto scrivendo un modulo del kernel. – mipadi
chiarire la domanda per mostrare il problema di programmazione che si sta tentando di risolvere. –