Solaris 动态跟踪指南

第 18 章 lockstat 提供器

lockstat 提供器提供了可用于理解锁定争用统计信息,或者实际了解锁定行为任何方面的探测器。lockstat(1M) 命令实际上是 DTrace 使用者,它使用 lockstat 提供器收集其原始数据。

概述

lockstat 提供器提供了两种类型的探测器:争用事件探测器和暂挂事件探测器。

争用事件探测器对应于同步元语的争用,并在强制线程等待资源可用时触发。通常会将 Solaris 优化为不出现争用情况,从而不会发生延迟的争用。应使用这些探测器来了解发生争用的情况。因为争用相对较少,所以启用争用事件探测器通常不会实际影响性能。

暂挂事件探测器对应于获取、释放或处理同步元语。这些探测器可用于回答有关处理同步元语的方式的任意问题。因为 Solaris 频繁(在一个繁忙系统中,每个 CPU 每秒几百万次)获取和释放同步元语,所以启用暂挂事件探测器产生的探测影响要远远大于启用争用事件探测器。虽然启用这些探测器会产生很强的探测效果,但这些效果并不是负面的;因此仍然可以在产品化的系统上放心启用这些探测器。

lockstat 提供器提供了对应于 Solaris 中的各种同步元语的探测器;这些元语和与其对应的探测器将在本章的剩余部分中讨论。

自适应锁定探测器

自适应锁定强制临界段互斥,在内核的大多数上下文中都可以获取该锁定。因为自适应锁定的上下文限制极少,所以它们组成了 Solaris 内核中绝大多数的同步元语。这些锁定的与争用相关的行为是自适应的:当线程尝试获取暂挂的自适应锁定时,它将确定拥有的线程当前是否正在 CPU 中运行。如果属主正在另一个 CPU 中运行,则获取线程将旋转。如果属主未运行,则获取线程将阻塞。

表 18–1 中介绍了与自适应锁定有关的四种 lockstat 探测器。对于每种探测器,arg0 都包含指向代表自适应锁定的 kmutex_t 结构的探测器。

表 18–1 自适应锁定探测器

adaptive-acquire

获取自适应锁定后立即触发的暂挂事件探测器。 

adaptive-block

在暂挂的自适应锁定上阻塞的线程重新唤醒并获取互斥之后,将触发的争用事件探测器。如果两种探测器都已启用,则 adaptive-block 将在 adaptive-acquire 之前触发。单个锁定获取可以触发 adaptive-block 和 adaptive-spin 探测器。adaptive-block 的 arg1 包含休眠时间(以纳秒为单位)。

adaptive-spin

在暂挂的自适应互斥上旋转的线程成功获取互斥后将触发的争用事件探测器。如果两种探测器都已启用,则 adaptive-spin 将在 adaptive-acquire 之前触发。单个锁定获取可以触发 adaptive-block 和 adaptive-spin 探测器。adaptive-spin 的 arg1 包含旋转时间:获取锁定之前,旋转循环花费的时间(以纳秒为单位)。

adaptive-release

释放自适应锁定之后将立即触发的暂挂事件探测器。 

旋转锁定探测器

在内核的某些上下文(如高级中断上下文和任何处理分发程序状态的上下文)中不能阻塞线程。在这些上下文中,此限制会禁止使用自适应锁定。在这些上下文中将改用旋转锁定来影响临界段的互斥。顾名思义,在存在争用的情况下,这些锁定的行为是旋转,直到拥有线程释放锁定。表 18–2 中介绍了与旋转锁定有关的三种探测器。

表 18–2 旋转锁定探测器

spin-acquire

获取旋转锁定之后将立即触发的暂挂事件探测器。 

spin-spin

在暂挂的旋转锁定上旋转的线程成功获取旋转锁定后将触发的争用事件探测器。如果两种探测器都已启用,则 spin-spin 将在 spin-acquire 之前触发。spin-spin 的 arg1 包含旋转时间:获取锁定之前,在旋转状态中花费的时间(以纳秒为单位)。独立的旋转计数本身没有什么意义,但可用于比较旋转次数。

spin-release

释放旋转锁定之后将立即触发的暂挂事件探测器。 

自适应锁定比旋转锁定更常见。以下脚本显示了两种锁定类型的总计,以提供支持此观察的数据。

lockstat:::adaptive-acquire
/execname == "date"/
{
	@locks["adaptive"] = count();
}

