1990 年代:前因——“硬盘太慢、驱动太多”
- 背景:早期 Linux 根文件系统要么在软盘、要么在 IDE 硬盘,内核把对应的软盘/IDE 驱动编进去即可顺利挂载。
- 矛盾出现:随着 SCSI、PCMCIA、USB、RAID 控制器等百花齐放,如果把所有可能的驱动都静态编进内核,内核体积会爆炸;如果把驱动当模块放在 /lib/modules,又陷入“挂载根文件系统需要根文件系统里的模块”的鸡与蛋问题。
- 解决思路:在内存里先伪造出一个“小硬盘”(ramdisk),把必备驱动、工具、甚至 fsck 都塞进这个内存盘;内核只要能识别内存本身,就能先挂 ramdisk,再从 ramdisk 里加载真正的存储驱动,最后 pivot 到真正的根文件系统 。
这就是 ramdisk 技术诞生的直接原因——启动阶段的“驱动跳板”。
1995:首次实现——rd.c 与 ramdisk 的诞生
- 1995 年左右的内核 1.x/2.0,出现了最早的 rd.c;它把一段连续的物理内存注册成块设备 /dev/ramX,再通过 mke2fs 格式化就能当磁盘用。
- 特点:
– 大小在编译时固定(默认 4 MB,最多 16 个设备);
– 需要完整走块设备层,先格式化再挂载,浪费内存和 CPU;
– 掉电即失,只能放启动时一次性数据 。
1999-2002:initrd 标准化——压缩 cramfs/ext2 镜像
- 为了解决早期 ramdisk 浪费内存的问题,社区把 ramdisk 做成压缩镜像(ext2、cramfs、romfs),由 bootloader 一次性读入内存,内核解压后挂载为临时根文件系统。
- 这就是我们今天说的 initrd(initial ramdisk)。
- 启动流程变为:
- bootloader 把内核 + initrd.gz 读进内存;
- 内核启动→解压 initrd→挂载为 /→执行 /linuxrc 或 /init;
- /init 加载 udev、驱动、建立 /dev 节点→挂载真正的根文件系统→switch_root 。
- 局限性:镜像大小固定,需要事先估算;解压后仍占用整块内存;脚本维护复杂。
2004-2006:initramfs 兴起——ramfs/tmpfs 取代块设备
- 内核 2.4/2.6 引入 initramfs,技术上基于 ramfs/tmpfs,而非块设备。
- 区别:
– 不再是块设备,而是直接利用 page cache 当文件系统,省掉一次格式化/缓存复制;
– 大小可变,按需增长,可回收;
– 使用 cpio 格式打包,可无缝嵌入内核镜像(CONFIG_INITRAMFS_SOURCE);
– 启动脚本统一为 /init,接口更简单 。 - 结果:initrd 退出主流,initramfs 成为各大发行版的默认启动机制。
2007-至今:brd 模块、tmpfs 日常化
- 传统 ramdisk 块设备演化为 brd(block ramdisk,drivers/block/brd.c),仍保留在源码,用于:
– 无盘机、嵌入式系统需要真正“磁盘”语义的场景;
– 测试块层、文件系统完整性校验等。 - 日常使用中,tmpfs 全面接管“内存当磁盘”的需求:
– /tmp、/run、/dev/shm 默认挂载 tmpfs;
– 读写速度 1 GB/s 以上,空间随用随还;
– 不再掉坑“固定大小、双份缓存” 。
副作用与经验教训
- 掉电即失:曾有人把 MySQL 数据目录放在 ramdisk 以求极速,结果断电订单归零 。
- 内存是昂贵资源:ramdisk 占用不可回收的连续内存,tmpfs 则可用 swap、可回收,性价比更高。
- 安全场景:ramdisk 的易失性反而成为“解密后即焚”的理想场所,例如高安全级别下的临时解密盘 。
一句话总结
ramdisk 最初是“为了启动而伪造的磁盘”,后来变成“为了速度而牺牲容量”的利器,最终被 initramfs + tmpfs 取代——它完成了“启动跳板”的使命,也留给我们一条宝贵经验:内存不是保险箱,速度和安全必须权衡。