文件系统与存储
1. 概述
1.1 功能简介
RK3576 平台提供丰富的存储接口,包括 eMMC 5.1、TF 卡(SD 3.0)以及 M.2 SSD 扩展支持。C-3576BQ 核心板板载 eMMC(可选 32GB/64GB/128GB),提供可靠的大容量板载存储;M4-R2 底板提供 TF 卡槽(SD 3.0,最大支持 128GB)和 M.2 SSD/算力模块接口,支持 SSD 硬盘存储扩展或算力模组算力扩展。本文档介绍 RK3576 平台的存储设备管理、分区布局、文件操作、文件系统类型选择、M.2 NVMe SSD 使用、系统备份与恢复、存储空间管理以及 eMMC 健康监控等内容。
1.2 应用场景
- 嵌入式系统启动:eMMC 作为系统启动盘,存储内核、根文件系统
- 数据采集存储:M.2 NVMe SSD 用于高速传感器数据记录、视频录制
- 边缘计算缓存:M.2 SSD 作为 AI 推理模型缓存和中间数据存储
- 系统备份恢复:基于 dd / rsync 的完整系统镜像备份与恢复
- 存储空间扩展:TF 卡/U 盘扩展存储、根文件系统在线扩容、SWAP 空间配置
- 介质健康监控:eMMC 寿命监控与预警,防止存储介质失效
1.3 工作原理
RK3576 存储子系统架构:
┌─────────────────────────────────────────────────────────┐
│ 用户空间 │
│ 应用程序 / 文件操作 (fopen, open, read, write) │
├─────────────────────────────────────────────────────────┤
│ 虚拟文件系统 (VFS) │
│ ext4 / F2FS / squashfs / vfat / tmpfs │
├─────────────────────────────────────────────────────────┤
│ 块设备层 │
│ /dev/mmcblk0 (eMMC) /dev/mmcblk1 (TF) │
│ /dev/nvme0n1 (M.2 NVMe SSD) │
├─────────────────────────────────────────────────────────┤
│ 存储控制器驱动 │
│ sdhci@... (eMMC/TF) │
│ pcie@... (PCIe 2.1 NVMe SSD) │
├─────────────────────────────────────────────────────────┤
│ 硬件 │
│ eMMC 5.1 | TF (SD 3.0) | M.2 NVMe SSD │
└─────────────────────────────────────────────────────────┘RK3576 关键存储硬件地址(以下为 RK 平台通用结构示例,具体数值以实际发布的 RK3576 SDK 设备树为准):
| 模块 | 设备树节点 | 寄存器基地址 | 功能 |
|---|---|---|---|
| eMMC/TF 控制器 | sdhci@... | 待确认 | eMMC 5.1 / SD 3.0 控制 |
| PCIe 控制器 | pcie@... | 待确认 | PCIe 2.1 通道(M.2 接口) |
具体寄存器基地址、通道数以实际发布的 RK3576 SDK 设备树为准(待板上确认)。
2. 硬件准备
2.1 硬件清单
| 组件 | 型号/规格 | 数量 | 备注 |
|---|---|---|---|
| 主控板 | C-3576BQ 核心板 + M4-R2 底板 | 1 | 运行 Linux 6.1 系统 |
| M.2 SSD(可选) | M.2 NVMe SSD 或算力模块 | 1 | M.2 接口(底板) |
| TF 卡(可选) | microSD 卡(32GB/128GB) | 1 | SD 3.0,最大 128GB |
| U 盘(可选) | USB 3.0/2.0 U 盘 | 1 | USB 扩展存储 |
| 调试串口线 | USB 转 TTL(3.3V) | 1 | 波特率 1500000 |
| 电源适配器 | 5V/2A DC(Type-C 或 6pin 供电) | 1 | 空载最小 1000mA |
2.2 硬件连接
M4-R2 底板存储连接:
├── eMMC — 板载焊接(C-3576BQ 核心板上,32GB/64GB/128GB 可选)
├── TF 卡槽 — M4-R2 底板(SD 3.0,最大支持 128GB)
├── M.2 接口 — M4-R2 底板(M.2 SSD 或算力模块扩展)
├── USB 口 — M4-R2 底板(U 盘扩展存储)
├── USB 调试口 ──► PC(串口调试)
└── DC 5V 电源 ──► 电源适配器2.3 硬件确认
# 确认 eMMC 信息
cat /sys/block/mmcblk0/device/name
cat /sys/block/mmcblk0/device/cid
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT
# NAME SIZE TYPE MOUNTPOINT
# mmcblk0 29.1G disk
# mmcblk0p1 512M part /boot
# mmcblk0p2 28.5G part /
# 确认 M.2 NVMe SSD(如已安装)
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT
# NAME SIZE TYPE MOUNTPOINT
# nvme0n1 238.5G disk
# 确认 TF 卡(如已插入)
ls /dev/mmcblk1*
# /dev/mmcblk1 /dev/mmcblk1p1
# 确认 USB 存储(如已插入)
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT | grep -E "sd[a-z]"
# 确认 PCIe 设备
lspci
# 01:00.0 Non-Volatile memory controller: Samsung Electronics ... NVMe SSD
# 确认 PCIe 链路状态
lspci -s 01:00.0 -v | grep LnkSta3. 软件准备
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 存储文件系统工具
# 确认支持的文件系统
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
# NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
# mmcblk0 179:0 0 29.1G 0 disk
# mmcblk0p1 179:1 0 512M 0 part /boot
# mmcblk0p2 179:2 0 28.5G 0 part /
# mmcblk1 179:32 0 29.3G 0 disk
# mmcblk1p1 179:33 0 29.2G 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 25.2G 4% /
# mmcblk1
# mmcblk1p1 ext4 11223344-5566-7788-99aa-bbccddeeff00 21.2G 23%
# 查看设备详细信息
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT,ROTA,DISC-GRAN
# NAME SIZE TYPE FSTYPE MOUNTPOINT ROTA DISC-GRAN
# mmcblk0 29.1G disk 0 0B
# mmcblk0p1 512M part vfat /boot 0 0B
# mmcblk0p2 28.5G part ext4 / 0 0B
# nvme0n1 238.5G disk 0 0B磁盘空间使用统计
# 查看文件系统使用情况
df -h
# Filesystem Size Used Avail Use% Mounted on
# /dev/mmcblk0p2 29G 1.2G 27G 4% /
# /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 29G 1.2G 27G 4% /
# 查找大文件
du -sh /* 2>/dev/null | sort -rh | head -10
# 786M /usr
# 312M /var
# 280M /lib
# 120M /opt
# 查看目录空间占用
du -sh /var/log
# 56M /var/log4.2 RK3576 典型分区结构
RK3576 Linux 系统在 eMMC 上的典型分区布局:
eMMC (mmcblk0) 典型分区布局:
┌────────────────────────────────────────────────────────┐
│ 分区 │ 起始地址 │ 大小 │ 文件系统 │ 挂载点 │
├───────────────┼─────────┼───────────┼─────────┼────────┤
│ Loader (可选) │ 0x0000 │ 8MB │ 无 │ 无 │
│ parameter.txt │ - │ - │ 无 │ 无 │
│ boot │ - │ 512MB │ vfat │ /boot │
│ rootfs │ - │ 剩余空间 │ ext4 │ / │
└────────────────────────────────────────────────────────┘
M.2 NVMe SSD 典型分区布局:
┌────────────────────────────────────────────────────────┐
│ 分区 │ 大小 │ 文件系统 │ 挂载点 │
├───────────────┼───────────────┼─────────┼─────────────┤
│ data │ 全部或按需划分 │ ext4 │ /data │
│ workspace │ 按需划分 │ ext4 │ /workspace │
└────────────────────────────────────────────────────────┘查看分区信息
# 使用 fdisk 查看分区
sudo fdisk -l /dev/mmcblk0
# Disk /dev/mmcblk0: 29.1 GiB, 31268536320 bytes, 61071360 sectors
# Units: sectors of 1 * 512 = 512 bytes
# Sector size (logical/physical): 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 61071359 60020736 28.6G 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 管理分区
# 创建新分区(交互模式)
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 标准库文件操作
/* file_ops.c — RK3576 文件操作示例 */
#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>
#include <time.h>
/* 基本文件读写 */
int basic_file_ops(void)
{
FILE *fp;
const char *filename = "/data/test_file.txt";
const char *write_data = "Hello RK3576 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.1.100:/root/
# 在目标板上运行
./file_opsPOSIX 文件操作
/* 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();
}目录操作
/* 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_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>
#include <limits.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.1.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 文件系统
# 创建 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 M.2 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 进行简单读写测试
# 写入测试(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 -y 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 整个设备
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 系统备份与恢复
完整系统镜像备份
# 备份整个 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 增量备份
# 完整备份
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
# 确认 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 存储空间管理
根文件系统在线扩容
# 查看当前分区大小
lsblk
# NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
# mmcblk0 179:0 0 29.1G 0 disk
# mmcblk0p1 179:1 0 512M 0 part /boot
# mmcblk0p2 179:2 0 28.5G 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 7471104 blocks long.
# 验证扩容结果
df -h /
# Filesystem Size Used Avail Use% Mounted on
# /dev/mmcblk0p2 29G 1.2G 27G 4% /SWAP 空间配置
# 创建 swap 文件(在 eMMC 或 M.2 SSD 上)
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: 3.7G 1.0G 2.0G 89M 700M 2.6G
# 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 -- 1200M 0 0 34560 0 0
# username -- 892M 1024M 2048M 8920 10000 200004.8 eMMC 健康监控
读取 eMMC 健康信息
# 读取 eMMC 设备信息
cat /sys/block/mmcblk0/device/name
# EMMC 设备型号(以实际板栽 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 寿命监控脚本
#!/bin/bash
# emmc_health_check.sh — eMMC 健康检查与预警脚本
EMMC_DEVICE="mmcblk0"
THRESHOLD=80 # 寿命阈值(百分比)
echo "========================================="
echo " RK3576 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 存储控制器架构
RK3576 存储控制器层次结构:
RK3576 存储子系统:
┌─────────────────────────────────────────────┐
│ A53/A72 CPU 核心 │
└────────────────────┬────────────────────────┘
│ AXI 总线
┌────────────────────┴────────────────────────┐
│ 系统互联 (NoC) │
└──────┬──────────────────┬───────────────────┘
│ │
┌────┴────┐ ┌─────┴─────┐
│ SDHCI │ │ PCIe │
│ @... │ │ @... │
└────┬────┘ └─────┬─────┘
│ │
┌────┴────┐ ┌─────┴─────┐
│ eMMC │ │ PCIe 2.1 │
│ 5.1 │ │ NVMe SSD │
│ /TF │ └───────────┘
└─────────┘注:上图中 SDHCI 与 PCIe 控制器的基地址因 RK3576 平台而异,具体数值以实际发布的 RK3576 SDK 设备树为准(待板上确认)。
5.2 eMMC 启动加载流程
RK3576 eMMC 启动流程:
上电复位
│
▼
BootROM (片内 ROM)
│ 从 eMMC 启动分区加载 Loader
▼
TPL/SPL (DDR 初始化)
│ 初始化 DDR 内存(LPDDR5)
▼
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 文件系统性能对比
RK3576 常用文件系统对比:
┌──────────┬──────────┬──────────┬──────────┬──────────┐
│ 特性 │ ext4 │ F2FS │ squashfs │ vfat │
├──────────┼──────────┼──────────┼──────────┼──────────┤
│ 日志支持 │ 有 │ 有 │ 无 │ 无 │
│ 压缩 │ 否 │ 透明压缩 │ xz/lz4 │ 否 │
│ 读写 │ 读写 │ 读写 │ 只读 │ 读写 │
│ 启动分区 │ 否 │ 否 │ 否 │ 是 │
│ 嵌入式 │ 通用 │ 闪存优化 │ 固件 │ 兼容 │
│ SSD 优化 │ 好 │ 最好 │ 不适用 │ 一般 │
│ 最大容量 │ 16TB │ 16TB │ 无限制 │ 32GB │
└──────────┴──────────┴──────────┴──────────┴──────────┘
选型建议:
├── 系统 rootfs → ext4(稳定可靠)或 F2FS(闪存优化)
├── 数据分区 → ext4 或 F2FS
├── 固件/只读分区 → squashfs
├── 启动分区 → vfat
└── 临时文件 → tmpfs(内存文件系统)5.4 I/O 调度策略
# 查看当前 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 调度策略
# M.2 NVMe SSD 推荐 none(直接下发,无调度开销)
echo none | sudo tee /sys/block/nvme0n1/queue/scheduler
# eMMC/TF 推荐 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/TF 使用 mq-deadline
ACTION=="add|change", KERNEL=="mmcblk*", ATTR{queue/scheduler}="mq-deadline"
EOF6. 常见问题
6.1 M.2 NVMe SSD 无法识别
现象: lspci 看到 NVMe 设备,但 lsblk 无 nvme0n1
解决方案:
# 检查 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
# 检查 dmesg 是否有错误
dmesg | grep -i nvme
dmesg | grep -i pcie
# 检查设备树中 PCIe 配置
# PCIe 控制器需在设备树中正确配置
ls /sys/bus/pci/devices/6.2 eMMC 读写速度慢
现象: eMMC 读写速度远低于标称值
解决方案:
# 检查当前 eMMC 工作模式
dmesg | grep -i "mmc\|hs200\|hs400"
# mmc0: new HS400 Enhanced strobe ...
# 确认是否运行在高速模式
dmesg | grep "mmc0"
# 应包含 HS400 或 HS200 字样
# 检查 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
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 恢复镜像后系统无法启动
解决方案:
# 检查分区表是否正确
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, rk3576-*.dtb, extlinux/extlinux.conf 或 boot.scr
# 使用串口调试查看启动日志
# 在 U-Boot 命令行检查:
#=> printenv
#=> boot相关文档
- C/C++应用开发入门 — C/C++ 开发基础
- Python应用开发 — Python 开发基础
- GStreamer多媒体开发 — 多媒体开发(视频录制涉及存储)
- SDMMC/eMMC驱动 — eMMC/TF 卡驱动
- PCIe驱动 — PCIe 驱动(M.2 NVMe 基础)
- 根文件系统构建 — 文件系统制作
- Linux_SDK编译 — SDK 编译(系统镜像构建)
