FreeBSD 中文社区 2025 第二季度问卷调查
FreeBSD 中文社区(CFC)
VitePress 镜像站QQ 群 787969044视频教程Ⅰ视频教程Ⅱ
  • FreeBSD 从入门到追忆
  • 中文期刊
  • 状态报告
  • 发行说明
  • 手册
  • 网络文章集锦
  • 笔记本支持报告
  • Port 开发者手册
  • 架构手册
  • 开发者手册
  • 中文 man 手册
  • 文章
  • 书籍
  • FreeBSD 中文期刊
  • 编辑日志
  • 2025-123 下游项目
    • FreeBSD 发布工程:新主管上任
    • GhostBSD:从易用到挣扎与重生
    • BSD Now 与将来
    • 字符设备驱动教程(第三部分)
    • 学会走路——连接 GPIO 系统
    • FreeBSD 中对 SYN 段的处理
    • FreeBSD 2024 年秋季峰会
  • 2024-1112 虚拟化
    • 字符设备驱动程序教程(第二部分)
    • 面向 Linux 和 Windows 用户的 bhyve
    • Xen 与 FreeBSD
    • Wifibox:一种嵌入式虚拟化无线路由器
    • 嵌入式 FreeBSD:Fabric——起步阶段
    • DGP:一种新的数据包控制方法
    • 会议报告:我在都柏林的 EuroBSDCon 体验
  • 2024-0910 内核开发
    • 字符设备驱动程序教程
    • VPP 移植到了 FreeBSD:基础用法
    • 利用 Kyua 的 Jail 功能提升 FreeBSD 测试套件的并行效率
    • FreeBSD 上的 Valgrind
    • 嵌入式 FreeBSD:探索 bhyve
    • TCP/IP 历险记:FreeBSD TCP 协议栈中的 Pacing
    • 实用软件:实现无纸化(Paperless)
  • 2024-0708 存储与文件系统
    • FreeBSD 中的 NVMe-oF
    • FreeBSD iSCSI 入门
    • 使用 ZFS 原生加密保护数据
    • 嵌入式 FreeBSD:打造自己的镜像
    • TCP LRO 简介
    • 基于 Samba 的时间机器备份
  • 2024-0506 配置管理对决
    • 基本系统中的 mfsBSD
    • rdist
    • Hashicorp Vault
    • 在 GitHub 上向 FreeBSD 提交 PR
    • 悼念 Mike Karels
    • 2024 年 5-6 月来信
    • 嵌入式 FreeBSD 面包板
    • TCP/IP 历险记:TCP BBLog
    • 实用软件:开发定制 Ansible 模块
  • 2024-0304 开发工作流与集成
    • FreeBSD 内核开发工作流程
    • FreeBSD 与 KDE 持续集成(CI)
    • 更现代的内核调试工具
    • 从零开始的 ZFS 镜像及 makefs -t zfs
    • 提升 Git 使用体验
  • 2024-0102 网络(十周年)
    • FreeBSD 中的 RACK 栈和替代 TCP 栈
    • FreeBSD 14 中有关 TCP 的更新
    • if_ovpn 还是 OpenVPN
    • SR-IOV 已成为 FreeBSD 的重要功能
    • FreeBSD 接口 API(IfAPI)
    • BATMAN:更优的可移动热点网络方式
    • 配置自己的 VPN——基于 FreeBSD、Wireguard、IPv6 和广告拦截
    • 实用软件:使用 Zabbix 监控主机
  • 2023-1112 FreeBSD 14.0
    • LinuxBoot:从 Linux 启动 FreeBSD
    • FreeBSD 容器镜像
    • 现在用 Webhook 触发我
    • 新的 Ports 提交者:oel Bodenmann (jbo@freebsd.org)
  • 2023-0910 Port 与软件包
    • 回忆录:与 Warner Losh(@imp)的访谈
    • 在你自己的仓库中定制 Poudriere 源
    • Wazuh 和 MITRE Caldera 在 FreeBSD Jail 中的使用
    • PEP 517
    • CCCamp 2023 旅行报告
  • 2023-0708 容器与云
    • 在 Firecracker 上的 FreeBSD
    • 使用 pot 和 nomad 管理 Jail
    • 会议报告:C 与 BSD 正如拉丁语与我们——一位神学家的旅程
    • 抒怀之旅:与 Doug Rabson 的访谈
    • 基于 Jail 的广告拦截教程
    • 我们收到的来信
  • 2023-0506 FreeBSD 三十周年纪念特刊
    • CheriBSD 近十多年的历程
    • AArch64:成为 FreeBSD 新的一级架构
    • 岁月如梭:我个人的时间线
    • 安装 FreeBSD 1.0:回顾 30 年前
    • ZFS 是如何进入 FreeBSD 的呢?
    • 我不是来自约克郡的,我保证!
    • 回忆录:采访 David Greenman Lawrence
    • FreeBSD 和早期的 Unix 社区
    • 早期的 FreeBSD 移植
    • FreeBSD 30 周年:成功的秘诀
    • FreeBSD 在日本:回忆之旅与今日之实
  • 2023-0304 嵌入式
    • CheriBSD port 和软件包
    • 让我们来试试 ChatGPT
    • GPU 直通
  • 2023-0102 构建 FreEBSD Web 服务器
    • ZFS 的原子 I/O 与 PostgreSQL
    • 虚拟实验室——BSD 编程研讨会
    • ZFS 简介
    • 会议报告:落基山庆祝女性计算机科学家
    • 进行中的工作/征求反馈:数据包批处理
    • 基金会与 FreeBSD 桌面
  • 2022-1112 可观测性和衡量标准
    • 在 FreeBSD 的 DDB 内核调试器中编写自定义命令
    • DTrace:老式跟踪系统的新扩展
    • 基于证书的 Icinga 监控
    • 活动监控脚本(activitymonitor.sh)
    • 实用 IPv6(第四部分)
    • EuroBSDCon 会议报道
    • 实用 Port:Prometheus 的安装与配置
    • 书评:《用火解决问题:管理老化的计算机系统(并为现代系统保驾护航)》Kill It with Fire: Manage Aging Computer Systems (and Future Proof Modern Ones)
  • 2022-0910 安全性
    • CARP 简介
    • 重构内核加密服务框架
    • PAM 小窍门
    • SSH 小窍门
    • 实用 IPv6(第三部分)
    • 书评:Understanding Software Dynamics(深入理解软件性能——一种动态视角)—— Richard L. Sites 著
    • 访谈:保障 FreeBSD 安全性
    • MCH 2022 会议报告
  • 2022-0708 科研、系统与 FreeBSD
    • 在 FreeBSD 上构建 Loom 框架
    • 教授本科生 Unix 课程
    • FreeBSD 入门研讨会
    • 实用 IPv6(第二部分)
    • 在 2022 年及以后推广 FreeBSD
    • 进行中的工作/征求反馈:Socket 缓冲区
    • FreeBSD 开发者峰会报告
    • 支持 Electromagnetic Field 2022
  • 2022-0506 灾难恢复
    • 使用 FreeBSD 构建高弹性的私有云
    • LLDB 14 —— FreeBSD 新调试器
    • 实用 IPv6(第一部分)
    • 利用 netdump(4) 进行事后内核调试
    • 进行中的工作/征求反馈:FreeBSD 启动性能
    • 实用 Port:在 OpenZFS 上设置 NFSv4 文件服务器
  • 2022-0304 ARM64 是一级架构
    • FreeBSD/ARM64 上的数据科学
    • Pinebook Pro 上的 FreeBSD
    • 嵌入式控制器的 ACPI 支持
    • 进行中的工作/征求反馈:Lumina 桌面征集开发人员
    • 实用 Port:如何设置 Apple 时间机器
  • 2022-0102 软件与系统管理
    • 为 FreeBSD Ports 做贡献
    • 使用 Git 贡献到 FreeBSD Ports
    • CBSD:第一部分——生产环境
    • 将 OpenBSD 的 pf syncookie 代码移植到 FreeBSD 的 pf
    • 进行中的工作/征求反馈:mkjail
    • 《编程智慧:编程鬼才的经验和思考》(The Kollected Kode Vicious)书评
    • 会议报告:EuroBSDCon 2021 我的第一次 EuroBSDCon:一位新组织者的视角
  • 2021-1112 存储
    • 开放通道 SSD
    • 构建 FreeBSD 社区
    • 与完美操作系统同行 27 年
    • 进行中的工作/征求反馈:OccamBSD
    • 通过 iSCSI 导入 ZFS ZIL——不要在工作中这样做——就像我做的那样
  • 2021-0910 FreeBSD 开发
    • FreeBSD 代码审查与 git-arc
    • 如何为 FreeBSD 实现简单的 USB 驱动程序
    • 内核开发技巧
    • 程序员编程杂谈
  • 2021-0708 桌面/无线网
    • 通往 FreeBSD 桌面的直线路径
    • FreeBSD 13 中的人机接口设备 (HID) 支持
    • Panfrost 驱动程序
    • 用 Git 更新 FreeBSD
    • FreeBSD 的新面孔
    • 想给你的桌面加点佐料?
  • 2021-0506 安全
    • 七种提升新安装 FreeBSD 安全性的方法
    • copyinout 框架
    • 使用 TLS 改善 NFS 安全性
    • Capsicum 案例研究:Got
    • 对 Jail 进行安全扫描
  • 2021-0304 FreeBSD 13.0
    • 展望未来
    • FreeBSD 13.0 工具链
    • FreeBSD 13.0 中有新加载器吗?
    • TCP Cubic 准备起飞
    • OpenZFS 中的 Zstandard 压缩
    • 会议报告:FreeBSD 供应商峰会
    • Git 不够吗?
  • 2021-0102 案例研究
    • Tarsnap 的 FreeBSD 集群
    • BALLY WULFF
    • Netflix Open Connect
    • FreeBSD 的新面孔
    • 写作学者的 FreeBSD
    • 在世界之巅
  • 2020-1112 工作流/持续集成(CI)
    • FreeBSD Git 快速入门
    • 使用 syzkaller 进行内核 Fuzzing
    • Mastering Vim Quickly 书评
    • 线上会议实用技巧
    • 在控制台上进行网络监控
  • 2020-0910 贡献与入门
    • 采访:Warner Losh,第 2 部分
    • 代码审查
    • 撰写良好的提交消息
    • 如何在不是程序员的情况下做出贡献——成为 FreeBSD 译者
    • 如何成为文档提交者
    • 谷歌编程之夏
    • 为 FreeBSD 期刊撰写文章
    • 你为什么使用 FreeBSD
    • FreeBSD 的新面孔
  • 2020-0708 基准测试/调优
    • FreeBSD Friday
    • 采访:Warner Losh,第 1 部分
    • 构建和运行开源社区
    • 在 FreeBSD 上轻松搭建我的世界(Minecraft)服务器
    • FreeBSD 的新面孔
  • 2020-0506 网络性能
    • 内核中的 TLS 卸载
    • 访谈:Michael W Lucas
    • FreeBSD 桌面发行版
    • 使用 Poudriere 进行 Port 批量管理
    • FreeBSD 的新面孔
