分类
devops

zram swap and /dev/shm explained

ZRAM Swap 原理详解

前言

有人看到 “zram 是用内存做 swap” 时会有疑惑:用内存做 swap?那不是用内存存内存,死循环了吗?

这篇文章用最直白的语言解释 zram 的原理、和磁盘 swap 的区别,以及为什么它不是 “死循环”。


Swap 是什么

Swap(交换空间)是操作系统的一种内存管理机制:

  • 物理内存不够时,内核把不常用的内存页挪到 swap 空间
  • 需要时,再从 swap 空间读回来
  • 本质是用空间换时间——牺牲一点速度,换取更多可用内存

磁盘 Swap(传统方式)

应用程序 → 物理内存(RAM)
               │ 内存不足时
               ▼
          磁盘(SSD/HDD)

特点:
  • 存储介质:SSD 或 HDD
  • 速度:慢(毫秒级 I/O 延迟)
  • 容量:大(取决于磁盘剩余空间)
  • CPU 开销:无
  • 适合场景:物理内存严重不足时的兜底方案

ZRAM Swap(压缩内存)

zram 不是 “用内存来存内存”,而是:

核心原理

正常情况(无 zram):
  应用程序 → 占用物理内存 100MB

内存压力大时(有 zram):
  应用程序 → 占用物理内存 50MB(常用数据保留)
  zram 设备 → 压缩存储 50MB 冷数据
              ↓ 压缩(通常 2:1 ~ 4:1)
          实际只占 15MB 物理内存

总计占用:50MB + 15MB = 65MB
相比原来:节省了 35MB

关键点

  1. 按需分配:zram 不是预先划走一块固定内存,而是按需压缩页面
  2. 压缩存储:数据在物理内存中,但以压缩形式存放,占用更少空间
  3. 不是 “用箱子装箱子”:那 15MB 是直接从可用内存池里分配的,是实实在在的物理内存,不存在循环

类比:真空压缩袋

你有一个行李箱(物理内存):

正常情况下:
  放 5 件羽绒服 → 占满整个箱子(100%)

用了 zram 压缩袋后:
  放 5 件羽绒服 → 用压缩袋抽真空
                    → 只占箱子一半空间(50% 压缩比)
                    → 还能再放 5 件进去

不是 "拿一个箱子装另一个箱子",
而是 "把衣服压缩变小,省出空间放更多东西"。

磁盘 Swap vs ZRAM 对比

特性 磁盘 Swap ZRAM Swap
存储介质 SSD/HDD 物理内存(压缩后)
读写速度 慢(ms 级) 快(μs 级)
容量 大(取决于磁盘) 小(通常设内存的 50%)
CPU 开销 有(压缩/解压)
重启后数据 保留 丢失
压缩比 不压缩 2:1 ~ 4:1
适合场景 内存极度不足 内存轻度不足,提高利用率

性能实测数据(参考)

操作            延迟
─────────────────────────────
内存直接访问      ~100ns
ZRAM 压缩/解压    ~1-5μs
SSD Swap 读写     ~100μs-1ms
HDD Swap 读写     ~10-20ms

ZRAM 比 SSD swap 快 100-1000 倍


为什么不是死循环

关键问题:zram 的压缩数据也占物理内存,这部分内存从哪里来?

答案:从可用内存池中来。

系统启动时:

┌────────────────────────────────────┐
│           物理内存 32GB              │
├────────────────────────────────────┤
│ 应用程序     │   可用内存           │
│ 12GB         │   20GB              │
└──────────────┴─────────────────────┘

设置 16GB zram swap(不预分配):

┌────────────────────────────────────┐
│           物理内存 32GB              │
├────────────────────────────────────┤
│ 应用程序     │  zram  │  可用内存   │
│ 12GB         │ (按需)  │  20GB      │
└──────────────┴────────┴────────────┘

内存压力时,内核把冷数据压缩到 zram:
占用的是那 20GB 可用内存的一小部分

如果可用内存为 0,zram 也无法再分配新页面
→ 系统 OOM 或使用磁盘 swap 兜底

这就好比: 你用手机拍了一张 10MB 的照片,发微信时微信会自动压缩成 1MB 再发送。压缩后的 1MB 还是存在手机内存(存储空间)里的,不是 “用存储存存储” 的死循环。


