ARM-驱动-01Linux系统移植

一、整体架构概览

复制代码
ubuntu(开发主机)
  /home/linux/nfs/              ← 通过 NFS 共享到开发板
        |
       NFS(网络文件系统)
        |
ARM 开发板 /mnt/                ← 挂载点

开发板存储介质:
  - 内存(RAM,512MB)          ← 断电丢失,系统在此运行
  - eMMC(8GB,芯片形式SD卡)   ← 产品最终部署介质
  - SD 卡                       ← 开发阶段推荐,可反复擦写

开发板连接方式:
  - 串口(底层调试,内核启动早期唯一调试手段)
  - 有线网络(NFS 文件共享 / TFTP 内核下载)
  - WiFi

三大核心任务:

  1. 移植 Linux 系统到 ARM 平台(烧写 bootloader + kernel + rootfs)
  2. 在 ubuntu 上交叉编译应用程序(在A平台编译,B平台运行)
  3. 将编译好的应用程序通过 NFS 部署到 ARM 上运行

⚠️ 关键原则:必须使用与目标平台匹配的交叉编译器,不匹配则程序无法执行。


二、应用开发 vs 内核开发

对比项 应用程序开发 内核/驱动开发
调试接口 网络(telnet)/ NFS 挂载目录 串口(唯一底层调试手段)
可用时机 操作系统启动完成后 内核启动早期即需介入
编译工具 交叉编译器(arm-linux-gnueabi-gcc) 同上,但需内核源码配合
运行环境 用户空间,依赖 OS 内核空间,直接操作硬件
出错后果 进程崩溃,系统不受影响 整机宕机 / 内核 panic

⚠️ 串口是内核开发的生命线,网络协议(telnet 等)仅在操作系统启动后才可用,无法用于内核早期调试。


三、Linux 系统三大组成部分

Linux 系统 = bootloader + kernel + rootfs,三者缺一不可:

复制代码
存储介质(SD卡 / eMMC)
├── bootloader 分区    ← 裸机程序,引导内核
├── kernel 分区        ← 内核镜像(zImage + dtb)
└── rootfs 分区        ← 根文件系统(所有文件的集合)

重要认知: 烧写到开发板的是一个完整的操作系统镜像(类似 Windows 安装盘),而非仅仅是内核。根文件系统(rootfs)成功挂载才标志着系统启动完成,而不是内核运行结束。

三者关系简述

组件 本质 职责
bootloader(uboot) 裸机程序 初始化硬件,搬移内核,引导启动
kernel(内核) 操作系统核心 管理硬件资源,提供系统服务
rootfs(根文件系统) 文件集合 提供命令、库、配置、用户程序

四、Linux 完整启动流程(核心重点)

4.1 三阶段启动概览

复制代码
系统上电
    ↓
① bootloader(uboot)------ 裸机程序,为内核启动准备环境
    ↓  CPU 控制权移交给内核
② kernel(内核)------ 操作系统核心程序
    ↓  挂载根文件系统
③ rootfs 挂载 + init 进程启动 ------ 进入用户空间

4.2 bootloader(uboot)详解

作用: 裸机程序,是内核运行前的"搬运工"和"环境准备者"。任何具备"为内核准备环境并引导启动"功能的程序都可称为 bootloader,uboot 是其中最常见的实现。

完整初始化顺序(重要!必背):

复制代码
1.  初始化异常向量表
2.  初始化 CPU(工作模式)
3.  初始化栈
4.  初始化时钟
5.  关看门狗
6.  关闭 Cache          ← ⚠️ 数据Cache必须关闭(否则DDR初始化结果不一致)
                              指令Cache 可选
7.  关 MMU              ← ⚠️ 页表未建立,开MMU会导致地址翻译出错
8.  关中断
9.  初始化内存(DDR)
10. 初始化相关设备(串口、网口)
11. 集成相关协议(tftp 等)
12. 搬移内核到内存
13. 向内核传参          ← ⚠️ 传递:根文件系统类型 / init进程路径 / 控制台 / IP
14. 引导内核启动
        ↓
    bootloader 不再控制 CPU,CPU 控制权移交给内核