由 GitBook 提供支持
LogoLogo

FreeBSD 中文社区(CFC) 2025

在本页
  • 概述
  • 配置
  • 桌面设置
  • 源代码
  • 开发专用的自定义 KERNCONF
  • 将源代码传输到构建机
  • 构建机设置
  • 构建配置
  • 网络配置
  • vm-bhyve (bhyve 前端)
  • 镜像种子
  • 新建测试虚拟机
  • 第一次启动
  • 在虚拟机中进行 PCIe 设备驱动开发
  • 主工作流循环(编辑、构建、安装、测试、重复)
  • 编辑
  • 构建
  • 安装
  • 测试
  • 调试测试内核
  • 进入调试器
  • 源代码级调试与 gdb
  • 实时远程调试
  • 核心转储分析
在GitHub上编辑
导出为 PDF
  1. 2024-0304 开发工作流与集成

FreeBSD 内核开发工作流程

上一页实用软件:开发定制 Ansible 模块下一页FreeBSD 与 KDE 持续集成(CI)

最后更新于1个月前

  • 原文链接:

  • 作者:Navdeep Parhar

如同其他软件那样,内核亦采用传统的工作流程进行开发,即克隆、编辑、构建、测试、调试和提交。但与用户空间软件不同,内核开发必然涉及反复重启(以及死锁和内核 panic),而且没有专用的测试系统,开发过程会非常不便。本文介绍了一种适用于内核开发的实用设置,使用虚拟机进行测试和调试。

