用 diskutil 或 fallocate 可快速制造磁盘满状态:macos 用 sparsebundle 挂载后填充,linux 用 fallocate 占满剩余空间;go 中应通过 errors.is(err, syscall.enospc) 稳定捕获,避免字符串匹配。

用 diskutil 或 fallocate 快速制造“磁盘满”状态
真实环境里磁盘写入失败往往在 os.WriteFile、io.WriteString 或数据库 flush 阶段抛出 no space left on device,但本地开发很难复现。最直接的办法是人为占满测试目录所在分区的可用空间。
macOS 用户用:diskutil apfs resizeContainer diskX 1g(先缩小容器再恢复可快速耗尽空闲块,慎用系统盘)
更安全的做法是建临时 sparsebundle 并挂载为只读/满载卷:
hdiutil create -size 10m -type SPARSEBUNDLE -fs 'APFS' /tmp/full.dmg hdiutil attach /tmp/full.dmg dd if=/dev/zero of=/Volumes/untitled/fill bs=1m # 此时 /Volumes/untitled 写入会立即报错
Linux 用户推荐:fallocate -l $(df . --output=avail | tail -1)k ./.full —— 占满当前目录所在文件系统的剩余空间(注意别在根目录跑)
⚠️ 切勿在生产或系统盘上运行这类命令;建议在 Docker 容器或独立测试目录中操作。
Go 代码里如何稳定触发并捕获 ENOSPC
Go 的 os 包底层调用系统 write(),当磁盘满时返回 *os.PathError,其 Err 字段是 syscall.Errno(28)(即 ENOSPC)。不能只判断错误字符串是否含 "no space",因为不同 locale 下文本会变。
正确做法是用 errors.Is 或类型断言检查底层 errno:
err := os.WriteFile("/Volumes/untitled/test.txt", []byte("hello"), 0644)
if errors.Is(err, syscall.ENOSPC) {
log.Println("磁盘已满,走降级逻辑")
return
}
if pathErr, ok := err.(*os.PathError); ok && pathErr.Err == syscall.ENOSPC {
// 同样有效,适合需要访问 pathErr.Op/path 的场景
}
常见误判点:
- os.IsNotExist(err) 或 os.IsPermission(err) 对 ENOSPC 返回 false
- strings.Contains(err.Error(), "space") 在 CI 环境或非英文系统下失效
- 忘记 import "syscall" 导致编译失败
单元测试中避免依赖真实磁盘的技巧
硬塞满磁盘不便于 CI/自动化,更可靠的方式是用接口抽象 I/O 操作,然后在测试中注入返回 syscall.ENOSPC 的 mock:
type FileWriter interface {
WriteFile(filename string, data []byte, perm fs.FileMode) error
}
// 生产实现
type RealWriter struct{}
func (r RealWriter) WriteFile(f ... ) error { return os.WriteFile(f...) }
// 测试实现
type MockWriter struct{}
func (m MockWriter) WriteFile(_, _ string, _ fs.FileMode) error {
return &os.PathError{Op: "write", Path: "test", Err: syscall.ENOSPC}
}
这样测试能覆盖所有分支,且不污染环境。如果函数内部直接调用 os.WriteFile 而没抽象,则需用 go:linkname 替换或改用 testify/mock 打桩——但后者对标准库函数支持有限,不如提前设计可插拔接口。
注意 ext4/xfs 与 APFS/HFS+ 对“满”的判定差异
Linux 默认保留 5% 空间给 root,普通用户在使用率 95% 时就会遇到 ENOSPC;而 macOS APFS 没有类似保留策略,满得更“彻底”。这意味着同一份测试脚本在不同系统上触发时机可能不同。
验证当前是否真满的方法:
- Linux:df -i 查 inode 是否耗尽(ENOSPC 也可能由 inodes 不足引起)
- macOS:df -k + diskutil info /Volumes/untitled | grep "Free Space"
- Go 运行时无法区分是空间还是 inode 满,统一返回 ENOSPC,所以测试时最好两种情况都覆盖
真正难测的是“写入中途磁盘变满”——比如大文件分块写入,前几块成功,最后一块失败。这种边界必须靠真实磁盘模拟,mock 很难还原时序细节。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











