2009-01-15 4 views
61

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

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

+0

«что» в какие сроки? какой код? какой модуль? какой пользователь? какая программа? я немного чувствую, что это не связано с программированием :) интересный нет- –

+1

Ну, это связано с программированием, так как я спрашиваю, потому что я пишу модуль ядра. – mipadi

+0

, пожалуйста, уточните вопрос, чтобы показать проблему программирования, которую вы пытаетесь решить. –

ответ

1

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

+2

Вы на самом деле попробовали это? –

+0

Сначала я думал об этом, но он не работает. – mipadi

+0

'lsof' помог мне разобраться. +1 –

4

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

6

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

40

На самом деле, похоже, есть способ перечислить процессы, требующие модуля/драйвера, однако я не видел его рекламируемым (вне документации ядра Linux), поэтому я напишу свои заметки здесь:

Прежде всего, большое спасибо за ответ @haggai_e; указателем на функции try_module_get и try_module_put в качестве тех, кто отвечает за управление подсчетом использования (refcount), был ключ, который позволил мне отследить процедуру.

Глядя далее на это, я как-то наткнулся на сообщение Linux-Kernel Archive: [PATCH 1/2] tracing: Reduce overhead of module tracepoints; который, наконец, указал на объект, присутствующий в ядре, известный как (я думаю) «отслеживание»; документация для этого находится в каталоге Documentation/trace - Linux kernel source tree. В частности, два файла объясняют механизм трассировки, events.txt и ftrace.txt.

Но есть также короткая «трассировка мини-HOWTO» в бегущей системе Linux в /sys/kernel/debug/tracing/README (см. Также I'm really really tired of people saying that there's no documentation…); обратите внимание, что в исходном дереве ядра этот файл фактически генерируется файлом kernel/trace/trace.c. Я проверил это на Ubuntu natty, и обратите внимание, что поскольку /sys принадлежит корню, вы должны использовать 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. Во-вторых, обратите внимание, что поскольку мы написали «родительский» 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. Поэтому мы можем просто прервать чтение из 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 - как 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 

...что добавляет довольно много накладных расходов (как в объёме данных журнала, так и в времени обработки, необходимых для его создания).


Ища это, я наткнулся на Debugging Linux Kernel by Ftrace PDF, который относится к инструменту trace-cmd, который в значительной степени делает подобное, как описано выше, - но через более простой интерфейс командной строки. Существует также графический интерфейс «front-end reader» для trace-cmd под названием KernelShark; оба из них также находятся в репозиториях Debian/Ubuntu через sudo apt-get install trace-cmd kernelshark. Эти инструменты могут быть альтернативой описанной выше процедуре.

В заключение, я хотел бы отметить, что, хотя приведенный выше пример testmod на самом деле не показывает использование в контексте множества претензий, я использовал ту же процедуру трассировки, чтобы обнаружить, что модуль USB, который я кодирую, неоднократно заявлено pulseaudio, как только устройство USB было подключено, - поэтому процедура, похоже, работает для таких случаев использования.

+5

Спасибо за комментарий, @RichardHansen - вопрос: «Есть ли способ выяснить, кто использует модуль»; и вы можете видеть в трассировке модуля, что, например, 'rmmod-21354', или' tr-6232' (имя процесса - идентификатор процесса) - это те, которые выполняют 'module_put', то есть изменяют refcount модуля, - который что эти процессы «используют» модуль; поэтому я бы сказал, что он отвечает именно тем, о чем попросил ОП ... Приветствия! – sdaau

2

Если вы используете 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 
$ 
+3

Ну, это не совсем правильно: у меня есть на моей машине: '$ lsmod | grep snd snd_seq 47263 1 snd_timer 19130 1 snd_seq snd_seq_device 5100 1 snd_seq ... ' ; поэтому «snd_seq» во что-то востребован (refcount равен 1), но нельзя сказать, почему, поскольку столбец после него пуст, и поэтому ни один другой модуль не утверждает его конкретно (но, возможно, если «ftrace» ядро ​​уже с самого начала процесса загрузки, можно было бы узнать, я думаю). – sdaau

+0

Во всех случаях это не работает. – Martin

+3

Это работает только в ситуации, когда lsmod также отображает что-то в столбце «used by»; rmmod не имеет больше логики, чем lsmod в отношении отображения зависимостей. – dannysauer

-2

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

  • Получение пути используемого в данный момент модуля с помощью «MODINFO»
  • гт -rfing это
  • Копирование нового модуля Я хотел, чтобы загрузить на пути он был в
  • Typing «Modprobe DRIVER_NAME.ko».
+0

Этот ответ на самом деле не отвечает на заданный вопрос. – kelnos

0

попытка kgdb и установить точку останова модуля