第2章:内核编译环境搭建
好,咱们正式开始动手了。这一章,我带你把编译环境搭起来。说白了,就是给你的电脑装上能编译蓝牙内核的「工具链」,再把内核源码搞到手,配置好,最后敲下编译命令。
很多初学者在这一步就卡住了。为什么?因为环境没配好,编译报错一堆,心态直接崩了。我当年也踩过这个坑,所以这一章我会把每个细节都讲透。
2.1 交叉编译工具链安装
先说说什么是交叉编译。你想想看,你的开发电脑是x86架构,但目标板(比如树莓派、RK3588)是ARM架构。x86的程序不能直接在ARM上跑,对吧?所以我们需要一个能在x86上生成ARM可执行文件的编译器——这就是交叉编译工具链。
我个人习惯用 Linaro 提供的工具链,稳定且社区支持好。当然,你也可以用芯片厂商提供的,比如 Rockchip 的 SDK 里就自带。
安装步骤
- 下载工具链
我建议直接去 Linaro 官网下载,或者用下面这个链接(以 gcc-arm-10.2-2020.11-x86_64-arm-none-linux-gnueabihf 为例):
wget https://releases.linaro.org/components/toolchain/binaries/latest-7/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz
- 解压到指定目录
tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz -C /opt/
- 配置环境变量
编辑~/.bashrc,在末尾添加:
export PATH=$PATH:/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin
export CROSS_COMPILE=arm-linux-gnueabihf-
export ARCH=arm
- 验证安装
source ~/.bashrc
arm-linux-gnueabihf-gcc --version
如果看到版本信息,说明安装成功了。
which arm-linux-gnueabihf-gcc 确认一下路径是否正确。
2.2 内核源码获取与配置
工具链装好了,接下来搞内核源码。这里我分两种情况讲:一种是官方主线内核,另一种是芯片厂商定制内核。
2.2.1 获取官方主线内核
如果你用的是通用开发板(比如树莓派、BeagleBone),直接从 kernel.org 拉取就行:
git clone https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
cd linux
git checkout v5.15.100 # 选择一个长期支持版本
为什么选 v5.15?嗯,这个版本对蓝牙 5.2 支持比较完善,而且 LTS 维护周期长。我个人做项目时,除非有特殊需求,否则都优先选 LTS 版本。
2.2.2 获取芯片厂商定制内核
如果你用的是全志、瑞芯微这类芯片,建议直接用厂商提供的 SDK。因为厂商会在内核里打上很多硬件相关的补丁,主线内核不一定能直接跑起来。
举个例子,Rockchip 的 SDK 通常包含在内核源码的 arch/arm/boot/dts/ 目录下,有专门针对自家芯片的设备树文件。
2.2.3 内核配置
配置内核,说白了就是告诉编译器:哪些功能要编译进内核,哪些编译成模块,哪些直接砍掉。
常用的配置命令有这几个:
| 命令 | 说明 |
|---|---|
make menuconfig |
基于 ncurses 的文本界面,最常用 |
make defconfig |
使用默认配置,适合快速验证 |
make savedefconfig |
生成精简的配置文件 |
我一般这样操作:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
先跑 defconfig 生成一个基础配置,再用 menuconfig 手动调整。这样效率最高。
/ 可以搜索配置项。比如你想找蓝牙相关的配置,直接搜 "BLUETOOTH",所有相关选项就都列出来了。
2.3 编译选项详解
配置完内核,接下来就是编译了。但编译选项怎么设,这里头有讲究。
2.3.1 常用编译选项
| 选项 | 含义 | 我的建议 |
|---|---|---|
-jN |
并行编译线程数 | 设为 CPU 核心数的 1.5 倍,比如 4 核设 -j6 |
ARCH=arm |
指定目标架构 | 必须与工具链匹配 |
CROSS_COMPILE |
交叉编译前缀 | 注意末尾的短横线不能少 |
O= |
指定输出目录 | 建议用,避免源码目录被污染 |
2.3.2 编译内核与模块
完整的编译命令长这样:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 zImage
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 modules
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 dtbs
这里解释一下:
zImage:编译生成压缩后的内核镜像modules:编译所有配置为模块的驱动dtbs:编译设备树文件
如果你只想编译蓝牙驱动模块,可以单独指定:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 M=net/bluetooth
这样只编译 net/bluetooth 目录下的代码,速度会快很多。我在调试蓝牙驱动时经常这么干。
-j 设得特别大,结果内存不够直接 OOM。建议根据你的机器配置来,稳一点。
2.3.3 常见编译错误及解决
编译过程中难免遇到错误。我列几个常见的:
- 找不到头文件:检查
CROSS_COMPILE是否设置正确,工具链的 sysroot 路径是否包含在内核源码中。 - 链接错误:通常是某个依赖模块没编译。比如编译蓝牙驱动时,需要先编译 crypto 子系统。
- 设备树编译失败:检查 dts 文件语法,可以用
make dtbs单独编译来定位问题。
我曾经遇到过一个问题:编译蓝牙驱动时一直报 undefined reference to 'hci_register_dev'。查了半天,发现是内核配置里把 CONFIG_BT 设成了 m,但 CONFIG_BT_HCIUART 设成了 y。这俩不一致,导致符号找不到。后来统一改成 m 就解决了。
2.4 本章小结
这一章我们做了三件事:装工具链、拿源码、配编译选项。看起来简单,但每一步都可能出问题。我的建议是:
- 工具链装好后,先编译一个最简单的 hello world 程序验证一下。
- 内核配置时,先跑
defconfig再手动调整,不要从零开始配。 - 编译时用
O=指定输出目录,方便清理和调试。
下一章,我们就要真正进入蓝牙驱动的代码世界了。到时候我会带你分析蓝牙核心子系统的源码结构,看看那些驱动到底是怎么工作的。
准备好了吗?我们继续。