基于虚拟机的内核开发设置有诸多优点:

  • 隔离性:虚拟机重启和崩溃不会影响主机系统。

  • 速度:虚拟机的重启速度远快于裸系统。

  • 可调试性:虚拟机易于设置用于实时源代码级的内核调试。

  • 灵活性:虚拟机可以用来构建“盒中网络”,进行网络代码的开发,而无需实际的物理网络。例如,可以在飞机上的笔记本里进行开发。

  • 可管理性:虚拟机易于创建、重新配置、克隆和迁移。

概述

用于构建内核的系统还运行着测试虚拟机,所有虚拟机都连接到一个内部桥接网络。主机提供 DHCP、DNS 和其他服务,供配置在桥接网络上的 IP 网络使用。源代码和构建产物都保存在主机上的一个自包含工作区域内,并通过内部网络导出。工作区域挂载在测试虚拟机内,并从那里安装新的测试内核。虚拟机配置有额外的串口用于远程内核调试,其中虚拟机内核中的 gdb 调试器与主机系统上的 gdb 客户端通过虚拟空调制解调器连接。

最简单的设置方式是使用一台系统(通常是笔记本和工作站)来处理所有任务。这是最便捷的开发环境,但该设备必须有足够的资源(CPU、内存和存储)来构建 FreeBSD 源代码、运行虚拟机。

在工作环境中,更常见的是有专用的开发和测试服务器,分别与开发人员的工作站分离。服务器级系统比桌面系统规格更高,更适合构建源代码和运行虚拟机。它们还提供了更多种类的 PCIe 扩展槽,非常适合 PCIe 设备驱动开发。

