llama.cpp 在网络盘上 mmap 失败

llama.cpp 从 NFS、SMB 或 CIFS 挂载的网络盘加载模型时报 mmap failed 错误。定位文件系统 mmap 限制、改用流式加载或本地缓存的修复方案。

在团队共享存储服务器(NFS v4 挂载)上存放 GGUF 模型文件,通过网络路径 /mnt/nas/models/llama3-70b-Q4_K_M.gguf 启动 llama.cpp b3290 时,报错 error loading model: mmap failed: Operation not supportedmmap error 45,模型无法加载。同样的文件拷贝到本地 SSD 后可以正常加载。这是因为 NFS、SMB(Samba)、CIFS 等网络文件系统对 mmap 的支持存在限制或默认禁用。

常见原因

1. NFS 文件系统不支持或限制了 mmap

标准 NFS 协议对 mmap 的支持取决于服务端和客户端的配置。NFS v3 通常不支持共享内存映射,NFS v4 支持但需要启用正确的挂载选项(rwnoatime 等)。某些 NFS 服务端(如 FreeNAS/TrueNAS 的默认配置)会禁用 mmap 以防止缓存一致性问题。

怎么判断mount | grep nfs 查看挂载选项;在挂载目录下执行 python3 -c "import mmap,os; f=open('/mnt/nas/test.bin','rb'); mmap.mmap(f.fileno(),0,access=mmap.ACCESS_READ)" 看是否报错。

2. SMB/CIFS 挂载缺少 mmap 支持标志

SMB 协议在 Linux 的 CIFS 内核模块下挂载时,某些版本默认不支持大文件 mmap,会在 mmap 系统调用时返回 ENODEVENOTSUP。GGUF 文件动辄几十 GB,超过某些 SMB 实现的 mmap 限制。

怎么判断mount | grep cifsdmesg | grep cifs | tail -20 查看是否有 mmap 相关的内核错误信息。

3. Samba 服务端的 mmap = no 配置

Samba 服务端的 smb.conf 中若设置了 mmap = no,会禁用服务端端的内存映射,但这通常不直接影响客户端 mmap——影响的是服务端处理效率。但某些版本的 Samba 在此配置下确实会导致客户端 mmap 调用失败。

怎么判断:联系服务器管理员查看 smb.conf 中的 mmapstrict sync 设置。

4. 文件权限允许读但 mmap MAP_PRIVATE 需要更宽泛的权限

llama.cpp 的 mmap 使用 MAP_PRIVATE 模式(写时复制),有些网络文件系统对 MAP_PRIVATE 的权限检查比本地文件系统更严格,即使文件有读权限也可能拒绝 mmap。

怎么判断:执行 strace ./llama-cli -m /mnt/nas/model.gguf 2>&1 | grep mmap 查看 mmap 系统调用的返回值和错误码;EPERMEACCES 说明是权限问题。

5. 网络延迟导致 mmap 预读超时

mmap 本质上是按需加载(demand paging),当 CPU 访问尚未加载的页面时触发缺页中断,OS 从文件系统读取对应数据。对于本地 SSD,这个过程是微秒级;对于网络文件系统,每次缺页中断可能需要几十毫秒,大量并发缺页中断会导致加载超时或性能极差(而非直接报错)。

怎么判断:模型能加载但速度极慢(每 token 需要数秒),即使是纯 CPU 推理也不至于这么慢。这是网络 IO 成为瓶颈的特征。

6. 文件系统挂载路径不稳定(断连/重挂)

NFS 挂载在网络不稳定时会短暂断连,若此时 llama.cpp 正在通过 mmap 读取文件,内核会收到 IO 错误并向进程发送 SIGBUS 信号,导致进程崩溃,错误信息看起来像 mmap 失败。

怎么判断dmesg | grep -i 'nfs\|mount\|transport' | tail -20 检查是否有网络文件系统断连记录;ping -i 0.2 <nas_ip> -c 50 检查网络稳定性。

最短修复路径

Step 1:使用 —no-mmap 禁用内存映射加载

# 最快的修复方式:禁用 mmap,改用 read() 系统调用加载
./llama-cli -m /mnt/nas/models/llama3-70b-Q4_K_M.gguf \
  --no-mmap \
  -ngl 35 \
  --ctx-size 4096 \
  -p "hello" -n 100

# Ollama 目前不直接暴露此参数,但可以通过环境变量
OLLAMA_NO_MMAP=1 ollama run llama3:70b "hello"

注意:--no-mmap 会导致完整模型文件被读取到系统内存(RAM),对于 70B Q4_K_M 需要约 40 GB 系统内存。若 RAM 不足,换用更小的量化版本。

Step 2:将模型文件缓存到本地再加载

