AIX 事务内存编程

事务内存 (TM) 是允许进程-线程执行存储操作的共享内存同步构造,这些操作似乎是其他进程-线程或应用程序的基础。

概述

TM 是允许执行代码的基于锁定的临界区的构造,而无需获取锁定。 IBM® POWER 8 处理器是实现 TM 编程的第一个处理器。

在以下某些情况下使用 TM 工具:
  • 乐观执行基于锁定的应用程序 – TM 支持推测性地执行代码的临界区,而无需获取锁定。 此方法通过使用未针对性能调优的当前锁定来为应用程序提供细颗粒度锁定的优势。
  • 使用高级语言的事务编程 – 事务编程模型是一种业内逐步成长的标准,提供与基于锁定的共享内存程序相关的生产力。
  • 检查点/回滚使用情况 – TM 用作检查点来复原体系结构状态。 此方法使推测性编译器在运行时代码优化或者生成以及检查点模拟期间得到优化。

要使用 TM 工具,进程线程使用 tbegin.tend. 指令标记存储访问或事务序列的开始和结束。 tbegin. 指令指示事务执行,在这期间会自动发生装入和存储。 tend. 指令结束事务执行。

如果某个事务过早停止,那么会回滚执行 tbegin. 指令之后做出的存储器更新。 相应地,寄存器子集的内容也会回滚为执行 tbegin. 指令之前的状态。 当事务过早停止时,会启动软件故障处理程序。 该故障可以是瞬态类型,也可以是持久类型。 故障处理程序可重试该事务,或者选择采用不同的锁定构造还是逻辑路径(这取决于故障性质)。

AIX® 操作系统支持使用 TM ,包括跨上下文切换和中断处理 TM 状态管理。

检查点状态

当启动事务时,会保存一组寄存器,以表示处理器的检查点状态。 如果事务发生故障,那么一组寄存器将复原为启动事务之前的点。 处理器的检查点状态也称为事务前状态。 检查点状态包括问题状态可写寄存器,CR0FXCCEBBHREBBRRBESCR 寄存器、性能监视寄存器和 TM SPR 除外。

注: 无法通过管理程序状态或问题状态直接访问检查点状态。
执行新的 treclaim. 指令后,检查点状态将复制到相应的寄存器中。 此过程允许有特权的代码保存或修改值。 执行新的 trechkpt. 指令之后,检查点状态将从各自用户可访问寄存器复制回推测性寄存器。
以下 TM SPR 将添加到处理器的机器状态中:
名称 标题 描述 有特权的 mtspr 有特权的 mfspr 大小(位) SPR
FSCR 工具状态和控制寄存器 控制问题状态中的可用工具并指示工具不可用中断的原因。 64 153
TEXASR 事务异常和摘要寄存器 包含事务故障处理程序所使用的事务级别和摘要信息。 0:31 位包含故障的原因。 64 130
TFHAR 事务故障处理程序地址寄存器 记录软件故障处理程序的 EA。 TFHAR 寄存器始终设置为启动了事务的 tbegin. 指令的 NIA。 64 128
TFIAR 事务故障指令地址寄存器 设置为导致故障的指令的精确 EA(如果可能)。 TFIAR 寄存器的准确性记录在 TEXASR 寄存器的“精确”字段(37 位)中。 64 129
TEXASRU 事务异常和摘要寄存器(上半部分) TEXASR 寄存器的半高位。 32 131
新 TEXASR 寄存器包含与事务状态和事务故障原因相关的信息。 下表描述 TEXASR 寄存器中包括的字段:
字段 值 - 含义 位数
故障代码(注意:位 7 指的是故障持久字段) 事务故障代码 0:7
不允许 0b1 - 不允许访问类型指令 8
嵌套溢出 0b1 - 超过了最大事务级别。 9
占用量溢出 0b1 - 超过了事务存储器访问的跟踪限制。 10
自诱导冲突 0b1 - 暂挂状态发生了自诱导冲突。 11
非事务性冲突 0b1 - 另一个处理器进行的非事务访问发生冲突。 12
事务冲突 0b1 - 另一个事务发生冲突。 13
事务无效冲突 0b1 - TLB 无效发生冲突。 14
特定于实施 0b1 - 特定于实施的条件导致了事务失败。 15
指令访存冲突 0b1 - 由线程或另一个线程从先前以事务方式写入的某个块执行的指令访存。 16
已为未来故障案例保留   17:30
异常终止 0b1 -执行特定 TM 指令导致异常中止。 31
已暂挂 0b1 -故障已记录为 "暂挂" 状态。 32
已预留   33
特权 进行故障记录时,线程处于特权状态 ([MSRHV||PR])。 34:35
故障摘要 (FS) 0b1 - 检测到故障并对其进行记录。 36
TFIAR 精确

