作业 1——基于 Kprobe 的跟踪器¶
- 截止日期: 周一,2024 年 4 月 8 日,23:59
作业目标¶
- 掌握与 Linux 内核中函数仪器化(instrumentation)相关的知识 (
kretprobes
机制) - 掌握 Linux 内核中的
/proc
文件系统 - 熟悉 Linux 内核特定的数据结构 (
哈希表
和链表
)
题目描述¶
构建一个内核操作监视器(surveillant)。
通过这个监视器,我们的目标是拦截:
kmalloc
和kfree
调用schedule
调用up
和down_interruptible
调用mutex_lock
和mutex_unlock
调用
监视器将在进程级别上保存上述每个函数的调用次数。对于 kmalloc
和 kfree
调用,将显示已分配和释放的内存总量。
监视器需要以内核模块的形式实现,名称为 tracer.ko
。
实现细节¶
拦截将通过记录每个上述函数的样本 (kretprobe
) 来完成。监视器将保留一个包含被监视进程的列表/哈希表,并为这些进程记录上述信息。
为了控制包含被监视进程的列表/哈希表,我们需要使用名为 /dev/tracer
的字符设备,主设备号为 10,次设备号为 42。它将暴露一个带有两个参数的 ioctl
接口:
第一个参数是对监视子系统的请求:
TRACER_ADD_PROCESS
TRACER_REMOVE_PROCESS
第二个参数是要执行监视请求的进程的 PID
为了使用主设备号 10 创建字符设备,你需要在内核中使用 miscdevice 接口。相关宏的定义可以在 tracer.h 头文件 中找到。
由于 kmalloc
函数是内联的,无法对分配的内存量进行仪器化,因此我们将检查 __kmalloc
函数:
- 我们需要使用
kretprobe
,它将保留已分配的内存量和已分配内存区域的地址。 kretprobe
结构中的.entry_handler
和.handler
字段将用于保留已分配的内存量和分配的内存起始地址。
static struct kretprobe kmalloc_probe = {
.entry_handler = kmalloc_probe_entry_handler, /* 进入处理程序 */
.handler = kmalloc_probe_handler, /* 返回 probe 处理程序 */
.maxactive = 32,
};
由于 kfree
函数只接收要释放的内存区域的地址,为了确定释放的总内存量,我们需要根据该区域的地址确定其大小。这是可行的,因为系统在检查 __kmalloc
函数时进行了地址大小的关联。
对于其余的仪器化函数,使用 kretprobe
就足够了。
static struct kretprobe up_probe = {
.entry_handler = up_probe_handler,
.maxactive = 32,
};
虚拟机内核已启用 CONFIG_DEBUG_LOCK_ALLOC
选项,其中 mutex_lock
符号是一个将展开为 mutex_lock_nested
的宏。因此,为了获取有关 mutex_lock
函数的信息,你需要对 mutex_lock_nested
函数进行仪器化。
添加到列表/哈希表的进程在执行结束时将从列表/哈希表中移除。此外,根据 TRACER_REMOVE_PROCESS
操作,系统将从调度列表/哈希表中移除一个进程。
监视器保留的信息将通过 procfs 文件系统显示在 /proc/tracer
文件中。每个受监视的进程,在 /proc/tracer
文件中都会有一个条目,其第一个字段是进程的 PID。该条目是只读的,对其进行读操作将显示保留的结果。显示条目内容的示例:
$cat /proc/tracer
PID kmalloc kfree kmalloc_mem kfree_mem sched up down lock unlock
42 12 12 2048 2048 124 2 2 9 9
1099 0 0 0 0 1984 0 0 0 0
1244 0 0 0 0 1221 100 1023 1023 1002
1337 123 99 125952 101376 193821 992 81921 7421 6392
快速开始¶
你必须从 src 目录中的代码模板开始实现作业。模板中只有一个名为 tracer.h 的头文件。你需要提供其余的实现。你可以添加任意数量的 *.c 源文件和额外的 *.h 头文件。你还应该提供一个名为 tracer.ko 的 Kbuild 文件来编译内核模块。请按照 作业仓库 的 README.md 文件 中的说明进行操作。
提示¶
要想增加获得最高分的机会,请阅读并遵循 Linux 内核中描述的 编码风格规范。
此外,使用以下静态分析工具来验证代码:
- checkpatch.pl
$ linux/scripts/checkpatch.pl --no-tree --terse -f /path/to/your/tracer.c
- sparse
$ sudo apt-get install sparse
$ cd linux
$ make C=2 /path/to/your/tracer.c
- cppcheck
$ sudo apt-get install cppcheck
$ cppcheck /path/to/your/tracer.c
扣分项¶
关于作业扣分的信息可以在 基本说明文件 中找到。此外,以下因素还将被考虑:
- -2:未正确释放资源 (
kretprobes
,/proc
中的条目) - -2:多个执行实例使用的数据的数据同步问题(例如,列表/哈希表)
在特殊情况下(作业通过了测试但不符合要求),以及如果作业未全部通过测试,成绩可能会降低得更多。
提交作业¶
作业将由 vmchecker-next 基础设施自动评分。提交作业将在 moodle 的 课程页面 上与相关作业相关联。你可以在 仓库 的 README.md 文件 中找到提交详细信息。
资源¶
- Documentation/kprobes.txt ——Linux 内核源代码中关于
kprobes
子系统的描述。 - samples/kprobes/ ——Linux 内核源代码中使用
kprobes
的一些示例。
我们建议你使用 gitlab 来存储你的作业。请按照 README 中的说明操作。