android如何以模块的方式编译内核驱动

发布时间:2022-01-12 15:16:09 作者:iii
来源:亿速云 阅读:371
# 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仓库

1.2 配置交叉编译工具链

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

1.3 内核配置确认

确保内核配置启用了模块化支持:

make menuconfig
# 检查以下选项:
#   Enable loadable module support → Y
#   Module unloading → Y
#   Forced module unloading → Y(可选)

二、驱动模块开发

2.1 标准驱动模块结构示例

一个最简单的字符设备驱动模块包含以下文件:

drivers/mydriver/
├── Kconfig
├── Makefile
└── mydriver.c

2.2 关键代码实现(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");

2.3 Makefile编写

模块编译需要特殊的Makefile语法:

# drivers/mydriver/Makefile
obj-$(CONFIG_MY_DRIVER) += mydriver.o

# 或直接编译为模块(无需内核配置)
obj-m += mydriver.o

# 多文件模块示例
# obj-m += complexdrv.o
# complexdrv-objs := file1.o file2.o

2.4 Kconfig配置(可选)

如需通过menuconfig配置模块:

# drivers/mydriver/Kconfig
config MY_DRIVER
    tristate "My Test Driver"
    default n
    help
      This is a sample kernel driver.

三、编译流程

3.1 单模块编译方式

# 在kernel根目录执行
make -C $(pwd) M=drivers/mydriver modules

# 输出结果:
#   drivers/mydriver/mydriver.ko

3.2 集成到系统编译

  1. 将模块目录放入drivers/
  2. drivers/Kconfig中添加:
    
    source "drivers/mydriver/Kconfig"
    
  3. drivers/Makefile中添加:
    
    obj-$(CONFIG_MY_DRIVER) += mydriver/
    

3.3 编译产物处理

生成的.ko文件需要打包到系统镜像中:

# 在设备mk文件中添加
PRODUCT_PACKAGES += mydriver.ko

# 或手动推送到vendor分区
TARGET_OUT_VENDOR_MODULES := $(TARGET_OUT_VENDOR)/lib/modules
LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_MODULES)

四、模块部署与测试

4.1 手动加载模块

adb push mydriver.ko /data/local/tmp
adb shell
su
insmod /data/local/tmp/mydriver.ko

# 检查加载情况
lsmod | grep mydrv
dmesg | grep mydrv

# 卸载模块
rmmod mydrv

4.2 开机自动加载

  1. 将.ko文件放入/vendor/lib/modules/
  2. 创建/vendor/etc/modules-load.d/mydrv.conf
    
    mydrv
    
  3. 确保init脚本有执行权限

4.3 模块依赖处理

如果模块依赖其他符号:

# 查看模块依赖
modinfo mydriver.ko

# 加载依赖模块
modprobe dependency_module

五、调试技巧

5.1 打印调试信息

printk(KERN_DEBUG "Debug message: val=%d\n", var);

通过dmesgcat /proc/kmsg查看输出

5.2 GDB调试

# 在编译时保留调试符号
make CONFIG_DEBUG_INFO=y

# 使用gdb加载vmlinux
aarch64-linux-android-gdb vmlinux

5.3 动态调试

echo -n 'file mydriver.c +p' > /sys/kernel/debug/dynamic_debug/control

六、常见问题解决

6.1 版本不匹配错误

insmod: ERROR: could not insert module: Invalid module format

解决方法: 1. 使用modinfo检查vermagic 2. 确保使用相同配置重新编译内核

6.2 符号未找到错误

Unknown symbol in module

解决方法: 1. 使用EXPORT_SYMBOL()导出所需符号 2. 确保依赖模块先加载

6.3 SELinux权限问题

avc: denied { module_load } 

解决方法: 1. 添加SELinux策略:

   allow kernel system_file:file { execute_no_trans };
  1. 或临时关闭SELinux:
    
    setenforce 0
    

七、进阶主题

7.1 设备树(Device Tree)支持

// 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);

7.2 热插拔支持

实现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字,实际字数可能因代码块和格式略有差异)

推荐阅读:
  1. 如何以Nginx脚本方式切割日志
  2. httpd + MariaDB + php模块方式编译

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

android

上一篇:Ubuntu​交叉编译的方法是什么

下一篇:android设备树内核menuconfig怎么使用

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》