0b0 - TFIAR 字段中的值为大约值。

0b1 - TFIAR 字段中的值为精确值。

37
ROT

当执行非 ROT tbegin. 指令时,设置为 0b0。

当启动 ROT 时,设置为 0b1。

38
已预留   39:51
事务级别 (TL) 活动事务的事务级别(嵌套深度 + 1)有以下值:
  • 0,已成功完成最近的事务。
  • 最近事务失败的事务级别,如果事务未成功完成。
注: 1 的值对应于 外部事务。 大于 1 的值对应于嵌套事务
52:63
注意:
  • 当记录事务故障时,只设置 TEXASR 寄存器的 8-31 位中的 1 位。 所设置的一位指示特定指令或事件导致了故障。
  • 仅回滚事务 (ROT) 是作为一个整体执行的一系列指令,或者未执行指令。 这导致允许用最低成本推测性地执行成批指令。 ROT 没有完全原子性作为正常事务或者其同步或序列化属性。 因此,ROT 不得用来操纵共享数据。

软件故障处理程序

当事务失败时,机器硬件将控制重定向至与最外层事务关联的故障处理程序。 当事务失败时,会将控制重定向至 tbegin. 指令后面的指令,CR0 设置为
0b101 || 0
0b010 || 0
因此,tbegin. 指令后的指令必须是基于 2 位 CR0 的转移指令。 例如,执行 tbegin. 指令之后,beq 转移指令基于 2 位的 CR0。 转移的目标必须是处理事务故障的代码段。 当 tbegin. 指令在启动事务时成功运行之后,CR0 将设置为
0b000 || 0 or 0b010 || 0
注: TEXASR 的位 0:31 报告故障原因。 位 0-7 中的故障代码 (FC) 字段用于以下情况:
  • 有特权的管理程序或系统管理程序代码通过使用 treclaim. 指令导致故障。
  • 问题状态代码通过使用 tabort. 指令的形式导致失败。
位 7 TEXASR 中值 1 指示,故障为持久型,并且当再次尝试事务时该事物必然会失败。 AIX 操作系统保留的故障代码指示 /usr/include/sys/machine.h. 目录中定义的故障原因。

样本事务

以下汇编程序代码示例显示将 GPR 5 中的值写入 GPR 4 中的地址的简单事务,将假定该事务会在执行的多个线程中共享。 如果事务由于持久原因发生故障,那么代码将回到 lock_based_update 标签处的另一个代码路径。 不会显示备用路径的代码。
trans_entry:
  tbegin                             # Start transaction
  beq          failure_hdlr          # Handle transaction failure
  stw          r5, 0(r4)             # Write to memory pointed to by r4.
  tend.                              # End transaction
  b            trans_exit
failure_hdlr:                        # Handle transaction failures:
  mfspr        r4, TEXASRU           # Read high-order half of TEXASR
  andis.       r5, r4, 0x0100        # Is the failure persistent?
  bne          lock_based_update     # If persistent, acquire lock and 
                                     # then perform the write.
  b            trans_entry           # If transient, try again.

lock_based_update:

trans_exit:

在运行时确定事务内存功能

程序可以通过使用 getsystemcfg 子例程读取 SC_TM_VER 系统变量来确定系统是否支持 POWER ISA 的 TM 类别。 /usr/include/sys/systemcfg.h 文件中提供了 __power_tm() 宏,用于确定程序中的 TM 功能。 此宏用于有条件使用 TM 功能(如果提供该功能)或者使用等同于基于锁定的代码路径的功能(如果未提供 TM 功能)的软件。

扩展上下文结构

较早版本的 AIX 操作系统引入了对扩展上下文结构的支持,以支持向量状态和用户密钥。 现有扩展上下文结构支持将进一步扩展为支持 TM 所需的机器状态。

