# 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進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。