第2课:恢复出厂、掌握 Linux 基础命令并完成首次 GCC 编译

本节路线图

|-----------------|---|------------------|---|------------------|
| 为什么先学"恢复出厂" | → | 恢复出厂与固化到eMMC | → | Linux目录结构要先看 |

|-------------------------------------------------------------------------------------|----------------------------------------|
| | 小猫提醒 这节有分区、烧录或删除类操作,先确认盘符和路径,再按回车。 |

|--------------------------------------------------------------------------------------|-------------------------------------------|
| | 猫头鹰提示 编译前先对齐目标架构和工具链名字,别让主机程序和板卡程序搞混。 |

第 1 课我们把开发环境和整体链路搭起来了,这一课要把"能进系统"和"能在系统上做事"两件事真正落地。对于 Linux 驱动和 Qt 开发来说,开发板一旦因为误操作导致系统损坏、目录混乱、权限错误或者编译链没装对,后面的实验会变得非常低效。所以本节不追求花哨,而是先把恢复能力、命令行能力和编译能力打牢。

本节路线图(参考原理图)

|---------------------------------------------------|---|----------------------------------|---|-----------------------------------------|---|-------------------------------------|
| 阶段 1:救援准备 下载并整理 sdcard_imageemmc_image | → | 阶段 2:SD 卡救援启动 分区、格式化、写入可启动镜像 | → | 阶段 3:eMMC 固化 从 SD 启动后执行烧写脚本并切回 eMMC | → | 阶段 4:编译验收 用 GCC/交叉 GCC 验证开发环境闭环 |

|-------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------|
| | 小猫提醒: 这一课会碰到 make_parted.shdeploy_image.shrm -rf 这类高风险动作。先核对盘符、挂载点和启动模式,再按回车。 |

|--------------------------------------------------------------------------------------|--------------------------------------------------------------------------|
| | **猫头鹰提示:**驱动和 Qt 开发最怕"环境坏了却以为是代码错了"。所以本节的重点不是背命令,而是建立"能恢复、能排查、能编译"的完整闭环。 |

本节目标

  • 学会在系统损坏或实验失败后恢复出厂,并重新固化到 eMMC。
  • 建立对 Linux 目录结构、文件权限和常用文件管理命令的基本认知。
  • 理解什么是交叉编译,为什么嵌入式开发离不开它。
  • 能在 Linux 环境下完成一次基础的 GCC/交叉 GCC 编译验证。

一、为什么先学"恢复出厂"

很多同学会低估这一节的重要性,觉得"恢复系统"只是售后动作,和开发没关系。实际上恰恰相反,在驱动调试、设备树修改、根文件系统裁剪、Qt 库替换这些工作中,只要某一步出错,就可能让系统无法正常启动。这个时候如果没有一套可复原的流程,整个开发就会被卡住。

恢复出厂流程的核心思路很简单:

  • 先把可启动的 SD 卡镜像烧好。
  • 再把 eMMC 镜像拷贝到 SD 卡启动后的系统里。
  • 从 SD 卡启动进入 Linux。
  • 通过串口或 SSH 登录板卡,执行 eMMC 分区和烧写脚本。
  • 最后切回 eMMC 启动,确认系统恢复成功。

这意味着 SD 卡不只是"临时启动盘",更像是一张救援卡。只要它准备好,哪怕后面把板卡系统改坏了,也能较快回到可工作的状态。

二、恢复出厂与固化到 eMMC 的实操逻辑

原始资料里的流程比较偏操作截图,这里把它整理成更容易复现的步骤。

0. 恢复出厂闭环参考图

|------------------------|---|------------------|---|--------------------------|---|-----------------------|
| Ubuntu 主机 准备恢复包与脚本 | → | SD 卡 制作救援启动盘 | → | 开发板 从 SD 启动并进入 Linux | → | eMMC 分区、部署、切回固化启动 |

|-------------------------------------------------------------------------------------|--------------------------------------------------------------|
| | **兔兔建议:**第一次操作时,建议你把"当前盘符""SD 挂载点""拨码开关状态"写在纸上。看起来笨一点,但会非常稳。 |

1. 准备镜像文件

先下载对应板卡型号的恢复镜像压缩包,解压后通常会得到两个目录:

  • sdcard_image
  • emmc_image

可以把整个恢复目录先放到 Ubuntu 用户主目录,例如:

复制代码
cp -r rst_to_factory /home/uisrc/

2. 制作 SD 启动卡

进入 sdcard_image 目录后,先切到 root 权限,再执行分区和镜像部署脚本:

复制代码
su
./make_parted.sh
./deploy_image.sh

