直接开工,别的不多说了。这个标题其实是个特别典型的 Linux 实用需求:在不花钱加硬件的情况下,把内存变成一块可以读写的“硬盘”,用来给系统提速。下面这篇就以 Ubuntu 为环境,把 RAM-based block device 从原理到实操、从选型到坑全部讲透。
1. 先把需求拆开:什么叫“基于内存的块设备”
1.1 三种容易混淆的“内存盘”方案
很多刚接触 Linux 的人,一搜“内存盘”会看到一堆名词:tmpfs、ramfs、ramdisk、zram、brd。这里必须先把概念捋清楚,不然照着网上的教程做完,发现根本不是自己想要的东西,那就浪费时间了。
第一种是 tmpfs,它挂载之后看起来是一个目录,可以往里放文件,底层数据都在内存里。tmpfs 是虚拟文件系统,它没有对应的物理块设备,所以 lsblk 里看不到它。它的大小可以动态调整,默认是物理内存的一半,支持 swap 回写。第二种是 ramfs,跟 tmpfs 很像,但没有大小限制,也没有 swap 回写,持续写入会一直吃内存直到把系统挤爆。第三种是本文的主角 block device 型内存盘,也就是通过内核模块(通常是 brd,旧称 rd)在内存里模拟一块真实的块设备,比如 /dev/ram0。这块设备可以格式化、分区、建文件系统、挂载,甚至可以拿去给数据库当数据文件存放地,或者给虚拟机做磁盘镜像。
注意:tmpfs 和 ramfs 虽然日常用的最多,但它们严格来说不是“块设备”。如果你需要的是一个有设备节点、能被 mkfs、能被 LVM 管理、能被各种存储工具直接识别的“盘”,那就要走 block device 路线。这也是标题里为什么特意强调“block device”的原因。
1.2 块设备内存盘能解决什么实际问题
那有人会问了:既然 tmpfs 用起来那么方便,我直接挂个 tmpfs 不就行了,搞块设备不是自找麻烦?这个问题的答案决定了你是否真的需要本文的方案。
tmpfs 的性能其实已经非常快了,绝大多数场景——比如放浏览器缓存、编译临时文件、放 socket 文件——直接用 tmpfs 就足够。但有几个场景必须上真正的块设备:
- 你要测试某个存储软件、文件系统或数据库引擎,需要一个性能极高且独立的内存设备,而不是一个挂载好的目录。
- 你要在内存里创建一个完整的分区表,比如做一个带 boot 分区的镜像,或者用
dd往里写裸数据。 - 你要给虚拟机或容器提供一块内存态虚拟磁盘,需要设备节点参与底层 IO 路径。
- 你在做嵌入式或内核相关开发,需要模拟无盘环境下的块设备。
这类需求下,tmpfs 没法直接替代。所以本文会把两份方案都讲到,但侧重点是块设备:从内核模块加载,到设备节点创建,到格式化挂载,再到开机自动生效,完整的生产可用流程。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 动手前的准备工作:环境梳理与内核模块加载
2.1 确认内核支持和模块是否就绪
在 Ubuntu 上创建内存块设备,底层依赖 Linux 内核的 brd 模块。这个模块从内核 2.6 时代就有了,一直到现在的 6.x 内核都稳定存在。它会把一段内存抽象成一个或多个 RAM 磁盘,并且提供 /dev/ram0、/dev/ram1 这种设备节点。
先查一下你的内核是不是支持:
bash复制grep BRD /boot/config-$(uname -r)
返回结果一般是 CONFIG_BLK_DEV_RAM=y 或 =m。=y 表示编译进内核,=m 表示作为模块加载。绝大多数 Ubuntu 桌面版和服务器版都是 =m。
然后再确认模块有没有自动加载:
bash复制lsmod | grep brd
如果没有任何输出,手动加载一次:
bash复制sudo modprobe brd
加载完之后,用 lsblk 看看设备有没有出现:
bash复制lsblk | grep ram
正常情况下你会看到 ram0 到 ram15 共 16 个设备,每个默认大小 16MB(有些内核版本是 4MB)。注意,/dev/ram0 这种设备在 devtmpfs 里默认就有节点,不需要手动 mknod。
2.2 不重启情况下创建指定大小的内存块设备
默认 16MB 太小,日常根本不够用。怎么办?两个办法:一是模块加载时通过参数指定,二是卸载后重新加载。
先卸载再重新加载,指定每个盘的大小和盘的数量:
bash复制sudo modprobe -r brd
sudo modprobe brd rd_nr=1 rd_size=2097152
这里的 rd_size 单位是 KB,所以 2097152 KB 就是 2GB。rd_nr=1 表示只创建一个盘。如果你想一次性创建 4 个每个 1GB 的盘,就写 rd_nr=4 rd_size=1048576。
重新加载后再看:
bash复制lsblk /dev/ram0
如果 SIZE 一栏显示 2G,说明成功了。这一步是后面所有实操的基础,建议你先敲一遍,确认环境没问题再往下走。
注意:
modprobe -r brd会移除所有 brd 设备。如果你系统里已经有人用/dev/ram0存了东西,卸载等于把数据全部丢掉。生产环境操作前必须确认该设备没被占用。
2.3 让模块参数在开机时自动生效
modprobe brd 的参数只对当前启动有效,重启之后会回到默认值。如果你希望每次开机都自动创建指定大小的内存盘,需要在 /etc/modprobe.d/ 下写一个配置文件。
新建一个 ramdisk.conf:
bash复制sudo vim /etc/modprobe.d/ramdisk.conf
写入:
code复制options brd rd_nr=2 rd_size=4194304
这样开机时内核加载 brd 模块就会自动带上参数,/dev/ram0 和 /dev/ram1 各自 4GB。注意,如果你用了 modprobe -r brd 再重新手动加载,/etc/modprobe.d/ 里的 options 也会自动生效,不需要反复敲参数。
3. 核心实操:一个可以当普通硬盘用的内存块设备
3.1 格式化与挂载:将 /dev/ram0 变成可用空间
有了设备节点,下一步就是把它当成一块全新的裸盘来用。先做文件系统,这里以常用的 ext4 为例:
bash复制sudo mkfs.ext4 /dev/ram0
如果你打算用 XFS 或其他文件系统也可以,命令对应换一下就行。格式化完成之后,创建一个挂载点并挂载:
bash复制sudo mkdir -p /mnt/ramdisk
sudo mount /dev/ram0 /mnt/ramdisk
挂载成功后,用 df -h /mnt/ramdisk 查看,你会看到一块 2GB(或你设定的容量)的盘,类型显示为 ext4,读写速度远高于普通 SSD。
这时候你往 /mnt/ramdisk 里写任何文件,都直接落在内存里。重启系统后,数据消失,设备重新变成一块空盘。这和 tmpfs 的行为类似,但因为它是块设备,所以你可以对它做很多 tmpfs 做不到的操作,比如:
bash复制sudo mkfs.ext4 -L RAMDISK /dev/ram0
sudo tune2fs -l /dev/ram0
sudo parted /dev/ram0 print
可以把 fdisk、parted、tune2fs 这些工具全部用在它身上,体验和操作一块真实硬盘完全一致。
3.2 设备持久化命名:不用 ram0 而用 /dev/disk/by-label
直接挂载 /dev/ram0 有个隐患:如果内核加载顺序变化,设备号可能漂移。比如 /dev/ram0 对应的物理内存还是那一块,但如果你同时加载了别的 brd 设备,名字可能乱。更稳妥的挂载方式是格式化时指定卷标,然后通过卷标挂载。
格式化时加卷标:
bash复制sudo mkfs.ext4 -L RAMDISK /dev/ram0
下次挂载时不用写设备名,写卷标路径:
bash复制sudo mount LABEL=RAMDISK /mnt/ramdisk
这样即使内核重新分配了设备号,只要卷标不变,就不会挂错盘。虽然内存盘在重启后数据会消失、需要重新格式化,但卷标这个习惯还是值得养成,尤其你后面管理的不只一块内存盘时。
3.3 数据安全与内存占用:必须提前想清楚的事
内存盘有个天然的风险:它占用的内存是系统可用内存的一部分。你创建了一个 2GB 的 brd 设备,这 2GB 空间并不一定是立刻全部分配,但只要你往里写数据,内存就会相应减少。写满之后,系统会进入内存压力,触发 swap 或 OOM。
所以在生产环境用内存盘,必须先把容量和用途算清楚:
- 明确这块盘是给谁用的。浏览器缓存、编译临时文件、数据库 temp 目录,容量需求完全不同。
- 设置盘大小要留足余量。系统本身还需要内存跑应用和 page cache,别把内存全部划给 brd。
- 重要数据不要放内存盘。如果你需要数据长期保存,应该定期从内存盘同步到磁盘,或者改用 SSD 做缓存层。
我看过有人把数据库的数据目录直接放到 ramdisk 上,结果服务器断电,整库数据直接清零。这种用法不是不行,但只在你能接受数据丢失、且具备备份机制的前提下才可考虑。
4. 性能实测与调优:到底比 SSD 快多少,以及如何让速度不衰减
4.1 用 dd 和 fio 做一次可重复的基准测试
口说无凭,内存盘快不快,拿数据说话。先做一个最简单的顺序写入测试:
bash复制sudo dd if=/dev/zero of=/mnt/ramdisk/testfile bs=1M count=1024 oflag=direct
这段命令向内存盘写入 1GB 数据,oflag=direct 是跳过 page cache,避免测试结果被缓存污染。你对比一下同样命令在 SSD 上的耗时,一般会有几十倍差距。
如果你系统里有 fio,可以做得更专业:
bash复制sudo fio --name=randwrite --ioengine=libaio --iodepth=16 --rw=randwrite --bs=4k --direct=1 --size=512M --numjobs=1 --runtime=30 --time_based --group_reporting
这个命令测的是 4K 随机写入,对内存盘来说非常轻松。你换到机械硬盘上跑同样的参数,结果会惨不忍睹;换到 SSD 上,大概率也远不如内存盘。
4.2 为什么测试结果会被“缓存”误导,以及如何校准
这里有个必须提及的坑:很多人在测试内存盘速度时,惊讶地发现它比 SSD 快不了多少,甚至差不多。这往往不是内存盘性能不行,而是你拿来做对比的 SSD 测试命中了 page cache。
Linux 对所有文件读写都会走 page cache。当你写一个文件到 SSD 时,数据其实先写进内存,后续再刷回磁盘;当你读一个文件时,如果它已经被缓存住,读操作直接从内存返回,不碰存储介质。这就导致测试 1GB 小文件时,SSD 看起来也有 2~3GB/s 的速度,和内存盘差距被缩小了。
所以正确做法是使用 oflag=direct 绕过 cache,或者用 fio 的 --direct=1 参数。另外,两个设备测试之前最好清一次缓存:
bash复制sudo sync && sudo sh -c 'echo 3 > /proc/sys/vm/drop_caches'
清缓存之后再跑一轮对比,内存盘和 SSD 的真实差距才会显现出来。
4.3 调优项:读写块大小、调度器、挂载参数
内存盘本身是块设备,所以它也要经过 IO 调度器。在大部分现代内核中,none 调度器对内存盘最友好,因为内存盘没有机械寻道问题,也不需要合并排序逻辑。你可以用一条命令查看当前调度器:
bash复制cat /sys/block/ram0/queue/scheduler
通常默认就是 none,如果显示别的,可以临时改一下:
bash复制echo none > /sys/block/ram0/queue/scheduler
挂载参数也可以适当调优。ext4 挂载时加上 noatime,nodiratime,减少不必要的元数据更新时间戳操作,对高并发小文件读写有帮助:
bash复制sudo mount -o noatime,nodiratime /dev/ram0 /mnt/ramdisk
如果你用它跑数据库之类的重 IO 应用,还可以考虑把 barrier=0 加上(ext4 是 barrier=0,XFS 不适用),但代价是异常断电时数据一致性下降。内存盘反正重启就清空,这个代价对临时数据来说可以接受。
5. 方案对比与选型:临时需求用 tmpfs,持久场景用 brd,压缩场景用 zram
5.1 tmpfs、brd、zram 的选择标准
本文从开头就在强调 block device 和文件系统的区别,但实际选型时,还要把 zram 加进来对比,因为 zram 也是内存块设备的一种,而且很多 Ubuntu 系统默认就用它做 swap。整理成一张表比较直观:
| 方案 | 类型 | 大小限制 | 数据持久性 | 压缩支持 | 适用场景 |
|---|---|---|---|---|---|
| tmpfs | 虚拟文件系统 | 默认内存一半,可调 | 重启丢失 | 无 | 缓存目录、socket、临时文件 |
| ramfs | 虚拟文件系统 | 无限制 | 重启丢失 | 无 | 极少量特殊场景,不推荐 |
| brd | 块设备 | 由模块参数固定 | 重启丢失 | 无 | 需要块设备语义的场景 |
| zram | 压缩块设备 | 动态按实际数据量 | 重启丢失 | 有 | swap、日志压缩、内存紧张场景 |
brd 和 zram 虽然都是块设备,但底层实现完全不同。brd 是一块内存直接映射成扇区,不压缩;zram 是每页数据先压缩再存内存,能压出更多可用空间,但代价是 CPU 开销。如果你内存本身就紧张,用 zram 比 brd 更合理。
5.2 浏览器临时文件迁移到内存盘:一个落地案例
顺着热搜词里的“如何将浏览器零时文件到ram盘中”,这里给一个非常实用的落地方案。浏览器缓存是最典型的临时文件,丢了不可惜,但频繁读写会磨损 SSD,还会拖慢浏览体验。把它迁移到内存盘后,浏览器的图片、JS 缓存响应会明显变快。
Chrome/Chromium 系的启动参数支持指定缓存目录:
bash复制google-chrome --disk-cache-dir=/mnt/ramdisk/chrome-cache
Firefox 则在 about:config 里修改 browser.cache.disk.parent_directory 指向 /mnt/ramdisk/firefox-cache。如果嫌手动改麻烦,可以在启动脚本里写死参数,或者用 desktop entry 的 Exec 行带上参数。
这么做之后,你会明显感觉到浏览器的滚动、切页、图片缩放都更跟手,因为缓存命中时的 IO 延迟从毫秒级降到了微秒级。但要注意:内存盘容量有限,缓存目录建议限制大小,比如 Chrome 通过 --disk-cache-size=52428800(50MB)来限定。
5.3 把 /tmp 换到内存盘:系统级提速的正确姿势
Ubuntu 系统里有很多程序会把临时文件写到 /tmp,比如软件安装包解压、桌面环境缓存、CI 构建中间产物。把 /tmp 挂载为 tmpfs 或内存盘,能减少大量磁盘写入,同时让这些临时操作变快。
tmpfs 是更稳妥的选择,因为内核自带对这种“无后备存储”文件系统的保护逻辑,不会无限吃内存。在 /etc/fstab 里加一行:
code复制tmpfs /tmp tmpfs defaults,size=2G,mode=1777 0 0
mode=1777 表示所有用户都能读写,且带 sticky bit,避免用户之间互相删文件。这是 Ubuntu 社区最常用的写法,比用 brd 做 /tmp 简单得多,也安全得多。如果你对“必须是块设备”没有执念,优先用这个方案。
6. 开机自启与系统集成:让内存盘每次启动都在
6.1 fstab 挂载 brd 内存盘的写法与实验
如果你决定用 brd 做持久化挂载(虽然数据重启后仍会丢失,但设备需要自动创建并挂载),fstab 是不可或缺的环节。在 /etc/fstab 里添加:
code复制/dev/ram0 /mnt/ramdisk ext4 defaults,noatime,nodiratime 0 0
但这里有个执行顺序问题:fstab 是在系统启动早期处理的,而 brd 模块如果还没加载,/dev/ram0 可能不存在,挂载就会失败。所以 fstab 里挂 brd 设备前,要确保 brd 模块先加载。这个先让内核通过 /etc/modprobe.d/ramdisk.conf 自动加载,再让 systemd 等待设备就绪,写 fstab 时就能正常挂载。
改完 fstab 后,先别急着重启,用下面命令验证一下配置有没有问题:
bash复制sudo mount -a
如果没有报错,说明 fstab 语法和挂载参数都正确。再重启一次,确认开机后盘已经自动挂载。
6.2 systemd 单元方式:比 fstab 更可控的挂载方式
fstab 方案有一个不足:如果初始化阶段设备还没准备好,systemd 可能会跳过挂载,导致后续服务访问不到路径。更可控的方式是写一个 systemd mount 单元。
在 /etc/systemd/system/mnt-ramdisk.mount 里写入:
code复制[Unit]
Description=Mount RAM disk to /mnt/ramdisk
After=dev-ram0.device
[Mount]
What=/dev/ram0
Where=/mnt/ramdisk
Type=ext4
Options=noatime,nodiratime
[Install]
WantedBy=multi-user.target
然后启用:
bash复制sudo systemctl daemon-reload
sudo systemctl enable mnt-ramdisk.mount
sudo systemctl start mnt-ramdisk.mount
注意,文件名的 - 对应路径中的 /,所以 /mnt/ramdisk 的单元名是 mnt-ramdisk.mount,不能写错。这种方式的优点是能明确指定依赖上一个设备节点,避免启动时序问题。
6.3 使用 systemd tmpfile 或 rc.local 做定期清理
内存盘数据虽然重启后消失,但在运行期间如果写满了,照样会让应用报错。比如你给浏览器设了缓存目录,内存盘满了会导致缓存写入失败,浏览器表现为异常卡顿。这就需要定期清理或限制大小。
如果是 tmpfs,fstab 里直接限制了 size,满了之后程序会收到 ENOSPC,不会拖垮系统。如果是 brd,这块盘是一个固定容量块设备,满了之后文件系统层面会报“No space left on device”。缓解方案是写一个 cron 或 systemd timer,定期清理目录里超过一定时长的文件。以浏览器缓存为例,可以每天凌晨执行:
bash复制find /mnt/ramdisk/chrome-cache -type f -mtime +1 -delete
生产环境我建议综合考虑:临时文件类需求优先 tmpfs,靠系统限制容量;只有确实需要块设备语义时才用 brd,并单独配定时清理。
7. 常见问题与排查技巧实录
7.1 /dev/ram0 不存在或大小为 0
最常见的问题就是 lsblk 看不到 /dev/ram0,或者看到大小为 0。原因基本是两个:
一是 brd 模块没加载。用 lsmod | grep brd 确认。没有的话 sudo modprobe brd。二是模块加载了,但设备名不是 ram0,而是 ram13 或 ram15 之类的。这是因为 rd_nr 参数控制了设备数量,而 devtmpfs 里可能有历史残留节点。用 ls /dev/ram* 通配看一下,选择实际存在的设备。
如果是大小为 0,多半是 brd 模块加载时用了 rd_size=0 或者系统里已有同名设备占用。可以执行:
bash复制sudo modprobe -r brd
sudo modprobe brd rd_size=1048576
重新加载后一般就能解决。
7.2 挂载时报错:wrong fs type, bad option, bad superblock
这种情况通常是文件系统没格式化,或者格式化和挂载类型不匹配。遇到后先看 dmesg:
bash复制dmesg | tail -20
常见原因是你跳过了 mkfs.ext4 直接挂载。内存盘重启后数据清零,每次开机都需要重新格式化一次。如果每次手动格式化麻烦,可以写成一个 systemd oneshot 服务,开机自动完成“创建文件系统 + 挂载”的动作。
7.3 内存盘把系统内存吃光,导致 OOM
这是内存盘最危险的坑。brd 设备在使用时,内核会为它的页分配内存,如果你创建了一个 8GB 的 ram0,又往里写了 8GB 数据,系统内存就会被占掉 8GB。应用可用内存大幅减少,很容易触发 OOM Killer。
排查方法是先用 free -h 看内存余量,再确认是不是 brd 占用的:
bash复制sudo cat /proc/meminfo | grep -i ram
或者直接看 slabtop、atop 里的内存占用。预防措施就是别把内存盘容量设置得过大,并给系统预留至少 1~2GB 余量。另外,tmpfs 有 size 限制机制,在同样的使用需求下其实更安全。
7.4 浏览器缓存迁移后没效果
如果你按上面的方法设置了 Chrome 缓存目录,但发现速度没有提升,可能是缓存没真正落在内存盘。验证方法:
bash复制sudo find /mnt/ramdisk/chrome-cache -type f | head
如果目录下没有文件,说明 Chrome 没认这个参数。原因可能是 Chrome 版本更新后参数名变了,或者目录权限不对。注意目录必须属于运行浏览器的用户,否则 Chrome 会静默回退到默认缓存路径。我踩过这个坑,明明是 --disk-cache-dir 写对了,但因为目录属主是 root,普通用户没有写权限,Chrome 直接忽略了这个参数。
解决方式是设置正确的属主和权限:
bash复制sudo chown -R $USER:$USER /mnt/ramdisk/chrome-cache
7.5 重启后挂载丢失,服务访问不到内存盘
开机自启的问题通常出在 systemd 对设备就绪事件的监听上。brd 设备虽然由模块参数创建,但 devtmpfs 生成节点的时间和 fstab 挂载的时机未必同步。用 systemd 单元方式配合 After=dev-ram0.device 能解决 90% 的问题,剩余 10% 可以在 unit 里加一行:
code复制Requires=systemd-modules-load.service
确保模块加载服务先跑完。
8. 最后分享一个我一直在用的落地组合
做了这么多年 Linux 环境优化,我的习惯是:临时文件优先 tmpfs,需要块设备语义才用 brd,内存紧张时加 zram 兜底。这个组合在 Ubuntu 桌面和服务器上都稳跑了好几年,没出过事故。
比如在笔记本上,我把 /tmp 挂成 tmpfs,将 Chrome 缓存指到 /tmp/browser-cache,编译项目时临时文件全部在内存里完成读写,编译速度提升非常明显,而且 SSD 的写入寿命也保住了。在服务器上,如果某个服务需要一块高速块设备做性能压测,我就用 brd 加自定义大小,压测完毕直接卸载,不污染磁盘。zram 则始终作为 swap 层,压缩效果在内存吃紧时非常可感,系统再也不容易进入卡死状态。
最后再给你一个建议:内存盘不是越多越好。每次规划内存盘之前,先 free -h 看一眼内存总量,算出系统峰值时还剩余多少可用内存,把内存盘容量控制在剩余内存的 50% 以内,留出足够的 page cache 空间。这样既能吃到内存盘的速度红利,又不至于因为一次突发写入触发 OOM。毕竟,内存盘的意义是加速,而不是把系统推到崩溃边缘。