4.3 kernel(内核)详解

内核管理的六大模块:

模块 说明
文件管理 文件系统读写
进程管理 进程创建、调度
内存管理 虚拟内存、页管理
网络管理 网络协议栈
设备管理 驱动框架
IPC 进程间通信

内核启动最后阶段:

复制代码
挂载根文件系统(rootfs)           ← 系统启动完成的标志
    ↓
启动 init 进程(由内核 init 退化/exec 而来)
    ↓
init(PID=1)→ 其他进程 → shell → 用户应用程序(userapp)

⚠️ init 进程是用户空间的第一个进程(PID=1),所有其他进程都是它的子孙。


4.4 rootfs(根文件系统)详解

作用: 第一个被挂载的文件系统,挂载点为 /,其成功挂载标志着系统启动完成。

复制代码
rootfs/
  ├── 配置文件        (系统启动配置)
  ├── 系统命令        (ls, cp, mount 等)
  ├── 库              (libc 等共享库)
  ├── 用户程序        (应用程序)
  └── 普通文件        (文本、mp3、jpg 等)

五、系统上电物理流程(以 SD 卡启动为例)

复制代码
系统上电
    ↓
芯片 Boot ROM 读取拨码开关,选择启动介质(SD卡)
    ↓
① CPU 拷贝 bootloader【前半部分】到 OCRAM(片上RAM)执行
   (此时 DDR 未初始化,只能用片上 OCRAM)
    ↓
② bootloader 前半部分初始化 DDR 内存
    ↓
③ bootloader 将自身【后半部分】搬移到 DDR 中执行
    ↓
④ bootloader 搬移内核到 DDR 指定地址(0x80800000)
    ↓
⑤ bootloader 设置 PC = 0x80800000,跳转至内核入口
    ↓
⑥ 内核运行,最终挂载 rootfs,启动 init

内核来源(两种情况):

场景 bootloader 需要做的事
内核在 SD 卡中 初始化SD卡控制器,读 kernel 分区写入 DDR
内核在 ubuntu 上(开发阶段) 初始化有线网卡 + 集成tftp协议,下载内核到 DDR

根文件系统来源(两种情况):

场景 挂载方式
rootfs 在 SD 卡中(量产) 内核直接挂载 SD 卡的 rootfs 分区
rootfs 在 ubuntu 上(开发阶段) 内核通过 NFS 挂载 ubuntu 的 rootfs 目录,bootloader 传参需包含 IP

六、内存地址映射与代码类型(重点)

6.1 内存地址映射

复制代码
DDR 物理地址空间:

0x80000000   ← DDR 起始地址
    ...
0x80800000   ← 内核默认加载地址(zImage 解压运行地址)
    ...
0x83000000   ← 设备树(dtb)加载地址
    ...

⚠️ 内核加载地址 0x80800000 由硬件内存布局决定,不可随意更改 ,因内核内部使用绝对跳转指令(ldr pc, =0x80800000),属于地址相关代码。

6.2 两种代码类型

类型 说明 使用场景
地址无关代码 加载地址和链接地址无关,放任意地址都能运行 bootloader 前半部分(在 OCRAM 运行)
地址相关代码 加载地址和链接地址必须保持一致,否则跑飞 bootloader 后半部分、内核(在 DDR 运行)

6.3 两种跳转指令

类型 指令 原理 特点
相对跳转(短跳转) bl 靠 PC 的偏移量跳转 地址无关,适合前半段
绝对跳转(长跳转) ldr pc, =addr 向 PC 赋一个绝对地址 地址相关,适合搬移后执行

在 bootloader 中的体现:

复制代码
bootloader 前半部分(OCRAM 运行)
    → 加载地址(OCRAM)≠ 链接地址(DDR)
    → 必须使用相对跳转 bl,即地址无关代码

bootloader 将自身搬移到 DDR 后
    → 加载地址 = 链接地址
    → 可以使用绝对跳转 ldr pc, =addr

七、网络配置

7.1 ubuntu 有线连接开发板(静态 IP)

