创建资源管理器

资源管理器 维护每个事务的数据并记录事务的操作。 如果事务处理系统 (TPS) 有多个资源管理器,则每个资源管理器都可以参与每个事务的提交、回滚和恢复操作。

每个资源管理器都必须导出一个接口,事务客户端可以使用该接口来访问数据库或资源管理器维护的其他资源。

通常,内核模式资源管理器必须按列出的顺序执行以下任务:

  1. 创建日志流。

    资源管理器可以使用 通用日志文件系统 (CLFS) 或其他日志记录功能来维护其日志流。 调用 ClfsCreateLogFile 会创建 CLFS 日志流。 资源管理器必须使用日志流来记录提交、回滚或恢复事务所需的任何信息。 此外,KTM 使用日志流来记录恢复事务可能需要的任何内部状态更改。

  2. 创建事务管理器对象。

    调用 ZwCreateTransactionManager 会创建一个事务管理器对象,并将资源管理器连接到资源管理器指定的其他 CLFS 日志流。

  3. 恢复事务管理器状态。

    调用 ZwRecoverTransactionManager 可读取事务管理器对象的日志流 (KTM 维护) ,并确定 TPS 是否在所有事务 (完成之前关闭,例如,因为系统) 崩溃。 KTM 根据日志流中的信息还原其内部状态。

  4. 创建资源管理器对象。

    调用 ZwCreateResourceManager 会创建一个资源管理器对象,并将其与以前创建的事务管理器对象相关联。

  5. 恢复资源管理器状态。

    调用 ZwRecoverResourceManager 会导致 KTM 针对上次关闭资源管理器时正在进行的任何事务发送资源管理器TRANSACTION_NOTIFY_RECOVER通知。 有关资源管理器应如何响应这些通知的信息,请参阅 处理恢复操作

  6. 从客户端接收事务。

    通常,客户端会创建一个事务对象,并使用资源管理器的客户端接口将事务对象的 GUID 传递给资源管理器。 例如,资源管理器可能会提供 一个 CreateDataObject 例程,该例程类似于 了解 TPS 组件 主题所描述的例程。

  7. 在每个事务中登记。

    调用 ZwOpenTransaction 会打开事务对象的句柄,然后对 ZwCreateEnlistment 的 调用将创建事务的登记。 通过登记,资源管理器可以接收一组指定的 事务通知

  8. 启用事务通知的接收。

    资源管理器可以调用 ZwGetNotificationResourceManager 来同步获取通知,也可以调用 TmEnableCallbacks 来注册 一个 ResourceManagerNotification 回调例程,每当有通知可用时 KTM 调用该回调例程。

  9. 来自客户端的服务资源访问请求,但不会永久更改。

    客户端创建事务对象后,通常调用资源管理器的 接口来访问资源管理器的资源。 例如,数据库的资源管理器可能会接收从数据库读取和写入数据库的请求。

    资源管理器必须在 CLFS 日志流 或其他日志记录功能中记录读取和写入操作的结果,直到收到将提交、回滚或恢复事务操作的通知。

  10. 提交或回滚客户端操作。

    最终,资源管理器会收到通知,要求开始提交或回滚客户端执行的操作。 作为响应,资源管理器必须使客户端操作永久或放弃它们。 有关如何处理提交和回滚通知的详细信息,请参阅 处理事务操作

    有时,资源管理器可能必须尝试强制 KTM 快速提供提交或回滚通知,这可能是因为资源管理器已确定设备意外删除。 在这种情况下,资源管理器可以调用 TmRequestOutcomeEnlistment

  11. 关闭登记对象句柄。

    资源管理器处理完事务后,必须调用 ZwClose 以关闭登记对象的句柄

  12. 关闭资源管理器对象句柄和事务管理器对象句柄。

    在卸载资源管理器之前,它必须调用 ZwClose 以关闭资源管理器对象的句柄和事务管理器对象的句柄。

必须在资源管理器的初始化代码中执行步骤 1 到步骤 5。 例如,如果资源管理器是内核模式驱动程序,则初始化代码是驱动程序的 DriverEntry 例程。

步骤 6 到 11 通常在响应来自事务客户端的请求的代码中执行。

必须在资源管理器的最终清理代码中执行步骤 12,例如内核模式驱动程序的 Unload 例程。

创建Read-Only登记

只读登记是未收到来自 KTM 的任何通知的登记。 资源管理器可以通过调用 ZwReadOnlyEnlistment 使任何登记成为只读的。 此调用会导致 KTM 停止向资源管理器传递通知。

在资源管理器调用 ZwCreateEnlistment 后,它可以随时调用 ZwReadOnlyEnlistment ,一直调用它通常调用 ZwPrepareComplete

你可能希望资源管理器调用 ZwReadOnlyEnlistment 有两个原因。

  • 资源管理器已参与事务,在收到TRANSACTION_NOTIFY_COMMIT通知之前的某个时间点,资源管理器确定它不再需要参与事务的提交操作。

    例如,当资源管理器收到TRANSACTION_NOTIFY_PREPARE通知时,它可能会确定事务的操作都没有更改资源管理器的数据库。 资源管理器可以调用 ZwReadOnlyEnlistment 而不是 ZwPrepareComplete 以从事务中删除自身。

  • 资源管理器从不参与任何事务的提交操作。

    例如,资源管理器可能会监视客户端发送的数据,而无需修改任何存储的数据库。 在这种情况下,资源管理器可能会在调用 ZwCreateEnlistment 后立即调用 ZwReadOnlyEnlistment。 此外,还可以选择使此类资源管理器 易变,如本主题的下一部分所述。

资源管理器调用 ZwReadOnlyEnlistment 后,可以调用 ZwClose 以关闭登记句柄。

创建Volatile-Resource管理器

易失性资源管理器是不维护持久数据的资源管理器。 例如,如果资源管理器不修改持久存储的数据库,则可以创建易失性资源管理器来监视客户端发送的数据。 易失性资源管理器通常不记录事务活动,因此无法执行恢复或回滚操作。

易失性资源管理器在调用 ZwCreateResourceManager 时必须设置RESOURCE_MANAGER_VOLATILE标志。 如果设置了此标志,KTM 不会在关联事务管理器对象的日志流中记录有关资源管理器的任何信息。

资源管理器还可以在调用 ZwCreateTransactionManager 时设置TRANSACTION_MANAGER_VOLATILE标志。 如果设置了此标志,KTM 不会为事务管理器对象创建日志流。 此外,连接到事务管理器对象的任何其他资源管理器也必须是可变的,并设置RESOURCE_MANAGER_VOLATILE标志。

将资源管理器添加到现有 TPS

如果必须向现有 TPS 添加其他资源管理器,有两种选择:

  • 新的资源管理器调用 ZwCreateTransactionManager 来创建自己的事务管理器对象。

    如果资源管理器不与 TPS 中的其他资源管理器通信,请使用此选项。

  • 新的资源管理器调用 ZwOpenTransactionManager 以连接到现有的事务管理器对象。

    如果资源管理器必须与 TPS 中的其他资源管理器通信,请使用此选项。 调用 ZwCreateTransactionManager 的资源管理器必须共享事务管理器对象的 GUID、日志流名称或对象名称,以便其他资源管理器可以调用 ZwOpenTransactionManager。 这些其他资源管理器可以调用 ZwQueryInformationTransactionManager 以获取有关事务管理器对象的其他信息。

将资源管理器添加到 TPS 后,可识别资源管理器的客户端可以调用资源管理器的客户端接口。