第二章 驱动开发环境搭建:Linux内核驱动开发环境配置、Windows WDDM驱动开发环境配置、GPU模拟器与虚拟化环境
说实话,驱动开发最让人头疼的往往不是代码本身,而是环境搭建。我见过太多新手,代码写得挺好,结果环境配了一周还没跑起来。这一章,我就把我在Linux和Windows两套平台上的实战经验,以及GPU模拟器的玩法,一次性讲清楚。
2.1 Linux内核驱动开发环境配置
Linux下搞GPU驱动,我个人习惯用Ubuntu LTS版本。为什么?因为社区支持最好,遇到坑能搜到答案。你想想看,要是用个冷门发行版,出了问题连个问的人都没有。
2.1.1 内核源码与编译工具链
首先,你得有一套完整的内核源码。别用系统自带的那个,版本太老。
# 下载主线内核
git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
cd linux
# 配置内核,我建议先拿当前系统的配置做底子
zcat /proc/config.gz > .config
make olddefconfig
# 编译内核(记得开多核)
make -j$(nproc)
嗯,这里要注意:编译内核时,CONFIG_DRM 和 CONFIG_DRM_GPU 这两个选项必须打开。我曾经帮一个同事排查问题,搞了半天发现他忘了开DRM支持,内核根本加载不了GPU驱动模块。
sudo apt-get install build-essential flex bison libncurses-dev libssl-dev
2.1.2 驱动模块的加载与调试
驱动写好了,怎么加载?我一般用 insmod 和 rmmod 这对组合。但说实话,生产环境千万别这么干,容易把系统搞崩。
# 编译驱动模块
make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
# 加载模块
sudo insmod gpu_driver.ko
# 查看内核日志
dmesg | tail -20
# 卸载模块
sudo rmmod gpu_driver
调试的话,我强烈推荐 printk 配合 dmesg。别嫌它土,在早期开发阶段,这比什么JTAG都好使。你想想看,GPU驱动刚跑起来,连个显示输出都没有,你拿什么图形化调试?
#define DEBUG,然后用 pr_debug() 打印信息。这样平时不输出,需要调试时通过 echo 'file gpu_driver.c +p' > /sys/kernel/debug/dynamic_debug/control 动态打开,非常灵活。
2.2 Windows WDDM驱动开发环境配置
Windows这边就复杂多了。WDDM(Windows Display Driver Model)是微软的GPU驱动框架,版本从WDDM 1.0到现在的WDDM 3.0,每个版本差异都挺大。我个人建议新手从WDDM 2.0开始学,因为文档最全,工具链也最成熟。
2.2.1 Visual Studio与WDK安装
首先,你得装Visual Studio 2022,然后装上Windows Driver Kit(WDK)。注意版本要匹配,VS2022配WDK 10.0.22621.0是我目前用得最稳的组合。
# 安装完成后,验证环境
# 打开 "Developer Command Prompt for VS 2022"
cl /?
# 如果能正常输出,说明编译环境OK
# 再打开 "x64 Native Tools Command Prompt"
msbuild /?
# 确认MSBuild可用
这里有个坑:WDK安装时默认不会装示例代码。我建议你手动勾选 "Windows Driver Kit Samples",里面有个 kmdf 目录下的 gpu 示例,是学习WDDM的绝佳起点。
2.2.2 WDDM驱动项目结构
一个标准的WDDM驱动,至少包含这几个文件:
| 文件 | 作用 |
|---|---|
| GpuDriver.c | 驱动入口,实现 DriverEntry 和 AddDevice |
| GpuAdapter.c | 适配器层,处理GPU硬件初始化 |
| GpuContext.c | 上下文管理,处理命令队列 |
| GpuAllocation.c | 显存分配与回收 |
| GpuDma.c | DMA操作,处理GPU与CPU的数据传输 |
说实话,WDDM的接口函数多得吓人,光 D3DKMT 开头的函数就有上百个。但别怕,核心就几个:D3DKMTOpenAdapterFromGpuDisplayName、D3DKMTQueryAdapterInfo、D3DKMTCreateContext。先把这些搞明白,其他的都是锦上添花。
bcdedit /set testsigning on
bcdedit /set loadoptions DISABLE_INTEGRITY_CHECKS
重启后,驱动就能加载未签名的版本了。
2.3 GPU模拟器与虚拟化环境
搞GPU驱动开发,最怕的就是把真机搞崩。我当年刚入行时,有一次调试显存分配,直接把显卡刷成了砖,那叫一个惨。所以,模拟器和虚拟化环境是必备的。
2.3.1 QEMU + virtio-gpu 模拟
QEMU配合virtio-gpu,是我最常用的模拟方案。它虽然性能不咋地,但胜在稳定,而且能完整模拟GPU的MMIO、中断、DMA等行为。
# 启动QEMU,开启virtio-gpu
qemu-system-x86_64 \
-m 4G \
-smp 4 \
-vga virtio \
-device virtio-gpu-pci \
-display sdl,gl=on \
-kernel /path/to/bzImage \
-initrd /path/to/initrd.img \
-append "console=ttyS0"
嗯,这里要注意:-display sdl,gl=on 这个参数很关键。如果不加,virtio-gpu只会用软件渲染,你根本测不出硬件加速的效果。我曾经在这个坑里浪费了两天,后来才发现是显示后端没开GL加速。
2.3.2 GPU Passthrough 与 SR-IOV
如果你需要更真实的测试环境,可以用GPU Passthrough。说白了,就是把物理显卡直接透传给虚拟机,让虚拟机里的驱动直接操作硬件。
但说实话,这个配置起来挺麻烦的。你需要:
- CPU支持VT-d(Intel)或AMD-Vi(AMD)
- 主板BIOS开启IOMMU
- 把GPU从宿主机驱动中解绑,再绑定到vfio-pci驱动
# 查看GPU的PCI地址
lspci | grep VGA
# 解绑GPU
echo "0000:01:00.0" > /sys/bus/pci/devices/0000:01:00.0/driver/unbind
# 绑定到vfio-pci
echo "vfio-pci" > /sys/bus/pci/devices/0000:01:00.0/driver_override
echo "0000:01:00.0" > /sys/bus/pci/drivers/vfio-pci/bind
2.3.3 软件模拟器:SWIFt与GPUOcelot
如果你只是想测试驱动逻辑,不想碰硬件,可以用纯软件模拟器。SWIFT(Simple Windows GPU Driver Framework)是我见过最轻量的GPU模拟器,它用软件模拟了WDDM的DDI接口,非常适合做单元测试。
GPUOcelot则更适合做CUDA驱动的模拟。它能把PTX指令翻译成LLVM IR,然后在CPU上执行。虽然慢,但能帮你验证指令调度的正确性。
好了,环境搭建这部分就讲到这里。下一章,我们会正式开始写GPU驱动的核心代码——从初始化到命令提交,一步步把GPU跑起来。你准备好了吗?