第一章:Linux内核开发环境搭建

说实话,做内核开发这么多年,我见过太多新手卡在环境搭建这一步。明明代码写得不错,结果编译环境没配好,折腾一整天连个hello world都跑不起来。今天我就把这一整套流程掰开揉碎了讲给你听。

1.1 虚拟机安装:你的第一个开发基地

我个人习惯用VMware Workstation,当然VirtualBox也行。但有一点要记住:别用Windows直接搞内核开发,那纯粹是给自己找麻烦。

我的推荐配置:

  • CPU:至少2核(4核更舒服)
  • 内存:4GB起步(编译内核时8GB才够用)
  • 硬盘:80GB(动态分配)
  • 网络:NAT模式(方便上网)

装好Ubuntu 22.04 LTS后,第一件事就是换源。你想想看,用默认源下载东西,那速度能急死人。

# 备份原源
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak

# 替换为清华源
sudo sed -i 's/archive.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list

sudo apt update && sudo apt upgrade -y

小技巧:装完系统先装个openssh-server,这样你就可以用SSH连进去操作,复制粘贴代码方便多了。

1.2 交叉编译工具链:为什么需要它?

做嵌入式开发,你肯定遇到过这种情况:你的电脑是x86架构,但目标板是ARM。这时候就需要交叉编译了。说白了,就是在你的PC上编译出能在ARM上跑的程序。

我曾经在项目里吃过这个亏——直接用gcc编译,然后往开发板上一丢,结果执行不了。嗯,从那以后我再也不敢偷懒了。

# 安装ARM交叉编译工具链
sudo apt install gcc-arm-linux-gnueabihf

# 验证安装
arm-linux-gnueabihf-gcc --version

# 写个简单的测试程序
cat > hello.c << EOF
#include <stdio.h>
int main() {
    printf("Hello, Kernel World!\n");
    return 0;
}
EOF

# 交叉编译
arm-linux-gnueabihf-gcc -o hello hello.c

# 查看文件类型
file hello
# 输出:hello: ELF 32-bit LSB executable, ARM, ...

注意:不同架构要选不同的工具链。比如ARM64用aarch64-linux-gnu-gcc,MIPS用mips-linux-gnu-gcc。别搞混了,我见过有人拿ARM工具链去编译MIPS程序,折腾半天才发现问题。

1.3 内核源码下载与编译

内核源码去哪下?官网 kernel.org 是最权威的。但我建议你用git clone,这样以后切换版本、打补丁都方便。

# 下载最新稳定版内核
git clone --depth=1 https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git

# 或者下载指定版本
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.tar.xz
tar -xf linux-6.1.tar.xz

编译内核前,先配置。这里有个坑:别用默认配置。默认配置会编译一大堆你用不到的驱动,编译时间长得能让你睡一觉。

# 进入内核目录
cd linux-6.1

# 生成默认配置
make defconfig

# 或者用当前系统的配置(推荐)
cp /boot/config-$(uname -r) .config
make olddefconfig

# 开始编译(-j后面跟CPU核心数)
make -j4

# 编译内核模块
make modules

# 安装模块
sudo make modules_install

# 安装内核
sudo make install

我的经验:第一次编译内核,建议用make localmodconfig。这个命令会分析你当前系统加载了哪些模块,只编译这些模块。我试过,编译时间从40分钟缩短到8分钟。

1.4 最小系统制作:从零到能跑

最小系统,说白了就是让内核能启动、能进shell。你需要三样东西:内核镜像、设备树文件、根文件系统。

根文件系统我用BusyBox来做,轻量又方便。你想想看,一个完整的Linux系统几百MB,BusyBox才几MB,但该有的命令它都有。

# 下载BusyBox
wget https://busybox.net/downloads/busybox-1.36.0.tar.bz2
tar -xf busybox-1.36.0.tar.bz2
cd busybox-1.36.0

# 配置
make defconfig
make menuconfig
# 记得勾选 "Build BusyBox as a static binary (no shared libs)"

# 编译
make -j4

# 安装到指定目录
make install CONFIG_PREFIX=../rootfs

然后创建必要的目录和设备节点:

cd ../rootfs
mkdir -p proc sys dev etc/init.d

# 创建init脚本
cat > etc/init.d/rcS << EOF
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t tmpfs none /tmp
echo "Welcome to Mini Linux!"
EOF

chmod +x etc/init.d/rcS

# 创建设备节点
sudo mknod dev/console c 5 1
sudo mknod dev/null c 1 3

避坑指南:我曾经犯过一个低级错误——忘记创建/dev/console设备节点。结果内核启动到最后一步,控制台输出不了任何东西,我还以为是内核崩了。折腾了两小时才发现问题。

最后,用QEMU测试你的最小系统:

# 制作根文件系统镜像
dd if=/dev/zero of=rootfs.img bs=1M count=32
mkfs.ext4 rootfs.img
sudo mount -o loop rootfs.img /mnt
sudo cp -r rootfs/* /mnt/
sudo umount /mnt

# 启动QEMU
qemu-system-arm -M vexpress-a9 \
    -kernel arch/arm/boot/zImage \
    -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb \
    -drive file=rootfs.img,format=raw \
    -append "root=/dev/mmcblk0 console=ttyAMA0" \
    -nographic

调试技巧:如果启动失败,加个earlyprintk参数到内核命令行。这个参数能让内核在早期启动阶段就输出调试信息,帮你定位问题。

看到控制台出现 Welcome to Mini Linux! 的那一刻,恭喜你,环境搭建成功了!接下来就可以正式开始内核驱动开发了。

嗯,这一章内容不少,但每一步都很关键。我建议你跟着操作一遍,遇到问题别慌,多半是路径写错了或者权限不够。下一章我们讲内核模块开发,到时候你会感谢今天搭建好的环境。