Есть ли способ выяснить, что использует модуль ядра Linux? - PullRequest
68 голосов
/ 16 января 2009

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

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

Ответы [ 7 ]

47 голосов
/ 19 марта 2013

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

7 голосов
/ 16 января 2009

В руководстве по программированию модуля ядра Linux говорится, что счетчик использования модуля контролируется функциями try_module_get и try_module_put. Возможно, вы сможете найти, где эти функции вызываются для вашего модуля.

4 голосов
/ 16 января 2009

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

3 голосов
/ 08 ноября 2013

Если вы используете rmmod БЕЗ параметра --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
$
1 голос
/ 16 января 2009

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

0 голосов
/ 14 ноября 2017

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

0 голосов
/ 17 августа 2016

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

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