配置

本文其余部分假设使用两台系统,并使用主机名“desktop”和“builder”来指代它们。源代码的主副本存储在 desktop 上,用户在此编辑并同步到 builder。接下来的操作都在 builder 上以 root 身份进行。builder 在其内部网络中也被称为“vmhost”。

WS=dev
DWSDIR=~/work/ws
WSDIR=/ws

在 desktop 上的所有检出的代码都假设位于一个公共父目录 ${DWSDIR} 下的 ${WS} 目录中。示例使用位于 ~user/work/ws 目录中的工作区“dev”。builder 上的目录 ${WSDIR} 是一个自包含的工作区,包含构建配置文件、源代码、共享的目录 obj 以及 gdb 的 sysroot。示例中使用 builder 上的 /ws 路径。

桌面设置

源代码

通过克隆官方仓库或镜像中的分支来创建本地工作副本。

desktop# pkg install git

desktop$ git ls-remote https://git.freebsd.org/src.git heads/main heads/stable/*

desktop$ git clone --single-branch -b main ${REPO} ${DWSDIR}/${WS}
desktop$ git clone --single-branch -b main https://git.freebsd.org/src.git ~/work/ws/dev

开发专用的自定义 KERNCONF

每个内核都是根据一个纯文本的内核配置(KERNCONF)文件构建的。在传统上,内核的标识字符串(uname -i 的输出)与其配置文件的名称相匹配。例如,GENERIC 内核是从名为 GENERIC 的文件构建的。为了调试和诊断,KERNCONF 中有许多参数,在早期开发过程中启用这些参数非常有用。主分支中的 GENERIC 配置已经启用了一个恰当的子集,适合开发工作。然而,现代编译器似乎会在低优化级别下也会 aggressively 优化掉变量和其他调试信息,因此有时需要禁用所有优化来构建内核。可以使用此处显示的自定义 KERNCONF(名为 DEBUG0)来实现这一目的。一般,包含现有配置再使用 nooptions/options 和 nomakeoptions/makeoptions 来进行调整,回比从头开始编写配置文件更为简单。

参数 DEBUG 会被添加到内核和模块的编译参数中。需增加堆栈大小,以适应未优化代码的更大堆栈占用。

desktop$ cat ${DWSDIR}/${WS}/sys/amd64/conf/DEBUG0
desktop$ cat ~/work/ws/dev/sys/amd64/conf/DEBUG0
include GENERIC
ident DEBUG0
nomakeoptions DEBUG
makeoptions DEBUG=”-g -O0”
options KSTACK_PAGES=16

将源代码传输到构建机

将源代码复制到构建机,在构建机上以 root 用户身份进行构建。切记,在对桌面上的源代码进行更改、在构建机上进行构建之前,要先同步内容。

desktop# pkg install rsync

desktop$ rsync -azO --del --no-o --no-g ${DWSDIR}/${DWS} root@builder:${WSDIR}/src/
desktop$ rsync -azO --del --no-o --no-g ~/work/ws/dev root@builder:/ws/src/

构建机设置

构建配置

在构建机的工作区中创建文件 make.conf 和 src.conf,而不要去修改 /etc 中的全局配置文件。目录 obj 也位于工作区,而非 /usr/obj。使用 meta 模式进行快速增量重建。meta 模式需要 filemon。在默认情况下,KERNCONF= 列表中的所有内核都会被构建,第一个内核会被安装。可以在命令行中用一个 KERNCONF 来覆盖默认设置。

builder# kldload -n filemon
builder# sysrc kld_list+=”filemon”

builder# mkdir -p $WSDIR/src $WSDIR/obj $WSDIR/sysroot
builder# mkdir -p /ws/src /ws/obj /ws/sysroot

builder# cat $WSDIR/src/src-env.conf
builder# cat /ws/src/src-env.conf
MAKEOBJDIRPREFIX?=/ws/obj
WITH_META_MODE=”YES”
builder# cat /ws/src/make.conf
KERNCONF=DEBUG0 GENERIC-NODEBUG
INSTKERNNAME?=dev
builder# cat /ws/src/src.conf
WITHOUT_REPRODUCIBLE_BUILD=”YES”

网络配置

