本文基于 NXP i.MX6ULL 嵌入式平台,结合实际开发操作链路,系统梳理嵌入式 Linux 驱动开发的体系,覆盖开发环境标准化搭建、Linux 系统三大件(U-Boot、内核、根文件系统)、完整启动流程、内核编译裁剪、根文件系统制作等环节。
一、嵌入式 Linux 开发体系与硬件基础
嵌入式 Linux 驱动开发的核心,是围绕「硬件平台初始化 - 内核引导 - 系统挂载 - 驱动适配」的完整链路展开,其底层依赖 i.MX6ULL 硬件平台与 Linux 操作系统的分层设计思想。
1. 硬件平台核心参数
本文基于正点原子 i.MX6ULL MINI 核心板,核心硬件配置如下:
- 主控:NXP i.MX6ULL,Cortex-A7 内核,主频最高 792MHz
- 内存:512MB DDR3L,用于系统运行、镜像加载与数据缓存
- 存储:8GB EMMC(核心板板载)+ 最高 32GB TF/SD 卡(底板扩展),用于系统镜像与文件系统固化
- 外设资源:集成 100M 以太网、RGB LCD 接口、I2C、SPI、CAN、ADC、GPIO、USB OTG 等常用外设,满足工业级开发需求
2. Linux 系统核心三大件
嵌入式 Linux 系统的运行,完全依赖三个核心组件,也是开发过程中需要重点适配、编译与调试的对象:
表格
| 组件 | 核心作用 | 开发产物 |
|---|---|---|
| U-Boot | 通用引导加载程序,完成硬件基础初始化、镜像加载与内核启动参数传递,是系统上电后运行的第一个可定制代码 | u-boot.imx |
| Linux 内核 | 操作系统核心,负责进程调度、内存管理、硬件驱动、网络协议栈、虚拟文件系统等核心能力 | zImage(内核镜像)+ imx6ull-alientek-emmc.dtb(设备树文件) |
| 根文件系统 rootfs | 内核启动后挂载的第一个文件系统,存放系统命令、动态库、配置文件、设备节点与用户程序,是用户空间与内核交互的载体 | rootfs.tar.bz2(压缩包)或 nfs 共享目录 |
Linux 内核采用分层设计,核心由进程调度、内存管理、虚拟文件系统 (VFS)、网络接口、进程间通信 5 个子系统构成;而设备驱动则是内核与硬件之间的桥梁,除网络设备外,字符设备、块设备均被映射到文件系统的文件节点,通过 open/read/write/close 等标准接口访问,即 Linux「一切皆是文件」的核心设计思想。
二、开发环境标准化搭建
稳定的开发环境是嵌入式开发的基础,核心目标是实现「Ubuntu 虚拟机既能访问公网下载软件,又能与开发板稳定通信」,同时配套开发所需的工具链与服务。
1. 虚拟机双网卡网络配置
采用「NAT 模式 + 桥接模式」双网卡组网,是嵌入式开发的最优方案,解决单网卡无法同时满足上网与开发板通信的痛点:
- 网络适配器 1:NAT 模式,负责 Ubuntu 访问公网,IP 网段默认配置为 192.168.78.0/24,自动获取 IP
- 网络适配器 2:桥接模式,桥接到电脑有线网卡,负责与开发板通信,配置静态 IP 192.168.1.100/24,与开发板 IP 192.168.1.50 同网段
核心配置步骤:
- VMware 中添加双网卡,分别配置 NAT 与桥接模式,桥接模式需指定到物理有线网卡,而非自动检测
- 修改 Ubuntu 网络配置文件
/etc/network/interfaces,NAT 网卡设为 dhcp 自动获取,桥接网卡设为 static 静态 IP - 配置默认路由走 NAT 网卡,确保公网访问正常,通过
route命令验证路由表,同时设置开机自启避免重启后配置失效 - 连通性验证:
ping www.baidu.com验证公网,ping 192.168.1.50验证与开发板的通信
2. 核心开发服务与工具配置
基于双网卡网络,需配置以下服务与工具,覆盖开发全流程需求:
- apt-get 源配置 :替换为阿里云源,执行
sudo apt-get autoclean && sudo apt-get update更新源,解决软件下载慢、依赖安装失败的问题 - minicom 串口工具 :通过
sudo apt-get install minicom安装,配置串口参数为 115200 8N1,关闭硬件流控,用于开发板终端交互与启动日志查看 - tftp 服务器 :安装
tftpd-hpa服务,指定共享目录/home/linux/tftpboot并赋予 777 权限,用于 U-Boot 阶段下载内核镜像与设备树文件,无需反复烧写存储介质 - nfs 服务器 :安装
nfs-kernel-server,修改/etc/exports配置共享目录/home/linux/nfs/rootfs,开放读写、同步、无 root 权限限制,用于开发阶段根文件系统的网络挂载 - 交叉编译工具链 :采用
gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf,将工具链路径添加到系统 PATH 环境变量,通过arm-linux-gnueabihf-gcc -v验证安装,实现 x86 主机编译 ARM 平台可执行程序
三、Linux 系统启动流程解析
1. BotROM 阶段
- 设置为 SD 卡启动
- 上电
- 执行 0x0 地址的代码
- imx6ull 芯片内部的 96K 的 BotRAM,使用的 imx6ull 芯片内部的 128k SRAM 空间
- 查看启动方式为 SD 卡启动
- 从 SD 卡中找 uboot.imx(uboot.bin + DCD 数据头)文件
- 从 DCD 数据头中找到时钟配置、DDR 配置信息初始化 DDR
- 将 uboot.bin 拷贝到 DDR 中
- 跳转执行 uboot.bin
2. uboot 阶段
-
重定位异常向量表
-
切换工作模式
-
关闭 MMU、DCache、看门狗
-
初始化串口、网口及相关外设
-
将 uboot 代码搬移到高地址
-
进入用户交互阶段
-
倒计时等待用户输入 Enter
-
如果用户不输入 Enter 执行 bootcmd
setenv bootcmd 'tftp 80800000 zImage;tftp 83000000 imx6ull-alientek-emmc.dtb;bootz 80800000 - 83000000'
saveenv -
找内核和设备树(tftp 80800000 zImage; tftp 83000000 dtb; bootz)
-
将 bootargs 写入到设备树 choosen 节点中
-
R0(固定值)、R1 (machine ID)、R2 (设备树的地址) 对内核传参
setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.1.100:/home/linux/nfs/rootfs,proto=tcp,nfsvers=3 rw ip=192.168.1.50:192.168.1.100:192.168.1.1:255.255.255.0::eth0:off'
saveenv
3. 内核阶段
- 解析设备树文件
- 启动内核
- 内存管理
- 多任务管理
- 进程间通信
- 网络通信
- 文件系统管理
- 根据设备树中的 choosen 节点挂载文件系统
- 文件系统使用 nfs 挂载
4. 文件系统
- 内核执行 linuxrc
- 加载 /etc/inittab 文件设置了开机执行脚本关机执行脚本..
- 执行 /etc/init.d/rcS设置环境变量挂载文件系统
- 执行 /etc/fstab挂载 sys 和 proc 文件系统
- 通过 fork+exec 启动终端
- 终端启动 fork+exec 启动 shell
- shell 执行用户输入 fork+exec
四、Linux 内核编译与裁剪
Linux 内核是一个高度可裁剪的开源项目,开发过程中需根据硬件平台与功能需求,完成内核的配置、编译与裁剪,仅保留需要的功能,减少镜像体积与启动耗时。
1. 编译前置准备
- 安装编译依赖:
sudo apt-get install lzop libncurses5-dev,lzop 用于 zImage 镜像压缩,ncurses 用于 menuconfig 图形化配置界面 - 获取对应版本内核源码:采用 NXP 官方维护的
linux-imx-rel_imx_4.1.15_2.1.0_ga版本,适配 i.MX6ULL 平台
2. 内核编译核心流程
内核编译通过 Makefile 管理,严格遵循「清理 - 加载默认配置 - 自定义裁剪 - 编译 - 产物部署」的流程,所有操作均在源码顶层目录执行:
-
清理历史编译文件
bash
运行
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distcleanARCH 指定目标架构为 arm,CROSS_COMPILE 指定交叉编译工具链前缀,distclean 清除所有历史配置与编译产物,确保编译环境纯净。
-
加载板级默认配置
bash
运行
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_alientek_emmc_defconfig该命令将开发板厂商提供的板级默认配置文件,写入内核顶层目录的.config 文件,.config 是内核编译的核心配置文件,所有功能的开启 / 关闭均由该文件定义。
-
图形化功能裁剪
bash
运行
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig打开图形化配置界面,按需开启 / 关闭内核功能,常用配置项包括:GPIO 子系统、I2C/SPI 总线驱动、CAN 总线、IIO ADC 驱动、LCD / 触摸屏驱动、摄像头 V4L2 驱动、网络协议栈等,无需的功能直接关闭,减少内核体积。
-
全量编译
bash
运行
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j16-j16 指定编译线程数,根据主机 CPU 核心数调整,提升编译速度。编译完成后,核心产物为:
- 内核镜像:
arch/arm/boot/zImage - 设备树二进制文件:
arch/arm/boot/dts/imx6ull-alientek-emmc.dtb
- 内核镜像:
-
产物部署 将编译生成的 zImage 与 dtb 文件,拷贝到 tftp 服务器的共享目录
/home/linux/tftpboot,U-Boot 即可通过 tftp 下载最新的内核与设备树,无需烧写存储介质。
3. 内核模块的加载方式
内核功能与驱动分为两种加载方式,适配不同的开发场景:
- 静态加载:将驱动代码编译进内核镜像,随内核启动自动执行初始化,适用于系统核心功能与必须开机加载的驱动
- 动态加载 :将驱动代码编译为独立的.ko 内核模块,内核启动后,通过
insmod命令加载、rmmod命令卸载,无需重新编译内核,是外设驱动开发的主要方式,大幅提升调试效率
五、根文件系统制作与配置
根文件系统是 Linux 系统运行的基础,内核启动后必须挂载根文件系统才能进入用户空间,嵌入式平台通常采用 busybox 制作极简根文件系统,占用空间小、易于移植。
1. 根文件系统核心目录结构
根文件系统遵循 Linux FHS 标准,核心目录与作用如下:
表格
| 目录 | 核心作用 |
|---|---|
| /bin | 存放系统基础命令(ls、cd、cat 等),由 busybox 生成 |
| /sbin | 存放系统管理员命令(ifconfig、mount、insmod 等) |
| /lib | 存放交叉编译工具链的动态链接库,为应用程序与系统命令提供运行依赖 |
| /dev | 存放设备节点文件,由内核与 mdev 自动生成,是用户空间访问硬件的入口 |
| /etc | 存放系统配置文件,包括启动脚本、环境变量、文件系统挂载配置等 |
| /proc、/sys | 虚拟文件系统挂载点,proc 存放内核进程与系统信息,sys 存放设备与驱动的属性信息 |
| /mnt | 临时挂载目录,用于 nfs、U 盘、SD 卡等存储介质的挂载 |
| /root | root 用户的家目录 |
2. 基于 busybox 的根文件系统制作流程
-
源码准备与工具链配置解压 busybox-1.29.0 源码,修改顶层 Makefile,指定 ARCH=arm,CROSS_COMPILE=arm-linux-gnueabihf-,匹配交叉编译工具链。
-
busybox 功能配置
bash
运行
make defconfig # 加载默认配置 make menuconfig # 图形化配置核心配置项:关闭静态编译(避免 DNS 解析异常)、开启 vi-style 行编辑、开启 mdev 功能与子目录支持,其余功能按需开启。
-
编译与安装
bash
运行
make make install CONFIG_PREFIX=/home/linux/nfs/rootfs将编译生成的命令与基础目录,安装到指定的 rootfs 目录。
-
动态库移植拷贝交叉编译工具链中的 libc 库、动态链接库到 rootfs 的 /lib 与 /usr/lib 目录,包括所有.so 动态库文件与.a 静态库文件,确保系统命令与应用程序能够正常运行。
-
必备目录与配置文件完善
- 创建 dev、proc、sys、tmp、mnt、root 等必备目录
- 编写 /etc/init.d/rcS 启动脚本,配置环境变量、挂载文件系统、启动 mdev
- 编写 /etc/fstab 文件,定义开机自动挂载的虚拟文件系统
- 编写 /etc/inittab 文件,配置 init 进程的启动动作与控制台交互
3. 根文件系统的两种使用方式
- 固化模式:将 rootfs 打包为 rootfs.tar.bz2,通过 mfgtool 工具烧写到 EMMC/SD 卡,设备上电后自动从本地存储挂载根文件系统,适用于量产与设备独立运行场景
- 开发模式:将 rootfs 目录配置为 nfs 共享目录,内核通过 bootargs 参数指定 nfs 挂载,修改文件系统中的配置、应用程序、驱动模块后,重启开发板即可生效,无需重新烧写,是开发调试阶段的最优选择
六、开发模式选择与实操建议
针对不同的开发阶段,需选择对应的系统部署与启动模式,兼顾开发效率与设备运行稳定性。
1. 三种主流开发模式对比
表格
| 模式 | 部署方式 | 适用场景 | 优势 |
|---|---|---|---|
| 模式一 | 全系统烧写到 SD 卡 | 量产多设备部署、系统刷机恢复 | 设备可独立运行,系统固化稳定 |
| 模式二 | 全系统烧写到 EMMC | 产品最终交付、设备独立运行 | 板载存储启动,无需外接 SD 卡,稳定性高 |
| 模式三 | U-Boot 烧写到 SD 卡 + zImage 通过 tftp 下载 + rootfs 通过 nfs 挂载 | 驱动开发、内核调试、应用程序开发 | 代码修改后无需烧写,重启即可生效,开发效率最高 |
2. 开发阶段核心建议
- 驱动与内核开发阶段,优先采用模式三,减少反复烧写存储介质的耗时,同时便于通过 nfs 直接修改文件系统中的驱动模块与测试程序
- 内核功能裁剪完成后,先通过 tftp 下载验证启动正常,再固化到 EMMC/SD 卡,避免启动失败导致设备变砖
- 外设驱动开发优先采用动态加载的.ko 模块方式,无需重新编译内核,快速迭代调试
- 系统启动失败时,优先通过串口日志定位问题阶段:无串口日志排查 U-Boot 与硬件;U-Boot 启动正常但内核不启动,排查设备树与内核配置;内核启动后挂载根文件系统失败,排查 bootargs 参数、nfs 服务配置与库文件完整性
七、总结
嵌入式 Linux 驱动开发的核心,是建立「硬件平台 - 引导程序 - 内核 - 根文件系统 - 用户程序」的完整链路认知,本文覆盖的环境搭建、系统启动流程、内核编译、根文件系统制作,是所有外设驱动开发的基础。