D 关键字 struct 为 structure 的缩写,用于引入由一组其他类型组成的新类型。新的结构类型可用作 D 变量和数组的类型,从而使您可以采用单一名称定义相关变量组。D 结构与 C 和 C++ 中对应的构造相同。如果您采用 Java 编程语言编写程序,可将 D 结构视为只包含数据成员但不包含方法的类。
我们假定您希望在 D 中创建一个较为复杂的系统调用跟踪程序,用于记录与 shell 执行的每个 read(2) 和 write(2) 系统调用有关的多种因素,如已用时间、调用数以及作为参数传递的最大字节计数。您可以编写一条 D 子句,在三个单独的关联数组中记录这些属性,如下例所示:
syscall::read:entry, syscall::write:entry /pid == 12345/ { ts[probefunc] = timestamp; calls[probefunc]++; maxbytes[probefunc] = arg2 > maxbytes[probefunc] ? arg2 : maxbytes[probefunc]; }
但是,该子句效率很低,因为 DTrace 必须创建三个单独的关联数组,并存储与每个数组的 probefunc 对应的同一元组值的各个副本。相反,可以使用结构来节省空间并使程序易于阅读和维护。首先,在程序源文件顶部声明一个新的结构类型:
struct callinfo { uint64_t ts; /* timestamp of last syscall entry */ uint64_t elapsed; /* total elapsed time in nanoseconds */ uint64_t calls; /* number of calls made */ size_t maxbytes; /* maximum byte count argument */ };
struct 关键字后跟一个可选标识符,用于引用现在称为 struct callinfo 的新类型。然后,将结构成员括在一组大括号 { } 中,并使用分号 (; ) 结束整个声明。定义每个结构成员使用的语法与 D 变量声明相同,即首先列出成员类型,然后是命名成员的标识符,以及另一个分号 (;)。
结构声明本身仅定义新类型,而不会创建任何变量或在 DTrace 中分配存储空间。完成声明之后,即可在整个 D 程序的其余部分将 struct callinfo 用作类型,并且每个类型为 struct callinfo 的变量都将存储结构模板描述的四个变量副本。这些成员将根据成员列表按顺序分配内存,并在成员间引入数据对象对齐所必需的填充空间。
您可以通过 "." 运算符编写以下格式的表达式,使用成员标识符名称来访问各成员值:
variable-name.member-name
以下示例为使用新结构类型的改进程序。请转到编辑器并键入以下 D 程序,然后将其保存在名为 rwinfo.d 的文件中:
struct callinfo { uint64_t ts; /* timestamp of last syscall entry */ uint64_t elapsed; /* total elapsed time in nanoseconds */ uint64_t calls; /* number of calls made */ size_t maxbytes; /* maximum byte count argument */ }; struct callinfo i[string]; /* declare i as an associative array */ syscall::read:entry, syscall::write:entry /pid == $1/ { i[probefunc].ts = timestamp; i[probefunc].calls++; i[probefunc].maxbytes = arg2 > i[probefunc].maxbytes ? arg2 : i[probefunc].maxbytes; } syscall::read:return, syscall::write:return /i[probefunc].ts != 0 && pid == $1/ { i[probefunc].elapsed += timestamp - i[probefunc].ts; } END { printf(" calls max bytes elapsed nsecs\n"); printf("------ ----- --------- -------------\n"); printf(" read %5d %9d %d\n", i["read"].calls, i["read"].maxbytes, i["read"].elapsed); printf(" write %5d %9d %d\n", i["write"].calls, i["write"].maxbytes, i["write"].elapsed); }
键入该程序后,请运行 dtrace -q -s rwinfo.d,指定其中一个 shell 进程。然后在 shell 中键入数条命令,并在完成 shell 命令输入后,在 dtrace 终端按 Ctrl-C 组合键,以触发 END 探测器并列显结果:
# dtrace -q -s rwinfo.d `pgrep -n ksh` ^C calls max bytes elapsed nsecs ------ ----- --------- ------------- read 36 1024 3588283144 write 35 59 14945541 # |