首先,选择一个未使用的网络和子网掩码来作为内部网络。示例中使用的是 192.168.200.0/24。第一个主机(192.168.200.1)始终是虚拟机主机(构建机)。两位数的主机号保留给已知虚拟机。三位数的主机号(例如 100)由 DHCP 服务器分配给未知虚拟机。

  1. 为作为虚拟交换机连接所有虚拟机和主机的桥接接口创建桥接接口,同时为桥接接口分配固定的 IP 地址和主机名。

    builder# echo '192.168.200.1 vmhost' >> /etc/hosts
    builder# sysrc cloned_interfaces="bridge0"
    builder# sysrc ifconfig_bridge0="inet vmhost/24 up"
    builder# service netif start bridge0
  2. 配置主机进行 IP 转发和 NAT,供虚拟机使用。这个步骤是可选的,仅当虚拟机需要访问外部网络时才进行配置。示例中使用的公共接口是 igb1。

    builder# cat /etc/pf.conf
    ext_if="igb1"
    int_if="bridge0"
    set skip on lo0
    scrub in
    nat on $ext_if inet from !($ext_if) -> ($ext_if)
    pass out
    builder# sysrc pf_enable="YES"
    builder# sysrc gateway_enable="YES"
  3. 在主机上启动 ntpd 服务,DHCP 服务器将把自己作为 ntp 服务器提供给虚拟机。

    builder# sysrc ntpd_enable="YES"
    builder# service ntpd start
  4. 配置 DHCP 和 DNS。 安装 dnsmasq,并将其配置为内部网络的 DHCP 和 DNS 服务器。

    builder# pkg install dnsmasq
    builder# cat /usr/local/etc/dnsmasq.conf
    no-poll
    interface=bridge0
    domain=vmlan,192.168.200.0/24
    localhost-record=vmhost,vmhost.vmlan,192.168.200.1
    synth-domain=vmlan,192.168.200.100,192.168.200.199,anon-vm*
    dhcp-range=192.168.200.100,192.168.200.199,255.255.255.0
    dhcp-option=option:domain-search,vmlan
    dhcp-option=option:ntp-server,192.168.200.1
    dhcp-hostsfile=/ws/vm-dhcp.conf

    将其添加为本地 resolv.conf 文件中的第一个名称服务器。dnsmasq 解析器仅为内部网络和构建机的回环接口提供服务。

    builder# sysrc dnsmasq_enable="YES"
    builder# service dnsmasq start
    builder# head /etc/resolv.conf
    search corp-net.example.com
    nameserver 127.0.0.1
    ...
    1. 将整个工作区导出到内部网络。

      builder# cat /etc/exports
      /ws/ws -ro -mapall=root -network 192.168.200.0/24
      builder# sysrc nfs_server_enable="YES"
      builder# sysrc nfsv4_server_only="YES"
      builder# service nfsd start

vm-bhyve (bhyve 前端)

vm-bhyve 是一款易于使用的 bhyve 前端工具。

选择一个 ZFS 存储池来存储虚拟机数据,并在存储池上创建一个用于 vm-bhyve 的数据集。在 rc.conf 中指定该存储池和数据集的名称,在正确设置 vm_dir 后初始化 vm-bhyve。

builder# kldload -n vmm
builder# kldload -n nmdm
builder# sysrc kld_list+=”vmm nmdm”
builder# pkg install vm-bhyve
builder# zfs create rpool/vm
builder# sysrc vm_dir=”zfs:rpool/vm”
builder# vm init
builder# sysrc vm_enable=”YES”
builder# service vm start

所有虚拟机都将使用文本模式的串口控制台,可通过 tmux 访问。

builder# pkg install tmux
builder# vm set console=tmux

将之前创建的桥接接口添加为 vm-bhyve 的交换机。

builder# vm switch create -t manual -b bridge0 vmlan

为新虚拟机设置合理的默认值。根据需要编辑默认模板文件 $vm_dir/.templates/default.conf。至少指定 2 个串口—一个用于串口控制台,另一个用于远程调试。将所有新虚拟机连接到 vmlan 交换机。

builder# vim /rpool/vm/.templates/default.conf
loader=”uefi”
cpu=2
memory=2G
comports=”com1 com2”
network0_type=”virtio-net”
network0_switch=”vmlan”
disk0_size=”20G”
disk0_type=”virtio-blk”
disk0_name=”disk0.img”

镜像种子

将 FreeBSD 安装在新虚拟机中最简单的方法是使用一个已经预安装 FreeBSD 的磁盘镜像。虚拟机将启动默认内核/开发内核,其用户空间需要与二者兼容,因此最好使用与开发相同版本的 FreeBSD。

可以在 FreeBSD.org 获取 RELEASE 版本和 main 分支,STABLE 分支的最新快照的磁盘镜像。

# fetch https://download.freebsd.org/releases/VM-IMAGES/14.0-RELEASE/amd64/Latest/FreeBSD-14.0-RELEASE-amd64.raw.xz
# fetch https://download.freebsd.org/snapshots/VM-IMAGES/15.0-CURRENT/amd64/Latest/FreeBSD-15.0-CURRENT-amd64.raw.xz