复制代码
1. VMware → 设置 → 网络适配器 → 更改为【桥接模式】
2. VMware → 编辑 → 虚拟网络编辑器 → Vmnet0 桥接到【有线网卡】
3. 修改网络配置文件:sudo vim /etc/network/interfaces
4. 重启网络服务
bash 复制代码
# /etc/network/interfaces
auto ens33
iface ens33 inet static
address   192.168.1.3
netmask   255.255.255.0
gateway   192.168.1.1

7.2 ubuntu 无线上网(DHCP)

复制代码
1. VMware → 设置 → 网络适配器 → 更改为【NAT 模式】
2. VMware → 编辑 → 虚拟网络编辑器 → Vmnet0 桥接到【无线网卡】
3. 修改网络配置文件:sudo vim /etc/network/interfaces
4. 重启网络服务
bash 复制代码
# /etc/network/interfaces
auto ens33
iface ens33 inet dhcp

7.3 开发板 IP 配置

bash 复制代码
# 在开发板 Linux 命令行执行(与主机同网段,IP不同)
ifconfig eth0 192.168.1.101 netmask 255.255.255.0

# 验证与主机连通性
ping 192.168.1.3

八、NFS 配置与挂载

8.1 ubuntu 端配置 NFS 服务

bash 复制代码
# 编辑导出配置文件
sudo vim /etc/exports

# 添加以下内容(允许所有客户端读写挂载)
/home/linux/nfs *(rw,sync,no_root_squash)

# 重启 NFS 服务
sudo /etc/init.d/nfs-kernel-server restart

⚠️ no_root_squash 表示客户端 root 用户在服务端也具有 root 权限,开发阶段必须加此选项,否则开发板上操作文件会遇到权限问题。

8.2 开发板端挂载 NFS

bash 复制代码
# 挂载 ubuntu 共享目录到开发板 /mnt
mount -t nfs -o nolock,nfsvers=3 192.168.1.3:/home/linux/nfs /mnt

# 验证挂载(能看到 ubuntu 上的文件即成功)
ls /mnt

# 验证双向同步
touch /mnt/test_file    # 开发板创建文件
# 在 ubuntu 上 ls /home/linux/nfs 能看到 test_file ✓

# 卸载(使用完毕后必须卸载,否则原目录文件会被隐藏)
umount /mnt

⚠️ 必须指定 nfsvers=3,否则可能因版本不兼容导致挂载失败。


九、系统烧写

9.1 存储介质选择原则

介质 适用阶段 原因
SD 卡 开发阶段(推荐) 可反复擦写,避免耗尽 eMMC 寿命
eMMC 产品最终部署 稳定后才进行烧写,速度快

⚠️ 操作系统必须烧写到非易失性存储器(eMMC/SD卡),不能烧写到内存(DDR),因为内存断电后数据丢失。

9.2 烧写流程(MFG 工具 + USB OTG)

复制代码
1. 开发板断电
2. 拨码开关拨至 USB 模式
3. 插入 SD 卡,连接 USB 线到主机
4. 上电,MFG 工具识别到设备
5. 点击"开始"烧写镜像
6. 烧写完成后,拨码开关拨回 SD 卡启动模式
7. 重新上电,系统从 SD 卡启动 ✓

十、交叉编译开发流程

10.1 交叉编译概念

复制代码
裸机开发:  下载(Download)程序到目标板
Linux开发:移植(Porting)/ 交叉编译

交叉编译 = 在 A 平台(x86 Ubuntu)编译,在 B 平台(ARM)运行

10.2 工具链安装

bash 复制代码
# 解压交叉编译工具链(以 arm-linux-gnueabi-gcc 为例)
tar -xvf arm-linux-gnueabihf-gcc-xxx.tar.bz2 -C /usr/local/

# 配置环境变量
vim ~/.bashrc
# 添加:
export PATH=$PATH:/usr/local/arm/bin

# 使配置生效
source ~/.bashrc

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

10.3 完整交叉开发闭环

