Есть ли способ выяснить, что использует модуль ядра Linux?

Если я загружаю модуль ядра и список загруженных модулей lsmod, я могу получить "счет использования" модуля (количество других модулей со ссылкой на модуль). Есть ли способ выяснить, что использует модуль?

Проблема в том, что модуль, который я разрабатываю, настаивает на том, что его количество использования равно 1, и поэтому я не могу использовать rmmod для его выгрузки, но его столбец "by" пуст. Это означает, что каждый раз, когда я хочу повторно скомпилировать и перезагрузить модуль, мне нужно перезагрузить компьютер (или, по крайней мере, я не могу найти другого способа его разгрузить).

Ответ 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-устройство было подключено, поэтому процедура, похоже, работает для таких случаев использования.

Ответ 3

Все, что вы получаете, это список модулей, которые зависят от других модулей (столбец Used by в lsmod). Вы не можете написать программу, чтобы узнать, почему модуль был загружен, если он все еще нужен для чего-либо или что может сломаться, если вы его выгрузите и все, что от него зависит.

Ответ 4

Если вы используете rmmod WITHOUT -force, он расскажет вам, что использует модуль. Пример:

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

Ответ 5

Вы можете попробовать lsof или fuser.

Ответ 6

попробуйте загрузить kgdb и установите точку останова на ваш модуль

Ответ 7

Для тех, кто отчаянно пытается понять, почему они не могут перезагрузить модули, мне удалось решить эту проблему на

  • Получение пути к используемому в данный момент модулю с помощью "modinfo"
  • rm -rfing it
  • Копирование нового модуля, который я хотел загрузить, в путь, который был в
  • Введите "modprobe DRIVER_NAME.ko".