文件系统与存储
1. 概述
1.1 功能简介
RK3588 提供三种常用存储:eMMC(焊在核心板上的闪存芯片,相当于板载硬盘)、SD 卡和 NVMe SSD。eMMC 5.1 最高支持 256GB 容量,提供可靠的大容量板载存储;SD 3.0 接口用于可移动存储介质;PCIe 4.0 x4 通道支持 NVMe SSD,理论带宽可达 8GB/s,适合高速大容量数据存储场景。本文档依次介绍:存储设备管理、分区布局、文件操作、文件系统类型选择、NVMe SSD 使用、系统备份与恢复、存储空间管理、eMMC 健康监控。
1.2 应用场景
- 嵌入式系统启动:eMMC 作为系统启动盘,存储内核、根文件系统
- 数据采集存储:NVMe SSD 用于高速传感器数据记录、视频录制
- 边缘计算缓存:NVMe SSD 作为 AI 推理模型缓存和中间数据存储
- 系统备份恢复:基于 dd / rsync 的完整系统镜像备份与恢复
- 存储空间扩展:根文件系统在线扩容、SWAP 空间配置
- 介质健康监控:eMMC 寿命监控与预警,防止存储介质失效
1.3 工作原理
从上到下看存储子系统:应用程序的文件操作先经过虚拟文件系统(VFS,内核为各种文件系统统一提供的接口层),再落到块设备(Linux 对磁盘类设备的抽象),由存储控制器驱动驱动硬件:
┌─────────────────────────────────────────────────────────┐
│ 用户空间 │
│ 应用程序 / 文件操作 (fopen, open, read, write) │
├─────────────────────────────────────────────────────────┤
│ 虚拟文件系统 (VFS) │
│ ext4 / F2FS / squashfs / vfat / tmpfs │
├─────────────────────────────────────────────────────────┤
│ 块设备层 │
│ /dev/mmcblk0 (eMMC) /dev/mmcblk1 (SD) │
│ /dev/nvme0n1 (NVMe SSD) │
├─────────────────────────────────────────────────────────┤
│ 存储控制器驱动 │
│ sdhci@fe2e0000 (eMMC/SD) │
│ pcie@fe170000 / pcie@fe180000 (PCIe 4.0 NVMe) │
├─────────────────────────────────────────────────────────┤
│ 硬件 │
│ eMMC 5.1 | SD 3.0 | PCIe 4.0 NVMe SSD │
└─────────────────────────────────────────────────────────┘RK3588 关键存储硬件地址:
| 模块 | 设备树节点 | 寄存器基地址 | 功能 |
|---|---|---|---|
| eMMC/SD 控制器 | sdhci@fe2e0000 | 0xFE2E0000 | eMMC 5.1 / SD 3.0 控制 |
| PCIe 3.0 x4 控制器 | pcie@fe170000 | 0xFE170000 | PCIe 3.0 通道 |
| PCIe 2.0 x1 控制器 | pcie@fe180000 | 0xFE180000 | PCIe 2.0 通道 |
| PCIe 3.0 x2 控制器 | pcie@fe190000 | 0xFE190000 | PCIe 3.0 通道 |
RK3588 的 NVMe SSD 通常挂载在 pcie@fe170000(PCIe 3.0 x4)或 pcie@fe190000(PCIe 3.0 x2)控制器上,具体取决于底板设计;PCIe 实际速率需根据硬件连接确认。
1.4 运行环境
以下结果来自实机(系统 linaro-alip,内核 6.1.141,Debian 12):
| 项 | |
|---|---|
| 根文件系统 | ext4 / 14 GB(/dev/mmcblk0p6) |
| /oem | 124 MB ext4(mmcblk0p7) |
| /userdata | 14.8 GB ext4(mmcblk0p8) |
| swap | 0 KB(未启用) |
| 总内存 | 3.8 GiB |
| zram | 已挂载(zram0) |
| 块设备 IO 调度器 | 默认 mq-deadline / bfq |
上述分区与 eMMC 实际布局一致;如需新增应用分区,可以调整 parameter.txt 后烧录新镜像。
实际跑过的验证命令和输出:
# 实际跑过的验证
$ lsblk -o NAME,SIZE,TYPE,MOUNTPOINTS
NAME SIZE TYPE MOUNTPOINTS
mmcblk0 29.2G disk
├─mmcblk0p1 4M part
├─mmcblk0p2 4M part
├─mmcblk0p3 64M part
├─mmcblk0p4 128M part
├─mmcblk0p5 32M part
├─mmcblk0p6 14G part /
├─mmcblk0p7 128M part /oem
└─mmcblk0p8 14.8G part /userdata
mmcblk0boot0 4M disk
mmcblk0boot1 4M disk
zram0 0B disk
$ df -h /
文件系统 大小 已用 可用 已用% 挂载点
/dev/root 14G 4.0G 9.1G 31% /
$ cat /proc/meminfo | head -3
MemTotal: 3989628 kB
MemFree: 2298920 kB
MemAvailable: 3336172 kB
$ cat /proc/partitions
major minor #blocks name
179 0 30535680 mmcblk0
179 1 4096 mmcblk0p1
...
179 8 15552576 mmcblk0p8从这份输出能把 eMMC 的容量账算清楚:总容量 29.2GB,减去 bootloader/parameter 占用的部分,剩下的正好是 14G(/)+ 128M(/oem)+ 14.8G(/userdata)+ 4M×5 个小分区 + boot0/boot1 区域。根分区已用 4 GB,可用 9.1 GB。
2. 硬件准备
2.1 硬件清单
NVMe SSD 和 SD 卡按需准备,其余是必需项:
| 组件 | 型号/规格 | 数量 | 备注 |
|---|---|---|---|
| 主控板 | C-3588LQ V1.0 核心板 + SHIMETAPI-M5-R2A V1.0 底板 | 1 | |
| NVMe SSD(可选) | M.2 2280 NVMe SSD | 1 | PCIe 3.0/4.0 均可 |
| SD 卡(可选) | microSD 卡(32GB/64GB) | 1 | SD 3.0 |
| 调试串口线 | USB 转 TTL(3.3V) | 1 | 波特率 1500000 |
| 电源适配器 | 12V/2A DC | 1 |
2.2 硬件连接
SHIMETAPI-M5-R2A 底板存储连接:
├── eMMC — 板载焊接(核心板上)
├── SD 卡槽 — SHIMETAPI-M5-R2A 底板
├── M.2 NVMe 接口 — SHIMETAPI-M5-R2A 底板(PCIe 通道)
├── USB 调试口 ──► PC(串口调试)
└── DC 12V 电源 ──► 电源适配器2.3 硬件确认
逐条执行并对照示例输出:能读到 eMMC 信息、能看到 nvme0n1、mmcblk1 和 PCIe 速率,说明对应硬件就位:
# 确认 eMMC 信息
cat /sys/block/mmcblk0/device/name
cat /sys/block/mmcblk0/device/cid
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT
# NAME SIZE TYPE MOUNTPOINT
# mmcblk0 58.3G disk
# mmcblk0p1 512M part /boot
# mmcblk0p2 56.8G part /
# 确认 NVMe SSD(如已安装)
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT
# NAME SIZE TYPE MOUNTPOINT
# nvme0n1 238.5G disk
# 确认 SD 卡(如已插入)
ls /dev/mmcblk1*
# /dev/mmcblk1 /dev/mmcblk1p1
# 确认 PCIe 设备
lspci
# 01:00.0 Non-Volatile memory controller: Samsung Electronics ... NVMe SSD
# 确认 PCIe 速度
lspci -s 01:00.0 -v | grep LnkSta
# LnkSta: Speed 8GT/s (downgraded), Width x4 (upgraded)3. 软件准备
3.1 必要工具
这些工具大部分已经预装,先确认再补装,每个包的用途见注释:
# 基本存储管理工具(通常已预装)
apt list --installed 2>/dev/null | grep -E "util-linux|e2fsprogs|nvme-cli"
# 安装缺少的工具
sudo apt-get update
sudo apt-get install -y \
util-linux \ # lsblk, fdisk, mount, umount
e2fsprogs \ # ext4 文件系统工具 (mkfs.ext4, fsck.ext4, resize2fs)
nvme-cli \ # NVMe SSD 管理工具
smartmontools \ # 硬盘健康监控 (smartctl)
parted \ # 高级分区工具
dosfstools \ # FAT 文件系统工具
f2fs-tools \ # F2FS 文件系统工具
rsync # 增量备份3.2 NVMe 管理工具
# 安装 nvme-cli
sudo apt-get install -y nvme-cli
# 验证安装
nvme --version
# nvme version 1.14 (or newer)
# 查看 NVMe 设备列表
nvme list
# Node SN Model Namespace Usage Format FW Rev
# /dev/nvme0n1 S4EVNX0R801234K Samsung 980 PRO 1 256.06 GB / 256.06 GB 512 B2QGXA7B3.3 存储文件系统工具
用 /proc/filesystems 查内核支持哪些文件系统;F2FS 若以模块形式提供,用 modinfo 能看到模块信息:
# 确认支持的文件系统
cat /proc/filesystems | grep -v nodev
# ext2
# ext3
# ext4
# vfat
# fuseblk
# fuse
# squashfs
# F2FS 支持(需内核配置)
modinfo f2fs
# filename: f2fs.ko
# description: Linux Flash-Friendly File System
# author: Samsung Electronics Co., Ltd.4. 操作步骤
4.1 存储设备管理
第一步是看清现状:lsblk 列出所有块设备和分区,df 看各文件系统的空间,du 逐层找大文件。
查看块设备信息
# 列出所有块设备
lsblk
# NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
# mmcblk0 179:0 0 58.3G 0 disk
# mmcblk0p1 179:1 0 512M 0 part /boot
# mmcblk0p2 179:2 0 56.8G 0 part /
# mmcblk1 179:32 0 14.9G 0 disk
# mmcblk1p1 179:33 0 14.8G 0 part
# nvme0n1 259:0 0 238.5G 0 disk
# 详细块设备信息
lsblk -f
# NAME FSTYPE LABEL UUID FSAVAIL FSUSE% MOUNTPOINT
# mmcblk0
# mmcblk0p1 vfat 1234-ABCD 480M 6% /boot
# mmcblk0p2 ext4 a1b2c3d4-e5f6-7890-abcd-ef1234567890 48.2G 7% /
# mmcblk1
# mmcblk1p1 ext4 11223344-5566-7788-99aa-bbccddeeff00 11.2G 22%
# 查看设备详细信息
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT,ROTA,DISC-GRAN
# NAME SIZE TYPE FSTYPE MOUNTPOINT ROTA DISC-GRAN
# mmcblk0 58.3G disk 0 0B
# mmcblk0p1 512M part vfat /boot 0 0B
# mmcblk0p2 56.8G part ext4 / 0 0B
# nvme0n1 238.5G disk 0 0B磁盘空间使用统计
# 查看文件系统使用情况
df -h
# Filesystem Size Used Avail Use% Mounted on
# /dev/mmcblk0p2 57G 4.2G 51G 8% /
# /dev/mmcblk0p1 511M 31M 480M 6% /boot
# /dev/nvme0n1 235G 42G 193G 18% /data
# 详细磁盘使用分析
df -hT
# Filesystem Type Size Used Avail Use% Mounted on
# /dev/mmcblk0p2 ext4 57G 4.2G 51G 8% /
# 查找大文件
du -sh /* 2>/dev/null | sort -rh | head -10
# 4.2G /usr
# 1.8G /var
# 1.2G /lib
# 856M /opt
# 查看目录空间占用
du -sh /var/log
# 256M /var/log4.2 RK3588 典型分区结构
RK3588 Linux 系统在 eMMC 上的典型分区布局,以及 NVMe SSD 的常见划分方式:
eMMC (mmcblk0) 典型分区布局:
┌────────────────────────────────────────────────────────┐
│ 分区 │ 起始地址 │ 大小 │ 文件系统 │ 挂载点 │
├───────────────┼─────────┼───────────┼─────────┼────────┤
│ Loader (可选) │ 0x0000 │ 8MB │ 无 │ 无 │
│ parameter.txt │ - │ - │ 无 │ 无 │
│ boot │ - │ 512MB │ vfat │ /boot │
│ rootfs │ - │ 剩余空间 │ ext4 │ / │
└────────────────────────────────────────────────────────┘
NVMe SSD 典型分区布局:
┌────────────────────────────────────────────────────────┐
│ 分区 │ 大小 │ 文件系统 │ 挂载点 │
├───────────────┼───────────────┼─────────┼─────────────┤
│ data │ 全部或按需划分 │ ext4 │ /data │
│ workspace │ 按需划分 │ ext4 │ /workspace │
└────────────────────────────────────────────────────────┘查看分区信息
# 使用 fdisk 查看分区
sudo fdisk -l /dev/mmcblk0
# Disk /dev/mmcblk0: 58.3 GiB, 62612029440 bytes, 122289120 sectors
# Units: sectors of 1 * 512 = 512 bytes
# Sector size (logical/physical): 512 bytes / 512 bytes
# I/O size (minimum/optimal): 512 bytes / 512 bytes
# Disklabel type: dos
# Disk identifier: 0x12345678
#
# Device Boot Start End Sectors Size Id Type
# /dev/mmcblk0p1 * 2048 1050623 1048576 512M c W95 FAT32 (LBA)
# /dev/mmcblk0p2 1050624 122289119 121238496 57.8G 83 Linux
# 使用 parted 查看分区
sudo parted /dev/mmcblk0 print
# 查看 NVMe 分区
sudo fdisk -l /dev/nvme0n1
# Disk /dev/nvme0n1: 238.47 GiB, 256060514304 bytes, 500118192 sectors使用 fdisk 管理分区
给新盘"分区 → 格式化 → 挂载"三步走。fdisk 是交互式工具,常用按键见注释;parted 加 -s 可以直接写进脚本。做完后 lsblk 能看到新分区、往挂载点写文件成功,就算完成:
# 创建新分区(交互模式)
sudo fdisk /dev/nvme0n1
# 常用 fdisk 命令:
# n — 创建新分区
# d — 删除分区
# p — 打印分区表
# t — 更改分区类型
# w — 写入分区表并退出
# q — 不保存退出
# 非交互方式创建分区(示例:在 NVMe 上创建 ext4 分区)
sudo parted -s /dev/nvme0n1 mklabel gpt
sudo parted -s /dev/nvme0n1 mkpart primary ext4 0% 100%
# 格式化新分区
sudo mkfs.ext4 -L data /dev/nvme0n1p1
# 挂载
sudo mkdir -p /data
sudo mount /dev/nvme0n1p1 /data4.3 文件操作
C 标准库文件操作
fopen/fwrite/fclose 这套带缓冲的标准库接口最常用。示例演示写入、读取、追加,再用 stat 查文件的大小、权限、类型等属性:
/* file_ops.c — RK3588 文件操作示例 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
/* 基本文件读写 */
int basic_file_ops(void)
{
FILE *fp;
const char *filename = "/data/test_file.txt";
const char *write_data = "Hello RK3588 Storage!\n";
char read_buf[256];
/* 写入文件 */
fp = fopen(filename, "w");
if (fp == NULL) {
perror("fopen write");
return -1;
}
fwrite(write_data, 1, strlen(write_data), fp);
fclose(fp);
printf("写入完成: %s\n", filename);
/* 读取文件 */
fp = fopen(filename, "r");
if (fp == NULL) {
perror("fopen read");
return -1;
}
memset(read_buf, 0, sizeof(read_buf));
while (fgets(read_buf, sizeof(read_buf), fp) != NULL) {
printf("读取: %s", read_buf);
}
fclose(fp);
/* 追加写入 */
fp = fopen(filename, "a");
if (fp == NULL) {
perror("fopen append");
return -1;
}
fprintf(fp, "追加数据: timestamp=%ld\n", (long)time(NULL));
fclose(fp);
return 0;
}
/* 文件信息查询 */
int file_info(const char *path)
{
struct stat st;
if (stat(path, &st) == -1) {
perror("stat");
return -1;
}
printf("文件: %s\n", path);
printf("大小: %ld bytes\n", (long)st.st_size);
printf("权限: %o\n", st.st_mode & 0777);
printf("所有者 UID: %d\n", st.st_uid);
printf("修改时间: %ld\n", (long)st.st_mtime);
if (S_ISREG(st.st_mode))
printf("类型: 普通文件\n");
else if (S_ISDIR(st.st_mode))
printf("类型: 目录\n");
else if (S_ISBLK(st.st_mode))
printf("类型: 块设备\n");
return 0;
}
int main(void)
{
basic_file_ops();
file_info("/data/test_file.txt");
return 0;
}# 编译并运行
aarch64-linux-gnu-gcc -o file_ops file_ops.c
scp file_ops root@192.168.49.100:/root/
# 在目标板上运行
./file_opsPOSIX 文件操作
open/read/write 是更贴近内核的低级接口,适合对性能和时序有要求的场合。fsync 强制把数据刷进物理存储;O_DIRECT 绕过页缓存直接读写,NVMe 高性能场景常用,但不是所有文件系统都支持:
/* posix_file_ops.c — POSIX 低级文件操作 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/mman.h>
/* 使用 open/read/write 的 POSIX 文件操作 */
int posix_file_ops(void)
{
int fd;
const char *filename = "/data/posix_test.bin";
ssize_t bytes_written, bytes_read;
char write_buf[4096];
char read_buf[4096];
/* 填充写入缓冲区 */
memset(write_buf, 0xAA, sizeof(write_buf));
/* 创建并写入文件(带 O_DIRECT 绕过页缓存) */
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1) {
perror("open write");
return -1;
}
bytes_written = write(fd, write_buf, sizeof(write_buf));
if (bytes_written != sizeof(write_buf)) {
perror("write");
close(fd);
return -1;
}
printf("写入 %zd bytes\n", bytes_written);
/* fsync 确保数据写入物理存储 */
fsync(fd);
close(fd);
/* 读取文件 */
fd = open(filename, O_RDONLY);
if (fd == -1) {
perror("open read");
return -1;
}
bytes_read = read(fd, read_buf, sizeof(read_buf));
if (bytes_read == -1) {
perror("read");
close(fd);
return -1;
}
printf("读取 %zd bytes\n", bytes_read);
close(fd);
/* 使用 O_DIRECT 进行直接 I/O(适用于 NVMe SSD 高性能场景) */
void *aligned_buf;
posix_memalign(&aligned_buf, 4096, 4096);
if (aligned_buf == NULL) {
perror("posix_memalign");
return -1;
}
fd = open(filename, O_RDONLY | O_DIRECT);
if (fd == -1) {
/* O_DIRECT 不一定被所有文件系统支持 */
perror("open O_DIRECT");
free(aligned_buf);
/* 继续执行,不退出 */
} else {
bytes_read = read(fd, aligned_buf, 4096);
printf("O_DIRECT 读取 %zd bytes\n", bytes_read);
close(fd);
free(aligned_buf);
}
return 0;
}
int main(void)
{
return posix_file_ops();
}目录操作
目录遍历用 opendir/readdir;示例还实现了 mkdirs,一次创建多级目录:
/* dir_ops.c — 目录操作 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <errno.h>
/* 递归列出目录内容 */
int list_directory(const char *path, int depth)
{
DIR *dir;
struct dirent *entry;
struct stat st;
char fullpath[1024];
dir = opendir(path);
if (dir == NULL) {
perror("opendir");
return -1;
}
while ((entry = readdir(dir)) != NULL) {
/* 跳过 . 和 .. */
if (strcmp(entry->d_name, ".") == 0 ||
strcmp(entry->d_name, "..") == 0)
continue;
snprintf(fullpath, sizeof(fullpath), "%s/%s",
path, entry->d_name);
if (stat(fullpath, &st) == -1) {
perror("stat");
continue;
}
/* 缩进显示层级 */
for (int i = 0; i < depth; i++)
printf(" ");
if (S_ISDIR(st.st_mode)) {
printf("[DIR] %s/\n", entry->d_name);
list_directory(fullpath, depth + 1);
} else {
printf("[FILE] %s (%ld bytes)\n",
entry->d_name, (long)st.st_size);
}
}
closedir(dir);
return 0;
}
/* 创建多级目录 */
int mkdirs(const char *path, mode_t mode)
{
char tmp[1024];
char *p = NULL;
snprintf(tmp, sizeof(tmp), "%s", path);
for (p = tmp + 1; *p; p++) {
if (*p == '/') {
*p = '\0';
if (mkdir(tmp, mode) == -1 && errno != EEXIST)
return -1;
*p = '/';
}
}
if (mkdir(tmp, mode) == -1 && errno != EEXIST)
return -1;
return 0;
}
int main(void)
{
printf("=== /data 目录内容 ===\n");
list_directory("/data", 0);
printf("\n=== 创建多级目录 ===\n");
mkdirs("/data/projects/app/src", 0755);
printf("目录创建完成\n");
return 0;
}文件系统事件监控 (inotify)
inotify 是内核的文件系统事件通知机制:目录里发生创建、删除、修改,内核都会产生事件,程序用 poll 等着读就行。跑起来后,在另一个终端 touch、echo、rm,监控端立刻打印对应事件:
/* inotify_monitor.c — 使用 inotify 监控文件系统事件 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/inotify.h>
#include <poll.h>
#define EVENT_BUF_LEN (10 * (sizeof(struct inotify_event) + NAME_MAX + 1))
int watch_directory(const char *path)
{
int fd, wd;
char buf[EVENT_BUF_LEN];
ssize_t len, i;
/* 创建 inotify 实例 */
fd = inotify_init();
if (fd == -1) {
perror("inotify_init");
return -1;
}
/* 添加监控目录 */
wd = inotify_add_watch(fd, path,
IN_CREATE | IN_DELETE | IN_MODIFY |
IN_MOVED_FROM | IN_MOVED_TO);
if (wd == -1) {
perror("inotify_add_watch");
close(fd);
return -1;
}
printf("正在监控目录: %s (Ctrl+C 退出)\n", path);
/* 事件读取循环 */
while (1) {
struct pollfd pfd = { .fd = fd, .events = POLLIN };
int ret = poll(&pfd, 1, 5000); /* 5 秒超时 */
if (ret == -1) {
perror("poll");
break;
}
if (ret == 0) {
/* 超时,继续等待 */
continue;
}
len = read(fd, buf, sizeof(buf));
if (len == -1) {
perror("read");
break;
}
/* 遍历事件 */
for (i = 0; i < len; ) {
struct inotify_event *event =
(struct inotify_event *)&buf[i];
if (event->len > 0) {
if (event->mask & IN_CREATE)
printf("[创建] %s\n", event->name);
if (event->mask & IN_DELETE)
printf("[删除] %s\n", event->name);
if (event->mask & IN_MODIFY)
printf("[修改] %s\n", event->name);
if (event->mask & IN_MOVED_FROM)
printf("[移出] %s\n", event->name);
if (event->mask & IN_MOVED_TO)
printf("[移入] %s\n", event->name);
}
i += sizeof(struct inotify_event) + event->len;
}
}
inotify_rm_watch(fd, wd);
close(fd);
return 0;
}
int main(int argc, char *argv[])
{
const char *watch_path = (argc > 1) ? argv[1] : "/data";
return watch_directory(watch_path);
}# 编译运行
aarch64-linux-gnu-gcc -o inotify_monitor inotify_monitor.c
scp inotify_monitor root@192.168.49.100:/root/
# 在目标板上监控 /data 目录
./inotify_monitor /data
# 另一个终端测试
touch /data/test_file # 输出: [创建] test_file
echo "hello" > /data/test_file # 输出: [修改] test_file
rm /data/test_file # 输出: [删除] test_file4.4 文件系统类型
不同分区适合不同的文件系统:ext4 通用、F2FS 为闪存优化、squashfs 只读压缩、vfat 兼容性最好。先逐个看创建和挂载方法,选型对比见 5.3 节。
ext4 文件系统
ext4 是 Linux 最常用的日志文件系统(日志:先把要做的改动记下来再落盘,异常断电后能较快恢复一致性):
# 创建 ext4 文件系统
sudo mkfs.ext4 -L "data" -m 1 -O ^has_journal /dev/nvme0n1p1
# -L "data" — 卷标
# -m 1 — 预留空间百分比(默认5%,降低以增加可用空间)
# -O ^has_journal — 禁用日志(可选,减少写入量延长 SSD 寿命)
# 挂载
sudo mount -o noatime,nodiratime /dev/nvme0n1p1 /data
# 查看 ext4 信息
tune2fs -l /dev/nvme0n1p1 | head -20
# 检查文件系统
sudo e2fsck -f /dev/nvme0n1p1
# 调整文件系统参数
sudo tune2fs -m 1 /dev/nvme0n1p1 # 预留空间 1%
sudo tune2fs -o journal_data_writeback /dev/nvme0n1p1 # 日志模式F2FS 文件系统
F2FS(Flash-Friendly File System)是专为闪存设备设计的文件系统,在 eMMC 和 NVMe SSD 上有较好的性能表现:
# 创建 F2FS 文件系统
sudo mkfs.f2fs -f -l "data" /dev/nvme0n1p1
# 挂载
sudo mount -t f2fs /dev/nvme0n1p1 /data
# 查看 F2FS 信息
sudo dump.f2fs /dev/nvme0n1p1 | head -30
# F2FS 挂载选项
# noatime — 不更新访问时间
# compress — 启用透明压缩
# gc_merge — 合并垃圾回收线程
# discard — 启用 TRIM/UNMAP(推荐 SSD 使用)
sudo mount -t f2fs -o noatime,discard /dev/nvme0n1p1 /datasquashfs 文件系统
squashfs 是只读压缩文件系统,适合固件分区、只读根文件系统这类不许改动的内容:
# 创建 squashfs 镜像
sudo mksquashfs /path/to/rootfs rootfs.squashfs \
-comp xz -b 1M -Xbcj arm -no-xattrs
# -comp xz — 压缩算法(xz 压缩率高,lz4 速度快)
# -b 1M — 块大小
# -Xbcj arm — ARM 架构过滤优化
# 挂载 squashfs
sudo mkdir -p /mnt/squashfs
sudo mount -t squashfs -o ro,loop rootfs.squashfs /mnt/squashfs
# 查看 squashfs 信息
sudo unsquashfs -s rootfs.squashfsvfat 文件系统
vfat(FAT32)主要用于启动分区,各类系统都认,兼容性最好:
# 创建 FAT32 文件系统
sudo mkfs.vfat -F 32 -n "BOOT" /dev/mmcblk0p1
# 挂载
sudo mount -o umask=0022 /dev/mmcblk0p1 /boot
# 检查
sudo fsck.vfat -a /dev/mmcblk0p14.5 NVMe SSD 管理
NVMe 设备信息查看
# 查看 NVMe 控制器信息
nvme id-ctrl /dev/nvme0
# VID : 0x144d (Samsung)
# SSVID : 0x144d
# SN : S4EVNX0R801234K
# MN : Samsung 980 PRO 256GB
# FR : B2QGXA7B
# ...
# 查看命名空间信息
nvme id-ns /dev/nvme0n1 -H
# 查看 NVMe 命令集
nvme id-ns /dev/nvme0n1 --vendor-specific
# 查看支持的命令
nvme supported-log /dev/nvme0
# 查看 NVMe 亮度日志
nvme smart-log /dev/nvme0
# temperature : 34 C
# available_spare : 100%
# available_spare_threshold : 10%
# percentage_used : 0%
# data_units_read : 12345
# data_units_written : 67890
# ...NVMe 性能测试
先用 dd 快速估测:加 oflag=direct / iflag=direct 绕过页缓存,测出来的数字才有参考价值;需要严谨数据再上 fio:
# 使用 dd 进行简单读写测试
# 写入测试(1GB 数据)
dd if=/dev/zero of=/data/test_write bs=1M count=1024 oflag=direct
# 1073741824 bytes (1.1 GB, 1.0 GiB) copied, 1.23456 s, 870 MB/s
# 读取测试
dd if=/data/test_write of=/dev/null bs=1M count=1024 iflag=direct
# 1073741824 bytes (1.1 GB, 1.0 GiB) copied, 0.98765 s, 1.1 GB/s
# 清理测试文件
rm /data/test_write
# 使用 fio 进行专业性能测试(需安装)
sudo apt-get install -fio
# 顺序写入测试
fio --name=seq_write --rw=write --bs=1M --size=1G --numjobs=1 \
--runtime=30 --time_based --direct=1 --filename=/data/fio_test
# 随机读写测试(4K 块)
fio --name=rand_rw --rw=randrw --bs=4k --size=256M --numjobs=4 \
--runtime=30 --time_based --direct=1 --filename=/data/fio_test
# 清理
rm /data/fio_testNVMe TRIM/UNMAP
TRIM 用来告诉 SSD 哪些数据块已删除,SSD 回收之后才能保持写入速度。可以手动 fstrim,也可以放进 crontab 定期执行,或在 /etc/fstab 里加 discard 让内核自动处理:
# 手动 TRIM 整个设备
sudo fstrim -v /data
# /data: 198.5 GiB (213070643200 bytes) trimmed
# 定期自动 TRIM — 添加到 crontab
# 编辑 crontab
crontab -e
# 添加(每周日凌晨 3 点执行 TRIM):
# 0 3 * * 0 /sbin/fstrim -v /data
# 在 /etc/fstab 中启用 discard 选项(推荐用于 SSD)
# /dev/nvme0n1p1 /data ext4 defaults,noatime,discard 0 24.6 系统备份与恢复
完整系统镜像备份
dd 把整块 eMMC 按字节复制成镜像文件,最彻底,但目标位置要有足够空间;镜像还可以再用 gzip 或 xz 压缩:
# 备份整个 eMMC 为镜像文件(需足够存储空间)
# 方法一:使用 dd
sudo dd if=/dev/mmcblk0 of=/data/emmc_backup.img bs=4M status=progress
# 记录输出的块数,用于恢复
# 方法二:使用 dd 备份分区
sudo dd if=/dev/mmcblk0p2 of=/data/rootfs_backup.img bs=4M status=progress
# 压缩备份(减少存储空间)
sudo dd if=/dev/mmcblk0 bs=4M | gzip > /data/emmc_backup.img.gz
# 恢复时:
# gunzip -c /data/emmc_backup.img.gz | sudo dd of=/dev/mmcblk0 bs=4M
# 使用 xz 压缩(压缩率更高,但更慢)
sudo dd if=/dev/mmcblk0 bs=4M | xz -T 0 > /data/emmc_backup.img.xzrsync 增量备份
rsync 只复制有变化的部分,配合 --link-dest 生成类似快照的增量备份,比整盘 dd 省时省空间:
# 完整备份
sudo rsync -aAXv --delete \
--exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","/lost+found","/data/*"} \
/ /data/backup/rootfs_full/
# 增量备份(基于上次完整备份)
sudo rsync -aAXv --delete \
--link-dest=/data/backup/rootfs_full \
--exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","/lost+found","/data/*"} \
/ /data/backup/rootfs_$(date +%Y%m%d)
# 从备份恢复
sudo rsync -aAXv /data/backup/rootfs_full/ /mnt/target/dd 命令恢复镜像
恢复是备份的逆过程。注意:目标设备的数据会被完全覆盖,执行前一定用 lsblk 确认目标选对了:
# 从镜像恢复(警告:目标设备数据将被覆盖!)
# 恢复前先确认目标设备
lsblk
# 确认 mmcblk0 是正确的目标
# 恢复完整镜像
sudo dd if=/data/emmc_backup.img of=/dev/mmcblk0 bs=4M status=progress
# 从压缩镜像恢复
gunzip -c /data/emmc_backup.img.gz | sudo dd of=/dev/mmcblk0 bs=4M
# 恢复后同步并检查
sync
sudo e2fsck -f /dev/mmcblk0p2
sudo resize2fs /dev/mmcblk0p2 # 如有必要调整文件系统大小4.7 存储空间管理
根文件系统在线扩容
分区和文件系统是两层结构,扩容也分两步:先扩分区(parted resizepart,或 fdisk 删除重建、起始扇区保持不变),再扩文件系统(resize2fs)。最后 df -h 看到 / 变大即成功:
# 查看当前分区大小
lsblk
# NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
# mmcblk0 179:0 0 58.3G 0 disk
# mmcblk0p1 179:1 0 512M 0 part /boot
# mmcblk0p2 179:2 0 56.8G 0 part /
# 查看分区表(确认分区是否占用全部空间)
sudo fdisk -l /dev/mmcblk0
# 如果分区未使用全部空间,扩容分区
# 方法一:使用 parted
sudo parted /dev/mmcblk0 resizepart 2 100%
# 方法二:使用 fdisk 删除并重建分区(不删除数据)
sudo fdisk /dev/mmcblk0
# d → 删除分区 2
# n → 创建新分区 2,起始扇区与原分区一致,结束扇区使用默认值
# w → 写入
# 扩容文件系统
sudo resize2fs /dev/mmcblk0p2
# resize2fs: On-line resizing required
# The filesystem on /dev/mmcblk0p2 is now 14837760 blocks long.
# 验证扩容结果
df -h /
# Filesystem Size Used Avail Use% Mounted on
# /dev/mmcblk0p2 57G 4.2G 51G 8% /SWAP 空间配置
swap 是磁盘上的"补充内存":物理内存吃紧时,内核把不活跃的页面换出去。下面的步骤是建 2GB swap 文件、启用,并写进 fstab 开机自动生效:
# 创建 swap 文件(在 eMMC 或 NVMe 上)
sudo fallocate -l 2G /swapfile
# 或使用 dd
# sudo dd if=/dev/zero of=/swapfile bs=1M count=2048
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
# 检查 swap 状态
free -h
# total used free shared buff/cache available
# Mem: 7.7G 1.2G 4.3G 125M 2.2G 6.2G
# Swap: 2.0G 0B 2.0G
# 持久化 swap(添加到 /etc/fstab)
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
# 调整 swappiness(0-100,值越低越少使用 swap)
sudo sysctl vm.swappiness=10
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf磁盘配额 (Quota)
配额用来限制每个用户能占用的空间和文件数,需要先在挂载选项里启用:
# 在 /etc/fstab 中启用配额
# /dev/mmcblk0p2 / ext4 defaults,usrquota,grpquota 0 1
# 重新挂载
sudo mount -o remount /
# 初始化配额数据库
sudo quotacheck -cugm /
sudo quotaon /
# 为用户设置磁盘配额
sudo edquota -u username
# 或使用命令直接设置
sudo setquota -u username 1G 2G 0 0 /
# 查看配额使用情况
sudo repquota /
# *** Report for user quotas on device /dev/mmcblk0p2
# Block grace time: 7days; Inode grace time: 7days
# Block limits File limits
# User used soft hard grace used soft hard grace
# root -- 4567M 0 0 34560 0 0
# username -- 892M 1024M 2048M 8920 10000 200004.8 eMMC 健康监控
读取 eMMC 健康信息
eMMC 芯片自己记录了寿命估计,内核把它暴露在 sysfs 里。life_time_est_typ_a/b 读数的含义见注释:
# 读取 eMMC 设备信息
cat /sys/block/mmcblk0/device/name
# RK3588_EMMC
cat /sys/block/mmcblk0/device/cid
# 150100524b3358424501643361013543
cat /sys/block/mmcblk0/device/csd
cat /sys/block/mmcblk0/device/date
# 查看 eMMC 生命周期计数
cat /sys/block/mmcblk0/device/life_time_est_typ_a
# 0x0B # 11% 已用
cat /sys/block/mmcblk0/device/life_time_est_typ_b
# 0x0A # 10% 已用
# life_time_est 值含义:
# 0x01 = 0%-10% 寿命已用
# 0x02 = 10%-20%
# ...
# 0x0B = 100%-110%(已超过额定寿命)
# 0x0C = 不可用eMMC 寿命监控脚本
下面的脚本把上面的检查汇总成一份报告,寿命超过阈值(默认 80%)时打印预警,建议加进 crontab 每天跑一次:
#!/bin/bash
# emmc_health_check.sh — eMMC 健康检查与预警脚本
EMMC_DEVICE="mmcblk0"
THRESHOLD=80 # 寿命阈值(百分比)
echo "========================================="
echo " RK3588 eMMC 健康检查报告"
echo " $(date '+%Y-%m-%d %H:%M:%S')"
echo "========================================="
# 设备基本信息
echo ""
echo "--- 设备信息 ---"
echo "设备名称: $(cat /sys/block/${EMMC_DEVICE}/device/name)"
echo "CID: $(cat /sys/block/${EMMC_DEVICE}/device/cid)"
echo "制造日期: $(cat /sys/block/${EMMC_DEVICE}/device/date)"
# 容量信息
echo ""
echo "--- 存储容量 ---"
lsblk -o NAME,SIZE /dev/${EMMC_DEVICE}
# 寿命信息
echo ""
echo "--- 寿命评估 ---"
TYP_A=$(cat /sys/block/${EMMC_DEVICE}/device/life_time_est_typ_a)
TYP_B=$(cat /sys/block/${EMMC_DEVICE}/device/life_time_est_typ_b)
# 转换为百分比
if [ "$TYP_A" -ge 1 ] && [ "$TYP_A" -le 11 ]; then
LIFE_A=$(( (TYP_A - 1) * 10 + 5 ))
else
LIFE_A="异常"
fi
if [ "$TYP_B" -ge 1 ] && [ "$TYP_B" -le 11 ]; then
LIFE_B=$(( (TYP_B - 1) * 10 + 5 ))
else
LIFE_B="异常"
fi
echo "寿命类型 A: ${LIFE_A}%"
echo "寿命类型 B: ${LIFE_B}%"
# 预警判断
if [ "$LIFE_A" != "异常" ] && [ "$LIFE_A" -ge "$THRESHOLD" ]; then
echo ""
echo "!!! 警告: eMMC 寿命类型 A 已超过 ${THRESHOLD}% 阈值 !!!"
echo " 建议: 备份数据并准备更换存储介质"
fi
if [ "$LIFE_B" != "异常" ] && [ "$LIFE_B" -ge "$THRESHOLD" ]; then
echo ""
echo "!!! 警告: eMMC 寿命类型 B 已超过 ${THRESHOLD}% 阈值 !!!"
echo " 建议: 备份数据并准备更换存储介质"
fi
# 文件系统错误检查
echo ""
echo "--- 文件系统状态 ---"
dmesg | grep -i "mmc\|ext4\|error" | tail -5
echo ""
echo "========================================="
echo " 检查完成"
echo "========================================="# 设置为可执行并运行
chmod +x emmc_health_check.sh
./emmc_health_check.sh
# 添加到定时任务(每天检查一次)
crontab -e
# 0 8 * * * /root/emmc_health_check.sh >> /var/log/emmc_health.log 2>&15. 代码详解
5.1 存储控制器架构
RK3588 存储子系统的层次结构:CPU 经系统互联访问 SDHCI 控制器(eMMC/SD)和 PCIe 控制器(NVMe),两个控制器的寄存器地址和支持的模式如下:
RK3588 存储子系统:
┌─────────────────────────────────────────────┐
│ A55/A76 CPU 核心 │
└────────────────────┬────────────────────────┘
│ AXI 总线
┌────────────────────┴────────────────────────┐
│ 系统互联 (NoC) │
└──────┬──────────────────┬───────────────────┘
│ │
┌────┴────┐ ┌─────┴─────┐
│ SDHCI │ │ PCIe │
│@fe2e0000│ │@fe170000 │
└────┬────┘ └─────┬─────┘
│ │
┌────┴────┐ ┌─────┴─────┐
│eMMC 5.1 │ │ PCIe 3.0 │
│ NAND │ │ NVMe SSD │
└─────────┘ └───────────┘
SDHCI 控制器 (sdhci@fe2e0000):
├── 支持 eMMC 5.1 (HS200, HS400, HS400-ES)
├── 支持 SD 3.0 (UHS-I)
├── 8-bit 数据总线 (eMMC) / 4-bit (SD)
└── 支持 ADMA2 DMA
PCIe 控制器:
├── pcie@fe170000: PCIe 3.0 x4 (8GT/s)
├── pcie@fe180000: PCIe 2.0 x1 (5GT/s)
└── pcie@fe190000: PCIe 3.0 x2 (8GT/s)5.2 eMMC 启动加载流程
从上电到应用程序跑起来,要经过 BootROM、TPL/SPL、U-Boot、内核一连串接力,各分区的分工也在图中列出:
RK3588 eMMC 启动流程:
上电复位
│
▼
BootROM (片内 ROM)
│ 从 eMMC 启动分区加载 Loader
▼
TPL/SPL (DDR 初始化)
│ 初始化 DDR 内存
▼
U-Boot
│ 从 eMMC Linux 分区加载内核和设备树
▼
Linux Kernel
│ 挂载 rootfs (ext4/F2FS)
▼
用户空间
│ systemd / init
▼
应用程序运行
eMMC 分区作用:
├── Loader 分区: TPL/SPL 引导程序
├── boot 分区: 内核 Image + DTB + boot.scr
└── rootfs 分区: 根文件系统 (ext4/F2FS)5.3 文件系统性能对比
四种常用文件系统的特性对比和选型建议:
RK3588 常用文件系统对比:
┌──────────┬──────────┬──────────┬──────────┬──────────┐
│ 特性 │ ext4 │ F2FS │ squashfs │ vfat │
├──────────┼──────────┼──────────┼──────────┼──────────┤
│ 日志支持 │ 有 │ 有 │ 无 │ 无 │
│ 压缩 │ 否 │ 透明压缩 │ xz/lz4 │ 否 │
│ 读写 │ 读写 │ 读写 │ 只读 │ 读写 │
│ 启动分区 │ 否 │ 否 │ 否 │ 是 │
│ 嵌入式 │ 通用 │ 闪存优化 │ 固件 │ 兼容 │
│ SSD 优化 │ 好 │ 最好 │ 不适用 │ 一般 │
│ 最大容量 │ 16TB │ 16TB │ 无限制 │ 32GB │
└──────────┴──────────┴──────────┴──────────┴──────────┘
选型建议:
├── 系统 rootfs → ext4(稳定可靠)或 F2FS(闪存优化)
├── 数据分区 → ext4 或 F2FS
├── 固件/只读分区 → squashfs
├── 启动分区 → vfat
└── 临时文件 → tmpfs(内存文件系统)5.4 I/O 调度策略
I/O 调度器决定块设备的读写请求按什么顺序下发。NVMe SSD 自身处理请求的能力强,一般设成 none(不调度、直接下发);eMMC/SD 推荐 mq-deadline。要长期生效就写成 udev 规则:
# 查看当前 I/O 调度策略
cat /sys/block/mmcblk0/queue/scheduler
# [none] mq-deadline kyber bfq
cat /sys/block/nvme0n1/queue/scheduler
# [none] mq-deadline kyber bfq
# 设置 I/O 调度策略
# NVMe SSD 推荐 none(直接下发,无调度开销)
echo none | sudo tee /sys/block/nvme0n1/queue/scheduler
# eMMC/SD 推荐 mq-deadline
echo mq-deadline | sudo tee /sys/block/mmcblk0/queue/scheduler
# 持久化(创建 udev 规则)
cat > /etc/udev/rules.d/60-io-scheduler.rules << 'EOF'
# NVMe SSD 使用 none 调度
ACTION=="add|change", KERNEL=="nvme*", ATTR{queue/scheduler}="none"
# eMMC/SD 使用 mq-deadline
ACTION=="add|change", KERNEL=="mmcblk*", ATTR{queue/scheduler}="mq-deadline"
EOF6. 常见问题
6.1 NVMe SSD 无法识别
现象:lspci 能看到 NVMe 设备,但 lsblk 里没有 nvme0n1。
先看 nvme 驱动有没有加载,再查 PCIe 链路状态和 dmesg 报错,最后核对设备树的 PCIe 配置:
# 检查 NVMe 驱动是否加载
lsmod | grep nvme
# nvme 45056 0
# nvme_core 131072 1 nvme
# 如果未加载,手动加载
sudo modprobe nvme
sudo modprobe nvme_core
# 检查 PCIe 链路状态
lspci -s 01:00.0 -v | grep LnkSta
# LnkSta: Speed 8GT/s, Width x4
# 检查 dmesg 是否有错误
dmesg | grep -i nvme
dmesg | grep -i pcie
# 检查设备树中 PCIe 配置
# PCIe 控制器需在设备树中正确配置
ls /sys/bus/pci/devices/6.2 eMMC 读写速度慢
现象:速度远低于标称值。
先从 dmesg 确认 eMMC 是否工作在 HS400 模式,再查 I/O 调度器和挂载选项;iotop 按进程看 I/O、iostat 按设备统计 I/O,用来确认是不是有后台进程在抢带宽:
# 检查当前 eMMC 工作模式
cat /sys/block/mmcblk0/device/iospeed
# 或
dmesg | grep -i "mmc\|hs200\|hs400"
# mmc0: new HS400 Enhanced strobe ...
# 确认是否运行在 HS400 模式
dmesg | grep "mmc0"
# 应包含 HS400 或 HS400-ES 字样
# 检查 I/O 调度策略
cat /sys/block/mmcblk0/queue/scheduler
# 优化文件系统挂载选项
# 重新挂载 ext4,添加 noatime,nodiratime
sudo mount -o remount,noatime,nodiratime /
# 检查是否有后台写入进程
iotop -oP
# 或
iostat -x 1 56.3 扩容后 resize2fs 报错
现象:执行 resize2fs 时提示 "The filesystem is already N blocks long"。
多半是分区还没扩到磁盘末尾。先核对分区表和文件系统的块数,再补上扩分区这一步:
# 检查分区实际大小
sudo fdisk -l /dev/mmcblk0p2
# 检查文件系统大小
sudo tune2fs -l /dev/mmcblk0p2 | grep "Block count"
# 如果分区已扩容但文件系统未更新:
sudo resize2fs /dev/mmcblk0p2
# 如果 resize2fs 报错 "Nothing to do",检查分区表
sudo parted /dev/mmcblk0 print
# 确认分区 2 的结束扇区已扩展到磁盘末尾
# 使用 parted 扩展分区(如果尚未扩展)
sudo parted /dev/mmcblk0 resizepart 2 100%
sudo resize2fs /dev/mmcblk0p26.4 NVMe SSD TRIM 不生效
现象:fstrim 执行后可用空间没有增加。
先确认挂载选项里带 discard,再手动 fstrim,并确认设备本身支持 TRIM:
# 确认文件系统是否支持 discard
mount | grep /data
# /dev/nvme0n1p1 on /data type ext4 (rw,relatime,discard)
# 如果没有 discard 选项,重新挂载
sudo mount -o remount,discard /data
# 或在 /etc/fstab 中添加 discard 选项
# /dev/nvme0n1p1 /data ext4 defaults,noatime,discard 0 2
# 手动 TRIM
sudo fstrim -v /data
# 检查 NVMe TRIM 支持
nvme id-ctrl /dev/nvme0 | grep -i trim
# 或
nvme supported-log /dev/nvme06.5 系统镜像恢复后无法启动
现象:dd 恢复镜像后系统起不来。
按"分区表 → 文件系统完整性 → boot 分区 → 串口启动日志"的顺序排查。如果镜像来自别的板子,还要考虑设备树和 U-Boot 是否匹配:
# 检查分区表是否正确
sudo fdisk -l /dev/mmcblk0
# 确认分区起始扇区和大小与原系统一致
# 检查 rootfs 分区文件系统完整性
sudo e2fsck -f /dev/mmcblk0p2
# 检查 boot 分区
sudo fsck.vfat -a /dev/mmcblk0p1
# 如果是不同板子的镜像,可能需要:
# 1. 更新设备树(DTB)
# 2. 更新 U-Boot
# 3. 检查内核配置是否匹配
# 检查 /boot 目录下的文件
ls -la /boot/
# 应包含: Image, rk3588-*.dtb, extlinux/extlinux.conf 或 boot.scr
# 使用串口调试查看启动日志
# 在 U-Boot 命令行检查:
#=> printenv
#=> boot相关文档
- 01_C-C++应用开发入门 — C/C++ 开发基础
- ch02 Python 应用开发 — Python 开发基础
- ch04 GStreamer 多媒体开发 — 多媒体开发(视频录制涉及存储)
- peripheral-drivers/ch16 PCIe 驱动 — PCIe 驱动(NVMe 基础)
- 05_系统启动优化 — 启动优化(涉及存储加载)
- system-development/ch01 Linux SDK 编译 — SDK 编译(系统镜像构建)