这里有两个常见细节:

  • 脚本会要求你输入盘符,常见是 sdb,一定要先确认设备名,避免误格式化主机硬盘。
  • make_parted.sh 负责分区和格式化,deploy_image.sh 负责把启动镜像和根文件系统写进去。

3. 把 eMMC 镜像拷贝到 SD 卡系统里

烧好 SD 卡之后,还要把 eMMC 用到的镜像目录拷贝进去,便于开发板启动后直接本地烧写:

复制代码
cp -r /home/uisrc/rst_to_factory/emmc_image/ /media/uisrc/rootfs/home/uisrc/

如果你的挂载点不是 /media/uisrc/rootfs/,就要根据实际挂载路径调整命令。这里特别能体现 Linux 基础命令的重要性,因为后面你会经常依赖 pwdlscp 来确认路径是否正确。

4. 从 SD 卡启动并烧写 eMMC

把板卡切到 SD 启动模式,上电进入系统后,用串口或 SSH 登录,再执行 eMMC 烧写:

复制代码
su
cd /home/uisrc/emmc_image
./make_parted.sh
./deploy_image.sh

完成后,切换拨码开关回 eMMC 启动模式,再次上电验证。如果系统能从 eMMC 正常启动,说明恢复流程闭环了。

5. 这一步为什么对后续驱动开发很关键

因为驱动实验里最怕的是"我不知道是代码问题,还是系统环境已经坏了"。有了恢复出厂手段后,你就可以把问题切开:

  • 如果恢复后系统正常,说明硬件和基础镜像大概率没问题。
  • 如果恢复后还不正常,就要优先查硬件、启动文件或镜像版本。

这会让排障效率高很多。

三、Linux 目录结构要先看懂什么

驱动开发和 Qt 开发虽然看起来一个偏底层、一个偏界面,但它们最终都跑在 Linux 文件系统之上。目录不清楚,后面找驱动节点、库文件、配置文件都会很痛苦。

先记住几个最常用的目录:

  • /boot:启动相关文件,经常和内核、启动镜像打交道。
  • /dev:设备文件目录,驱动调试时最常去看。
  • /etc:系统配置文件目录。
  • /home:普通用户主目录,平时开发资料通常放这里。
  • /lib:动态库目录,Qt 程序和很多系统程序都依赖它。
  • /media:外部介质挂载点,U 盘、SD 卡、共享目录经常会出现在这里。
  • /proc:进程和内核状态的虚拟文件系统。
  • /sys:设备模型和驱动信息非常重要的虚拟文件系统。
  • /usr:大量应用程序和共享资源所在目录。
  • /var:日志、缓存和运行时数据常放在这里。

对驱动开发来说,最值得形成习惯的是这两个对应关系:

  • 看设备节点时,多去 /dev
  • 看设备枚举、总线和属性时,多去 /sys

后面我们讲字符设备、平台驱动、设备树的时候,这两个目录会反复出现。

目录参考图:遇到问题时先去哪里看

目录 你通常先看什么 驱动 / Qt 开发里常见动作
/dev 设备节点有没有出现 测试程序打开串口、LED、I2C、SPI 节点
/sys 总线、属性、驱动绑定关系 排查设备树、平台驱动、class 信息
/lib / /usr/lib 动态库是否齐全 Qt 程序运行失败时检查依赖库
/boot 内核、启动镜像、启动参数 恢复系统、替换内核、核对启动文件

四、权限模型必须先过关

Linux 和 Windows 很不一样的一点,是它天然面向多用户、多权限场景。很多"命令执行失败"其实不是命令错了,而是你没有权限。

一个典型权限串长这样:

复制代码
-rwxr-xr--

可以把它拆成三组:

  • 拥有者权限:rwx
  • 用户组权限:r-x
  • 其他用户权限:r--

其中:

  • r 表示可读
  • w 表示可写
  • x 表示可执行

1. 用数字表示权限

数字权限最常见:

  • r = 4
  • w = 2
  • x = 1

比如:

  • 755 = rwx r-x r-x
  • 644 = rw- r-- r--
  • 777 = rwx rwx rwx

2. 三个最常用的权限命令

复制代码
chgrp users test
chown root:root test
chmod 755 test

你可以把它们理解成:

  • chgrp:改属组
  • chown:改属主和属组
  • chmod:改读写执行权限

3. 驱动开发里为什么总会碰到权限问题

因为很多设备节点默认只有 root 可访问。如果你的测试程序或 Qt 程序打开 /dev/xxx 失败,除了怀疑驱动逻辑,也要先确认:

  • 节点有没有创建成功
  • 当前用户有没有访问权限
  • 设备节点属主和属组是否合理

