На самом деле, кажется, есть способ перечислить процессы, которые требуют модуль / драйвер - однако я не видел его рекламируемого (за пределами документации ядра Linux), поэтому я буду записывать свои заметки здесь:
Прежде всего, большое спасибо за ответ @ haggai_e ; указатель на функции try_module_get
и try_module_put
как ответственные за управление счетом использования (refcount) был ключом, который позволил мне отследить процедуру.
Заглядывая дальше в онлайн, я как-то наткнулся на сообщение Архив ядра Linux: трассировка [PATCH 1/2]: Уменьшить накладные расходы на точки трассировки модуля ; который в конце концов указал на объект, присутствующий в ядре, известный как (я думаю) «отслеживание»; документация для этого находится в каталоге Documentation / trace - дерево исходников ядра Linux . В частности, два файла объясняют средство трассировки: events.txt и ftrace.txt .
Но в работающей системе Linux в /sys/kernel/debug/tracing/README
также есть краткое "мини-руководство по трассировке" (см. Также Мне очень надоело, что люди говорят, что документации нет ... ); обратите внимание, что в исходном дереве ядра этот файл фактически создается файлом kernel / trace / trace.c . Я проверил это на Ubuntu natty
и обратите внимание, что поскольку /sys
принадлежит root, вы должны использовать sudo
для чтения этого файла, как в sudo cat
или
sudo less /sys/kernel/debug/tracing/README
... и это относится почти ко всем другим операциям в /sys
, которые будут описаны здесь.
Прежде всего, вот простой минимальный код модуля / драйвера (который я собрал из упомянутых ресурсов), который просто создает файловый узел /proc/testmod-sample
, который возвращает строку «Это testmod». когда это читается; это 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");
Этот модуль может быть собран со следующими Makefile
(просто поместите его в тот же каталог, что и testmod.c
, а затем запустите make
в этом же каталоге):
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
Когда этот модуль / драйвер собран, на выходе получается объектный файл ядра testmod.ko
.
На данный момент мы можем подготовить трассировку событий, связанных с try_module_get
и try_module_put
; те в /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
Обратите внимание, что в моей системе трассировка по умолчанию включена:
$ sudo cat /sys/kernel/debug/tracing/tracing_enabled
1
... однако, модуль трассировки (в частности) не является:
$ sudo cat /sys/kernel/debug/tracing/events/module/enable
0
Теперь мы должны сначала создать фильтр, который будет реагировать на события module_get
, module_put
и т. Д., Но только для модуля testmod
. Для этого сначала нужно проверить формат мероприятия:
$ 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
Здесь мы видим, что есть поле с именем name
, которое содержит имя драйвера, по которому мы можем фильтровать. Чтобы создать фильтр, мы просто echo
строка фильтра в соответствующий файл:
sudo bash -c "echo name == testmod > /sys/kernel/debug/tracing/events/module/filter"
Здесь, сначала обратите внимание, что, поскольку мы должны вызвать sudo
, мы должны обернуть все перенаправление echo
в качестве команды аргумента sudo
-ed bash
. Во-вторых, обратите внимание, что поскольку мы писали в «parent» module/filter
, а не в конкретные события (которые были бы module/module_put/filter
и т. Д.), Этот фильтр будет применяться ко всем событиям, перечисленным как «children» каталога module
.
Наконец, мы включаем трассировку для модуля:
sudo bash -c "echo 1 > /sys/kernel/debug/tracing/events/module/enable"
С этого момента мы можем прочитать файл журнала трассировки; для меня, читая блокировку,
работала "конвейерная" версия файла трассировки - вот так:
sudo cat /sys/kernel/debug/tracing/trace_pipe | tee tracelog.txt
На данный момент мы ничего не увидим в журнале - поэтому пришло время загрузить (и использовать, и удалить) драйвер (в другом терминале, из которого читается trace_pipe
):
$ sudo insmod ./testmod.ko
$ cat /proc/testmod-sample
This is testmod.
$ sudo rmmod testmod
Если мы вернемся к терминалу, где читается trace_pipe
, мы должны увидеть что-то вроде:
# 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
Это почти все, что мы получим для нашего testmod
драйвера - пересчет изменяется только тогда, когда драйвер загружен (insmod
) или выгружен (rmmod
), а не когда мы читаем cat
, Таким образом, мы можем просто прервать чтение из trace_pipe
с помощью CTRL + C в этом терминале; и вообще остановить трассировку:
sudo bash -c "echo 0 > /sys/kernel/debug/tracing/tracing_enabled"
Здесь обратите внимание, что большинство примеров относится к чтению файла /sys/kernel/debug/tracing/trace
вместо trace_pipe
, как здесь. Тем не менее, одна проблема заключается в том, что этот файл не предназначен для «передачи по конвейеру» (поэтому вы не должны запускать tail -f
для этого файла trace
); но вместо этого вы должны перечитывать trace
после каждой операции. После первого insmod
мы получим одинаковый результат от cat
-ing как trace
, так и trace_pipe
; однако после rmmod
чтение файла trace
даст:
<...>-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
... то есть: на данный момент, insmod
уже давно завершен, и поэтому он больше не существует в списке процессов - и, следовательно, не может быть найден через записанный идентификатор процесса (PID) в то время - таким образом, мы получаем пробел <...>
в качестве имени процесса. Следовательно, в этом случае лучше регистрировать (через tee
) текущий результат от trace_pipe
. Кроме того, обратите внимание, что для очистки / сброса / удаления файла trace
нужно просто записать 0:
sudo bash -c "echo 0 > /sys/kernel/debug/tracing/trace"
Если это кажется нелогичным, обратите внимание, что trace
- это специальный файл, и он всегда сообщит, что размер файла равен нулю в любом случае:
$ 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
... даже если он "полный".
Наконец, обратите внимание, что если бы мы не реализовали фильтр, мы получили бы журнал всех вызовов модулей в работающей системе - который бы регистрировал любой вызов (также фоновый) в grep
и такие, как те, которые используют модуль 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
... что добавляет немало накладных расходов (как для количества данных журнала, так и для времени обработки, необходимого для его генерации).
При поиске я наткнулся на Отладка ядра Linux с помощью Ftrace PDF , которая относится к инструменту trace-cmd , который в значительной степени делает то же, что и выше, - но через более простой интерфейс командной строки. Для trace-cmd
также имеется графический интерфейс «читателя внешнего интерфейса», называемый KernelShark ; оба они также находятся в репозиториях Debian / Ubuntu через sudo apt-get install trace-cmd kernelshark
. Эти инструменты могут быть альтернативой процедуре, описанной выше.
Наконец, я просто хотел бы отметить, что, хотя приведенный выше пример testmod
на самом деле не показывает использование в контексте нескольких заявок, я использовал ту же процедуру трассировки, чтобы обнаружить, что USB-модуль, который я кодирую, был неоднократно заявлялось pulseaudio
, как только USB-устройство было подключено - поэтому, похоже, процедура работает для таких случаев использования.