# unxz -c FreeBSD-14.0-RELEASE-amd64.raw.xz > seed-14_0.img
# unxz -c FreeBSD-15.0-CURRENT-amd64.raw.xz > seed-main.img
# du -Ash seed-main.img; du -sh seed-main.img
6.0G seed-main.img
1.6G seed-main.img

也可以从源代码构建磁盘镜像。以下示例展示了如何构建一个无调试内核且进行了一些其他节省空间的参数的镜像。

# cd /usr/src
# make -j1C KERNCONF=GENERIC-NODEBUG buildworld buildkernel
# make -j1C -C release WITH_VMIMAGES=1 clean obj
# make -j1C -C release WITHOUT_KERNEL_SYMBOLS=1 WITHOUT_DEBUG_FILES=1 \
NOPORTS=1 NOSRC=1 WITH_VMIMAGES=1 VMFORMATS=raw VMSIZE=4g SWAPSIZE=2g \
KERNCONF=GENERIC-NODEBUG vm-image

# cp /usr/obj/usr/src/amd64.amd64/release/vm.ufs.raw seed-main.img
# du -Ash seed-main.img; du -sh seed-main.img
6.0G seed-main.img
626M seed-main.img

修改标准镜像,以便将其用作内部网络上的测试虚拟机。

从镜像中创建一个内存磁盘并挂载 UFS 分区。当虚拟机启动时,这将是预安装操作系统的根分区。

# mdconfig -af seed-main.img
md0
# gpart show -p md0
# mount /dev/md0p4 /mnt

从 rc.conf 中删除主机名配置,以强制使用 DHCP 服务器提供的主机名。

# sysrc -R /mnt -x hostname
# sysrc -R /mnt -x ifconfig_DEFAULT
# sysrc -R /mnt ifconfig_vtnet0=”SYNCDHCP”
# sysrc -R /mnt ntpd_enable=”YES”
# sysrc -R /mnt ntpd_sync_on_start=”YES”
# sysrc -R /mnt kld_list+=”filemon”

启用 SSH 访问虚拟机。请注意,这是个实验环境,网络在实验室内,并且不担心作为 root 用户运行及重用相同的主机密钥。将主机密钥和 root 用户的 .ssh 文件复制到正确的位置。在所有虚拟机上使用相同的密钥很方便。更新 SSH 配置以允许 root 登录并启用 SSH 服务。

# cp -a .../vm-ssh-hostkeys/ssh_host_*key* /mnt/etc/ssh/
# cp -a .../vm-root-dotssh /mnt/root/.ssh
# vim /mnt/etc/sshd_config
PermitRootLogin yes
# sysrc -R /mnt sshd_enable=”YES”

将第一个串口配置为潜在控制台,将第二个串口配置为远程内核调试端口。

# vim /mnt/boot/loader.conf
kern.msgbuf_show_timestamp=”2”
hint.uart.0.flags=”0x10”
hint.uart.1.flags=”0x80”

创建工作区的挂载点,并在 fstab 中添加条目,以便在启动时挂载。/dev/fd 和 /proc 一般是有用的。

# mkdir -p /mnt/ws
# vim /mnt/etc/fstab
...
fdesc   /dev/fd fdescfs rw      0       0
proc    /proc   procfs  rw      0       0
vmhost:/ /ws nfs ro,nfsv4 0 0

待就绪,卸载并销毁内存磁盘。

# umount /mnt
# mdconfig -du 0

seed-main.img 文件已准备好使用。

新建测试虚拟机

创建一个新的虚拟机并记录其自动生成的 MAC 地址。更新配置,使得 DHCP 服务为已知虚拟机提供分配的主机名和 IP 地址。这些静态分配的地址不能与动态地址池中的地址重叠。本文中的约定是使用两位数字的主机号来表示已知虚拟机,三位数字的主机号表示动态分配的 dhcp-range。

创建一个 dhcp-host 条目,包含分配给虚拟机的主机名、其 MAC 地址和一个固定的 IP 地址,该地址不属于动态范围。然后重新加载解析器。

builder# vm create vm0
builder# vm info vm0 | grep fixed-mac-address
builder# echo ‘vm0,58:9c:fc:03:40:dc,192.168.200.10’ >> /ws/vm-dhcp.conf
builder# service dnsmasq reload

将 disk0.img 文件替换为种子镜像的副本,再将其大小增加到所需的虚拟机磁盘大小。虚拟机的磁盘镜像可以在虚拟机停止运行时随时调整大小。第一次启动虚拟机时使用调整大小后的磁盘时,运行“service growfs onestart”。

builder# cp seed-main.img /rpool/vm/vm0/disk0.img
builder# truncate -s 30G /rpool/vm/vm0/disk0.img

第一次启动

在首次启动前,检查虚拟机的配置。

builder# vm configure vm0

以前台启动虚拟机并进入控制台,或在后台启动虚拟机后再连接到其控制台。控制台只是一个名为虚拟机名称的 tmux 会话。