所以权限并不是"系统运维知识",而是驱动调试的基础能力。

五、这几个 Linux 命令一定要熟

原始资料里介绍了很多基础命令,这里不求一口气背完,但下面这些是每天都会用到的。

1. ls:看目录和文件

复制代码
ls
ls -a
ls -l
ls -al

最常用的是 ls -al,它能同时看到隐藏文件和详细属性。很多时候你以为"目录里什么都没有",其实只是漏了隐藏文件。

2. cdpwd:搞清楚你现在在哪

复制代码
cd /root/test
cd ./test
cd ..
cd ~
pwd
pwd -P

这里最容易出错的不是命令本身,而是相对路径和绝对路径混用。建议初学阶段尽量多用绝对路径,先减少路径判断错误。

3. mkdirrmdir:创建和删除目录

复制代码
mkdir test
mkdir -p test1/test2/test3
rmdir test
rmdir -p test1/test2/test3

-p 很实用,它能递归创建或递归删除空目录。

4. cprmmv:文件管理三件套

复制代码
cp ~/test /tmp/test2
cp -r source_dir target_dir
rm -i test2
rm -rf old_dir
mv test mvtest
mv mvtest mvtest2

这里我建议你形成两个习惯:

  • 删除前先 ls 确认目标
  • 初学阶段多用 -i 交互确认,减少误删

因为在板卡根文件系统里,一次 rm -rf 用错目录,可能就直接把实验环境删坏了。

六、什么是 GCC 编译,为什么嵌入式里要讲"交叉编译"

对于桌面开发者来说,本地编译是很自然的事情:在自己的电脑上写代码、编译、运行,三个动作都在同一平台完成。

但嵌入式开发不是这样。

我们常见的开发模式是:

  • 在 x86 的 Ubuntu 主机上写代码
  • 编译出 ARM 或 AArch64 目标程序
  • 再把程序放到开发板上运行

这就是交叉编译。

参考原理图:本地编译与交叉编译的分工

|-----------------------------------|---|-----------------------------------------------------------|---|-----------------------------------|
| x86 Ubuntu 主机 写代码、调用 GCC、管理工程 | → | 交叉工具链 arm-linux-gnueabi-gcc aarch64-linux-gnu-gcc | → | ARM / AArch64 开发板 运行目标程序并验证结果 |

|--------------------------------------------------------------------------------------|----------------------------------------------------------------------|
| | 猫头鹰提示: "能编译"不等于"能在板卡上跑"。一定要再用 fileldd、实际运行结果去确认目标架构和依赖是否正确。 |

1. 本地编译和交叉编译的区别

  • 本地编译:编译平台和运行平台相同
  • 交叉编译:编译平台和运行平台不同

比如:

  • 在 Windows 上编出 .exe 给 Windows 跑,是本地编译
  • 在 Ubuntu 上编出 ARM Linux 程序给 MPSoC 板卡跑,是交叉编译

2. 为什么必须交叉编译

嵌入式板卡资源有限,这是最直接的原因。

  • 板卡 CPU、内存、存储空间都比 PC 紧张
  • 本地安装完整编译环境成本高
  • 编译大型项目会很慢
  • 真正量产时,不可能给每块板子都装一套开发工具链

所以最合理的做法,是在性能更强的主机上完成编译,再把结果部署到目标板。

七、交叉编译链到底是什么

很多同学会把编译器简单理解成一个 gcc 命令,但严格来说,真正工作的是一整套工具链。

一个 C 程序从源码到可执行文件,通常会经历:

  1. 预处理
  2. 编译
  3. 汇编
  4. 链接

对应会涉及头文件展开、宏替换、语法检查、汇编生成、目标文件生成和最终链接。也就是说,交叉编译不是"把一个命令改个名字"这么简单,而是一整套面向目标平台的工具集合。

八、怎么理解交叉编译器的命名

资料里提到两个已经安装过的交叉编译器:

  • gcc-arm-linux-gnueabi
  • gcc-aarch64-linux-gnu

你可以先粗略理解它们的差异:

  • arm-linux-gnueabi:偏 32 位 ARM 目标
  • aarch64-linux-gnu:偏 64 位 ARM 目标

名称里通常会体现几件事:

  • 目标架构
  • 运行系统
  • ABI 或工具链约定

现阶段不用死记所有命名规则,但要建立一个意识:编译器名字不一样,往往就意味着目标平台不一样。你后面编 Qt 程序、驱动测试程序时,一旦工具链选错,最终生成的程序就可能根本不能在板卡上运行。

九、第一次 GCC 编译该怎么做

