ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

darwin-xnu 内核 POST 自检框架(XNUPOST)实战指南:boot-args 配置、测试编写与 Panic 断言机制

darwin-xnu 内核 POST 自检框架(XNUPOST)实战指南:boot-args 配置、测试编写与 Panic 断言机制 操作系统驱动开发【免费下载链接】darwin-xnuLegacy mirror of Darwin Kernel. Replaced by https://github.com/apple-oss-distributions/xnu项目地址https://gitcode.com/gh_mirrors/da/darwin-xnu点击查看免费下载darwin-xnu 的osfmk/tests与bsd/tests目录内置了一套在内核启动阶段boot-time运行的 Power On Self TestsPOST框架用于在设备上电、系统尚未完全启动时验证内存分配器、调度器、VM、IPC 端口等关键子系统的基本功能。本文以 osfmk/tests/README.md 为骨架结合 kernel_tests.c、bsd_tests.c、xnupost.h 与 ktest.h 等源码完整讲解如何通过 boot-args 启停内核自检、用范围语法挑选指定测试、编写并注册新的 POST 测试用例以及利用 panic widget 验证崩溃与断言路径。读完本文你将能独立在 darwin-xnu 内核源码上新增一条自检用例并掌握其在 on-desk 调试与 BATs自动化批量测试两类环境下的完整运行方法。Kernel POST 是什么在内核里跑的上电自检osfmk/tests与bsd/tests两个目录中的测试是在内核启动阶段boot-time以内核代码形态运行的测试而不是用户态测试程序。它们的主要目标是在内核尚未交出控制权给用户态之前先验证内存分配器zalloc/kalloc、调度与线程scheduling、虚拟内存VM、IPC 端口等底层子系统的可用性——这些子系统一旦在启动早期就存在缺陷越早暴露越容易定位。从 kernel_tests.c 开头可以看到它的编译约束#if !(DEVELOPMENT || DEBUG) #error Testing is not enabled on RELEASE configurations #endif也就是说POST 测试代码只会被编译进 DEVELOPMENT 或 DEBUG 配置的内核RELEASE 内核中会被整体编译掉配合osfmk/conf/files中optional config_xnupost的条件编译见下文。这与 README 中Compiled out of RELEASE kernels的特性一一对应。框架的核心特性可归纳为五点特性说明编译裁剪仅 DEVELOPMENT/DEBUG 内核包含测试代码RELEASE 内核完全剔除boot-args 开关通过kernPOST启动参数启用0x1用于 on-desk桌面/单机调试测试0x3用于 BATs 自动化测试自动跳过 panic 测试在 on-desk 环境无控制器接管下被设计为预期触发 panic的测试会被自动标记为 SKIPPED只有在 BATs 环境有控制器可复位重启才会真正运行免完整安装运行 POST 不需要把整个系统装到设备上仅需加载 kernelcache 即可断言与 panic 路径可测框架提供 panic widget 机制可以校验 assert/panic 是否按预期触发并返回结果如何运行 Kernel POST从 iBoot 到串口日志README 给出了标准的运行流程这里逐条展开并补充细节启动 usbterm 并把目标机器/设备置于 iBoot 环境POST 运行在内核启动早期需要借助 iBoot 的串口/USB 终端能力交互。设置 boot-args 启用内核测试kernPOST0x10x1是 on-desk 调试模式。源码 kernel_tests.c 中定义了三个位标志#define POSTARGS_RUN_TESTS 0x1 #define POSTARGS_CONTROLLER_AVAILABLE 0x2 #define POSTARGS_CUSTOM_TEST_RUNLIST 0x4 uint64_t kernel_post_args 0x0;由此可见0x3 0x1 | 0x2即运行测试 存在自动化控制器BATs这正是 README 中0x3用于 BATs 的原因。0x4位则会在设置了kernPOST_config时被自动置上见 kernel_tests.c。加载 kernelcacheusb get /patch/to/kc启动内核镜像bootx观察 nanokdp 串口输出关注带有[KTEST] test前缀的日志标签。测试结果最终由ktest层的输出函数ktest_emit.c 等统一格式化打印。需要强调的是kernPOST的解析入口是 kernel_tests.c 中的xnupost_parse_config()kern_return_t xnupost_parse_config() { if (parse_config_retval ! KERN_INVALID_CAPABILITY) { return parse_config_retval; } PE_parse_boot_argn(kernPOST, kernel_post_args, sizeof(kernel_post_args)); if (PE_parse_boot_argn(kernPOST_config, kernel_post_test_configs[0], sizeof(kernel_post_test_configs)) TRUE) { kernel_post_args | POSTARGS_CUSTOM_TEST_RUNLIST; } if (kernel_post_args ! 0) { parse_config_retval KERN_SUCCESS; goto out; } parse_config_retval KERN_NOT_SUPPORTED; out: return parse_config_retval; }它通过PE_parse_boot_argn()读取kernPOST与kernPOST_config两个启动参数只有当kernPOST非零或显式给出了kernPOST_config时parse_config_retval才会是KERN_SUCCESS测试才会进入可运行状态。返回值会被缓存避免重复解析。只运行某一个测试kernPOST_config 范围语法内核 POST 支持通过 boot-args 精确挑选测试子集。例如调试第 8 号测试时设置kernPOST_config8则只有 8 号测试会运行。配置还支持闭区间范围多个范围用逗号分隔且范围是**上下限都包含inclusive**的kernPOST_config1_3,5_9999 # 跳过 4 号测试将运行 1、2、3、5、6……及之后的所有测试 kernPOST_config1_3,4_9999 # 什么都不跳过1_3 与 4_9999 无缝衔接其底层实现在 kernel_tests.c 的xnupost_should_run_test()中逐段解析逗号分隔的lower_upper对下划线写法调用get_range_bounds()取出上下界判断test_num是否落在任意一个闭区间内。同时 xnupost_list_tests() 会在列目录TOC阶段给每个测试分配递增的测试编号xt_test_num并依据kernPOST_config把未选中的测试标记为XT_CONFIG_IGNORE最终由xnupost_run_tests()跳过它们kernel_tests.cif ((testp-xt_config XT_CONFIG_IGNORE)) { T_SKIP(Test is marked as XT_CONFIG_IGNORE); testp-xt_test_actions XT_ACTION_SKIPPED; continue; }因此测试编号与kernel_post_tests[]数组中的静态顺序一一对应可以通过kernPOST_config按编号精确控制。如何添加一个新的 POST 测试README 给出了非常简洁的四步流程这里结合源码把每一步补全到可直接落地1. 选择测试文件位置测试可以放在两个位置内核侧Mach 层如调度、VM、IPC放osfmk/tests/BSD 侧如文件系统、套接字、拷贝路径放bsd/tests/。两个目录中已存在大量可参考的样例例如 bitmap_test.c、test_thread_call.c、vfp_state_test.c以及 BSD 侧的 copyio_tests.c、ctrr_test_sysctl.c。2. 编写测试源文件并登记到编译配置若新增一个独立的*.c文件例如my_tests.c文件头部需要包含#include xnupost.hxnupost.h提供了测试所需的函数声明与宏XNUPOST_TEST_CONFIG_*、T_REGISTER_*、xnupost_*系列接口其开头还有一道编译期检查xnupost.h#ifndef CONFIG_XNUPOST #error Testing is not enabled if CONFIG_XNUPOST is not enabled #endif然后在编译清单中登记格式为osfmk/tests/my_tests.c optional config_xnupost写入osfmk/conf/filesMach 层或bsd/conf/filesBSD 层。现有登记项可参考 osfmk/conf/files 中的optional config_xnupost行——这保证了 RELEASE 内核不会把这些文件编进去。3. 声明测试函数并注册到测试表测试函数原型必须是kern_return_t my_sample_tests(void);然后把它加进测试表数组。Mach 层测试表是 kernel_tests.c 中的kernel_post_tests[]BSD 层测试表是 bsd_tests.c 中的bsd_post_tests[]。注册方式struct xnupost_test kernel_post_tests[] { XNUPOST_TEST_CONFIG_BASIC(my_sample_tests), // 简单测试 XNUPOST_TEST_CONFIG_TEST_PANIC(panic_test) // 预期触发 panic 的测试 };这两个宏定义在 xnupost.hXNUPOST_TEST_CONFIG_BASIC(func)xt_config XT_CONFIG_RUN0x0预期返回值T_STATE_PASS测试名自动生成为xnu. #funcXNUPOST_TEST_CONFIG_TEST_PANIC(func)xt_config XT_CONFIG_EXPECT_PANIC0x2同样预期T_STATE_PASS。其中XT_CONFIG_RUN / XT_CONFIG_IGNORE / XT_CONFIG_EXPECT_PANIC的定义见 xnupost.h。struct xnupost_test的完整字段编号、起止时间戳、动作状态、函数指针、名称等也在 xnupost.h。4. 用 KERN_SUCCESS 报告成功测试函数返回KERN_SUCCESS表示通过返回其他错误码表示失败。README 给出了一个完整的mach_absolute_time单调性测试示例这里原样保留并补充宏含义kern_return_t my_sample_tests() { uint64_t test_begin_timestamp 0; uint64_t cur_timestamp 0, tmp; T_SETUPBEGIN; // 进入 setup 段 test_begin_timestamp mach_absolute_time(); T_ASSERT_NOTNULL(test_begin_timestamp, mach_absolute_time returned 0.); T_SETUPEND; // 结束 setup 段 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; }注意README 示例中的T_EXPECT_TRUE是示意性写法ktest.h 中实际提供的通用断言宏是T_EXPECT(expr, msg, ...)与T_ASSERT(expr, msg, ...)另有大量按类型细分的T_EXPECT_EQ_* / T_ASSERT_EQ_* / T_EXPECT_NE_* / T_ASSERT_NE_*等便捷宏覆盖 char、short、int、long、long long、指针、字符串等类型以及T_EXPECT_NULL / T_ASSERT_NULL / T_EXPECT_NOTNULL / T_ASSERT_NOTNULL。5. 状态清理要求重要内核在 POST 测试完成后还要继续完成引导因此测试必须做好状态清理释放内存、归还锁等不能污染后续启动流程。如果某个测试无法清理状态、必须重启才能继续则应按 README 的建议将其注册为XNUPOST_TEST_CONFIG_TEST_PANIC类型并在函数末尾主动 panic——这样测试控制器会负责复位重启并继续执行下一个测试。T_* 宏体系T_EXPECT 与 T_ASSERT 的区别框架为测试编写提供了整套T_*宏声明与定义集中在 ktest.h 与 ktest.c 中。核心语义T_ASSERT系列检查条件一旦失败立即返回KERN_FAILURE通过T_SYM(assertion_check)()触发断言检查确保失败后不再继续执行后续测试代码T_EXPECT系列仅记录该用例失败但继续执行后续测试代码适合收集多处独立问题的场景。两者在实现上正是expect assertion_check的关系ktest.h#define T_EXPECT(expr, msg, ...) do {\ T_SAVEINFO;\ T_SYM(temp1)._int (int)(!!(expr));\ T_SYM(set_current_expr)(T_TOSTRING(expr));\ T_SET_AUX_VARS;\ T_SYM(set_current_msg)(msg, ## __VA_ARGS__);\ T_SYM(testcase)(T_SYM(temp1)._int);\ } while(0) #define T_ASSERT(expr, msg, ...) do {\ T_EXPECT(expr, msg, ## __VA_ARGS__);\ T_ASSERTION_CHECK;\ } while(0)其余高频宏一览宏用途T_START/T_FINISH标记整个测试段开始/结束开始前的输出被忽略T_BEGIN(name)/T_END标记单个测试用例开始/结束T_SETUPBEGIN/T_SETUPEND标记 setup 段对应T_MAIN/T_SETUP两种 testcase 模式T_SKIP(msg, ...)标记当前测试被跳过T_LOG(msg, ...)打印日志即串口上[KTEST]输出T_PASS/T_FAIL显式通过/失败一个用例T_ASSERT_FAIL(msg, ...)显式断言失败并中止T_PERF(metric, value, unit, desc)记录一项性能指标如T_PERF(min_bit_entropy_1000, 12, bits, ...)供性能数据采集T_EXPECTFAIL/T_QUIET标记下一用例为预期失败 / 仅在失败时输出T_TESTRESULT/T_TESTCASERESULT读取最近一次测试/用例结果结果状态常量在 ktest.hT_STATE_UNRESOLVED(0)、T_STATE_PASS(1)、T_STATE_FAIL(2)、T_STATE_SETUPFAIL(3)。xnupost_run_tests()会用T_TESTRESULT与注册时的xt_expected_retval比较得到XT_ACTION_PASSED/XT_ACTION_FAILEDkernel_tests.c。以 kernel_tests.c 的zalloc_test()为例可看到宏的实际组合用法kern_return_t zalloc_test(void) { zone_t test_zone; void * test_ptr; T_SETUPBEGIN; test_zone zone_create(test_uint64_zone, sizeof(uint64_t), ZC_DESTRUCTIBLE); T_ASSERT_NOTNULL(test_zone, NULL); T_ASSERT_EQ_INT(test_zone-z_elems_free, 0, NULL); T_SETUPEND; T_ASSERT_NOTNULL(test_ptr zalloc(test_zone), NULL); zfree(test_zone, test_ptr); /* A sample report for perfdata */ T_PERF(num_threads_at_ktest, threads_count, count, # of threads in system at zalloc_test); return KERN_SUCCESS; }如何在 BATs 中运行 POSTBATs自动化批量测试系统新增了名为kernel_POST的测试类型用于在精简测试环境darwinLTELean Test Environment下运行这些内核自检。README 给出的命令模板~osdev/tat/dev/bin/bats build -b build -t darwinLTE -p xnu:branch -r radarnum参数含义-b build指定构建号-t darwinLTE指定测试类型为 darwin 精简环境-p xnu:branch指定要测试的 xnu 分支-r radarnum绑定一个 radar缺陷跟踪单编号。在这种模式下应使用kernPOST0x3即POSTARGS_RUN_TESTS | POSTARGS_CONTROLLER_AVAILABLE让框架知道存在可复位重启的控制器。也正因为此被设计为预期 panic 的测试只有在 BATs 环境下才会真正运行——这正是 README 特性列表第三条Automatically skips tests that are designed to panic kernel for on-desk testing, but run in BATs environment的机制来源。对应实现见 kernel_tests.cif ((testp-xt_config XT_CONFIG_EXPECT_PANIC) !(kernel_post_args POSTARGS_CONTROLLER_AVAILABLE)) { T_SKIP(Test expects panic but no controller is present); testp-xt_test_actions XT_ACTION_SKIPPED; continue; }如何测试 panic 与 assertion 路径很多内核 API 的健壮性恰恰体现在传入非法参数时应当 assert/panic。XNUPOST 为此提供了panic widgetpanic 钩子机制测试可以预先注册一个回调当内核因预期原因触发 panic/assert 时该回调被调用来判定这次 panic 是否符合预期并把结果SUCCESS/FAILURE反馈给正在运行的测试。便捷注册宏xnupost.h 提供了两级注册接口extern xt_panic_return_t _xt_generic_assert_check(const char * s, void * str_to_match, void ** outval); kern_return_t xnupost_register_panic_widget(xt_panic_widget_func funcp, const char * funcname, void * context, void ** outval); #define T_REGISTER_PANIC_WIDGET(func, ctx, outval) \ xnupost_register_panic_widget((func), #func, (ctx), (outval)) #define T_REGISTER_ASSERT_CHECK(assert_str, retval) \ T_REGISTER_PANIC_WIDGET(_xt_generic_assert_check, (void *)__DECONST(char *, assert_str), retval)T_REGISTER_PANIC_WIDGET(func, ctx, outval)注册一个自定义 widget 函数T_REGISTER_ASSERT_CHECK(assert_str, retval)便捷宏直接注册针对断言消息子串的匹配检查——当 panic/assert 消息中包含assert_str时判定命中。一个完整的断言测试用例README 给出的foo(arg 0)断言测试模板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); }流程拆解先用T_REGISTER_ASSERT_CHECK(arg 0, assert_retval)注册断言检查out 参数assert_retval用于接收 widget 的返回值调用foo(-1)故意触发断言断言assert_retval (void *)XT_RET_W_SUCCESS确认断言确实被命中且被判定为预期成功。widget 的五种返回语义widget 函数通过返回值告诉框架如何处理当前 panic定义见 xnupost.h返回值语义XT_PANIC_UNRELATED(0x8)与本次测试无关继续走正常 panic 流程XT_RET_W_FAIL(0x9)报告测试 FAILURE并从 panic 中返回不真正崩溃XT_RET_W_SUCCESS(0xA)报告测试 SUCCESS并从 panic 中返回XT_PANIC_W_FAIL(0xB)报告测试 FAILURE并继续进入 panicXT_PANIC_W_SUCCESS(0xC)报告测试 SUCCESS并继续进入 panicwidget 的数据结构xnupost.htypedef xt_panic_return_t (*xt_panic_widget_func)(const char * panicstr, void * context, void ** outval); struct xnupost_panic_widget { void * xtp_context_p; /* 回调可追踪的上下文指针 */ void ** xtp_outval_p; /* 输出参数widget 向运行中的测试回传某个值 */ const char * xtp_func_name; /* widget 名称用于串口输出跟踪 */ xt_panic_widget_func xtp_func; };XNUPOST panic widget 的底层工作原理在 DEBUG/DEVELOPMENT 内核上panic()路径会被修改先回调 XNUPOST 系统README 中记为xnupost_process_panic()本仓库中对应实现名为xnupost_process_kdb_stop()见 kernel_tests.c。该回调会检查是否启用了测试kernel_post_args非零以及是否有注册的 widget。其核心逻辑kern_return_t xnupost_process_kdb_stop(const char * panic_s) { xt_panic_return_t retval 0; struct xnupost_panic_widget * pw xt_panic_widgets; const char * name unknown; if (xt_panic_widgets.xtp_func_name) { name xt_panic_widgets.xtp_func_name; } /* bail early on if kernPOST is not set */ if (kernel_post_args 0) { return KERN_INVALID_CAPABILITY; } if (xt_panic_widgets.xtp_func) { T_LOG(%s: Calling out to widget: %s, __func__, xt_panic_widgets.xtp_func_name); retval pw-xtp_func(panic_s, pw-xtp_context_p, pw-xtp_outval_p); } else { return KERN_INVALID_CAPABILITY; } switch (retval) { case XT_RET_W_SUCCESS: T_EXPECT_EQ_INT(retval, XT_RET_W_SUCCESS, %s reported successful handling. Returning from kdb_stop., name); /* KERN_SUCCESS means return from panic/assertion */ return KERN_SUCCESS; case XT_RET_W_FAIL: T_FAIL(%s reported XT_RET_W_FAIL: Returning from kdb_stop, name); return KERN_SUCCESS; case XT_PANIC_W_FAIL: T_FAIL(%s reported XT_PANIC_W_FAIL: Continuing to kdb_stop, name); return KERN_FAILURE; case XT_PANIC_W_SUCCESS: T_EXPECT_EQ_INT(retval, XT_PANIC_W_SUCCESS, %s reported successful testcase. But continuing to kdb_stop., name); return KERN_FAILURE; case XT_PANIC_UNRELATED: default: T_LOG(UNRELATED: Continuing to kdb_stop.); return KERN_FAILURE; } }可以看到五个关键分支返回KERN_SUCCESS的XT_RET_W_SUCCESS/XT_RET_W_FAIL会从 panic 中返回内核继续执行后续代码——这是不真正崩溃也能验证断言的关键返回KERN_FAILURE的XT_PANIC_W_*/XT_PANIC_UNRELATED会继续进入真正的 panic/kdb_stop交由控制器复位重启。内置的通用断言检查_xt_generic_assert_check()kernel_tests.c实现也很直观用strnstr在 panic 字符串中匹配注册时给定的子串命中则返回XT_RET_W_SUCCESS并写入 out 参数xt_panic_return_t _xt_generic_assert_check(const char * s, void * str_to_match, void ** outval) { xt_panic_return_t ret XT_PANIC_UNRELATED; if (NULL ! strnstr(__DECONST(char *, s), (char *)str_to_match, strlen(s))) { T_LOG(%s: kdb_stop string: %s MATCHED string: %s, __func__, s, (char *)str_to_match); ret XT_RET_W_SUCCESS; } if (outval) { *outval (void *)(uintptr_t)ret; } return ret; }widget 注册与重置的约束也很明确全局只保留一个widget 槽位xt_panic_widgets单例见 kernel_tests.c重复注册会返回KERN_RESOURCE_SHORTAGEkernel_tests.c每个测试运行前都会调用xnupost_reset_panic_widgets()清空槽位kernel_tests.c 与 kernel_tests.c。关于示例README 提到kernel_tests.c中的check_panic_test()/panic_test()本仓库该文件当前版本中对应注释形式保留在 kernel_tests.c 的kcdata_api_assert_tests()注释块中以及用T_REGISTER_ASSERT_CHECK验证 kcdata API 参数校验断言的写法——那段被注释的样例正是注册断言 widget → 触发 API 断言 → 校验XT_RET_W_SUCCESS三步曲的完整参考非常适合直接改写为自定义 widget 测试。从源码看 POST 的执行编排最后补全框架的执行编排便于整体理解。测试入口由kernel_do_post()调用xnupost_run_tests(kernel_post_tests, kernel_post_tests_count)kernel_tests.cBSD 侧对应bsd_do_post()。xnupost_run_tests()的主循环kernel_tests.c依次完成T_START开始输出收集逐个测试先重置 panic widgetsT_BEGIN(name)开始用例记录xt_begin_timemach_absolute_time()跳过两类用例预期 panic 但无控制器、被kernPOST_config标记为 IGNORE记为XT_ACTION_SKIPPED调用testp-xt_func()执行测试T_END结束用例用T_TESTRESULT与xt_expected_retval比对得出XT_ACTION_PASSED或XT_ACTION_FAILED记录xt_end_timeT_FINISH结束整体输出。此外还有xnupost_list_tests()/kernel_list_tests()用于列出测试目录TOC即分配编号与打印[TEST] TOC#...行xnupost_reset_tests()用于清空测试数据以便重跑。测试数据的导出/估算接口xnupost_export_testdata()、xnupost_get_estimated_testdata_size()则在 xnupost.h 中声明供控制器侧回收结果使用。现有测试一览源码实证当前仓库 kernel_tests.c 中注册的 Mach 层测试包括zalloc_testzone 分配器、RandomULong_test随机数熵校验、test_os_log与test_os_log_parallel日志系统、arm64 平台的arm64_munger_test/ex_cb_test/arm64_pan_test/arm64_ropjop_test、kcdata_api_testkcdata 内核数据接口、console_serial_*串口测试、pmap_coredump_testarm/arm64校验 lowGlo 布局与页表遍历、bitmap_post_test、test_thread_call、ts_kernel_primitive_test/ts_kernel_sleep_inheritor_test/ts_kernel_gate_test/ts_kernel_turnstile_chain_testturnstile 优先级继承与 gate 原语、ts_kernel_timingsafe_bcmp_test、kprintf_hhx_test、vfp_state_testARM VFP 状态以及vm_tests、counter_tests。BSD 侧 bsd_tests.c 注册了arm64_lock_test、pmap_test、ctrr_test、arm64_late_pan_test、kalloc_test、ipi_test、arm64_spr_lock_test、copyio_test等。这些用例本身即是绝佳的编写模板想验证分配器就看zalloc_test想验证随机数质量就看RandomULong_test想验证并发同步原语就看ts_kernel_*系列想验证 panic widget 就看被注释保留的kcdata_api_assert_tests()参考实现。小结darwin-xnu 的 Kernel POSTXNUPOST是一套与内核深度耦合的启动期自检体系通过kernPOST0x1on-desk或kernPOST0x3BATs开启用kernPOST_config1_3,5_9999这类闭区间语法精确挑选用例新增测试只需写函数 改编译清单 注册进测试表三步配合T_ASSERT/T_EXPECT/T_SETUPBEGIN/T_PERF等一整套T_*宏即可获得结构化的串口输出与结果统计而 panic widget 机制则让断言/崩溃路径也能像普通功能路径一样被自动化验证。理解并熟练使用这套框架是进行 darwin-xnu 内核底层子系统回归验证与调试的必备技能。赞分享操作系统驱动开发【免费下载链接】darwin-xnuLegacy mirror of Darwin Kernel. Replaced by https://github.com/apple-oss-distributions/xnu项目地址https://gitcode.com/gh_mirrors/da/darwin-xnu点击查看免费下载相关推荐PyGithub 测试指南Replay Data 回放机制、测试编写与断言自动更新实战PyGithub 测试指南Replay Data 回放机制、测试编写与断言自动更新实战 本篇指南围绕 PyGithub 官方文档 doc/testing.rs开发工具AVA测试框架断言机制终极指南如何编写可靠的JavaScript测试AVA测试框架断言机制终极指南如何编写可靠的JavaScript测试 AVA测试框架 以其强大的断言机制和出色的错误报告著称让开发者能够编写 可靠的Java测试XNU 内核完全指南从 Darwin 混合内核架构到构建、调试与扩展开发XNU 内核完全指南从 Darwin 混合内核架构到构建、调试与扩展开发 XNUX is Not Unix是 Darwin 操作系统的核心也是 macO操作系统驱动开发创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表