您好,登录后才能下订单哦!
# Android如何以模块的方式编译内核驱动
## 前言
在Android系统开发中,内核驱动模块的开发与调试是一个重要环节。与直接将驱动编译进内核不同,以模块(.ko文件)的形式编译驱动具有灵活性强、便于调试、无需重新烧录整个系统等优势。本文将详细介绍在Android环境下如何以模块化方式编译内核驱动,涵盖环境配置、Makefile编写、模块加载/卸载等全流程。
---
## 一、环境准备
### 1.1 获取Android内核源码
模块化编译需要完整的内核源代码,获取方式通常有两种:
```bash
# 方式1:通过AOSP仓库获取(推荐)
repo init -u https://android.googlesource.com/kernel/manifest -b common-android13-5.15
repo sync
# 方式2:从芯片厂商获取
# 如高通平台会提供kernel-msm仓库
Android内核需要使用特定工具链编译,工具链路径通常在AOSP预编译目录中:
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-android-
export PATH=$PATH:/path/to/aosp/prebuilts/clang/host/linux-x86/clang-r450784d/bin
确保内核配置启用了模块化支持:
make menuconfig
# 检查以下选项:
# Enable loadable module support → Y
# Module unloading → Y
# Forced module unloading → Y(可选)
一个最简单的字符设备驱动模块包含以下文件:
drivers/mydriver/
├── Kconfig
├── Makefile
└── mydriver.c
#include <linux/module.h>
#include <linux/fs.h>
#define DEVICE_NAME "mydrv"
static int major_num;
static int mydrv_open(struct inode *inode, struct file *file) {
printk(KERN_INFO "mydrv opened\n");
return 0;
}
static struct file_operations fops = {
.open = mydrv_open,
};
static int __init mydrv_init(void) {
major_num = register_chrdev(0, DEVICE_NAME, &fops);
printk(KERN_INFO "mydrv module loaded, major=%d\n", major_num);
return 0;
}
static void __exit mydrv_exit(void) {
unregister_chrdev(major_num, DEVICE_NAME);
printk(KERN_INFO "mydrv module unloaded\n");
}
module_init(mydrv_init);
module_exit(mydrv_exit);
MODULE_LICENSE("GPL");
模块编译需要特殊的Makefile语法:
# drivers/mydriver/Makefile
obj-$(CONFIG_MY_DRIVER) += mydriver.o
# 或直接编译为模块(无需内核配置)
obj-m += mydriver.o
# 多文件模块示例
# obj-m += complexdrv.o
# complexdrv-objs := file1.o file2.o
如需通过menuconfig配置模块:
# drivers/mydriver/Kconfig
config MY_DRIVER
tristate "My Test Driver"
default n
help
This is a sample kernel driver.
# 在kernel根目录执行
make -C $(pwd) M=drivers/mydriver modules
# 输出结果:
# drivers/mydriver/mydriver.ko
drivers/
下drivers/Kconfig
中添加:
source "drivers/mydriver/Kconfig"
drivers/Makefile
中添加:
obj-$(CONFIG_MY_DRIVER) += mydriver/
生成的.ko文件需要打包到系统镜像中:
# 在设备mk文件中添加
PRODUCT_PACKAGES += mydriver.ko
# 或手动推送到vendor分区
TARGET_OUT_VENDOR_MODULES := $(TARGET_OUT_VENDOR)/lib/modules
LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_MODULES)
adb push mydriver.ko /data/local/tmp
adb shell
su
insmod /data/local/tmp/mydriver.ko
# 检查加载情况
lsmod | grep mydrv
dmesg | grep mydrv
# 卸载模块
rmmod mydrv
/vendor/lib/modules/
/vendor/etc/modules-load.d/mydrv.conf
:
mydrv
如果模块依赖其他符号:
# 查看模块依赖
modinfo mydriver.ko
# 加载依赖模块
modprobe dependency_module
printk(KERN_DEBUG "Debug message: val=%d\n", var);
通过dmesg
或cat /proc/kmsg
查看输出
# 在编译时保留调试符号
make CONFIG_DEBUG_INFO=y
# 使用gdb加载vmlinux
aarch64-linux-android-gdb vmlinux
echo -n 'file mydriver.c +p' > /sys/kernel/debug/dynamic_debug/control
insmod: ERROR: could not insert module: Invalid module format
解决方法:
1. 使用modinfo
检查vermagic
2. 确保使用相同配置重新编译内核
Unknown symbol in module
解决方法:
1. 使用EXPORT_SYMBOL()
导出所需符号
2. 确保依赖模块先加载
avc: denied { module_load }
解决方法: 1. 添加SELinux策略:
allow kernel system_file:file { execute_no_trans };
setenforce 0
// kernel/arch/arm64/boot/dts/vendor/mydevice.dtsi
mydrv@0 {
compatible = "vendor,mydrv";
reg = <0x0 0x1000>;
status = "okay";
};
驱动中通过of_match_table
匹配:
static const struct of_device_id mydrv_of_match[] = {
{ .compatible = "vendor,mydrv" },
{}
};
MODULE_DEVICE_TABLE(of, mydrv_of_match);
实现uevent
回调:
static int mydrv_uevent(struct device *dev, struct kobj_uevent_env *env) {
add_uevent_var(env, "MODALIAS=mydrv:v1");
return 0;
}
通过模块化方式编译Android内核驱动,开发者可以显著提高开发效率。本文涵盖了从环境搭建到实际部署的全流程,重点介绍了模块化编译的特殊配置要求。在实际项目中,建议结合kbuild
系统实现更复杂的模块管理,同时注意内核版本兼容性和系统安全策略的限制。
注意:不同Android版本和芯片平台的具体实现可能有所差异,建议参考对应平台的
kernel/Documentation/kbuild/modules.txt
文档。 “`
(全文约2750字,实际字数可能因代码块和格式略有差异)
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。