本节不追求复杂项目,先验证"工具链通不通"。

1. 本机 GCC 验证

先确认 Ubuntu 主机本地 GCC 可用:

复制代码
gcc --version

再写一个最简单的 hello.c

复制代码
#include <stdio.h>

int main(void) {
    printf("hello linux\\n");
    return 0;
}

编译并运行:

复制代码
gcc hello.c -o hello
./hello

如果这一步成功,说明本机基础编译环境正常。

2. 交叉 GCC 验证

再确认交叉编译器是否存在:

复制代码
arm-linux-gnueabi-gcc --version
aarch64-linux-gnu-gcc --version

然后用交叉编译器把同一个 hello.c 编成目标板程序:

复制代码
arm-linux-gnueabi-gcc hello.c -o hello_arm
aarch64-linux-gnu-gcc hello.c -o hello_aarch64

这两个生成文件通常不能在当前 x86 Ubuntu 主机直接运行,但可以用 file 命令确认它们的目标架构:

复制代码
file hello
file hello_arm
file hello_aarch64

这一步的意义非常大,因为它相当于把"写代码 -> 编译 -> 得到目标平台程序"的基本通路先打通了。后面不管是写驱动配套的测试程序,还是写 Qt 应用,都离不开这条链路。

十、把这三部分串起来看,才是完整开发思路

这一课看似内容很散,其实正好构成了嵌入式 Linux 开发最常见的三种底层能力:

  • 系统坏了,能恢复
  • 进系统后,能熟练管理文件和路径
  • 写完代码后,能正确编译出目标平台程序

这三件事缺任何一个,后面的驱动和 Qt 实验都会反复卡住。

比如一个很真实的场景:

  • 你修改了某个库或脚本,把系统搞乱了
  • 用恢复流程把板卡救回来
  • cpmvchmod 重新整理部署文件
  • 用交叉 GCC 编译测试程序验证驱动节点是否可用

这才是工程里真正会发生的事情。

常见问题

1. 为什么我能进系统,但还是说不会 Linux 开发

因为"能登录"不等于"能定位问题"。真正开发时,你必须能看路径、改权限、复制文件、判断程序架构、恢复系统,这些都属于基本功。

2. chmod 777 是不是最省事

短期确实省事,但不推荐养成习惯。权限一旦全部放开,后面更难分辨到底是谁能访问、为什么能访问。调试时可以临时使用,正式项目里要更克制。

3. 为什么交叉编译好的程序在 Ubuntu 主机上运行不了

因为它不是为当前主机架构生成的。你在 x86 上编出来的 ARM 程序,应该放到 ARM 板卡上运行。

4. 为什么恢复出厂还要经过 SD 卡

因为很多时候 eMMC 上的系统已经不可依赖了,而 SD 卡更适合作为外部启动和修复介质。

课后练习

  • 自己完整走一遍 SD 卡恢复和 eMMC 固化流程,把关键命令抄成操作清单。
  • ls -alpwdcdmkdir -pcp -rmvrm -i 各做一次练习。
  • 新建一个 hello.c,分别用本机 GCC 和交叉 GCC 编译,并用 file 命令观察产物差异。
  • 试着解释 /dev/sys/etc/home 四个目录分别在后续驱动开发里会扮演什么角色。

下一课预告

下一课我们会继续往开发基本功推进,开始把 Makefile、编译组织方式和调试习惯串起来。等这部分稳定之后,再进入真正的驱动框架和 Qt 工程实践,整条学习曲线会顺得多。

相关推荐
源远流长jerry2 小时前
RDMA Memory Region (MR) 机制详解:地址转换与内存保护
linux·服务器·网络·tcp/ip·架构·mr
c++逐梦人2 小时前
Linux进程信号
linux·服务器
清水白石0082 小时前
协程不是线程:深入理解 Python async/await 运行机制
java·linux·python
fengpan20042 小时前
ubuntu下vscode使用串口
linux·运维·服务器
IMPYLH2 小时前
Linux 的 cut 命令
linux·运维·服务器·数据库
Lhan.zzZ2 小时前
深入浅出 Qt 信号槽连接方式:从 AutoConnection 到 BlockingQueuedConnectionQt
开发语言·c++·qt
草莓熊Lotso3 小时前
MySQL 内置函数指南:日期、字符串、数学函数实战
android·java·linux·运维·数据库·c++·mysql
艾莉丝努力练剑3 小时前
【Linux信号】Linux进程信号(上):信号产生方式和闹钟
linux·运维·服务器·c++·人工智能·ubuntu·云原生
Ronin3053 小时前
【Qt窗口】Qt窗口
开发语言·qt·qt窗口