- 操作系统
- 驱动开发
【免费下载链接】darwin-xnu
Legacy mirror of Darwin Kernel. Replaced by https://github.com/apple-oss-distributions/xnu
XNU(X is Not Unix)是 Darwin 操作系统的核心,也是 macOS 与 iOS 的基础内核。它融合了 CMU 的 Mach 微内核、FreeBSD 子系统与 C++ 编写的 IOKit 驱动框架,形成独特的混合内核。本文以仓库根目录的 README.md 为骨架,结合源码与配套文档,系统讲解 XNU 的目录结构、内核变体(debug / development / release / profile)构建方法、kernelcache 生成与安全回退、kdp 远程调试、条件编译规范、头文件安装机制、内核自检(XNUPOST)与用户态测试,并深入libkdd内核分块数据格式,帮助你从源码层面理解并驾驭这一核心系统。
XNU 是什么:一个“X 不是 Unix”的混合内核
XNU 全称X is Not Unix,是 Darwin 操作系统(macOS 与 iOS 的基础系统)的内核。它并非单一架构的内核,而是三种技术路线的组合(见 README.md):
- Mach 内核:源自卡内基梅隆大学(CMU)的微内核研究成果,负责任务/线程调度、虚拟内存、IPC 端口等底层抽象,对应仓库中的
osfmk/目录; - FreeBSD 子系统:提供 POSIX 风格的系统调用、网络协议栈、文件系统与安全模型,对应
bsd/目录; - IOKit:一套 C++ API 驱动框架,用于编写设备驱动与内核扩展(kext),对应
libkern/与iokit/目录。
README 明确说明 XNU 支持x86_64 架构的单处理器与多处理器配置。这一“混合”定位决定了 XNU 的目录组织、构建系统与测试策略都围绕三大部分展开。
XNU 源码树:理解仓库顶层目录
README 给出了源码树的顶层目录清单,理解它们是对后续构建与开发的基础:
| 目录 | 职责 |
|---|---|
config/ | 受支持架构与平台的导出 API 配置(如MASTER、各.exports文件) |
SETUP/ | 内核配置、版本管理(newvers)与 kextsymbol 管理的基础工具集 |
EXTERNAL_HEADERS/ | 从其他项目同步的头文件,避免构建时产生依赖环,需随上游源码更新而同步 |
libkern/ | C++ IOKit 库代码,负责驱动与 kext 的处理 |
libsa/ | 内核启动引导代码 |
libsyscall/ | 面向用户态程序的 syscall 库接口 |
libkdd/ | 解析内核分块数据(kernel chunked data)的用户态库 |
makedefs/ | 内核构建的顶层规则与宏定义(MakeInc.*) |
osfmk/ | 基于 Mach 内核的子系统(进程、内存、IPC 等) |
pexpert/ | 平台相关代码,如中断处理、原子操作等 |
security/ | 强制访问控制(MAC)策略接口及实现 |
bsd/ | BSD 子系统代码 |
tools/ | 测试、调试与内核性能剖析工具集 |
例如config/MASTER是机器无关的内核主配置文件,由SETUP/config下的doconf脚本(SETUP/config/doconf)结合机器相关主文件生成具体配置,其中以<foo,bar>属性选择器控制行的取舍,无属性注释的行对所有配置生效。
构建 XNU 内核:KERNEL_CONFIGS 与 ARCH_CONFIGS
构建语法与关键参数
XNU 的 make 系统通过KERNEL_CONFIGS与ARCH_CONFIGS两个变量驱动构建(README.md):
make SDKROOT=<sdkroot> ARCH_CONFIGS=<arch> KERNEL_CONFIGS=<variant>各参数含义:
<sdkroot>:磁盘上 macOS SDK 的路径,默认值为/;<variant>:可选debug、development、release、profile,决定编译标志与内核代码中的断言(assert)是否启用;<arch>:目标架构,如X86_64。
常用构建命令
构建与当前运行系统相同架构的内核,直接执行:
make make SDKROOT=macosx.internal通过ARCH_CONFIGS指定架构、KERNEL_CONFIGS指定变体(可同时构建多个变体):
make SDKROOT=macosx.internal ARCH_CONFIGS=X86_64 KERNEL_CONFIGS=DEVELOPMENT make SDKROOT=macosx.internal ARCH_CONFIGS=X86_64 KERNEL_CONFIGS="RELEASE DEVELOPMENT DEBUG"注意:默认架构取构建机器的架构,默认内核配置为DEVELOPMENT。
构建产物包括可引导镜像kernel.[config]以及带符号的二进制kernel.[config].unstripped(例如kernel.development与kernel.development.unstripped)。
安装内核到 DSTROOT
使用install_kernels目标把内核安装到指定 DSTROOT:
make install_kernels DSTROOT=/tmp/xnu-dst构建 FAT 内核二进制
通过环境变量或 make 参数定义多个架构即可生成 FAT 内核:
make ARCH_CONFIGS="X86_64" exporthdrs all其他有用的 make 选项
README 列出的常用选项(均已在当前仓库 make 系统中支持):
make MAKEJOBS=-j8:使用 8 个进程并行构建,默认值为活跃 CPU 数的 2 倍;make -j8:标准的命令行并行选项同样可用;make -w:追踪递归 make 调用,与VERBOSE=YES配合使用;make BUILD_LTO=0:禁用 LLVM 链接时优化(LTO);make REMOTEBUILD=user@remotehost:在远程主机上执行构建;make BUILD_JSON_COMPILATION_DATABASE=1:生成 Clang JSON Compilation Database(实现见 SETUP/json_compilation_db/);- 彩色构建输出:设置环境变量
XNU_LOGCOLORS=y,或向 make 传递LOGCOLORS=y。
调试体验优化提示
如果你希望获得更满意的内核调试体验——能访问所有局部变量与参数,又不想承担 DEBUG 内核的额外检查开销——README 建议在 make 命令中追加:
CFLAGS_DEVELOPMENTARM64="-O0 -g -DKERNEL_STACK_MULTIPLIER=2" \ CXXFLAGS_DEVELOPMENTARM64="-O0 -g -DKERNEL_STACK_MULTIPLIER=2"其中DEVELOPMENT与ARM64应替换为实际构建的配置与平台。以-O0 -g关闭优化、保留完整调试信息,并借KERNEL_STACK_MULTIPLIER放大内核栈,为内核态调试留出更多空间。
调试信息格式:DWARF 与 STABS
默认情况下,XNU 在 install 阶段会生成 DWARF 调试信息仓库(bundle),即名为kernel.development.<variant>.dSYM的包。若想改用更老的STABS格式(调试信息直接嵌入kernel.development.unstripped镜像),设置环境变量后重新构建:
export BUILD_STABS=1 make构建 KernelCaches:kextd 自动生成与 kextcache 手动构建
单独的内核镜像并不能直接引导测试——必须把内核与 kext 链接成单一可引导镜像(kernelcache)。README 提供了两种机制:
方式一:kextd 自动生成 kernelcache
kextd守护进程会持续监视/System/Library/Extensions目录的变化,因此可以通过以下步骤让系统自动重建 kernelcache:
cp BUILD/obj/DEVELOPMENT/X86_64/kernel.development /System/Library/Kernels/ touch /System/Library/Extensions ps -e | grep kextdtouch触发目录时间戳变化,kextd 检测后即开始重建。
方式二:手动调用 kextcache
kextcache -q -z -a x86_64 -l -n -c /var/tmp/kernelcache.test -K /var/tmp/kernel.test /System/Library/Extensions其中-K指定内核路径、-c指定输出 kernelcache 路径、-a指定架构。
在目标机器上运行 KernelCache:boot.plist 与安全回退
开发内核与 iBoot 支持通过引导参数(boot-args)配置,使机器能安全地引导进测试内核,若出问题还能回退到之前使用的 kernelcache。README 给出了完整的五步设置:
创建内核缓存,使用 kextcache 命令生成
/kernelcache.test;备份现有引导配置到备用文件:
cp /Library/Preferences/SystemConfiguration/com.apple.Boot.plist /next_boot.plist更新 kernelcache 与 boot-args:
plutil -insert "Kernel Cache" -string "kernelcache.test" /next_boot.plist plutil -replace "Kernel Flags" -string "debug=0x144 -v kernelsuffix=test " /next_boot.plist复制新配置到
/Library/Preferences/SystemConfiguration/:cp /next_boot.plist /Library/Preferences/SystemConfiguration/boot.plistBless 卷宗,使新配置在下次引导生效:
sudo -n bless --mount / --setBoot --nextonly --options "config=boot"
其中--nextonly标志指定boot.plist配置仅用于下一次引导。因此一旦内核 panic,只需重新上电即可轻松恢复到原有内核——这是测试内核时最关键的兜底机制。
生成 tags 与 cscope 数据库
在顶部目录设置好构建环境后,可生成代码导航数据库:
make tags # 在大小写敏感卷上同时生成 ctags 与 etags,大小写不敏感卷仅生成 ctags make TAGS # 仅生成 etags make cscope # 生成 cscope 数据库条件编译规范:CPU 特性、新功能与既有功能
XNU 提供三类条件编译机制(README.md),选择顺序有明确建议:
- CPU 特性:若代码仅随目标 CPU 架构而变,优先检查架构特性宏,如
__LP64__、__LITTLE_ENDIAN__等,而不是平台宏; - 新功能:若代码整体实现了一个新功能,应在
config/MASTER中定义新特性,并使用生成的CONFIG预处理标记,例如名为config_virtual_memory的功能用#if CONFIG_VIRTUAL_MEMORY检查。这一实践保证了既有功能只需切换特性开关即可迁移到其他平台; - 既有功能:若代码与已有功能强相关,可直接使用既有特性,例如实现与可信内核(trusted kernel)相关的新功能时使用
SECURE_KERNEL。
重要约束:XNU 不定义TargetConditionals.h中的平台宏(TARGET_OS_OSX、TARGET_OS_IOS等),应避免基于目标平台编译。已废弃的TARGET_OS_EMBEDDED宏因其定义过宽也应避免使用。从源码结构看,config/MASTER中大量options行(如INET、HW_AST、MACH)正是通过这套机制生成各平台的CONFIG_*开关。
安装新的内核头文件:文件清单与安装列表
安装位置
XNU 将头文件安装到以下位置(README.md):
| 位置 | 用途 |
|---|---|
$(DSTROOT)/System/Library/Frameworks/Kernel.framework/Headers | 内核扩展(kext)使用 |
$(DSTROOT)/System/Library/Frameworks/Kernel.framework/PrivateHeaders | 仅供 Apple 内部开发的内核扩展使用 |
$(DSTROOT)/usr/include/ | 用户级应用程序使用 |
$(DSTROOT)/System/DriverKit/usr/include/ | 用户态驱动(DriverKit)使用 |
$(DSTROOT)/System/Library/Frameworks/System.framework/PrivateHeaders | Apple 内部用户级程序使用 |
文件清单(file list)
头文件所在目录应有一个 Makefile 负责生成各安装位置的安装清单。如果是在某目录新增第一个头文件,需要参照 bsd/sys/Makefile 创建 Makefile,并把头文件加入正确的文件清单:
DATAFILES:用户级可用,安装到$(DSTROOT)/usr/include;DRIVERKIT_DATAFILES:DriverKit 用户态驱动可用,安装到$(DSTROOT)/System/DriverKit/usr/include;PRIVATE_DATAFILES:Apple 内部用户级可用,安装到$(DSTROOT)/System/Library/Frameworks/System.framework/PrivateHeaders;KERNELFILES:内核级可用,安装到Kernel.framework的Headers与PrivateHeaders;PRIVATE_KERNELFILES:Apple 内部内核扩展可用,安装到Kernel.framework的PrivateHeaders。
以 bsd/sys/Makefile 为例,可确认这些清单在仓库中的真实形态:DATAFILES、DRIVERKIT_DATAFILES、PRIVATE_DATAFILES、KERNELFILES、PRIVATE_KERNELFILES均以多行变量列出具体头文件。
安装列表(install list)与 MD/MI 划分
Makefile 将上述文件清单组合成安装列表,供构建系统使用。安装列表分为**机器相关(MD,machine-dependent)与机器无关(MI,machine-independent)**两类:头文件若与架构相关,使用机器相关列表(如INSTALL_MD_LIST);若对所有架构都安装,则使用机器无关列表(如INSTALL_MI_LIST)。
README 给出的默认安装列表、成员清单与默认位置如下:
| 安装列表 | 成员文件清单 | 默认位置 |
|---|---|---|
INSTALL_MI_LIST | ${DATAFILES} | $(DSTROOT)/usr/include |
INSTALL_DRIVERKIT_MI_LIST | ${DRIVERKIT_DATAFILES} | $(DSTROOT)/System/DriverKit/usr/include |
INSTALL_MI_LCL_LIST | ${PRIVATE_DATAFILES} | $(DSTROOT)/System/Library/Frameworks/System.framework/PrivateHeaders |
INSTALL_KF_MI_LIST | ${KERNELFILES} | $(DSTROOT)/System/Library/Frameworks/Kernel.framework/Headers |
INSTALL_KF_MI_LCL_LIST | ${KERNELFILES} ${PRIVATE_KERNELFILES} | $(DSTROOT)/System/Library/Frameworks/Kernel.framework/PrivateHeaders |
EXPORT_MI_LIST | ${KERNELFILES} ${PRIVATE_KERNELFILES} | 仅导出到 xnu 内部(bsd/、osfmk/ 等)供编译,不装入 SDK |
INSTALL_MODULEMAP_INCDIR_MI_LIST | ${MODULEMAP_INCDIR_FILES} | $(DSTROOT)/usr/include(INCDIR 根) |
若希望把头文件安装到上述路径的子目录,用INSTALL_MI_DIR与EXPORT_MI_DIR指定目录名:
INSTALL_MI_DIR = dirname EXPORT_MI_DIR = dirname用预处理宏控制头文件内容
单个头文件可能被安装到多个位置,但你未必希望所有内容在所有位置都可见(例如只想导出函数到内核层而非用户层)。解决办法是利用 C 预处理指令(#ifdef、#ifndef、#endif)控制安装前生成的文本:内核只保留条件宏为 TRUE 的代码,并剥离 FALSE 分支。
预定义宏及其可见范围(README.md):
| 宏 | 可见范围 |
|---|---|
PRIVATE | System Private Interfaces:xnu 内部可见,并暴露于 System/Kernel framework 的 AppleInternal "PrivateHeaders" 部分 |
KERNEL_PRIVATE | 全部 xnu 内核与 Apple 内部内核扩展可见,从用户头文件中省略 |
BSD_KERNEL_PRIVATE | 仅 xnu/bsd 模块内可见 |
MACH_KERNEL_PRIVATE | 仅 xnu/osfmk 模块内可见 |
XNU_KERNEL_PRIVATE | 仅 xnu 内可见 |
KERNEL | xnu 与内核扩展内可见,用户级头文件不可见;仅Kernel.framework/Headers与PrivateHeaders安装的头文件包含该代码 |
DRIVERKIT | 仅 DriverKit SDK 头文件(用户态驱动使用)可见 |
添加新的系统调用与测试内核
添加新 syscall
README 预留了 “How to add a new syscall” 章节(未给出详细步骤),但内核系统调用的实际入口分散在osfmk/(Mach trap)与bsd/(BSD syscall)两侧。若要在当前仓库实践,可参照 libsyscall/mach/ 下各.defs文件与bsd/sys/中现有 syscall 的实现来建立调用链。
测试机制总览
XNU 提供多种测试机制(README.md):
断言(Assertions):DEVELOPMENT 与 DEBUG 内核配置默认启用断言,便于开发者验证不变量与条件;
内核上电自检(XNUPOST):
XNUPOST配置允许构建带基础测试函数集的内核,在首个用户空间进程启动前运行。由于 XNU 是 Mach 与 BSD 的混合体,测试分两处添加:- osfmk/tests/:测试 Mach 内核结构与 API;
- bsd/tests/:测试 BSD 接口;
- 详细文档见 osfmk/tests/README.md。
用户级测试:
tools/tests/目录存放验证 syscall 及其他特性的测试。使用xnu_testsmake 目标构建全部测试:make RC_ProjectName=xnu_tests SDKROOT=/path/to/SDK这些测试是独立程序,可在终端运行,通过标准 POSIX 退出码(0 表示成功)和/或 stdout 报告测试状态。可在 tools/tests/Makefile 中确认
RC_ProjectName对测试构建的控制逻辑。
深入 XNUPOST:内核上电自检框架
osfmk/tests/README.md 详细描述了 XNUPOST 的特性与用法,这是 README 测试章节的直接延伸:
特性
- 编译时从 RELEASE 内核中剔除;
- 通过 boot-arg
kernPOST启用:0x1用于桌面测试,0x3用于 BATs(自动化测试)环境; - 自动跳过专为 panic 设计的内核测试(桌面测试跳过,BATs 环境运行);
- 无需完整安装到设备,仅 kernelcache 即可运行;
- 可同时检查断言与 panic 路径。
运行方法
启动 usbterm 并将目标设备置于 iBoot 后:
设置 boot-args 包含 "kernPOST=0x1" # 开机启用内核测试 usb get /path/to/kc # 加载 kernelcache bootx # 引导镜像在 nanokdp 串口输出中观察带[KTEST] <test> logs标签的日志。
只运行指定测试
通过 boot-argkernPOST_config配置,支持单测与范围:
kernPOST_config=8 # 只运行测试 #8 kernPOST_config=1_3,5_9999 # 跳过 #4,运行 1、2、3 与 5 及之后 kernPOST_config=1_3,4_9999 # 不跳过任何测试,上下界均含添加新测试
- 按测试领域在 osfmk/tests/ 或 bsd/tests/ 添加新
.c文件,#include <xnupost.h>获取测试所需的函数与宏,并在osfmk/conf/files或bsd/conf/files中登记(例如osfmk/tests/my_tests.c optional config_xnupost;仓库 bsd/conf/files 中可见bsd/tests/bsd_tests.c optional config_xnupost等真实登记行); - 声明测试函数:
kern_return_t my_sample_tests(void); - 加入测试数组,如 osfmk/tests/kernel_tests.c 中的
struct xnupost_test kernel_post_tests[]:
struct xnupost_test kernel_post_tests[] = { XNUPOST_TEST_CONFIG_BASIC(my_sample_tests), // 简单测试 XNUPOST_TEST_CONFIG_TEST_PANIC(panic_test) // 预期 panic 的测试 };仓库中kernel_post_tests[]的实例确认了XNUPOST_TEST_CONFIG_BASIC与XNUPOST_TEST_CONFIG_TEST_PANIC的实际用法(如zalloc_test、test_thread_call等)。
示例测试函数(使用T_*宏族):
kern_return_t my_sample_tests(void) { uint64_t test_begin_timestamp = 0; uint64_t cur_timestamp = 0, tmp; T_SETUPBEGIN; test_begin_timestamp = mach_absolute_time(); T_ASSERT_NOTNULL(test_begin_timestamp, "mach_absolute_time returned 0."); T_SETUPEND; T_LOG("Testing mach_absolute_time for 100 iterations"); for (int i = 0; i < 100; i++) { tmp = mach_absolute_time(); T_EXPECT_TRUE((cur_timestamp <= tmp), "Time went backwards"); cur_timestamp = tmp; } T_LOG("Completed mach_absolute_time tests."); return KERN_SUCCESS; }以KERN_SUCCESS报告成功,其他错误码报告失败。注意:测试必须做好状态清理,内核预期在测试后继续引导;若无法清理而需要重启,则使用XNUPOST_TEST_CONFIG_TEST_PANIC类型并在函数末尾 panic,让测试控制器在自动化中重启并运行下一个测试。
T_EXPECT 与 T_ASSERT 的区别
T_ASSERT宏检查条件,失败时以KERN_FAILURE返回,确保后续测试代码不再执行;T_EXPECT仅报告该测试用例失败,但继续执行后续测试代码。
测试 panic 与断言:panic widget
xnupost 子系统提供注册panic widget的机制,用于检查特定条件并报告测试 SUCCESS/FAILURE。在 debug/development 内核上,panic()代码被改造为调用xnupost_process_panic(),该回调判断测试是否启用、是否注册了检查 panic 的 widget,并据返回值决定动作。widget 可返回:
XT_PANIC_UNRELATED:与本测试无关,继续 panic;XT_RET_W_FAIL:报告 FAILURE 并从 panic 返回;XT_RET_W_SUCCESS:报告 SUCCESS 并从 panic 返回;XT_PANIC_W_FAIL:报告 FAILURE 并继续 panic;XT_PANIC_W_SUCCESS:报告 SUCCESS 并继续 panic。
panic widget 数据保存在内部数组struct xnupost_panic_widget(含上下文指针、输出指针、widget 名称与回调函数)中。断言检查示例(来自 osfmk/tests/README.md):
kern_return_t test_foo_arg_assertion(void) { void * assert_retval = NULL; kern_return_t kr = T_REGISTER_ASSERT_CHECK("arg > 0", &assert_retval); T_ASSERT(kr == KERN_SUCCESS, "register assertion handler"); foo(-1); /* this will cause assert to fire */ T_ASSERT(assert_retval == (void *)XT_RET_W_SUCCESS, "verify assertion was hit"); }内核数据描述符:libkdd 与 Kernel Chunked Data
为什么需要 libkdd
XNU 在不同 API 间传递数据有多种格式,最标准的是 syscall 参数;但对复杂数据,内核往往依赖以 C 结构体保存在内存中的数据。这种打包式传输机制很脆弱,会导致用户态程序与内核 API 之间的接口破坏。libkdd/ 目录存放用户态库,用于解析与内核同版本提供的自定义数据。内核分块数据(kernel chunked data)格式在 libkdd/README.md 中有完整描述,库 API 定义在 libkdd/kdd.h。
KCDATA 格式布局
数据按通用格式组织为一段连续的块(chunk):
| 8 - bytes | |---------------------------| ------ offset = 00 | type = MAGIC | LENGTH | # BEGIN Header | 0 | |---------------------------| ------ offset = 16 | type | size | # chunk header | flags | |---------------------------| ------ offset = 32 | data | # arbitrary data (len=16) |___________data____________| |---------------------------| ------ offset = 48 | type | size | # chunk header | flags | |---------------------------| ------ offset = 64 | data | # arbitrary data (len=32) | data | |___________data____________| |---------------------------| ------ offset = 96 | type = END | size=0 | # chunk header | 0 |type字段描述数据类型,例如TASK_CRASHINFO_UUID表示其后为 uuid;这些类型需在task_corpses.h中定义,便于用户态检查工具消费。部分类型范围预留给 int、long 等特殊类型。
这种可扩展格式的最大价值在于:内核可以按需加入更多信息,而无需用户态工具重编译即可保持兼容——例如引入新的rusage结构版本不会破坏现有工具。
带描述的自描述数据
进一步地,数据可以携带文本描述。内核侧示例:
kcdata_add_uint64_with_description(cdatainfo, 0x700, "NUM MACH PORTS");用户态工具即使事先未编译进对该字段的了解,也能根据描述打印数据。README 给出了真实的缓冲区分块样例(PID、PARENT PID等带描述块)。
复合数据的容器标记
对于需要包含多个可选字段的复杂内核数据类型,使用容器标记(container markers)打包。例如 stackshot 收集某任务在众多子系统中的状态(io 统计、vm 计数、进程名/标志、syscall 计数):
kcdata_add_container_marker(kcdata_p, KCDATA_TYPE_CONTAINER_BEGIN, STACKSHOT_KCCONTAINER_TASK, task_uniqueid); // add multiple data, or add_<type>_with_description()s here kcdata_add_container_marker(kcdata_p, KCDATA_TYPE_CONTAINER_END, STACKSHOT_KCCONTAINER_TASK, task_uniqueid);按需描述自定义格式
得益于格式的自描述特性,内核提供方可描述某个数据类型(用唯一数字标识)并在缓冲区中使用,消费方解析类型信息即可理解数据。示例:描述结构体sample_disk_io_stats的字段布局:
struct sample_disk_io_stats { uint64_t disk_reads_count; uint64_t disk_reads_size; uint64_t io_priority_count[4]; uint64_t io_priority_size; } __attribute__ ((packed)); struct kcdata_subtype_descriptor disk_io_stats_def[] = { {KCS_SUBTYPE_FLAGS_NONE, KC_ST_UINT64, 0 * sizeof(uint64_t), sizeof(uint64_t), "disk_reads_count"}, {KCS_SUBTYPE_FLAGS_NONE, KC_ST_UINT64, 1 * sizeof(uint64_t), sizeof(uint64_t), "disk_reads_size"}, {KCS_SUBTYPE_FLAGS_ARRAY, KC_ST_UINT64, 2 * sizeof(uint64_t), KCS_SUBTYPE_PACK_SIZE(4, sizeof(uint64_t)), "io_priority_count"}, {KCS_SUBTYPE_FLAGS_ARRAY, KC_ST_UINT64, (2 + 4) * sizeof(uint64_t), sizeof(uint64_t), "io_priority_size"}, };随后把类型定义写入缓冲区:
kcdata_add_type_definition(kcdata_p, KCTYPE_SAMPLE_DISK_IO_STATS, "sample_disk_io_stats", &disk_io_stats_def[0], sizeof(disk_io_stats_def)/sizeof(struct kcdata_subtype_descriptor));调试内核:kdp 远程调试协议与 lldb
基本调试流程
XNU 支持通过远程内核调试协议kdp进行调试(README.md)。默认内核在 panic 时配置为重启;为调试活内核,kdp 服务器会监听以太网上的 UDP 连接。常见引导参数:
debug=0x144:设置调试变量,在 panic 时启动 kdp debugserver;-v:在内核启动日志打印到屏幕(默认 XNU 只显示带引导图案的灰屏);kdp_match_name=en1:覆盖 kdp 默认端口选择,支持以太网、雷电(thunderbolt)与串口调试。
调试 panic 的内核时,配合带符号的未剥离内核二进制使用 LLVM 调试器 lldb:
lldb kernel.development.unstripped连接 panic 机器使用kdp_remote [ip addr]或gdb_remote [hostip : port]命令。
加载内核专用调试脚本
每个内核在构建时都会打包内核专用的调试脚本。出于安全考虑,这些特殊命令与脚本在 lldb 连接机器时不会自动加载。若希望始终加载这些宏,在~/.lldbinit中添加:
settings set target.load-script-from-symbol-file truetools/lldbmacros目录保存所有这些命令的源码,详细用法见 tools/lldbmacros/README.md。
lldbmacros:内核调试命令与类型摘要
从 tools/lldbmacros/README.md 可以看到,这套框架建立在 lldb 的 Python 脚本桥接之上,命令与摘要通过@lldb_command、@lldb_type_summary()等装饰器注册(入口脚本为 tools/lldbmacros/xnu.py)。核心用法:
kgmhelp列出xnu.py提供的全部命令;- 启用内核类型摘要:
(lldb) showlldbtypesummaries,之后print (thread_t)0x...即可直接看到线程摘要(thread_id、processor、优先级、状态、等待队列等); - 通用选项(
--之后):-h帮助、-s <regexp>只打印匹配行、-o <file>输出到文件、-v增加详细度、-p <plugin>交给插件处理; - 长输出命令可用
-- -i获得即时终端输出; - 修改命令代码后用
(lldb) xnudebug reload memory热重载,不必退出调试会话; - 显示原始数据而不触发摘要用
showraw命令; - 开发者可通过环境变量
DEBUG_XNU_LLDBMACROS=1禁用从符号自动加载宏,从而加载本地修改版xnu.py。
该框架还被随构建打包进每个内核的 dSYM,使 lldb 命令与内核源码变更保持版本锁定(rev-locking)。
总结
从本文可以看到,XNU 是一个层次清晰的混合内核工程:README.md 给出了从源码树、构建、kernelcache、条件编译、头文件安装到测试与调试的完整开发链路;config/MASTER、bsd/sys/Makefile 等配置文件印证了构建与安装机制;osfmk/tests/README.md 与 osfmk/tests/kernel_tests.c 展示了内核自检框架的实际形态;libkdd/README.md 阐明了内核与用户态之间的自描述数据协议;tools/lldbmacros/README.md 则提供了从崩溃现场提取信息的实用工具链。掌握这些环节,你就具备了在 XNU 源码层面完成从构建、测试到调试的完整开发闭环能力。
- 操作系统
- 驱动开发
【免费下载链接】darwin-xnu
Legacy mirror of Darwin Kernel. Replaced by https://github.com/apple-oss-distributions/xnu
相关推荐
Darwin-XNU构建系统全解析:从源码到可启动内核
Darwin XNU构建系统全解析:从源码到可启动内核 本文深入解析Darwin XNU内核构建系统的完整架构和工作机制,涵盖多架构构建配置、不同内核变体的区别
操作系统驱动开发darwin-xnu 内核调试指南:基于 lldbmacros 的 lldb 内核调试平台深入解析
darwin xnu 内核调试指南:基于 lldbmacros 的 lldb 内核调试平台深入解析 导读 本文围绕 darwin xnu 仓库 tools/ll
操作系统驱动开发XNU 内核移植完整指南:从 AArch64 到 ARMv7/ARMv6 架构实战
XNU 内核移植完整指南:从 AArch64 到 ARMv7/ARMv6 架构实战 XNU 内核作为 macOS 和 iOS 操作系统的核心,其跨架构移植技术对
操作系统嵌入式
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考