当首次使用 TM 时,会为每个事务进程-线程分配和锁定扩展上下文。 如果扩展上下文区域无法分配和锁定,那么将接收到导致进程终止的 SIGSEGV 信号。

机器上下文信息包括在为信号处理程序提供的 sigcontext 结构中。 当信号处理程序返回时,会激活 sigcontext 结构中提供的机器上下文。 sigcontext 结构实际上是较大的 ucontext 结构的一部分。 这两个结构最多有 sizeof(struct sigcontext) 是完全相同的。 当 AIX 操作系统构建要传递到信号处理程序的信号上下文时,将在信号处理程序的堆栈上构建 ucontext 结构。 信号上下文的机器上下文部分必须包含无意中断上下文的所有活动的机器状态(包括易失性和非易失性)。 ucontext 结构包含一个指示器,以确定扩展上下文信息是否可用。

ucontext 结构中的 __extctx 字段是扩展上下文结构的地址(如 /usr/include/sys/context.h 文件中所定义)。 ucontext 结构中的字段 __extctx_magic 指示,当 __extctx_magic 字段的值等于 __EXTCTX_MAGIC 时扩展上下文信息是否有效。 使用 TM 功能的线程的附加机器状态将作为上下文扩展的成员复原并保存到作为信号传递和返回一部分的 ucontext 结构。

如果应用程序选择显式地启用事务内存的使用,那么它将采用已有用于 __extctx 字段空间的扩展大小 ucontext 结构,编译器编译的 __EXTABI__ 的隐式定义包含了该字段。 扩展的 ucontext 结构还可由 __AIXEXTABI 的显式定义选取。

当处于事务或暂挂状态时,libc 的 getcontext()setcontext()makecontext()swapcontext() 子例程不受支持。 当调用事务内的子例程时,getcontext()setcontext()makecontext() 子例程将导致 TM_LIBC 类型的持久事务故障,该类型在 /usr/include/sys/machine.h 文件中定义。

当调用事务内的 swapcontext() 子例程时,将导致以下行为:
  • swapcontext() 子例程处于事务状态时,将导致 TM_LIBC 类型的持久事务故障。
  • swapcontext() 子例程处于暂挂状态时,将导致事务崩溃,换入所指定的 ucontext 结构并且所指定的 ucontext 结构恢复执行程序。 未定义 swapcontext() 子例程返回后产生的状态和后续行为。

如果调用处于非事务状态的 getcontext()setcontext()swapcontext() 子例程,子例程不会将任何扩展 TM 上下文检索或复原到 ucpoucp 参数所指向的 ucontext 结构中或从中进行检索或复原。 当调用 setcontext()swapcontext() 子例程并且存在扩展 TM 上下文时,不会指示任何错误。

有关扩展上下文的详细信息,请参阅 /usr/include/sys/context.h 头文件。

信号传递

当正在进行以非事务方式传递的事务时应用程序接收到的异步信号。 当处于事务状态时,不允许传递异步信号,而会导致 TM_SYNC_SIGNAL 类型的持久事务故障(如 /usr/include/sys/machine.h 文件中所定义)。

对齐中断和程序中断

在事务状态下,由于非法操作或需要枚举的操作导致 TM_ALIGN_INT 类型或 TM_INV_OP 类型的持久事务故障(如 /usr/include/sys/machine.h 文件中所定义),因此导致对齐中断和程序中断。 当处于暂挂状态时,正常情况下,会通过使用非推测性地语义来处理对齐和程序中断。

系统调用数

建议不调用事务内的系统调用。 仅当事务通过 tsuspend. 指令处于暂挂状态时,才支持事务内的系统调用。

当在处理器或线程是事务性的情况下调用系统调用并且未暂挂事务时, AIX 内核不会调用系统调用,并且关联的事务将持续失败。 当发生此错误时,TEXASR 寄存器的 FC 字段包含 TM_ILL_SC 故障代码(如 /usr/sys/include/machine.h 文件中所定义)。

假定当应用程序员已显式暂挂旨在作为持久事务的事务时,任何操作都在暂挂事务状态下执行。 即使事务失败,也不会回滚处于暂挂状态时调用的系统调用所执行的任何操作。

AIX 操作系统不支持在事务状态下进行系统调用,因为无法在系统调用下回滚由 AIX 执行的任何操作 (包括 I/O)。

