1. 创建第一个内核模块

任务目标

在Android内核开发环境中,编写一个最小可加载的内核模块(.ko文件),实现模块加载时的初始化和卸载时的清理功能,并通过dmesg验证模块加载/卸载时打印的内核日志。

环境与依赖

  • 已编译的Android内核源码树,版本建议与目标设备内核一致(例如kernel 4.19/5.10/5.15)
  • 交叉编译工具链(如aarch64-linux-android-或clang)
  • Android设备已解锁并启用root权限,或使用模拟器(goldfish/x86_64内核)
  • 内核配置需开启模块支持:CONFIG_MODULES=y

核心代码

创建文件hello_module.c,实现模块的加载与卸载回调:

```c #include #include #include static int __init hello_init(void) { printk(KERN_INFO "[hello_module] 模块已加载,初始化成功\n"); return 0; } static void __exit hello_exit(void) { printk(KERN_INFO "[hello_module] 模块已卸载,清理完成\n"); } module_init(hello_init); module_exit(hello_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Android Kernel Dev"); MODULE_DESCRIPTION("第一个Android内核模块示例"); MODULE_VERSION("1.0"); ```

Makefile

在相同目录下创建Makefile,指向已编译的内核源码树:

```makefile # 将 KDIR 替换为实际内核源码路径 KDIR := /path/to/android-kernel CROSS_COMPILE := aarch64-linux-android- CC := $(CROSS_COMPILE)gcc obj-m := hello_module.o all: $(MAKE) -C $(KDIR) M=$(PWD) ARCH=arm64 CROSS_COMPILE=$(CROSS_COMPILE) modules clean: $(MAKE) -C $(KDIR) M=$(PWD) ARCH=arm64 CROSS_COMPILE=$(CROSS_COMPILE) clean ```

若使用clang编译,将CC替换为clang并增加LLVM=1参数。

编译与推送

  1. 执行make,生成hello_module.ko文件。
  2. 连接设备并确保adb可用:adb root,然后adb remount(或使用adb push到/data/local/tmp)。
  3. 推送模块:adb push hello_module.ko /data/local/tmp/

加载与验证

  1. 进入设备shell:adb shell
  2. 加载模块:insmod /data/local/tmp/hello_module.ko
  3. 查看内核日志:dmesg | grep hello_module,应看到输出:[hello_module] 模块已加载,初始化成功
  4. 卸载模块:rmmod hello_module
  5. 再次查看日志:dmesg | grep hello_module,应看到:[hello_module] 模块已卸载,清理完成

通过判定标准

  • insmod命令无错误返回,且dmesg中出现加载成功的日志行。
  • rmmod命令无错误返回,且dmesg中出现卸载成功的日志行。
  • 若设备使用SELinux enforcing模式,需确认内核允许模块加载(通常需要adb root后执行setenforce 0,或使用userdebug版本)。

常见问题

  • insmod失败提示“Exec format error”:交叉编译架构与设备不匹配,检查ARCH和CROSS_COMPILE设置。
  • 提示“Operation not permitted”:设备未root或SELinux阻止,执行adb root并尝试setenforce 0。
  • dmesg无输出:确认printk级别未被屏蔽,可尝试echo 8 > /proc/sys/kernel/printk提高日志级别。
内核模块编译与加载验证流程 hello_module.c init + exit 回调 Makefile KDIR + 交叉编译 make 编译 生成 hello_module.ko adb push 推送至 /data/local/tmp insmod 加载 触发 hello_init() dmesg 验证日志 检查 "模块已加载" 输出 rmmod 卸载 触发 hello_exit() 最终判定:两条日志均出现 → 通过