builder# vm start -i vm0

builder# vm start vm0
builder# vm console vm0

第一次启动虚拟机时,检验以下内容:

  • 虚拟机的主机名是由 DHCP 服务器分配的。登录提示中可以看到主机名和 tty。

    FreeBSD/amd64 (vm0) (ttyu0)
    login:
  • 虚拟机的 uart0 是控制台,uart1 用于远程调试。

    vm0# dmesg | grep uart
    [1.002244] uart0: console (115200,n,8,1)
    ...
    [1.002252] uart1: debug port (115200,n,8,1)
  • 工作区已挂载到预期位置。

    vm0# mount | grep nfs
    vmhost:/ on /ws (nfs, read-only, nfsv4acls)
    vm0# ls /ws
    ...
  • 可以通过 SSH 在物理机和桌面访问(使用虚拟机物理机作为跳板)虚拟机。

    builder# ssh root@vm0
    
    desktop$ ssh -J root@builder root@vm0

在虚拟机中进行 PCIe 设备驱动开发

PCI 直通允许物理机将 PCIe 设备导出(通过直通)到虚拟机,从而让虚拟机直接访问 PCIe 设备。能在虚拟机内进行真实 PCIe 硬件的设备驱动开发。

设备由物理机上的驱动程序 ppt 声明,并在虚拟机中显示为连接到虚拟机的 PCIe 根复合体。系统上的 PCIe 设备由 BSF(或 BDF)三元组标识,在虚拟机中可能会有所不同。

使用 pciconf 和 vm-bhyve 能获取系统中 PCIe 设备的列表,并记录需要直通的设备的 BSF 三元组。注意,pciconf 选择器以冒号分隔 BSF,而 bhyve/vmm/ppt 使用斜杠(/)分隔来标识设备。例如,选择器为“none193@pci0:136:0:4”的 PCIe 设备,在 bhyve/ppt 标注中是“136/0/4”。

builder# pciconf -ll
builder# vm passthru

让驱动程序 ppt 声明将要直通的设备。这可以防止正常驱动程序附加到该设备。

builder# vim /boot/loader.conf
pptdevs="136/0/4 137/0/4"

重启以使 loader.conf 的更改生效,或者尝试在系统运行时将设备从其驱动程序中分离并附加到 ppt。

builder# devctl detach pci0:136:0:4
builder# devctl clear driver pci0:136:0:4
builder# devctl set driver pci0:136:0:4 ppt
# (对 137 设备重复以上步骤)

验证 ppt 驱动程序是否附加到设备,并检查 vm-bhyve 是否已准备好使用它们。

builder# pciconf -ll | grep ppt
ppt0@pci0:136:0:4:      020000   00   00   1425   640d   1425   0000
ppt1@pci0:137:0:4:      020000   00   00   1425   640d   1425   0000
builder# vm passthru | awk ‘NR == 1 || $3 != “No” {print}’
DEVICE     BHYVE ID     READY        DESCRIPTION
ppt0       136/0/4      Yes         T62100-CR Unified Wire Ethernet Controller
ppt1       137/0/4      Yes          T62100-CR Unified Wire Ethernet Controller

重新配置测试虚拟机,并列出应直通到该虚拟机的设备。

builder# vm configure vm0
passthru0="136/0/4"
passthru1="137/0/4"

启动测试虚拟机并验证 PCIe 设备是否可见。请注意,虚拟机中的 BSF 与物理机中的实际硬件 BSF 不同。

vm0# pciconf -ll
...
none0@pci0:0:6:0:       020000   00   00   1425   640d   1425   0000
none1@pci0:0:7:0:       020000   00   00   1425   640d   1425   0000
...

主工作流循环(编辑、构建、安装、测试、重复)

编辑

在桌面上编辑源代码,再将其发送到构建机。

desktop$ cd ~/work/ws/dev
desktop$ gvim sys/foo/bar.c
...
desktop$ rsync -azO --del --no-o --no-g ~/work/ws/dev root@builder:/ws/src/

构建

builder# alias wsmake=’__MAKE_CONF=${WSDIR}/src/make.conf SRC_ENV_CONF=${WSDIR}/src/src-env.conf SRCCONF=${WSDIR}/src/src.conf make -j1C’
builder# alias wsmake=’__MAKE_CONF=/ws/src/make.conf SRC_ENV_CONF=/ws/src/src-env.conf SRCCONF=/ws/src/src.conf make -j1C’

builder# cd ${WSDIR}/src/${WS}
builder# cd /ws/src/dev
builder# wsmake kernel-toolchain(仅需一次)
builder# wsmake buildkernel

