Solaris 动态跟踪指南

类型名称空间

本节讨论 D 名称空间以及与各种类型有关的名称空间问题。在传统语言(如 ANSI-C)中,类型可见性由类型是嵌入在函数中还是嵌入在其他声明中来确定。在 C 程序的外部范围中声明的类型与单个全局名称空间关联,这些类型在整个程序中可见。C 头文件中定义的类型通常包括在此外部范围中。与这些语言不同,D 可以访问多个外部范围中的类型。

D 是一种可以很方便地在软件栈的多个层(包括操作系统内核、关联的可装入内核模块集和系统上正在运行的用户进程)中进行动态观察的语言。单个 D 程序可以实例化探测器,以便从编译到独立二进制对象的多个内核模块或其他软件实体中收集数据。因此,在 DTrace 和 D 编译器可以使用的总体类型中,可能存在名称相同但定义不同的多种数据类型。为了解决这种情况,D 编译器将每种类型与所包含程序对象标识的名称空间关联。通过在任何类型名称中指定对象名称和反引号 (`) 作用域运算符,可以访问特定程序对象的类型。

例如,如果名为 foo 的内核模块包含以下 C 类型声明:

typedef struct bar {
	int x;
} bar_t;

则可以在 D 中使用以下类型名称来访问 struct barbar_t

struct foo`bar				foo`bar_t

在类型名称合适的任何上下文中(包括当为 D 探测子句中的 D 变量声明或强制类型转换表达式指定类型时)都可以使用反引号运算符。

D 编译器还提供了两种特殊的内置类型名称空间(分别使用名称 CD)。C 类型名称空间最初使用标准 ANSI-C 内部类型(如 int)填充。此外,使用 C 预处理程序 cpp(1) 并使用 dtrace -C 选项获取的类型定义将由 C 范围处理并添加到其中。因此,可以将已可见的类型指令所在的 C 头文件包含到另一类型名称空间中,而不会引起编译错误。

D 类型名称空间初始使用 D 内部类型(如 intstring)及内置 D 类型别名(如 uint32_t)填充。出现在 D 程序源代码中的任何新类型声明都将自动添加到 D 类型名称空间中。如果在 D 程序中创建由来自其他名称空间的成员类型组成的复杂类型(如 struct),这些成员类型将会根据声明复制到 D 名称空间中。

当 D 编译器遇到未使用反引号运算符指定显式名称空间的类型声明时,编译器将会使用指定的类型名称搜索活动的类型名称空间集以找到匹配项。将始终首先搜索 C 名称空间,然后搜索 D 名称空间。如果在 C 或 D 名称空间中未找到类型名称,则将会按内核模块 ID 的升序搜索活动的内核模块的类型名称空间。此顺序保证,将首先搜索构成核心内核的二进制对象,然后搜索任何可装入的内核模块,但不能保证可装入模块间的任何顺序属性。访问可装入内核模块中定义的类型时,应使用作用域运算符以避免与其他内核模块发生类型名称冲突。

D 编译器使用为核心 Solaris 内核模块提供的压缩 ANSI-C 调试信息,以便自动访问与操作系统源代码关联的类型,而无需访问相应的 C 包含文件。此符号调试信息可能并不对系统上的所有内核模块都可用。如果尝试访问模块的名称空间内的类型,而该模块缺少要用于 DTrace 的压缩 C 调试信息,则 D 将会报告错误。