在 FreeBSD Linuxulator 中运行NVIDIA Cuda

我在构建 FreeBSD机器学习 环境,一直没有解决 在bhyve中实现NVIDIA GPU passthrough ,但是发现基于 Linuxulator 是一种FreeBSD平台特有的解决方案:

linuxulator思路

PyTorch and Stable Diffusion on FreeBSD 思路相同,通过结合FreeBSD nvidia-driverLinuxulator 运行Linux版本CUDA来实现一个机器学习环境:

  • FreeBSD Host主机安装 nvidia-driver (NVIDIA公司为FreeBSD提供了原生的驱动,但是没有提供CUDA)

  • 安装 libc6-shim ( 会同时 依赖安装 nvidia-driverlinux-nvidia-libs )来获取 nvidia-sglrun (能够提供CUDA)

  • 接下来就可以安装 miniconda 以及运行 PytorchStable Diffusion模型方法

备注

另一种解决思路是进一步使用 FreeBSD Linux Jail 来构建隔离的 Linuxulator 运行环境: 在FreeBSD Linux Jail中运行NVIDIA Cuda

其实方案的核心都是使用 Linuxulator 来实现Linux CUDA运行在FreeBSD nvidia-driver 上,性能和稳定性会受到一定影响,但是终究还是能够实 FreeBSD机器学习 环境。使用 FreeBSD Linux Jail 比直接使用 Linuxulator 的环境更干净隔离一些,其他差别不大。

两种方案各有优势:

  • 采用 Linuxulator 对于使用者来说还是直面FreeBSD,使用感受较好,也可以少安装一些环境依赖降低系统复杂度

  • 采用 FreeBSD Linux Jail 则提供了隔离且模拟的Linux运行环境,相当于使用了Linux容器(比喻不当),而且在Linux Jail基础上采用 podman (推测待实践)来部署OCI标准运行时,可以进一步实现 Kubernetes 计算节点

BIOS开启 Above 4G Decoding BIOS设置

备注

对于大于8G显存的GPU卡,需要开启 Above 4G Decoding BIOS设置 才能正常工作。本步骤是基于 在FreeBSD Linux Jail中运行NVIDIA Cuda 实践问题排查解决所总结的前置步骤。

如果不启用 Above 4G Decoding BIOS设置 ,那么NVIDIA GPU初始化可能失败,系统日志 dmesg 显示:

dmesg 显示分配BAR错误
nvidia0: <Unknown> on vgapci0
vgapci0: child nvidia0 requested pci_enable_io
vgapci0: 0x800000000 bytes of rid 0x14 res 3 failed (0, 0xffffffffffffffff).
nvidia0: NVRM: NVIDIA MEM resource alloc failed, BAR1 @ 0x14.
nvidia0: NVRM: NVIDIA hardware alloc failed.
device_attach: nvidia0 attach returned 6

Above 4G Decoding BIOS设置 针对我的 纳斯NASSE C246 ITX主板 需要配置2个BIOS位置:

  • Above 4G Decoding

../../../_images/above_4gb_decoding.png
  • above 4GB mmio BIOS Assignment

../../../_images/above_4gb_mmio_bios_assignment.png

linuxulator实现CUDA实践

NVIDIA为FreeBSD提供了原生的 nvidia-driver 驱动,所以方案类似 Docker运行NVIDIA容器 ,首先在FreeBSD Host中安装 nvidia-driver 以及支持 Linuxulator 运行模拟Linux驱动的 linux-nvidia-libs 库文件(我理解是将Linux层调用Linux版本 nvidia-driver 的API转换成调用FreeBSD版本 nvidia-driver )。此外,附加安装 libc6-shim 能够获得一个 nv-sglrun 工具来包装使用CUDA:

在FreeBSD上安装 nvidia-driver (原生) 和 CUDA (Linux版)
pkg install nvidia-driver linux-nvidia-libs libc6-shim
  • 手工加载 NVIDIA 驱动:

手工加载nvidia驱动
kldload nvidia

此时检查 kldstat | grep nvidia 看到有一个内核模块加载:

检查 kldstat 输出中有 nvidia
kldstat | grep nvidia
  • 需要配置系统启动时自动加载 nvidia-devier ,设置 /boot/loader.conf :

设置 /boot/loader.conf
# NVIDIA
nvidia_load="YES"

警告

我遇到一个非常奇怪的问题,在 /boot/loader.conf 添加了 nvidia_load="YES" ,但是重启没有自动加载 nvidia 驱动,而是启动后每次都需要手工执行 kldload nvidia 加载。这让我很困惑,参考 Loading kernel modules automatically 似乎是配置文件有隐含特殊字符导致的,但是我没有找到解决方法。

输出显示:

检查 kldstat 输出中有 nvidia
22    1 0xffffffff83800000  604e158 nvidia.ko
nvidia-smi 输出显示GPU工作正常
Fri Sep 26 19:51:25 2025
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 580.82.07              Driver Version: 580.82.07      CUDA Version: N/A      |
+-----------------------------------------+------------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  NVIDIA Graphics Device         Off |   00000000:01:00.0 Off |                    0 |
| N/A   32C    P0             36W /  150W |       0MiB /  23040MiB |      2%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+