安装

  1. 将内核安装到虚拟机中。INSTKERNNAME 在 make.conf 中设置,因此 /boot/${INSTKERNNAME} 中的测试内核不会与 /boot/kernel 中的原始内核冲突,后者是发生问题时的安全回退。亦可在命令行中显式指定。

    vm0# alias wsmake='__MAKE_CONF=${WSDIR}/src/make.conf SRC_ENV_CONF=${WSDIR}/src/src-env.conf SRCCONF=${WSDIR}/src/src.conf make -j1C'
    vm0# alias wsmake='__MAKE_CONF=/ws/src/make.conf SRC_ENV_CONF=/ws/src/src-env.conf SRCCONF=/ws/src/src.conf make -j1C'
    
    vm0# cd ${WSDIR}/src/${WS}
    vm0# cd /ws/src/dev
    vm0# wsmake installkernel
  2. 如果在构建机上使用 gdb 进行源代码级调试,还需要将内核安装到构建机的 sysroot 中。使用与虚拟机中相同的 INSTKERNNAME 和 KERNCONF。

    builder# cd /ws/src/dev
    builder# wsmake installkernel DESTDIR=/ws/sysroot

测试

选择下次重启时使用的测试内核,或永久性地设置。

vm0# nextboot -k ${WS}
vm0# nextboot -k dev
vm0# shutdown -r now

vm0# sysrc -f /boot/loader.conf kernel=”${WS}”
vm0# sysrc -f /boot/loader.conf kernel=”dev”
vm0# shutdown -r now

在初始测试时,使用调试版的 KERNCONF(例如,之前提到的自定义 DEBUG0 或主线中的 GENERIC)是个好习惯,然后可以切换到发布版内核(例如,主线中的 GENERIC-NODEBUG)。

调试测试内核

验证当前是否正在运行测试内核。

vm0# uname -i
DEBUG0
vm0# sysctl kern.bootfile
kern.bootfile: /boot/dev/kernel

后端

提供了两个调试后端,并且可以随时切换当前后端。

vm0# sysctl debug.kdb.available
vm0# sysctl debug.kdb.current

vm0# sysctl debug.kdb.current=ddb
vm0# sysctl debug.kdb.current=gdb

进入调试器

  1. 自动:当发生 panic 时。如果设置了此 sysctl,内核会在发生 panic 时进入调试器(而非重启)。

    vm0# sysctl debug.debugger_on_panic
  2. 手动:从虚拟机内部进入。

    vm0# sysctl debug.kdb.enter=1
  3. 手动:从物理机进入。如果虚拟机卡住且无响应,可以向虚拟机注入一个 NMI。

    builder# bhyvectl --vm=vm0 --inject-nmi

源代码级调试与 gdb

源代码级调试需要源代码、二进制文件和调试文件,这些文件在物理机和虚拟机中都有,但位置各异。

实时远程调试

确保调试后端设置为 gdb。如果虚拟机已经通过 ddb 后端进入调试器,可以通过交互方式切换到 gdb 后端。

vm0# sysctl debug.kdb.current=gdb

db> gdb

当内核进入调试器时,内核中的远程 gdb 存根会激活。从物理机连接到 gdb 存根。连接通过虚拟串口线进行,该线连接到虚拟机的第二个串口(虚拟机内部的 uart1)。

builder# gdb -iex ‘set sysroot ${WSDIR}/sysroot’ -ex ‘target remote /dev/nmdm-${VM}.2B’ ${WSDIR}/sysroot/boot/${INSTKERNNAME}/kernel
builder# gdb -iex ‘set sysroot /ws/sysroot’ -ex ‘target remote /dev/nmdm-vm0.2B’ /ws/sysroot/boot/dev/kernel

核心转储分析

与实时调试相同,只是目标是 vmcore,而非远程调试。

builder# gdb -iex ‘set sysroot ${WSDIR}/sysroot’ -ex ‘target vmcore ${VMCORE}’ ${WSDIR}/sysroot/boot/${INSTKERNNAME}/kernel

builder# scp root@vm0:/var/crash/vmcore.0 /ws/tmp/
builder# gdb -iex ‘set sysroot /ws/sysroot’ -ex ‘target vmcore /ws/tmp/vmcore.0’ /ws/sysroot/boot/dev/kernel

Navdeep Parhar 使用 FreeBSD 已逾 20 年,自 2009 年起成为 FreeBSD 开发者。他目前在 Chelsio Communications 工作,负责为 Chelsio Terminator 系列网卡开发 FreeBSD 软件。他是 cxgbe(4) 驱动程序的作者和维护者,感兴趣的领域包括网络栈、设备驱动程序、通用内核调试和分析。

可以在 下载 FreeBSD 源代码。通常在主分支上进行新的开发工作,适合最新稳定分支的更改会在经过一段时间的稳定期后从主分支合并回去。

https://git.FreeBSD.org/src.git
FreeBSD Kernel Development Workflow