复制代码
Ubuntu 主机
    │
    ├─ 1. 用交叉编译器编译程序
    │      arm-linux-gnueabihf-gcc hello.c -o arm-app
    │
    ├─ 2. 程序自动出现在 NFS 共享目录
    │      /home/linux/nfs/arm-app
    │
    └─ 3. 开发板通过 NFS 挂载后直接运行
           /mnt/arm-app      ← 直接执行,无需手动拷贝

ARM 开发板
    ├─ 挂载 NFS:mount -t nfs ... /mnt
    ├─ 运行程序:/mnt/arm-app
    └─ 如需离线运行:cp /mnt/arm-app /usr/bin/  再 umount

十一、完整开发流程总结

复制代码
ubuntu 开发主机
    │
    ├── 交叉编译内核(zImage + dtb)→ 放入 TFTP 目录
    ├── 交叉编译应用程序            → 放入 NFS 目录
    ├── 配置 NFS 服务(共享 rootfs)
    └── 配置 TFTP 服务(提供内核下载)
                │
                │ 有线网络
                ▼
ARM 开发板
    ├── uboot 通过 TFTP 下载内核 → bootz 启动
    ├── 内核通过 NFS 挂载 rootfs
    └── 用户通过 NFS 目录开发调试应用程序

十二、关键命令速查

bash 复制代码
# ===== Ubuntu 主机端 =====
# 修改网络配置
sudo vim /etc/network/interfaces

# 重启网络服务
sudo /etc/init.d/networking restart
sudo systemctl restart networking

# 重启 NFS 服务
sudo /etc/init.d/nfs-kernel-server restart

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

# ===== 开发板端 =====
# 设置 IP
ifconfig eth0 192.168.1.101 netmask 255.255.255.0

# 挂载 NFS(完整格式)
mount -t nfs -o nolock,nfsvers=3 192.168.1.3:/home/linux/nfs /mnt

# 卸载 NFS
umount /mnt

# 验证网络
ping 192.168.1.3

十三、常见概念速查

概念 说明
交叉编译 在 x86 ubuntu 上编译出能在 ARM 上运行的程序
NFS 网络文件系统,ubuntu 共享目录给开发板,双向实时同步
TFTP 简单文件传输协议,uboot 阶段通过它下载内核
OCRAM 片上 RAM,CPU 上电后可直接访问,用于加载 bootloader 前半部分
eMMC 芯片形式的SD卡,焊接在板上,容量大(8GB)
dtb 设备树二进制文件,描述硬件信息,内核启动时读取
init 进程 用户空间第一个进程,PID=1,所有进程的祖先
zImage 压缩后的内核镜像,默认加载地址 0x80800000
MFG 工具 NXP 官方烧写工具,通过 USB OTG 烧写镜像到 SD卡/eMMC
bootloader 不特指 uboot,泛指一切"为内核准备环境并引导启动"的程序
地址无关代码 加载地址与链接地址无关,可放任意位置运行
地址相关代码 加载地址必须等于链接地址,否则跑飞
相关推荐
somi72 小时前
ARM-04-驱动-Misc ,Platform ,DTS
arm开发·单片机·嵌入式硬件·自用
fenglllle16 小时前
使用AI能力编译ARM版本的截图软件
arm开发·人工智能
somi72 天前
ARM-驱动-02-Linux 内核开发环境搭建与编译
linux·运维·arm开发
XINVRY-FPGA2 天前
XC7VX690T-2FFG1157I Xilinx AMD Virtex-7 FPGA
arm开发·人工智能·嵌入式硬件·深度学习·fpga开发·硬件工程·fpga
cpp_learners2 天前
Linux ARM架构 使用 linuxdeployqt 打包QT程序
linux·arm开发·qt
森G2 天前
3.1、移植Qt程序到ARM平台----移植Qt程序到ARM平台(扩展)
arm开发·c++·qt
炭烤毛蛋2 天前
rk3588 适配音频解码芯片 es8388
arm开发·音视频·rk3588·es8388
路溪非溪2 天前
Linux中Netlink简介和使用总结
linux·网络·arm开发·驱动开发
Bohemian—Rhapsody2 天前
麒麟v10-arm架构部署rabbitmq
arm开发·架构·rabbitmq