RK3588 内存与存储优化指南
适用平台: RK3588 Linux 6.1 | Debian 12 / Ubuntu 22.04
硬件: C-3588LQ V1.0 核心板 + SHIMETAPI-M5-R2A V1.0 底板
文档版本: v1.0 | 2026-08-19
1. 概述
RK3588 SoC 搭载 LPDDR4/LPDDR4X 双通道 32 位内存控制器,最高支持 3200MHz,理论峰值带宽 25.6GB/s。存储方面提供 eMMC 5.1(sdhci@fe2e0000)、SD 3.0,以及可直连 NVMe SSD 的 PCIe 3.0 x4 通道。平台还集成了 DMA4 高速 DMA 控制器(DMA 指直接内存访问,外设和内存之间搬数据可以不经过 CPU)和 ARM SMMUv3 IOMMU(为设备做地址翻译和隔离的单元)。
这篇指南会带你过一遍:LPDDR4/4X 调优、内存带宽测试、eMMC 性能优化、NVMe SSD 配置、SD 卡优化、文件系统调优(ext4/f2fs/btrfs)、zram/zswap 内存压缩、DMA 缓存与 IOMMU、内存碎片整理、overcommit 调优、huge pages 以及内存泄漏检测。
1.1 应用场景
- 高性能计算:LPDDR4X 双通道高带宽支撑多路视频并发处理和 AI 推理
- 高速数据采集:NVMe SSD 作为传感器数据、摄像头视频流的高速写入介质
- 内存受限优化:通过 zram/zswap 压缩回收可用内存,扩展有效容量
- DMA 批量传输:DMA4 控制器在内存与外设间搬移大量数据,降低 CPU 占用
- IOMMU 安全隔离:为 PCIe 设备启用地址翻译与访问隔离
1.2 硬件资源总览
| 模块 | 设备树节点 | 寄存器基地址 | 功能描述 |
|---|---|---|---|
| 内存控制器 | — | bootloader 初始化 | LPDDR4/LPDDR4X 双通道 32 位,最高 3200MHz |
| eMMC 控制器 | sdhci@fe2e0000 | 0xFE2E0000 | eMMC 5.1 (HS400) |
| SD 卡控制器 | sdmmc@fe2b0000 | 0xFE2B0000 | SD 3.0 |
| PCIe 3.0 x4 控制器 | pcie@fe170000 | 0xFE170000 | NVMe 主通道 |
| DMA 控制器 | — | DMA4 | 高速 DMA 数据搬移 |
| IOMMU | smmu@fd000000 | 0xFD000000 | ARM SMMUv3 地址翻译与隔离 |
2. 硬件准备
2.1 硬件清单
| 组件 | 型号/规格 | 备注 |
|---|---|---|
| 主控板 | C-3588LQ V1.0 核心板 + SHIMETAPI-M5-R2A V1.0 底板 | LPDDR4X 8/16/32GB |
| NVMe SSD(可选) | M.2 2280 NVMe(PCIe 3.0/4.0) | 接入底板 M.2 Key-M 插槽 |
| SD 卡(可选) | microSD 32/64GB A2/V30 UHS-I | SD 3.0 |
| 调试串口线 | USB 转 TTL 3.3V | 波特率 1500000 |
| 电源适配器 | 12V/2A DC 或 Type-C 5V/4A |
2.2 硬件确认
先确认内存容量、DDR 频率、存储设备都在预期状态,注释里是 16GB 配置的示例输出:
# 内存信息
cat /proc/meminfo | head -5
# MemTotal: 16119744 kB (16GB 配置示例)
# DDR 频率(需 root)
dmesg | grep -i "lpddr\|dram\|memory"
# eMMC 设备
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT
# NAME SIZE TYPE MOUNTPOINT
# mmcblk0 58.3G disk
# NVMe SSD
sudo nvme list
sudo lspci -s $(lspci | grep -i nvme | awk '{print $1}') -v | grep LnkSta
# LnkSta: Speed 8GT/s (PCIe 3.0), Width x4
# SD 卡
ls /dev/mmcblk1*2.3 设备树 Overlay 配置
如果需要通过设备树 overlay 启用 NVMe,启动配置里加上对应的 FDTOVERLAYS 行,参考下面的示例:
LABEL rk3588-linux
KERNEL /boot/vmlinuz-6.1.0-1011-rockchip
FDT /boot/dtbs/6.1.0-1011-rockchip/rk3588-xxx.dtb
FDTOVERLAYS /boot/dtbs/6.1.0-1011-rockchip/overlays/rk3588-nvme.dtbo
APPEND root=/dev/mmcblk0p2 rootfstype=ext4 rw console=ttyS2,1500000n83. 软件准备
3.1 必要工具
把文件系统工具、存储工具和测试工具一次装齐(fio 测磁盘、stress-ng/sysbench 压测、memtester 测内存稳定性):
sudo apt-get update
sudo apt-get install -y \
util-linux e2fsprogs f2fs-tools btrfs-progs \
nvme-cli mmc-utils sysstat numactl dmidecode \
stress-ng sysbench fio hdparm memtester3.2 内核配置确认
确认内核版本,并检查 zram/zswap、IOMMU 等模块是否已就绪:
uname -r
# 期望: 6.1.x-xxx-rk3588
lsmod | grep -E "mmc_block|nvme|zram|zswap"
zgrep CONFIG_ZSWAP /proc/config.gz
zgrep CONFIG_IOMMU /proc/config.gz
grep -i huge /proc/meminfo4. 操作步骤
4.1 LPDDR4/4X 内存调优
4.1.1 查看内存频率与时序
DDR 内存也有自己的调频接口(devfreq 的 dmc 节点),先看当前状态。注意本机用的是 RK 私有的 dmc_ondemand 调频器,不是通用的 simple_ondemand:
cat /sys/class/devfreq/dmc/cur_freq
cat /sys/class/devfreq/dmc/available_frequencies
cat /sys/class/devfreq/dmc/governor
# 输出:
# cur_freq = 528000000 (空闲时降到 528MHz)
# available = 528000000 1068000000 1560000000 2112000000(4 档,最高 2.112GHz)
# governor = dmc_ondemand (RK 私有调频器,不是 simple_ondemand)4.1.2 调整 DDR 频率
跑带宽敏感的任务(视频、AI 推理)时,可以把 DDR 锁在最高频率,避免它在负载变化时反复升降频:
# 锁定 performance 模式
echo performance | sudo tee /sys/class/devfreq/dmc/governor
# 锁定最高频率
MAX_FREQ=$(cat /sys/class/devfreq/dmc/available_frequencies | awk '{print $NF}')
echo $MAX_FREQ | sudo tee /sys/class/devfreq/dmc/min_freq
echo $MAX_FREQ | sudo tee /sys/class/devfreq/dmc/max_freq
# 验证
cat /sys/class/devfreq/dmc/cur_freq最后再读一次 cur_freq,数值等于最高档(2112000000)即锁定成功。
4.2 内存带宽基准测试
4.2.1 STREAM Benchmark
STREAM 是业界常用的内存带宽基准测试,跑一遍就知道内存子系统的实际吞吐:
cd /var/tmp
# ⚠ 本机访问 www.cs.virginia.edu 超时,用 GitHub 镜像:
curl -sL https://raw.githubusercontent.com/jeffhammond/STREAM/master/stream.c -o stream.c
gcc -O2 -fopenmp -DSTREAM_ARRAY_SIZE=40000000 -DNTIMES=10 stream.c -o stream -lm
OMP_NUM_THREADS=8 ./stream下面的输出取自本机(8 线程,4GB LPDDR4X @2.112GHz):
Function Best Rate MB/s Avg time Min time Max time
Copy: 23029.8 0.028086 0.027790 0.028326
Scale: 22970.3 0.028073 0.027862 0.028209
Add: 21956.4 0.043966 0.043723 0.044162
Triad: 21409.1 0.045124 0.044841 0.045364Triad ≈ 21.4 GB/s(理论峰值 2.112GHz×2ch×32bit DDR ≈ 33.8 GB/s,实际达 63%,属于正常水平)。 单线程 memcpy 参考:11.59 GB/s(128MB best-of-5)。
4.2.2 内存稳定性测试
调完频率最好验证一下稳定性。memtester 会对指定大小的内存反复做各种模式的读写校验,出厂未装,apt 可以装:
# memtester 出厂未装(apt 可装)
sudo apt install -y memtester
sudo memtester 128M 1
# (128M 1 轮): 全部 ok,无错误
free -h
numactl --hardware 2>&1 # 本机 NUMA 单节点(UMA,ARM64 常见)结果全部 ok、无错误即可认为稳定。
4.3 eMMC 5.1 性能优化
4.3.1 性能测试
用 hdparm 和 fio(专业 I/O 测试工具)分别测缓存读和顺序/随机读写。注意测试用文件模式,别直接对裸分区写,有写坏风险:
# 缓存读取速度( hdparm 出厂未装,apt 安装后可用;用 dd 替代同样可行)
sudo apt install -y hdparm
sudo hdparm -Tt /dev/mmcblk0
# 顺序读写测试(⚠ 建议用文件模式,直接打裸分区有写坏风险;
# 根分区是 mmcblk0p6,文档原文的 p2 是别的分区布局)
fio --name=seq --filename=/var/tmp/fio.tmp --rw=read --bs=128k \
--size=256M --direct=1 --runtime=10 --group_reporting
# 4K 随机读写
fio --name=rr --filename=/var/tmp/fio.tmp --rw=randread --bs=4k \
--size=64M --direct=1 --runtime=10 --group_reporting本机结果(eMMC,文件模式 direct=1):
| 项目 | 结果 |
|---|---|
| dd 顺序写 256MB (oflag=direct) | 103 MB/s |
| dd 顺序读 256MB (iflag=direct) | 227 MB/s |
| fio 顺序读 128k | 170.4 MiB/s(IOPS 1364) |
| fio 4K 随机读 | 16.45 MiB/s(IOPS 4212) |
| fio 4K 随机写 | 41.26 MiB/s(IOPS 10564) |
4.3.2 预读与 I/O 调度器调优
预读(readahead)是内核提前多读一段磁盘内容的机制,顺序读多的场景加大它能提速;I/O 调度器决定读写请求发给磁盘的先后顺序,eMMC 这类快设备用 none(不做重排)反而更好:
# 设置预读值(2MB);出厂 readahead=256(128KB)
sudo blockdev --setra 4096 /dev/mmcblk0
sudo blockdev --getra /dev/mmcblk0 # 验证
# I/O 调度器;本机可选 [mq-deadline] kyber none,默认 mq-deadline
cat /sys/block/mmcblk0/queue/scheduler
echo none | sudo tee /sys/block/mmcblk0/queue/schedulergetra 读回 4096、scheduler 显示 none 即生效。
4.3.3 挂载优化与 TRIM
TRIM 的作用是告诉 eMMC 哪些数据块已经废弃,让主控后台回收,长期写入后速度不下滑的关键就在这。配合 noatime 减少无谓的元数据写入:
# 在线应用优化挂载选项
sudo mount -o remount,noatime,nodiratime,commit=60,discard /
# fstab 持久化
# /dev/mmcblk0p2 / ext4 errors=remount-ro,noatime,nodiratime,commit=60,discard 0 1
# 定期 TRIM(成功)
sudo fstrim -v /
# 输出: /: 33.5 MiB (35160064 bytes) trimmed
# eMMC 健康状态( mmc 工具出厂未装,apt 装 mmc-utils 后可用)
sudo apt install -y mmc-utils
sudo mmc extcsd read /dev/mmcblk0 | grep -i "life\|health"fstrim 输出 trimmed 字节数、extcsd 读出 life/health 信息,就说明这条链路都通了。
4.4 NVMe SSD 优化
4.4.1 设备检测与调优
接好 SSD 后先确认链路速率,再做和 eMMC 同样的调度器与预读调整:
sudo nvme list
sudo nvme smart-log /dev/nvme0n1
# I/O 调度器设为 none
echo none | sudo tee /sys/block/nvme0n1/queue/scheduler
# 调整队列深度与预读
echo 2048 | sudo tee /sys/block/nvme0n1/queue/nr_requests
sudo blockdev --setra 4096 /dev/nvme0n14.4.2 FIO 基准测试
用 fio 分别测顺序读、随机读和随机写,numjobs 和 iodepth 用来模拟多队列并发:
# 顺序读取
fio --name=seqread --ioengine=libaio --direct=1 --bs=128k \
--rw=read --size=1G --numjobs=4 --runtime=30 --group_reporting \
--filename=/dev/nvme0n1
# 随机 4K 读取
fio --name=randread --ioengine=libaio --direct=1 --bs=4k \
--rw=randread --size=256M --numjobs=8 --iodepth=32 --runtime=30 \
--group_reporting --filename=/dev/nvme0n1
# 随机 4K 写入
fio --name=randwrite --ioengine=libaio --direct=1 --bs=4k \
--rw=randwrite --size=256M --numjobs=8 --iodepth=32 --runtime=30 \
--group_reporting --filename=/dev/nvme0n14.4.3 格式化与挂载
格式化成 ext4 并写入 fstab,重启后自动挂载:
sudo mkfs.ext4 -L nvme_data /dev/nvme0n1
sudo mkdir -p /mnt/nvme
sudo mount /dev/nvme0n1 /mnt/nvme
echo '/dev/nvme0n1 /mnt/nvme ext4 defaults,noatime,nodiratime,discard 0 2' | sudo tee -a /etc/fstab用 mount -a 不报错、df 能看到 /mnt/nvme 即配置完成。
4.5 SD 卡优化
SD 卡速度有限,把预读调小一点(避免占内存),再用 fio 测个读速确认状态:
sudo blockdev --setra 2048 /dev/mmcblk1
fio --name=sd_read --filename=/dev/mmcblk1 --rw=read --bs=64k --size=256M --numjobs=1 --runtime=30 --group_reporting4.6 文件系统优化
4.6.1 ext4 调优
ext4 是通用选择,调优点在关日志、降保留空间和挂载选项:
sudo mkfs.ext4 -O ^has_journal,^metadata_csum -E stride=128,stripe-width=256 -m 1 -b 4096 /dev/nvme0n1p1
sudo mount -o noatime,nodiratime,discard,commit=60 /dev/nvme0n1p1 /mnt/nvme
sudo tune2fs -m 1 /dev/nvme0n1p1
sudo tune2fs -o journal_data_writeback /dev/nvme0n1p14.6.2 f2fs 调优(推荐 eMMC/SD)
f2fs 是专为闪存设计的文件系统,在 eMMC 和 SD 卡上通常表现更好:
sudo mkfs.f2fs -f /dev/mmcblk0p3
sudo mount -o discard,noatime,extent_cache=on,active_logs=6,mode=adaptive /dev/mmcblk0p3 /mnt/emmc4.6.3 btrfs 调优(推荐 NVMe 大容量)
btrfs 自带透明压缩,大容量 NVMe 上开启 zstd 压缩能省空间也不太损失速度:
sudo mkfs.btrfs -f -d single -m single -s 4096 /dev/nvme0n1p1
sudo mount -o noatime,ssd,compress=zstd:3,space_cache=v2 /dev/nvme0n1p1 /mnt/nvme4.7 内存压缩:zram / zswap
内存不够用时,zram 的做法是划一块内存出来当压缩的交换分区(swap),换出去的数据压缩后仍留在内存里,比写盘快得多;zswap 则是内核在换页到磁盘前先压缩缓冲的机制。两者都适合没有硬盘交换分区的板子。
4.7.1 zram 快速配置
一步步来:加载模块、选压缩算法、定大小(物理内存的一半)、格式化成 swap 并启用:
sudo modprobe zram num_devices=1
echo lz4 | sudo tee /sys/block/zram0/comp_algorithm
# 设置为物理内存 50%
ZRAM_SIZE=$(($(awk '/MemTotal/{print $2}' /proc/meminfo) / 2))
echo ${ZRAM_SIZE}k | sudo tee /sys/block/zram0/disksize
sudo mkswap /dev/zram0
sudo swapon -p 100 /dev/zram0
free -h
# 监控压缩率
cat /sys/block/zram0/mm_statfree -h 的 Swap 行出现 zram 的大小即生效;mm_stat 里可以看到压缩前后占用对比。
4.7.2 zram 持久化
上面的配置重启就没了,用脚本加 systemd 服务把它固化下来:
sudo tee /usr/local/bin/setup-zram.sh << 'EOF'
#!/bin/bash
modprobe zram num_devices=1
echo lz4 > /sys/block/zram0/comp_algorithm
ZRAM_SIZE=$(($(awk '/MemTotal/ {print $2}' /proc/meminfo) / 2))
echo ${ZRAM_SIZE}k > /sys/block/zram0/disksize
mkswap /dev/zram0 && swapon -p 100 /dev/zram0
EOF
sudo chmod +x /usr/local/bin/setup-zram.sh
sudo tee /etc/systemd/system/zram.service << 'EOF'
[Unit]
Description=Setup zram swap for RK3588
Before=swap.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/setup-zram.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload && sudo systemctl enable zram.serviceenable 输出创建符号链接的提示后,重启一次,free -h 仍能看到 swap 即持久化成功。
4.7.3 zswap 配置
zswap 改的是内核参数,可以启动参数里加,也可以运行时用 sysctl 开:
# 内核启动参数追加: zswap.enabled=1 zswap.compressor=zstd zswap.max_pool_percent=30
sudo sysctl -w vm.zswap.enabled=1
sudo sysctl -w vm.zswap.compressor=zstd
sudo sysctl -w vm.zswap.max_pool_percent=30
sudo tee /etc/sysctl.d/99-zswap.conf << 'EOF'
vm.zswap.enabled = 1
vm.zswap.compressor = zstd
vm.zswap.max_pool_percent = 30
EOF
sudo sysctl --system4.7.4 swappiness 调优
swappiness 决定内核多积极地把内存页换出去:zram 场景换页便宜,可以调高;传统硬盘 swap 换页很贵,要调低:
# zram 场景建议 60-100,传统 swap 建议 10-30
sudo sysctl -w vm.swappiness=60
echo "vm.swappiness = 60" | sudo tee -a /etc/sysctl.d/99-swap.conf4.8 DMA 缓存与 IOMMU
4.8.1 DMA 测试
内核自带 dmatest 模块,可以验证 DMA 控制器搬数据是否正常:
ls /sys/class/dma/
sudo modprobe dmatest
echo 100 | sudo tee /sys/module/dmatest/parameters/iterations
echo 1048576 | sudo tee /sys/module/dmatest/parameters/test_buf_size
sudo sh -c 'echo 1 > /sys/module/dmatest/parameters/run'
dmesg | tail -10
# 期望: summary 100 tests, 0 failures, 0 errorsdmesg 里看到 summary 100 tests, 0 failures, 0 errors 即 DMA 工作正常。
4.8.2 IOMMU 配置
IOMMU 开启后,外设只能访问分配给它的内存区域,既安全也利于 DMA 一致性。确认分组和日志,再按需调整启动参数:
ls /sys/kernel/iommu_groups/
dmesg | grep -i "smmu\|iommu"
# 启用 IOMMU(内核启动参数)
# arm-smmu-v3.iommu_forcedac=1 或 iommu.passthrough=0
sudo sysctl -w vm.zone_reclaim_mode=0
echo "vm.zone_reclaim_mode = 0" | sudo tee -a /etc/sysctl.d/99-performance.conf4.9 内存碎片整理与 overcommit 调优
4.9.1 碎片整理
内存用久了会碎片化,大块连续内存变少,DMA 和 huge pages 都吃这种内存。/proc/buddyinfo 能看到各阶空闲块的数量,触发一次整理可以缓解:
cat /proc/buddyinfo
echo 1 | sudo tee /proc/sys/vm/compact_memory
cat /proc/vmstat | grep -E "compact_|pgcompact"4.9.2 overcommit 调优
overcommit 是内核对内存分配请求的态度:0 表示启发式允许超分,进程申请多大虚拟内存一般都答应:
sudo sysctl -w vm.overcommit_memory=0
sudo sysctl -w vm.overcommit_ratio=80
sudo tee /etc/sysctl.d/99-memory.conf << 'EOF'
vm.overcommit_memory = 0
vm.overcommit_ratio = 80
EOF4.9.3 Huge Pages
huge pages(大页)把默认 4KB 的页换成 2MB,页表项大幅减少,大内存应用能少耗 CPU 在地址翻译上:
grep -i huge /proc/meminfo
echo 1024 | sudo tee /proc/sys/vm/nr_hugepages
echo "vm.nr_hugepages = 1024" | sudo tee /etc/sysctl.d/99-hugepages.conf
sudo sysctl --system
sudo mkdir -p /mnt/huge
sudo mount -t hugetlbfs nodev /mnt/huge分配后再 grep HugePages_Total,数值到 1024 即成功。
4.10 内存泄漏检测
怀疑程序漏内存时,先用 ps 看谁吃内存最多,再用 valgrind(内存错误检测工具)定位,或者干脆把目标进程的 RSS 持续记录下来观察趋势:
# 进程内存监控
watch -n 2 'ps aux --sort=-%mem | head -20'
# valgrind 检测
valgrind --leak-check=full --show-leak-kinds=all ./your_application
# 进程内存历史记录
PID=<目标PID>
for i in $(seq 1 60); do
echo "$(date +%H:%M:%S) $(awk '/VmRSS/ {print $2}' /proc/$PID/status) kB" >> /tmp/rss_log.txt
sleep 10
done5. 代码详解
5.1 DMC 频率控制代码
把 4.1.2 的手动操作写成 C 程序,核心就是读写那几个 sysfs 节点:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#define DMC_FREQ_PATH "/sys/class/devfreq/dmc/cur_freq"
#define DMC_GOVERNOR "/sys/class/devfreq/dmc/governor"
#define DMC_AVAIL_FREQ "/sys/class/devfreq/dmc/available_frequencies"
#define DMC_MIN_FREQ "/sys/class/devfreq/dmc/min_freq"
#define DMC_MAX_FREQ "/sys/class/devfreq/dmc/max_freq"
static int write_sysfs(const char *path, const char *value)
{
int fd = open(path, O_WRONLY);
if (fd < 0) { perror(path); return -1; }
int ret = write(fd, value, strlen(value));
close(fd);
return (ret < 0) ? -1 : 0;
}
static long read_sysfs_long(const char *path)
{
FILE *fp = fopen(path, "r");
if (!fp) { perror(path); return -1; }
long val = 0;
fscanf(fp, "%ld", &val);
fclose(fp);
return val;
}
int dmc_lock_max(void)
{
long avail[16];
char buf[256];
int count = 0;
FILE *fp = fopen(DMC_AVAIL_FREQ, "r");
if (!fp) { perror("open available_frequencies"); return -1; }
while (count < 16 && fscanf(fp, "%ld", &avail[count]) == 1)
count++;
fclose(fp);
if (count == 0) { fprintf(stderr, "No available frequencies\n"); return -1; }
write_sysfs(DMC_GOVERNOR, "performance");
snprintf(buf, sizeof(buf), "%ld", avail[count - 1]);
write_sysfs(DMC_MAX_FREQ, buf);
write_sysfs(DMC_MIN_FREQ, buf);
long cur = read_sysfs_long(DMC_FREQ_PATH);
printf("DMC locked at %ld Hz\n", cur);
return 0;
}
int main(void)
{
long cur = read_sysfs_long(DMC_FREQ_PATH);
printf("RK3588 DMC - current: %ld Hz\n", cur);
dmc_lock_max();
return 0;
}编译运行:gcc -O2 -o dmc_ctrl dmc_ctrl.c && sudo ./dmc_ctrl
5.2 内存架构
从 CPU 到内存通道的层次结构一目了然:
┌──────────────────────────────────────────────────┐
│ Cortex-A76 / Cortex-A55 │
│ L1 I-Cache (64KB) / D-Cache (64KB) │
│ L2 Cache (512KB) │
├──────────────────────────────────────────────────┤
│ L3 Cache (共享 4MB) │
├──────────────────────────────────────────────────┤
│ 互联总线 (NoC / DSU) │
├──────────────────────────────────────────────────┤
│ 内存控制器 (DMC) │
│ 通道 A ──── 16-bit LPDDR4X 3200MHz │
│ 通道 B ──── 16-bit LPDDR4X 3200MHz │
│ 聚合带宽 ── 25.6 GB/s (理论峰值) │
└──────────────────────────────────────────────────┘5.3 内存泄漏监控脚本
这个脚本每 10 秒检查一次内存占用百分比,超过阈值(默认 85%)就把当时占用最高的几个进程记进日志:
#!/bin/bash
# mem-watchdog.sh — 监控内存使用并记录异常
LOG_DIR="/var/log/mem-watchdog"
LOG_FILE="$LOG_DIR/mem-$(date +%Y%m%d).log"
THRESHOLD=85
mkdir -p "$LOG_DIR"
while true; do
MEM_TOTAL=$(awk '/MemTotal/ {print $2}' /proc/meminfo)
MEM_AVAIL=$(awk '/MemAvailable/ {print $2}' /proc/meminfo)
MEM_PERCENT=$(( (MEM_TOTAL - MEM_AVAIL) * 100 / MEM_TOTAL ))
if [ "$MEM_PERCENT" -ge "$THRESHOLD" ]; then
echo "[$(date)] WARNING: ${MEM_PERCENT}% used" >> "$LOG_FILE"
ps aux --sort=-%mem | head -6 | tail -5 >> "$LOG_FILE"
fi
sleep 10
done6. 常见问题
Q1: LPDDR4X 频率无法提升到最高档
先查 governor 是不是 performance,再看是不是过热降频(超过 85 度会降频),最后翻内核日志:
cat /sys/class/devfreq/dmc/governor # 确认为 performance
cat /sys/class/thermal/thermal_zone*/temp # 检查温度(超过 85 度会降频)
dmesg | grep -i "dmc\|devfreq\|ddr"Q2: NVMe SSD 速度达不到标称值
多半是链路没跑到 PCIe 3.0 x4,或者调度器没设对,两项都确认一下:
sudo lspci -s $(lspci | grep NVMe | awk '{print $1}') -v | grep LnkSta
# 确认: Speed 8GT/s Width x4(PCIe 3.0)
cat /sys/block/nvme0n1/queue/scheduler # 应为 noneQ3: zram 启用后系统卡顿
通常是 zram 给得太大或压缩算法太重,缩小到物理内存的 25% 并换 lz4:
swapoff /dev/zram0
echo lz4 > /sys/block/zram0/comp_algorithm
echo $(( $(awk '/MemTotal/ {print $2}' /proc/meminfo) * 25 / 100 ))K > /sys/block/zram0/disksize
mkswap /dev/zram0 && swapon -p 100 /dev/zram0Q4: eMMC 长时间写入后速度下降
闪存久了需要 TRIM 回收空闲块,执行一次并看看健康度:
sudo mount -o remount,discard /
sudo fstrim -v /
sudo mmc extcsd read /dev/mmcblk0 | grep -i "life\|health"Q5: 内存泄漏进程排查
按内存占用排序找嫌疑进程,用 valgrind 复现,再看进程的内存分布汇总:
ps aux --sort=-%mem | head -5
valgrind --leak-check=full --show-leak-kinds=all ./suspect_binary
cat /proc/<PID>/smaps_rollupQ6: Huge Pages 分配失败
一般是启动早期内存太碎,先清零再分批分配:
echo 0 | sudo tee /proc/sys/vm/nr_hugepages
echo 512 | sudo tee /proc/sys/vm/nr_hugepages
grep -i huge /proc/meminfo7. 相关文档
- 01quick-start-guide/01系统启动与基础操作.md -- 系统启动流程与启动参数配置
- 01Linux系统/07performance-debugging/01_性能优化概述.md -- 优化方法总览
- 04application-development/06文件系统与存储.md -- 文件系统管理与 NVMe 使用
- 05hardware-reference/00硬件事实基线.md -- 核心板与底板硬件事实基线