+-----------------------------------------------------------------------------------------+
| Processes:                                                                              |
|  GPU   GI   CI              PID   Type   Process name                        GPU Memory |
|        ID   ID                                                               Usage      |
|=========================================================================================|
|  No running processes found                                                             |
+-----------------------------------------------------------------------------------------+

注意,这里输出信息中仅显示 Driver Version: 580.82.07 ,而CUDA版本是空白的 CUDA Version: N/A 。这时因为当前使用的 nvidia-smi 仅仅是FreeBSD原生的 nvidia-driver

  • 通过 nv-sglrun 运行 nvidia-smi 则是执行Linux程序(通过 Linuxulator ),会通过CUDA,此时会看到CUDA版本信息:

通过 nv-sglrun 运行 nvidia-smi
nv-sglrun nvidia-smi

输出信息:

通过 nv-sglrun 运行 nvidia-smi 输出信息中有CUDA版本信息
/usr/local/lib/libc6-shim/libc6.so: shim init
/usr/local/lib/libc6-shim/libc6.so: unable to load libinotify.so.0 (Shared object "libinotify.so.0" not found, required by "nvidia-smi")
Fri Sep 26 19:53:59 2025
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 580.82.07              Driver Version: 580.82.07      CUDA Version: 13.0     |
+-----------------------------------------+------------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  NVIDIA Graphics Device         Off |   00000000:01:00.0 Off |                    0 |
| N/A   33C    P0             36W /  150W |       0MiB /  23040MiB |      0%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+

+-----------------------------------------------------------------------------------------+
| Processes:                                                                              |
|  GPU   GI   CI              PID   Type   Process name                        GPU Memory |
|        ID   ID                                                               Usage      |
|=========================================================================================|
|  No running processes found                                                             |
+-----------------------------------------------------------------------------------------+

shim

备注

shkhln开发了一个 uvm_ioctl_override.c 工具提供了用于Linux二进制程序的shim,能够在FreeBSD中包装使用CUDA

编译shim : 在 uvm_ioctl_override.c 提供

  • 安装 Rocky Linux 9 开发工具(在 Linuxulator 快速起步 中已经安装过 linux_base-rl9 Rocky Linux 9 userland):

安装Rocky Linux 9 开发工具
pkg install linux-rl9-devtools

这样在Host主机的 /compat/linux/bin/cc 就是 Rocky Linux 9 提供的 gcc11

  • 获取 uvm_ioctl_override.c :

获取 uvm_ioctl_override.c
fetch https://gist.githubusercontent.com/shkhln/40ef290463e78fb2b0000c60f4ad797e/raw/f640983249607e38af405c95c457ce4afc85c608/uvm_ioctl_override.c
  • 编译:

编译 uvm_ioctl_override.c
  /compat/linux/bin/cc --sysroot=/compat/linux -m64 -std=c99 -Wall -ldl -fPIC -shared -o dummy-uvm.so uvm_ioctl_override.c

初始化设置

初始化设置需要2个步骤:

  • 安装缺失的软件包 linux-rl9-libglvnd (针对 Rocky Linux 9 ,早期针对CentOS 7则安装 linux-c7-libglvnd ),目的是获取 libGL.so.1 :

安装缺失的软件包 linux-rl9-libglvnd 获取 libGL.so.1
pkg install linux-rl9-libglvnd
  • 建立 /sbin/md5 软链接: FreeBSD 13.1开始提供 /sbin/md5sum ,installer需要该执行程序,所以比较可靠的方式是在 ~/bin 目录建立软链接:

建立 /sbin/md5sum 软链接
mkdir -p ~/bin
ln -sf /sbin/md5 ~/bin/md5sum

安装路径

设置一个安装路径用于所有的Linux程序安装,我使用 ${linux_dir} 作为存放所有Linux程序的基础目录(原文采用 ${BASE_PATH} 环境变量):

使用 ${linux_dir} 作为存放所有Linux程序的基础目录
export linux_dir="zdata/linux"

echo 'export linux_dir="zdata/linux"' >> ~/.shrc

# ZFS
zfs create zdata/linux

Conda

备注

Install minianaconda on FreeBSD 提到直接下载官方 Miniconda3-latest-Linux-x86_64.sh 进行安装:

下载 Miniconda installer for Linux 最新版本进行安装
fetch https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
chmod +x Miniconda3-latest-Linux-x86_64.sh

./Miniconda3-latest-Linux-x86_64.sh

但我实践发现也同样报错 [PYI-15462:ERROR] Module object for struct is NULL! (见下文,可能是同时安装了 Python 3.12 和 3.11 导致的)

需要在FreeBSD系统中安装原生python,运行 Miniconda3-latest-Linux-x86_64.sh 会依赖本地安装的python来执行脚本内容:

安装python3
# 找出发行版提供的python3相关版本命名
pkg search python3

# 安装 python 3.11
pkg install python311

备注

这里选择安装 python 3.11

原因 可能 是安装 python312 会同时安装 python311 ,似乎会导致 miniconda3 安装成勋报错:

安装 miniconda 报错
...
Do you accept the license terms? [yes|no]
>>> yes

Miniconda3 will now be installed into this location:
/root/miniconda3

  - Press ENTER to confirm the location
  - Press CTRL-C to abort the installation
  - Or specify a different location below

[/root/miniconda3] >>> /${linux_dir}/conda
PREFIX=/zdata/linux/conda
Unpacking bootstrapper...
Unpacking payload...
[PYI-15660:ERROR] Module object for struct is NULL!
Traceback (most recent call last):
  File "struct.py", line 13, in <module>
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1322, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 1262, in _find_spec
  File "<frozen importlib._bootstrap_external>", line 1559, in find_spec
  File "<frozen importlib._bootstrap_external>", line 1533, in _get_spec
  File "<frozen importlib._bootstrap_external>", line 1636, in find_spec
  File "<frozen importlib._bootstrap_external>", line 1679, in _fill_cache
OSError: [Errno 22] Invalid argument: '/zdata/linux/conda/install_tmp/_MEIV8CoEC/lib-dynload'

miniconda [PYI-15660:ERROR] Module object for struct is NULL! 错误是由于Python环境损坏或者混乱导致 PyInstaller 无法正确捆绑内置的 struct 模块。这个错误通常在 Conda 环境中使用 PyInstaller 时发生。

根据 linux-miniconda-installer Port details 说明,软件包依赖 lang/python311

PyTorch and Stable Diffusion on FreeBSD 使用 pkg 安装Linux Conda( miniconda ):

安装 miniconda
pkg install linux-miniconda-installer

# 直接执行 miniconda-installer 会提示不可以root身份执行
# 改为 sudo -u admin miniconda-installer
# 则提示 miniconda-installer 需要参考 https://repo.anaconda.com/miniconda/ 指定安装版本
# 例如: /usr/local/bin/miniconda-installer 3.11 25.3.1-1

# sudo -u admin miniconda-installer 3.12 25.7.0-2
# 则直接因为指定了 python3.12 提示 This miniconda requires python3.12. Install lang/python312 and try again.

# 但如果安装 python3.12 会导致系统同时有2个python版本出现 [PYI-15660:ERROR] Module object for struct is NULL!
# 而且在安装了 python3.12 后执行 sudo -u admin miniconda-installer 3.12 25.7.0-2 (此时系统中有3.11和3.12两个Python)
# 显示报错 fetch: Miniconda3-py312_25.7.0-2-Linux-x86_64.sh: stat()
# 这其实就是python环境混乱导致PyInstaller无法绑定内置的struct模块

sudo -u admin miniconda-installer 3.11 25.7.0-2

# 还是报错 fetch: Miniconda3-py311_25.7.0-2-Linux-x86_64.sh: stat()

# 原来这个 miniconda-installer 只是拼凑一个下载,例如上文拼凑
# fetch: Miniconda3-py311_25.7.0-2-Linux-x86_64.sh: stat()
# 这是根据操作系统使用的python来下载安装包,所以改为执行
fetch https://repo.anaconda.com/miniconda/Miniconda3-py311_25.7.0-2-Linux-x86_64.sh

chmod +x Miniconda3-py311_25.7.0-2-Linux-x86_64.sh

#./Miniconda3-py311_25.7.0-2-Linux-x86_64.sh
./Miniconda3-py311_25.7.0-2-Linux-x86_64.sh -b -u -p /zdata/linux/conda

目前还存在的问题是

安装 miniconda 依然报错
PREFIX=/zdata/linux/conda
Unpacking bootstrapper...
Unpacking payload...
[PYI-16429:ERROR] Module object for struct is NULL!
Traceback (most recent call last):
  File "struct.py", line 13, in <module>
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1322, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 1262, in _find_spec
  File "<frozen importlib._bootstrap_external>", line 1559, in find_spec
  File "<frozen importlib._bootstrap_external>", line 1533, in _get_spec
  File "<frozen importlib._bootstrap_external>", line 1636, in find_spec
  File "<frozen importlib._bootstrap_external>", line 1679, in _fill_cache
OSError: [Errno 22] Invalid argument: '/zdata/linux/conda/install_tmp/_MEIv1pXYW/lib-dynload'

google ai提示可能需要配置好Linux二进制兼容层: If the error persists, there may be a mismatch between the Conda-shipped libraries and the versions in your Linux base installation. Manually finding and replacing shared libraries is not recommended and is often a fragile fix.

想到既然我已经构建过 使用 ubuntu-base tgz 包部署Linux Jail Ubuntu ,那么先尝试在 FreeBSD Linux Jail 中安装 miniconda (反正也是集中在一个目录),然后将 conda 目录取出在Host的 Linuxulator 环境运行,看看问题到底在哪里。

参考