实际配置示例

#!/bin/bash
# 配置 4GB zram swap(适合 8GB 内存的机器)

ZRAM_SIZE=$((4 * 1024 * 1024 * 1024))  # 4GB

# 添加 zram 设备
ZRAM_DEV=$(cat /sys/class/zram-control/hot_add)

# 设置大小(使用 lz4 算法,速度优先)
echo lz4 > /sys/block/zram${ZRAM_DEV}/comp_algorithm
echo ${ZRAM_SIZE} > /sys/block/zram${ZRAM_DEV}/disksize

# 启用 swap(优先级 100,比磁盘 swap 优先)
mkswap /dev/zram${ZRAM_DEV}
swapon -p 100 /dev/zram${ZRAM_DEV}

查看 zram 使用情况:

# 压缩比、实际占用内存等
cat /sys/block/zram*/stat

# 或使用 zramctl
zramctl

输出示例:

NAME       ALGORITHM DISKSIZE  DATA  COMPR  TOTAL  STREAMS
/dev/zram0 lz4            4G  2.1G  987M  1002M       4
                            ↑    ↑     ↑
                      原始数据  压缩后  含元数据

总结

  • zram 不是死循环 — 它只是把冷数据压缩存放,压缩后的数据占用的还是物理内存,但占得更少
  • 比磁盘 swap 快很多 — 因为数据在内存中,只是经过了压缩/解压,不需要走 I/O
  • 适合桌面/开发机 — 内存不是完全不够,但想提高利用率
  • 不适合内存已经完全不够的场景 — 那时必须用磁盘 swap 兜底

理解 zram 的关键就一句话:

“不是用箱子装箱子,而是把衣服压缩了再放回箱子。”


/dev/shm 详解

前言

/dev/shm 是 Linux 下一个容易被忽视但非常实用的特殊文件系统。很多人用过它但不一定清楚它的原理和限制。


什么是 /dev/shm

/dev/shm 是一个 tmpfs(临时文件系统),挂载在内存上的虚拟文件系统。

/dev/shm 的本质:
  • 类型:tmpfs(temporary file system)
  • 存储介质:物理内存(RAM)+ swap
  • 挂载点:/dev/shm
  • 默认大小:物理内存的 50%
  • 持久性:重启后数据丢失(volatile)

查看当前系统的 /dev/shm

$ df -h /dev/shm
Filesystem      Size  Used Avail Use% Mounted on
tmpfs           6.3G     0  6.3G   0% /dev/shm

$ mount | grep /dev/shm
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev,relatime,inode64)

和 ZRAM 的关系

/dev/shm 和 zram 是两个完全不同的概念,但容易混淆:

特性 /dev/shm (tmpfs) ZRAM
类型 文件系统 块设备
用途 共享内存、临时文件 swap 空间
数据是否压缩 否(原始数据) 是(压缩存储)
是否占用内存 是(但压缩后更小)
是否可以设置为 swap 可以(但没必要) 专门做 swap

一个简单区分:

/dev/shm → "把内存当成硬盘用"(文件系统)
zram     → "把内存当成 swap 用"(块设备 + 压缩)

谁在使用 /dev/shm

POSIX 共享内存(shm_open)

$ ls -la /dev/shm/
-rw------- 1 bill bill 1048576 May 27 18:30 my_shared_mem

Chrome/Chromium 浏览器

Chrome 使用 /dev/shm 作为共享内存缓存,加速多进程通信。

# Chrome 的共享内存文件
$ ls /dev/shm/
com.google.Chrome.*

如果 /dev/shm 太小,Chrome 可能会报 “共享内存不足” 的警告。

Docker/BuildKit

docker/build-push-action--shm-size 参数就是设置 build 容器的 /dev/shm 大小:

- name: Build
  uses: docker/build-push-action@v6
  with:
    shm-size: 2g  # 设置 /dev/shm 为 2GB

某些编译工具(如 Go 的编译器、LLVM)在并行编译时会大量使用共享内存,默认的 64MB 可能不够。

Python multiprocessing

Python 的 multiprocessing 模块默认使用 /dev/shm 做共享内存:

from multiprocessing import shared_memory

# 创建 10MB 共享内存块
shm = shared_memory.SharedMemory(name="my_shm", create=True, size=10*1024*1024)

和 TMPFS 的关系

/dev/shm 是 tmpfs 的一种。其他常见的 tmpfs 挂载点:

$ mount | grep tmpfs
tmpfs on /tmp             type tmpfs (rw,nosuid,nodev)   # 某些发行版
tmpfs on /dev/shm         type tmpfs (rw,nosuid,nodev)
tmpfs on /run             type tmpfs (rw,nosuid,nodev)
tmpfs on /sys/fs/cgroup   type tmpfs (ro,nosuid,nodev)

tmpfs 的特点:

写入 tmpfs 的文件:
  ✓ 读写速度快(纯内存操作)
  ✓ 重启后自动清空
  ✓ 动态分配(用多少占多少,不是预分配)

  ✗ 受物理内存 + swap 限制
  ✗ 重启后数据丢失

调整 /dev/shm 大小

临时调整(立即生效)

# 调整为 8GB
sudo mount -o remount,size=8G /dev/shm

# 或调整为物理内存的 60%
sudo mount -o remount,size=60% /dev/shm

永久调整

# 编辑 /etc/fstab
tmpfs /dev/shm tmpfs defaults,noexec,nosuid,size=8G 0 0

查看使用情况

# 最简单的
df -h /dev/shm

# 查看具体哪些文件占用了 /dev/shm
du -sh /dev/shm/*
lsof /dev/shm 2>/dev/null | head -20

/dev/shm vs /tmp 的区别

有些人会把 /tmp 也挂载为 tmpfs,但两者有区别:

/dev/shm /tmp(如果挂载为 tmpfs)
用途 共享内存(进程间通信) 临时文件
标准 POSIX 标准(shm_open) 无标准
清理策略 手动删除或重启清除 通常自动清理(如 systemd tmpfiles)
固定挂载点 不是

不建议把 /tmp 换成 tmpfs 的原因:

如果 /tmp 是 tmpfs(纯内存):
  1. 大文件下载到 /tmp 会占满内存
  2. 系统意外重启会丢失 /tmp 下的数据
  3. 某些程序依赖 /tmp 持久性(如 /var/tmp)

更好的做法:
  /tmp → 磁盘文件系统(如 ext4/xfs)
  /dev/shm → tmpfs(只用于共享内存)

/dev/shm 的常见问题

问题:Chrome 报 “共享内存不足”

症状:Chrome 标签页卡顿,报 "Shared memory" 相关错误
原因:/dev/shm 默认太小(物理内存的 50%),Chrome 多进程通信占满
解决:扩大 /dev/shm
sudo mount -o remount,size=4G /dev/shm

问题:Docker build 失败(OOM)

症状:Docker build 过程中报 "Killed" 或 "Cannot allocate memory"
原因:编译工具(如 Rust 的并行编译)使用了大量共享内存
解决:设置 --shm-size
# docker-compose.yml
services:
  build:
    build:
      context: .
      shm_size: '2gb'

问题:tmpfs 数据丢失

症状:重启后 /dev/shm 下的文件消失了
原因:这是正常行为!tmpfs 是 volatile 存储
解决:需要持久化的数据不要放在 /dev/shm

总结

概念 本质 用途
ZRAM 压缩内存块设备 提高内存利用率(冷数据压缩)
/dev/shm 内存文件系统 进程间共享内存
Swap(磁盘) 磁盘交换空间 内存不足时兜底

三个概念的关系:

物理内存(RAM)
    │
    ├── 应用程序直接使用(热数据)
    │
    ├── zram(冷数据,压缩后继续存放在内存中)
    │    └── 压缩后的数据也占用物理内存
    │
    ├── /dev/shm(共享内存,文件系统方式使用内存)
    │
    └── swap(磁盘,内存真不够时溢出到磁盘)
          └── 磁盘上的 swap 也可能同时存在

一句话总结:
ZRAM = “把内存压缩,省出空间”
/dev/shm = “把内存当硬盘,快速读写临时数据”
磁盘 Swap = “内存真不够了,用硬盘兜底”