1. 项目概述
作为一名嵌入式Linux开发者,我最近在LubanCat H618开发板上完成了第一个Linux内核模块"Hello World"的开发和测试。整个过程虽然基础,但涉及的环境搭建、交叉编译、内核模块开发等环节都是Linux驱动开发的必经之路。本文将详细记录从环境准备到模块测试的全过程,特别会重点说明那些官方文档中没提到的实操细节和避坑经验。
LubanCat H618是基于全志H618处理器的嵌入式开发板,运行Ubuntu系统。我们需要在x86主机上搭建交叉编译环境,为ARM64架构编译内核和驱动模块。这个过程中最耗时的不是代码编写,而是环境配置和问题排查——这也是大多数嵌入式Linux新手的共同痛点。
2. 环境准备与问题排查
2.1 基础软件包安装
按照野火官方文档,首先需要安装交叉编译工具链和相关依赖:
bash复制sudo apt update
sudo apt install gcc make git bc libssl-dev liblz4-tool \
device-tree-compiler bison flex u-boot-tools \
gcc-aarch64-linux-gnu
这里有几个关键点需要注意:
libssl-dev是内核编译必需的加密库支持lz4-tool提供内核镜像压缩支持device-tree-compiler用于处理设备树文件gcc-aarch64-linux-gnu是ARM64的交叉编译器
提示:建议在干净的Ubuntu 20.04系统上操作,避免已有环境造成冲突。我尝试在长期使用的开发机上配置时,遇到了各种奇怪的依赖问题。
2.2 解决apt被锁问题
在执行上述命令时,很可能会遇到如下错误:
code复制E: Could not get lock /var/lib/dpkg/lock-frontend
这是因为Ubuntu默认开启了自动更新服务,会定期锁定包管理器。解决方法如下:
-
关闭软件更新器的自动检查:
- 打开"Software & Updates"
- 切换到"Updates"标签页
- 将"Automatically check for updates"设为Never
-
禁用unattended-upgrades服务:
bash复制sudo systemctl disable unattended-upgrades.service
sudo systemctl stop unattended-upgrades
- 删除可能存在的锁文件:
bash复制sudo rm /var/lib/apt/lists/lock
sudo rm /var/cache/apt/archives/lock
sudo rm /var/lib/dpkg/lock*
经验分享:这个问题在云服务器上尤其常见。除了上述方法,还可以尝试
sudo killall apt apt-get终止相关进程。如果问题依旧,重启系统往往是最快的解决方案。
2.3 验证交叉编译器
安装完成后,检查交叉编译器版本:
bash复制aarch64-linux-gnu-gcc -v
正常应输出类似信息:
code复制gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.1)
如果提示命令未找到,可能需要安装特定版本:
bash复制sudo apt install gcc-9-aarch64-linux-gnu
sudo ln -s /usr/bin/aarch64-linux-gnu-gcc-9 /usr/bin/aarch64-linux-gnu-gcc
3. 内核源码获取与编译
3.1 获取内核源码
为保持与开发板系统的兼容性,必须使用板卡厂商提供的内核源码:
bash复制mkdir LubanCat_H618_SDK && cd LubanCat_H618_SDK/
git clone -b linux-5.4.125 https://gitee.com/LubanCat/kernel-allwinner.git
源码目录结构说明:
arch/arm64: ARM64架构相关代码drivers: 设备驱动代码include: 内核头文件Makefile: 顶层编译文件
注意:国内用户建议使用Gitee镜像源,GitHub源可能速度较慢。如果下载中断,可以使用
git fetch --unshallow继续下载。
3.2 内核配置与编译
编译内核前需要先清理环境并加载默认配置:
bash复制make mrproper
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- linux_h618_defconfig
关键编译参数说明:
ARCH=arm64: 指定ARM64架构CROSS_COMPILE=aarch64-linux-gnu-: 指定交叉编译器前缀-j16: 使用16个线程并行编译(根据CPU核心数调整)
开始完整编译:
bash复制make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j16
编译过程可能需要30分钟到2小时不等,取决于主机性能。成功编译后会在arch/arm64/boot/下生成Image内核镜像。
避坑指南:如果编译中途报错,通常是缺少依赖包。常见的缺失包有libelf-dev、python3-dev等。建议先执行
sudo apt build-dep linux-image-$(uname -r)安装所有可能的依赖。
4. Hello World内核模块开发
4.1 模块源代码解析
创建helloworld.c文件,内容如下:
c复制#include <linux/init.h>
#include <linux/module.h>
static int hello_init(void)
{
printk(KERN_EMERG "[ KERN_EMERG ] Hello World Init\n");
printk("[ default ] Hello World Init\n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_EMERG "[ KERN_EMERG ] Hello World Exit\n");
printk("[ default ] Hello World Exit\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("wuzibin");
MODULE_DESCRIPTION("hello world");
MODULE_ALIAS("hello world_test");
关键点说明:
module_init/module_exit:注册模块加载和卸载时的回调函数printk:内核打印函数,支持8个日志级别(KERN_EMERG为最高级)- MODULE_*宏:声明模块的许可证、作者等信息(必须要有GPL兼容许可证)
4.2 Makefile编写
创建Makefile文件:
makefile复制KERNEL_DIR=../../kernel/
ARCH=arm64
CROSS_COMPILE=aarch64-linux-gnu-
export ARCH CROSS_COMPILE
obj-m := helloworld.o
all:
$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules
clean:
$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean
Makefile关键语法解析:
obj-m:指定编译为可加载模块-C $(KERNEL_DIR):切换到内核源码目录执行MakefileM=$(CURDIR):告诉内核Makefile模块源码的位置modules:编译模块目标
技术细节:内核构建系统会使用这个Makefile来调用内核顶层的Makefile,自动处理模块的编译、链接等过程,最终生成.ko文件。
4.3 模块编译与测试
执行编译:
bash复制make
成功编译后会生成helloworld.ko文件。将其拷贝到开发板:
bash复制scp helloworld.ko cat@192.168.0.121:/home/cat/
在开发板上测试模块:
bash复制# 加载模块
sudo insmod helloworld.ko
# 查看内核日志
dmesg | tail -n 5
# 查看已加载模块
lsmod | grep hello
# 卸载模块
sudo rmmod helloworld
预期输出:
code复制[ 1234.567890] [ KERN_EMERG ] Hello World Init
[ 1234.567891] [ default ] Hello World Init
5. 常见问题与调试技巧
5.1 模块版本不匹配
错误信息:
code复制insmod: ERROR: could not insert module helloworld.ko: Invalid module format
解决方法:
- 确保模块与运行的内核版本完全一致
- 使用
modinfo helloworld.ko查看模块依赖的内核版本 - 在开发板上执行
uname -r查看实际运行的内核版本
5.2 打印信息未显示
可能原因:
- 当前控制台日志级别高于printk使用的级别
- 内核缓冲区大小限制
调试方法:
bash复制# 查看当前日志级别
cat /proc/sys/kernel/printk
# 临时设置最低日志级别(所有信息都显示)
echo 8 > /proc/sys/kernel/printk
# 查看完整内核日志
dmesg
5.3 交叉编译工具链问题
常见错误:
code复制arm-linux-gnueabihf-gcc: command not found
解决方案:
- 确认交叉编译器前缀是否正确(ARM64应为aarch64-linux-gnu-)
- 检查PATH环境变量是否包含交叉编译器路径
- 使用
apt list --installed | grep gcc-aarch64确认是否安装
6. 进阶开发建议
完成基础模块后,可以尝试以下扩展:
- 添加模块参数:
c复制static char *name = "world"; module_param(name, charp, S_IRUGO); - 实现/proc接口:
c复制#include <linux/proc_fs.h> static struct proc_dir_entry *ent; ent = proc_create("hello", 0, NULL, &fops); - 添加设备号注册:
c复制dev_t devno; alloc_chrdev_region(&devno, 0, 1, "hello");
在实际项目中,建议:
- 使用Git管理内核模块代码
- 编写完整的Kconfig和Makefile支持内核菜单配置
- 为模块添加详细的注释和API文档
- 实现自动加载脚本(如通过udev规则)
从Hello World模块到实际驱动开发,需要逐步掌握Linux设备模型、中断处理、DMA操作等高级主题。这个简单的开始已经包含了最核心的模块加载机制和内核API使用方法。
