Ответ 1
На самом деле, похоже, есть способ перечислить процессы, требующие модуля/драйвера, однако я не видел его рекламируемым (вне документации ядра Linux), поэтому я напишу свои заметки здесь:
Прежде всего, большое спасибо за @haggai_e ответ; указателем на функции try_module_get
и try_module_put
, так как те, кто отвечает за управление подсчетом использования (refcount), были ключом, который позволил мне отследить процедуру.
Глядя дальше на этот онлайн, я как-то наткнулся на сообщение Архив ядра Linux: [PATCH 1/2]: уменьшите накладные расходы на контрольные точки модуля; который, наконец, указал на объект, присутствующий в ядре, известный как (я думаю) "отслеживание"; документация для этого находится в каталоге Документация/трассировка - исходное дерево ядра Linux. В частности, два файла объясняют средство трассировки, events.txt и ftrace.txt.
Но есть также короткая "трассировка мини-HOWTO" на запущенной системе 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
node, который возвращает строку "Это 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
. Во-вторых, обратите внимание, что поскольку мы написали "родительский" module/filter
, а не конкретные события (которые были бы module/module_put/filter
и т.д.), Этот фильтр будет применяться ко всем событиям, перечисленным как "дети" каталога 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
, как здесь. Тем не менее, одна из проблем заключается в том, что этот файл не предназначен для "piped" (поэтому вы не должны запускать tail -f
в этом файле trace
); но вместо этого вы должны перечитать trace
после каждой операции. После первого insmod
мы получим тот же результат из cat
- как для 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
. Также обратите внимание, что для очистки/reset/стирания файла 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, что в значительной степени аналогично выше, но через более простой интерфейс командной строки. Существует также графический интерфейс "front-end reader" для trace-cmd
, называемый KernelShark; оба из них также находятся в репозиториях Debian/Ubuntu через sudo apt-get install trace-cmd kernelshark
. Эти инструменты могут быть альтернативой описанной выше процедуре.
Наконец, я бы просто отметил, что, хотя приведенный выше пример testmod
не показывает использование в контексте нескольких претензий, я использовал ту же процедуру трассировки, чтобы обнаружить, что код USB, который я кодирую, был неоднократно заявляемый pulseaudio
, как только USB-устройство было подключено, поэтому процедура, похоже, работает для таких случаев использования.