尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

数据契约解读:.NET Runtime 的 ObjectiveCMarshal 契约与 Objective-C 互操作标签内存

数据契约解读:.NET Runtime 的 ObjectiveCMarshal 契约与 Objective-C 互操作标签内存 数据契约解读.NET Runtime 的 ObjectiveCMarshal 契约与 Objective-C 互操作标签内存【免费下载链接】runtime.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.项目地址: https://gitcode.com/GitHub_Trending/runtime6/runtime本篇文章围绕 .NET Runtime 诊断数据契约体系中的ObjectiveCMarshal契约展开完整解读其 API 面、V1 版本的数据描述符/全局变量/常量定义并结合仓库中托管侧实现与 cdac 契约侧实现深入剖析GetTaggedMemory如何定位 Objective-C 跟踪对象所关联的标签内存tagged memory帮助读者掌握诊断工具读取运行时内存、还原互操作状态的核心方法论。从数据契约到 ObjectiveCMarshal在深入ObjectiveCMarshal契约之前需要先理解它所属的体系。.NET 诊断数据契约Diagnostic Data Contract是一套面向诊断工具调试器、分析器、profiler 等的契约文档它描述了 .NET 运行时进程内存中内部数据结构子集的物理形态地址、字段大小、偏移量与语义。契约本质上是运行时对诊断工具的一份承诺只要工具按照契约读取进程内存并解释数据就能计算出关于运行时当前状态的有效信息从而摆脱传统 DAC/DBI 二进制必须与运行时版本严格匹配的困境详见 数据契约设计文档。ObjectiveCMarshal契约正是该体系中的一员。正如契约文档 ObjectiveCMarshal.md 开篇所述This contract is for getting information related to Objective-C interop marshaling.它的职责是让诊断工具能够获取与 Objective-C 互操作封送marshaling相关的运行时信息——具体而言就是定位某个被跟踪的托管对象所关联的、由ObjectiveCMarshal引用跟踪机制分配的标签内存块。契约 API 面GetTaggedMemory契约文档定义了该契约对外暴露的唯一 API// Get the tagged memory for an Objective-C tracked reference object. // Returns TargetPointer.Null if the object does not have tagged memory. // On success, size is set to the size of the tagged memory in bytes; otherwise size is set to default. TargetPointer GetTaggedMemory(TargetPointer address, out TargetNUInt size);参数与返回值的语义如下成员类型含义addressTargetPointer被跟踪的 Objective-C 引用对象托管对象的地址sizeoutTargetNUInt成功后为标签内存的字节大小失败时保持默认值返回值TargetPointer标签内存块的地址对象没有标签内存时返回TargetPointer.Null注意TargetPointer与TargetNUInt是契约层对目标进程地址/原生整数的抽象类型其宽度由目标运行时决定32 位/64 位这也是数据契约跨架构能力的基础。Version 1契约的完整定义契约文档为ObjectiveCMarshal定义了唯一版本 V1并附带了机器生成BEGIN GENERATED/END GENERATED标记的契约使用清单这些信息是诊断工具实现契约所需的最小依赖集合。使用的数据描述符Data DescriptorFieldTypeMeaningSystem.Runtime.InteropServices.ObjectiveC.ObjectiveCMarshalObjcTrackingInformation_memorypointer指向与被跟踪 Objective-C 对象关联的标签内存块的指针在 cdac 契约实现中这一描述符对应 Data/ObjcTrackingInformation.cs[CdacType(System.Runtime.InteropServices.ObjectiveC.ObjectiveCMarshalObjcTrackingInformation)] internal sealed partial class ObjcTrackingInformation : IDataObjcTrackingInformation { [Field(_memory)] public partial TargetPointer Memory { get; } }其中_memory正是托管侧 ObjectiveCMarshal.cs 中internal IntPtr _memory;字段的契约映射指向每个跟踪对象专属的标签内存块。使用的全局变量GlobalTypeMeaningObjectiveCMarshal.s_objectspointerObjectiveCMarshal.s_objects的地址即映射被跟踪对象 → 跟踪信息的ConditionalWeakTableSystem.Runtime.InteropServices.ObjectiveC.ObjectiveCMarshal.s_objectspointer同上完全限定名形式s_objects是契约算法查找标签内存的入口。托管侧源码中它的定义如下ObjectiveCMarshal.csprivate static readonly ConditionalWeakTableobject, ObjcTrackingInformation s_objects new();它是一张ConditionalWeakTable键为被跟踪的托管对象值为该对象对应的ObjcTrackingInformation内含_memory标签内存指针与弱引用句柄。契约层通过[StaticReference(s_objects)]注解定位该静态字段见 Data/ObjectiveCMarshal.cs[CdacType(System.Runtime.InteropServices.ObjectiveC.ObjectiveCMarshal)] internal sealed partial class ObjectiveCMarshal : IDataObjectiveCMarshal { [StaticReference(s_objects)] public static partial TargetPointer? ObjectTrackingInfoTable(Target target); }依赖的其他契约Contract NameConditionalWeakTable算法需要遍历s_objects这张表完成地址 → 跟踪信息的查找因此依赖ConditionalWeakTable契约其独立契约文档见 ConditionalWeakTable.md提供的TryGetValue能力。契约常量NameTypePurposeValueTaggedMemorySizeInPointersint标签内存块中以目标平台指针大小计的元素个数2该常量是契约算法计算标签内存字节大小的唯一依据size TaggedMemorySizeInPointers * PointerSize。契约算法GetTaggedMemory 的完整实现契约文档给出了 V1 的参考实现伪代码下面逐段解读其执行流程const int TaggedMemorySizeInPointers 2; TargetPointer GetTaggedMemory(TargetPointer address, out TargetNUInt size) { size default; TargetPointer objectsTable target.ReadPointer( /* System.Runtime.InteropServices.ObjectiveC.ObjectiveCMarshal.s_objects static field address */); if (objectsTable TargetPointer.Null) return TargetPointer.Null; if (target.Contracts.ConditionalWeakTable.TryGetValue( objectsTable, address, out TargetPointer trackingInfoAddress)) { TargetPointer taggedMemory target.ReadPointer( trackingInfoAddress /* Object data offset */ /* System.Runtime.InteropServices.ObjectiveC.ObjectiveCMarshalObjcTrackingInformation::_memory offset */); if (taggedMemory ! TargetPointer.Null) { size new TargetNUInt(TaggedMemorySizeInPointers * (ulong)target.PointerSize); return taggedMemory; } } return TargetPointer.Null; }执行流程可以拆解为四步读取静态表地址通过s_objects静态字段地址读取ConditionalWeakTable实例指针若为Null直接返回Null说明引用跟踪尚未初始化或表为空。表查找调用ConditionalWeakTable契约的TryGetValue以address被跟踪对象地址为键查找对应的ObjcTrackingInformation地址。读取标签内存指针从跟踪信息地址加上ObjcTrackingInformation::_memory字段偏移读出标签内存块指针。返回结果若_memory非空则按常量计算字节大小2 * PointerSize并返回内存地址否则返回TargetPointer.Null。值得说明的是size的计算方式TaggedMemorySizeInPointers以指针个数为单位乘以target.PointerSize后得到字节数天然适配 32 位与 64 位目标平台——这正是数据契约跨架构设计的体现。源码级印证托管侧的引用跟踪实现契约描述的一切都根植于托管侧的ObjectiveCMarshal类型。打开 ObjectiveCMarshal.cs可以看到该类型仅在 macOS/iOS/tvOS/Mac Catalyst 平台可用[SupportedOSPlatform]特性提供了一套完整的 Objective-C 互操作封送能力Initialize注册 begin/end 引用跟踪回调、isReferencedCallback、对象进入终结队列回调以及未处理异常传播处理器且只能调用一次重复初始化抛InvalidOperationException。CreateReferenceTrackingHandle为对象分配引用跟踪 GC 句柄同时通过 out 参数返回标签内存。GetOrCreateReferenceTrackingMemory获取必要时创建与对象关联的跟踪内存返回SpanIntPtr。SetMessageSendCallback覆盖 Objective-C 运行时的msgSend系列导出如MsgSend、MsgSendFpret、MsgSendStret等用于支持可变参数等场景。其中与契约最直接相关的两个内部结构是s_objects第 45 行契约中全局变量一节所指的静态字段ConditionalWeakTableobject, ObjcTrackingInformation类型。ObjcTrackingInformation第 223-292 行契约中数据描述符一节所描述的跟踪信息结构。其内部常量TAGGED_MEMORY_SIZE_IN_POINTERS 2与契约常量TaggedMemorySizeInPointers 2完全一致构造函数通过NativeMemory.AllocZeroed(TAGGED_MEMORY_SIZE_IN_POINTERS * IntPtr.Size)分配零初始化的标签内存并存入_memory字段EnsureInitialized为对象分配WeakTrackResurrection类型的 GC 句柄终结器中在对象确实不可达时才释放标签内存与句柄。从源码结构可以推断标签内存的整个生命周期完全由托管侧管理分配、零初始化、随对象终结而释放而诊断工具仅需按契约读取无需修改任何运行时状态——这正是只读式诊断契约的典型形态。契约实现侧cdac 中的 ObjectiveCMarshal_1契约的算法规范在诊断工具侧落地为具体实现。仓库的 cdacCross-DAC即新一代数据契约读取器中ObjectiveCMarshal_1结构体实现了IObjectiveCMarshal契约接口见 Contracts/ObjectiveCMarshal_1.cspublic TargetPointer GetTaggedMemory(TargetPointer address, out TargetNUInt size) { size default; TargetPointer? objectsTable Data.ObjectiveCMarshal.ObjectTrackingInfoTable(_target); if (objectsTable is null || objectsTable.Value TargetPointer.Null) return TargetPointer.Null; IConditionalWeakTable cwt _target.Contracts.ConditionalWeakTable; if (cwt.TryGetValue(objectsTable.Value, address, out TargetPointer trackingInfoAddress)) { ObjcTrackingInformation trackingInfo _target.ProcessedData.GetOrAddObjcTrackingInformation(trackingInfoAddress); if (trackingInfo.Memory ! TargetPointer.Null) { const int TAGGED_MEMORY_SIZE_IN_POINTERS 2; size new TargetNUInt(TAGGED_MEMORY_SIZE_IN_POINTERS * (ulong)_target.PointerSize); return trackingInfo.Memory; } } return TargetPointer.Null; }可以清晰看到它与文档伪代码的逐句对应文档伪代码cdac 实现target.ReadPointer(s_objects 静态字段地址)Data.ObjectiveCMarshal.ObjectTrackingInfoTable(_target)借助StaticReference定位静态字段target.Contracts.ConditionalWeakTable.TryGetValue(...)_target.Contracts.ConditionalWeakTable.TryGetValue(...)手工偏移计算_memory_target.ProcessedData.GetOrAddObjcTrackingInformation(...)trackingInfo.Memory字段偏移由[Field(_memory)]描述符自动解析TaggedMemorySizeInPointers * PointerSizeTAGGED_MEMORY_SIZE_IN_POINTERS * (ulong)_target.PointerSize这种文档即规范、实现即验证的对应关系正是 数据契约设计文档 所倡导的契约以 C# 风格伪代码定义算法工具按契约实现运行时在契约描述符中声明支持的契约版本从而实现调试器与运行时解耦。契约版本管理与演进ObjectiveCMarshal目前仅有 V1 一个版本。根据数据契约的版本管理规则见 datacontracts_design.md每个契约版本使用字符串版本标识符不同标识代表不同实现更高并不一定更新同一个契约的所有版本存放在同一份文档即docs/design/datacontracts/ObjectiveCMarshal.md中便于横向对比差异当运行时修改数据结构或算法且与旧版本不兼容时需要定义并使用新版本而兼容性的修订如修正文档 bug、补充可选的全局变量/类型/字段/算法则允许在原版本上以追加式修订方式进行对某个主版本发行而言支持的契约版本集合应从首个 RC 到最终服务版本保持固定确保诊断工具在整个发行生命周期内可用。这一机制意味着即使未来运行时内部改变了标签内存的存储方式例如调整ObjcTrackingInformation布局或替换s_objects表结构也只会催生新的契约版本而 V1 的语义定义对旧运行时依然有效。小结ObjectiveCMarshal契约以最小化的信息暴露一个 API、一个数据描述符、一个全局变量、一个依赖契约、一个常量完整描述了 Objective-C 引用跟踪对象标签内存的定位方法。透过契约文档的伪代码对照托管侧 ObjectiveCMarshal.cs 中s_objects与ObjcTrackingInformation的实际布局以及 cdac 侧 ObjectiveCMarshal_1.cs 的实现读者可以完整掌握契约文档 → 托管源码 → 诊断工具实现三层对应的阅读方法。这一方法论同样适用于docs/design/datacontracts/目录下的其余契约如ConditionalWeakTable、GCHandle、GC等是理解 .NET 诊断数据契约体系的关键起点。【免费下载链接】runtime.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.项目地址: https://gitcode.com/GitHub_Trending/runtime6/runtime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表