setjmp() 和 longjmp() 子例程

由于设置跳跃缓冲区和跳回缓冲区的影响, libc 的 setjmp()longjmp() 子例程在事务或暂挂状态下不受支持。 请考虑以下场景
  1. 如果调用事务内的 setjmp() 子例程,并且在该事务结束之后调用相应的 longjmp() 子例程,那么将跳至现在无效的推测性状态。
  2. 如果调用事务之前的 setjmp() 子例程,那么相应的 longjmp() 子例程将变为该事务开始之前的状态,无论事务已结束、失败还是异常终止。
  3. 如果调用事务内的 setjmp() 子例程然后该事务异常终止,那么 setjmp() 子例程不一定会对转移缓冲区进行更新。

当调用事务内的 setjmp() 子例程时,将导致 TM_LIBC 类型或 TM_ILL_SC 类型的持久事务故障(如 /usr/include/sys/machine.h 文件中所定义)。

当调用事务内的 longjmp() 子例程时,将导致以下行为:
  • longjmp() 子例程处于事务状态时,它会导致在 /usr/include/sys/machine.h 文件中定义的 TM_LIBC 类型或 TM_ILL_SC 类型的持久事务失败。
  • longjmp() 子例程处于暂挂状态时,将导致事务崩溃,复用所指定的转移缓冲区,并且执行程序将返回到相应的 setjmp() 子例程。 setjmp() 子例程从 longjmp() 子例程返回后,未定义产生的状态和后续行为。

编译器

支持事务内存的 AIX 操作系统编译器必须符合 AIX ABI。 当启用 TM 时,C 或 C++ 编译器必须预定义 __EXTABI__。 请参阅编译器文档以了解详细信息。

汇编程序

/usr/ccs/bin/as 中的 AIX 操作系统汇编程序支持在 POWER ISA 中为 TM 定义的其他指令集,并由 POWER8 处理器实现。 可使用 –m pwr8 汇编方式或源文件内的 .machine pwr8 pseudo op 以启用 TM 指令的汇编。 有关更多信息,请参阅汇编语言参考。

调试器

/usr/ccs/bin/dbx 调试器支持 TM 程序的机器级别调试。 此支持包括对新事务内存指令进行反向编译以及查看 TM SPR(TEXASRTEXASRUTFIARTFHAR 寄存器)的能力。

设置事务内的断点将导致事务无条件地失败,无论是否遇到断点都是如此。 因此,建议的方法是调试未能对事务的故障处理程序设置断点的事务,然后在遇到断点时查看 TEXASRTFIAR 寄存器以确定故障原因和位置。

在 dbx 中,可通过使用带有 $texasr$tfiar$tfhar 参数的 print 子命令查看 TEXASRTFIARTFHAR 寄存器。 可通过 list 子命令查看与 TFIARTFHAR 寄存器中找到的地址关联的代码行,例如:
(dbx) list at $tfiar
dbx tm_status 子命令用于查看和解释 TEXASR 寄存器的内容。 此子命令用于确定事务故障的性质。

PTT_READ_TM ptrace 形式的新操作中启用了第三方调试器,用于读取线程的 TM 状态。 请参阅 ptrace 文档以了解详细信息。

跟踪支持

AIX 跟踪工具已扩展为包括一组由 AIX 操作系统执行的 TM 操作的跟踪事件,包括导致事务故障的抢占以及可能导致事务故障的其他各种操作。 跟踪事件标识 675 可用作 tracetrcrpt 命令的输入来查看 TM 相关的跟踪事件。

核心文件

AIX 操作系统还支持将 TM 机器状态包含为使用 TM 的进程或线程的核心文件的一部分。 如果某个进程或线程正在使用或曾使用 TM,那么会将 TM 机器状态包括在该线程的核心映像中。
注: 仅在 AIX 操作系统的当前核心文件格式中支持 TM 状态。 可使用 dbx 命令来读取和查看启用了 TM 的核心文件的 TM 机器状态。

AIX 线程库

对于使用 M:N 线程的应用程序,不支持使用事务内存。 在多个线程共享单个内核线程的环境中,事务性线程中可能发生未定义的行为。 使用 M:N 线程的应用程序使用事务内存可能导致持久事务故障,故障代码为 TEXASR 寄存器中设置的 TM_PTH_PREEMPTED