# 方法 1:完整拷贝到本地 SSD(推荐)
LOCAL_MODEL_DIR="/tmp/llama_models"
mkdir -p "$LOCAL_MODEL_DIR"
rsync -ah --progress /mnt/nas/models/llama3-70b-Q4_K_M.gguf "$LOCAL_MODEL_DIR/"

# 然后从本地路径加载
./llama-cli -m "$LOCAL_MODEL_DIR/llama3-70b-Q4_K_M.gguf" -ngl 35 -p "hello" -n 100
# 方法 2:使用 tmpfs(若 RAM 够大,速度最快)
sudo mount -t tmpfs -o size=50G tmpfs /mnt/tmpfs_model
cp /mnt/nas/models/llama3-70b-Q4_K_M.gguf /mnt/tmpfs_model/
./llama-cli -m /mnt/tmpfs_model/llama3-70b-Q4_K_M.gguf -ngl 35 -p "hello" -n 100

Step 3:修改 NFS 挂载选项(Linux)

# 查看当前挂载选项
mount | grep nfs

# 重新挂载,添加 rw 和 cache 相关选项
sudo umount /mnt/nas
sudo mount -t nfs4 -o rw,noatime,nodiratime,nofail,intr,rsize=1048576,wsize=1048576 \
  nas-server:/models /mnt/nas

# 验证 mmap 是否可用
python3 -c "
import mmap, os
with open('/mnt/nas/test_1mb.bin', 'rb') as f:
    mm = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
    print('mmap OK, size:', len(mm))
    mm.close()
"

Step 4:使用 —mlock 锁定内存(防止换出)

# 对于通过 --no-mmap 加载的模型,锁定内存防止被 swap 出去
./llama-cli -m /mnt/nas/models/model-Q4_K_M.gguf \
  --no-mmap \
  --mlock \
  -ngl 35 \
  -p "test" -n 50
# 注意:--mlock 需要足够的物理 RAM,且可能需要 root 权限或调整 ulimit

Step 5:配置模型文件的本地自动同步脚本

#!/bin/bash
# sync_models.sh:每天同步 NAS 上的新模型到本地 SSD
LOCAL_DIR="/data/local_models"
NAS_DIR="/mnt/nas/models"

rsync -avh --delete \
  --include="*.gguf" \
  --exclude="*" \
  "$NAS_DIR/" "$LOCAL_DIR/"

echo "同步完成: $(date)"

预防建议

  • 在生产环境中始终从本地存储加载模型,NAS 只作为长期存档,不直接用于推理。
  • 启动脚本中加入模型路径检测:若路径在网络挂载目录下,自动添加 --no-mmap 参数。
  • 对于需要多机共享模型的场景,考虑用 vLLM 或 TorchServe 搭建专用推理服务,模型只在服务节点本地存储。
  • NAS 挂载时加入 _netdev 选项(fstab 中),确保系统启动时先建立网络连接再挂载,避免挂载竞争。
  • 定期检查 NFS 挂载的稳定性(df -h /mnt/nas 命令,若报错说明挂载断开),发现问题时主动重挂而非等待 llama.cpp 报错。
  • 若必须用网络存储,NVMe over Fabric(NVMe-oF)的延迟远低于 NFS,适合对推理速度有要求的场景。

常见问答 (FAQ)

Q: --no-mmap 会影响推理速度吗? A: 会有轻微影响。mmap 允许 OS 按需加载模型权重(懒加载),首次访问某层时才从磁盘读入;--no-mmap 在启动时将整个模型读入 RAM,启动时间更长,但推理过程中反而更快(不会有缺页中断)。若 RAM 足够,--no-mmap 在实际推理中性能更好。

Q: 模型文件在 S3 或 OSS 上,能用 FUSE 挂载后直接加载吗? A: 技术上可以(如 s3fs 或 rclone),但极不推荐。FUSE + 对象存储的延迟是本地 SSD 的 100 倍以上,加载 70B 模型可能需要几小时。正确做法是先完整下载到本地,再加载。

Q: Windows 上的 SMB 网络驱动器有同样的问题吗? A: 是的,Windows 的 UNC 路径(\\server\share\model.gguf)对 mmap 的支持同样有限。llama.cpp 的 Windows 版本会在检测到网络路径时自动回退到读取模式,但回退行为不稳定。建议始终映射到本地驱动器字母并拷贝到本地。

Q: Docker 容器内用 bind mount 挂载 NFS 目录也会有这个问题吗? A: 会,Docker 的 bind mount 直接使用宿主机的文件系统。若宿主机挂载了 NFS,容器内看到的是 NFS 路径,mmap 限制同样适用。解决方案同上:要么 --no-mmap,要么先拷贝到容器内的本地路径。

相关阅读

标签: #local-llm #llama-cpp #排查