lockstat:::spin-acquire
/execname == "date"/
{
	@locks["spin"] = count();
}

在一个窗口中运行此脚本,在另一个窗口中运行 date(1) 命令。终止 DTrace 脚本时,将会看到与以下示例类似的输出:


# dtrace -s ./whatlock.d
dtrace: script './whatlock.d' matched 5 probes 
^C
spin                                                             26
adaptive                                                       2981

如此输出所指示的那样,在运行 date 命令时获取的锁定,超过 99% 都为自适应锁定。令人吃惊的是,在执行像 date 这样简单的操作时获取了如此多的锁定。大量的锁定是具有极大可伸缩性的系统(如 Solaris 内核)所需的细分锁定的自然产物。

线程锁定

线程锁定是一种特殊类型的旋转锁定,用于锁定线程,以更改线程的状态。线程锁定暂挂事件可用作旋转锁定暂挂事件探测器(即,spin-acquire 和 spin-release),但争用事件具有各自特定于线程锁定的探测器。表 18–3 中介绍了线程锁定暂挂事件探测器。

表 18–3 线程锁定探测器

thread-spin

线程在线程锁定上旋转之后将触发的争用事件探测器。与其他争用事件探测器一样,如果争用事件探测器和暂挂事件探测器都已启用,则 thread-spin 将在 spin-acquire 之前触发。但与其他争用事件探测器不一样,thread-spin 在实际获取锁定之前触发。因此,多个 thread-spin 探测器触发可能与单个 spin-acquire 探测器触发对应。

读取器/写入器锁定探测器

读取器/写入器锁定强制执行允许临界段中有多个读取器或单个写入器的策略,但不允许同时存在二者。这些锁定通常用于搜索操作比修改操作更频繁且临界段中具有足够的时间的结构。如果临界段时间比较短,则读取器/写入器锁定将在用于实现锁定的共享内存中隐式串行化,从而使它们无法比自适应锁定提供更大的优势。有关读取器/写入器锁定的更多详细信息,请参见 rwlock(9F)。

表 18–4 中介绍了与读取器/写入器锁定有关的探测器。对于每种探测器,arg0 都包含指向代表自适应锁定的 krwlock_t 结构的指针。

表 18–4 读取器/写入器锁定探测器

rw-acquire

获取读取器/写入器锁定之后将立即触发的暂挂事件探测器。如果作为读取器获取锁定,则 arg1 将包含常量 RW_READER,如果作为写入器获取锁定,则将包含常量 RW_WRITER。

rw-block

在暂挂的读取器/写入器锁定上阻塞的线程重新唤醒并获取锁定之后,将触发的争用事件探测器。arg1 包含当前线程要获取锁定必须休眠的时间长度(以纳秒为单位)。如果作为读取器获取锁定,则 arg2 将包含常量 RW_READER,如果作为写入器获取锁定,则将包含常量 RW_WRITER。arg3 和 arg4 包含有关阻塞原因的更多信息。在当前线程阻塞的情况下,只有在作为写入器暂挂锁定时,arg3 才不为零。arg4 包含当前线程阻塞时的读取器计数。如果 rw-block 和 rw-acquire 探测器都已启用,则 rw-block 将在 rw-acquire 之前触发。

rw-upgrade

在线程已成功将读取器/写入器锁定从读取器升级到写入器之后,将触发的暂挂事件探测器。升级没有关联的争用事件,因为仅可以通过非阻塞接口 rw_tryupgrade(9F) 进行升级。

rw-downgrade

在线程将其读取器/写入器锁定的拥有权从写入器降级到读取器之后,将触发的暂挂事件探测器。降级没有关联的争用事件,因为降级在没有争用的情况下始终会成功。 

rw-release

释放读取器/写入器锁定之后,将立即触发的暂挂事件探测器。如果作为读取器暂挂释放的锁定,则 arg1 将包含常量 RW_READER,如果作为写入器暂挂释放的锁定,则将包含常量 RW_WRITER。由于升级和降级,获取锁定时,锁定可能并没有释放。

稳定性

lockstat 提供器使用 DTrace 的稳定性机制描述其稳定性,如下表所示。有关稳定性机制的更多信息,请参见第 39 章。

元素 

名称稳定性 

数据稳定性 

相关性类 

提供器 

发展中 

发展中 

公用

模块 

专用 

专用 

未知 

功能 

专用 

专用 

未知 

名称 

发展中 

发展中 

公用

参数 

发展中 

发展中 

公用