Linux 커널 모듈을 사용하는 것이 무엇인지 알아내는 방법이 있습니까?
커널 모듈을로드하고로로드 된 모듈을 나열하면 모듈 lsmod의 "사용 횟수"(모듈을 참조하는 다른 모듈의 수)를 얻을 수 있습니다. 그래도 모듈을 사용하는 것이 무엇인지 알아내는 방법이 있습니까?
문제는 내가 개발중인 모듈이 사용 횟수가 1이라고 주장하므로 rmmod언로드하는 데 사용할 수 없지만 "by"열이 비어 있다는 것입니다. 즉, 모듈을 다시 컴파일하고 다시로드 할 때마다 시스템을 재부팅해야합니다 (또는 적어도 언로드 할 다른 방법을 찾을 수 없습니다).
사실, 모듈 / 드라이버를 요구하는 프로세스를 나열하는 방법이있는 것 같습니다. 그러나 광고 된 것을 보지 못했기 때문에 (리눅스 커널 문서 외부에서) 여기에 메모를 적어 두겠습니다.
우선 @haggai_e 의 답변에 감사드립니다 . 함수에 대한 포인터 try_module_get와 try_module_put사용 횟수 (refcount)를 관리하는 담당자가 절차를 추적 할 수있는 열쇠였습니다.
온라인에서 더 자세히 살펴보면 Linux-Kernel Archive : [PATCH 1/2] 추적 : 모듈 추적 점의 오버 헤드 감소 ; 마지막으로 "추적"이라고 알려진 커널에있는 기능을 가리 켰습니다. 이에 대한 문서는 Documentation / trace-Linux 커널 소스 트리 디렉토리에 있습니다. 특히, 두 파일은 추적 기능, events.txt 및 ftrace.txt를 설명합니다 .
그러나 실행중인 Linux 시스템에 대한 짧은 "tracing mini-HOWTO" /sys/kernel/debug/tracing/README도 있습니다 (또한 문서가 없다고 말하는 사람들이 정말 지쳤습니다… ); 커널 소스 트리에서이 파일은 실제로 kernel / trace / trace.c 파일에 의해 생성됩니다 . 나는 우분투에서이 테스트를 한 natty이후 있음을, 그리고 참고 /sys루트가 소유, 사용 할 sudo같이이 파일을 읽 sudo cat거나
sudo less /sys/kernel/debug/tracing/README
... /sys여기에서 설명 할 다른 모든 작업에 적용됩니다.
먼저, 여기에 /proc/testmod-sample"This is 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_putetc 이벤트 에 반응 하지만 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기타) 가 아닌 "parent"에 썼으므로이 필터는 module디렉토리의 "하위 항목"으로 나열된 모든 이벤트에 적용됩니다 .
Finally, we enable tracing for module:
sudo bash -c "echo 1 > /sys/kernel/debug/tracing/events/module/enable"
From this point on, we can read the trace log file; for me, reading the blocking, "piped" version of the trace file worked - like this:
sudo cat /sys/kernel/debug/tracing/trace_pipe | tee tracelog.txt
At this point, we will not see anything in the log - so it is time to load (and utilize, and remove) the driver (in a different terminal from where trace_pipe is being read):
$ sudo insmod ./testmod.ko
$ cat /proc/testmod-sample
This is testmod.
$ sudo rmmod testmod
If we go back to the terminal where trace_pipe is being read, we should see something like:
# 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
That is pretty much all we will obtain for our testmod driver - the refcount changes only when the driver is loaded (insmod) or unloaded (rmmod), not when we do a read through cat. So we can simply interrupt the read from trace_pipe with CTRL+C in that terminal; and to stop the tracing altogether:
sudo bash -c "echo 0 > /sys/kernel/debug/tracing/tracing_enabled"
Here, note that most examples refer to reading the file /sys/kernel/debug/tracing/trace instead of trace_pipe as here. However, one problem is that this file is not meant to be "piped" (so you shouldn't run a tail -f on this trace file); but instead you should re-read the trace after each operation. After the first insmod, we would obtain the same output from cat-ing both trace and trace_pipe; however, after the rmmod, reading the trace file would give:
<...>-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
... that is: at this point, the insmod had already been exited for long, and so it doesn't exist anymore in the process list - and therefore cannot be found via the recorded process ID (PID) at the time - thus we get a blank <...> as process name. Therefore, it is better to log (via tee) a running output from trace_pipe in this case. Also, note that in order to clear/reset/erase the trace file, one simply writes a 0 to it:
sudo bash -c "echo 0 > /sys/kernel/debug/tracing/trace"
If this seems counterintuitive, note that trace is a special file, and will always report a file size of zero anyways:
$ 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
... even if it is "full".
Finally, note that if we didn't implement a filter, we would have obtained a log of all module calls on the running system - which would log any call (also background) to grep and such, as those use the binfmt_misc module:
...
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
... which adds quite a bit of overhead (in both log data ammount, and processing time required to generate it).
While looking this up, I stumbled upon Debugging Linux Kernel by Ftrace PDF, which refers to a tool trace-cmd, which pretty much does the similar as above - but through an easier command line interface. There is also a "front-end reader" GUI for trace-cmd called KernelShark; both of these are also in Debian/Ubuntu repositories via sudo apt-get install trace-cmd kernelshark. These tools could be an alternative to the procedure described above.
Finally, I'd just note that, while the above testmod example doesn't really show use in context of multiple claims, I have used the same tracing procedure to discover that an USB module I'm coding, was repeatedly claimed by pulseaudio as soon as the USB device was plugged in - so the procedure seems to work for such use cases.
It says on the Linux Kernel Module Programming Guide that the use count of a module is controlled by the functions try_module_get and try_module_put. Perhaps you can find where these functions are called for your module.
All you get are a list of which modules depend on which other modules (the Used by column in lsmod). You can't write a program to tell why the module was loaded, if it is still needed for anything, or what might break if you unload it and everything that depends on it.
If you use rmmod WITHOUT the --force option, it will tell you what is using a module. Example:
$ 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
$
You might try lsof or fuser.
try kgdb and set breakpoint to your module
For anyone desperate to figure out why they can't reload modules, I was able to work around this problem by
- Getting the path of the currently used module using "modinfo"
- rm -rfing it
- Copying the new module I wanted to load to the path it was in
- Typing "modprobe DRIVER_NAME.ko".
'IT TIP' 카테고리의 다른 글
| CSS 픽셀이 분수 일 수 있습니까? (0) | 2020.11.05 |
|---|---|
| Angular 2의 동적 템플릿 URL (0) | 2020.11.05 |
| Docker, Puppet 및 Vagrant를 사용하여 LAMP 웹 애플리케이션을 개발하는 방법은 무엇입니까? (0) | 2020.11.05 |
| 접미사 트리 및 시도. (0) | 2020.11.05 |
| 이 Rails JSON 인증 API (Devise 사용)는 안전합니까? (0) | 2020.11.05 |