可以配置 Solaris Cluster OS™ 系统,以使用基于内存的互连(如 Dolphin-SCI)和分层系统软件组件。这些组件实现了一种用户级节点间通讯机制,此机制基于对驻留在远程节点上的内存进行直接访问。此机制称为远程共享内存 (Remote Shared Memory, RSM)。本章定义了 RSM 应用编程接口 (RSM Application Programming Interface, RSMAPI)。
API 框架介绍 RSM API 框架。
API 库函数介绍 RSM API 库函数。
RSMAPI 用法示例介绍了用法示例。
在共享内存模型中,应用程序进程会在其本地地址空间中创建一个 RSM 导出段。一个或多个远程应用程序进程创建 RSM 导入段,在导出段与导入段之间建立互连的虚拟连接。所有进程将使用其特定地址空间的本地地址,来实现对共享段的内存引用。
应用程序进程通过为 RSM 导出段分配可在本地寻址的内存来创建此导出段。使用标准 Solaris 接口(如 System V 共享内存、mmap(2) 或 valloc(3C))即可实现此分配。然后,进程会调用 RSMAPI 来创建段,用于为已分配的内存提供引用句柄。RSM 段通过一个或多个互连控制器进行发布。可以远程访问已发布的段。另外,还将发布允许导入该段的节点的访问权限列表。
将为导出的段分配一个段 ID。通过该段 ID 及其创建进程的群集节点 ID,导入进程可唯一地指定一个导出段。如果成功创建了导出段,则会向进程返回一个段句柄,以便在后续段操作中使用。
应用程序进程通过使用 RSMAPI 来创建导入段,便可以对已发布的段进行访问。创建导入段之后,应用程序进程便建立了互连的虚拟连接。如果成功创建此导入段,则会向应用程序进程返回一个 RSM 导入段句柄,以便在后续段导入操作中使用。建立虚拟连接之后,如果互连支持内存映射,则应用程序可能会请求 RSMAPI 提供内存映射以进行本地访问。如果不支持内存映射,则应用程序可以使用 RSMAPI 提供的内存访问元语。
RSMAPI 提供了一种机制,该机制可以支持远程访问错误检测并能解决写入顺序内存模型问题。此机制称为屏障 (barrier)。
RSMAPI 提供一种通知机制,可以同步本地访问和远程访问。当导入进程启动数据写入操作完成后,导出进程便可以调用函数使自身阻塞。当导入进程完成写入后,此进程会通过调用信号函数来使导出进程解除阻塞。解除阻塞之后,导出进程便可以处理数据。
RSM 应用程序支持组件以软件包形式提供,如下所示:
导出 RSMAPI 函数的共享库 (/usr/lib/librsm.so)。
内核代理 (Kernel Agent, KA) 伪设备驱动程序 (/usr/kernel/drv/rsm),代表用户库通过 RSMAPI 接口与内存互连驱动程序连接。
用于获取互连拓扑的群集接口模块。
互连驱动程序服务模块 (/kernel/misc/rsmops)。
提供 API 函数和数据结构原型的头文件 (/opt/SUNWrsmdk/include)。
系统中配置的可为特定互连提供 RSM 支持的可选 librsm.so 扩展。此扩展以库的形式 (librsminterconnect.so) 提供。
API 库函数支持以下操作:
互连控制器操作
群集拓扑操作
内存段操作,包括段管理和数据访问
屏障 (barrier) 操作
事件操作
控制器操作提供了访问控制器的机制,还可以确定底层互连的特征。下面列出了有关控制器操作的信息:
获取控制器
获取控制器属性
释放控制器
rsm_get_controller 操作可获取给定控制器实例(如 sci0 或 loopback)的控制器句柄。返回的控制器句柄用于后续 RSM 库调用。
返回值:如果成功,则返回 0。否则返回错误值。
控制器句柄无效
控制器不存在
内存不足
库版本无效
地址错误
此函数可用于释放与给定控制器句柄关联的控制器。每个 rsm_release_controller 调用都必须对应一个 rsm_get_controller。当与某个控制器关联的所有控制器句柄都被释放后,与此控制器关联的系统资源将被释放。尝试访问已释放控制器句柄,或者尝试访问已释放控制器句柄上的导入段或导出段都是非法操作。执行此类尝试的结果是不确定的。
返回值:如果成功,则返回 0。否则返回错误值。
控制器句柄无效
此函数可用于获取指定控制器句柄的属性。以下列出了此函数当前已定义的属性:
typedef struct { uint_t attr_direct_access_sizes; uint_t attr_atomic_sizes; size_t attr_page_size; size_t attr_max_export_segment_size; size_t attr_tot_export_segment_size; ulong_t attr_max_export_segments; size_t attr_max_import_map_size; size_t attr_tot_import_map_size; ulong_t attr_max_import_segments; } rsmapi_controller_attr_t;
返回值:如果成功,则返回 0。否则返回错误值。
控制器句柄无效
地址错误
导出操作和导入操作所需的关键互连数据包括:
导出群集节点 ID
导入群集节点 ID
控制器名称
作为基本约束,为段导入指定的控制器必须与用于对应的段导出的控制器具有物理连接。此接口定义互连拓扑有助于应用程序建立有效的导出和导入策略。所提供的数据包括本地节点 ID、本地控制器实例名称以及每个本地控制器的远程连接规范。
导出内存的应用程序组件可以使用此接口提供的数据来查找现有本地控制器集。此接口提供的数据还可用于正确分配控制器,以便创建和发布段。应用程序组件可以通过与硬件互连和应用程序软件分发一致的控制器集来有效分发导出的段。
必须向要导入内存的应用程序组件通知内存导出中所用的段 ID 和控制器。通常,此信息通过预定义的段和控制器对进行传送。导入组件可以使用拓扑数据来确定适用于段导入操作的控制器。
此函数返回一个指针,该指针指向应用程序指针所指定位置的拓扑数据。下面定义了拓扑数据结构。
返回值:如果成功,则返回 0。否则返回错误值。
拓扑指针无效
内存不足
地址错误
rsm_free_interconnect_topology 操作可释放通过 rsm_get_interconnect_topology 分配的内存。
返回值:无。
从 rsm_get_topology_data 返回的指针会引用 rsm_topology_t structure。此结构为每个本地控制器提供本地节点 ID 以及指向 connections_t 结构的指针数组。
typedef struct rsm_topology { rsm_nodeid_t local_nodeid; uint_t local_cntrl_count; connections_t *connections[1]; } rsm_topology_t;
RSM 段 ID 可以由应用程序指定,或者由系统使用 rsm_memseg_export_publish() 函数生成。指定段 ID 的应用程序需要使用保留范围的段 ID。要保留一组段 ID,请使用 rsm_get_segmentid_range 并在段 ID 配置文件 /etc/rsm/rsm.segmentid 中定义保留范围的段 ID。应用程序可以使用 rsm_get_segmentid_range 函数来获取为应用程序保留的段 ID 范围。此函数会读取在 /etc/rsm/rsm.segmentid 文件中针对给定应用程序 ID 定义的段 ID 范围。
应用程序 ID 是指用于标识应用程序的以空字符结尾的字符串。应用程序可以使用任何等于或大于 baseid 并且小于 baseid+length 的值。如果修改了 baseid 或 length,则返回到应用程序的段 ID 可能会超出保留的范围。要避免此问题,请使用相对于保留的一组段 ID 的偏移来获取段 ID。
/etc/rsm/rsm.segmentid 文件中的各项形式如下:
#keyword appid baseid length reserve SUNWfoo 0x600000 100 |
这些项由可以用制表符或空格分隔的字符串组成。第一个字符串是关键字 reserve,后跟应用程序标识符(不包含空格的字符串)。应用程序标识符之后是 baseid,即保留范围的起始段 ID(十六进制)。baseid 之后是 length,即保留的段 ID 数。注释行的第一列中包含 #。此文件不应包含空行。为系统保留的段 ID 在 /usr/include/rsm/rsm_common.h 头文件中定义。应用程序不能使用为系统保留的段 ID。
rsm_get_segmentid_range 函数返回 0 表示成功。如果此函数失败,则会返回以下错误值之一:
传递的地址无效
未在 /etc/rsm/rsm.segmentid 文件中定义应用程序 ID
配置文件 /etc/rsm/rsm.segmentid 不存在或无法读取。文件格式配置错误
通常,RSM 段表示一组映射到连续虚拟地址范围的非连续物理内存页。通过 RSM 段导出和段导入操作,可以在互连系统之间共享物理内存区域。物理页所在节点的进程称为内存的导出者。为远程访问发布的导出段将具有给定节点所特有的段标识符。段 ID 可以由导出者或 RSMAPI 框架指定。
互连节点的进程通过创建 RSM 导入段来对导出的内存进行访问。RSM 导入段与一个导出段连接,而不是与本地物理页连接。如果互连支持内存映射,则导入者可以使用导入段的本地内存映射地址来读写导出的内存。如果互连不支持内存映射,则导入进程会使用内存访问元语。
导出内存段时,应用程序首先通过常规操作系统接口(如 System V 共享内存接口、mmap 或 valloc)来分配其虚拟地址空间中的内存。分配内存之后,应用程序将调用 RSMAPI 库接口来创建和标记段。标记段之后,RSMAPI 库接口将物理页绑定到已分配的虚拟范围。绑定物理页之后,RSMAPI 库接口会发布段以供导入进程访问。
如果虚拟地址空间是使用 mmap 获取的,则映射必须为 MAP_PRIVATE。
导出端内存段操作包括:
创建和销毁内存段
发布和取消发布内存段
重新绑定内存段的后备存储
使用 rsm_memseg_export_create 建立新内存段可以在创建时将物理内存与该段进行关联。此操作将返回新内存段的导出端内存段句柄。段在创建进程的生命周期内一直存在,或者在使用 rsm_memseg_export_destroy 销毁该段之前一直存在。
如果在导入端断开连接之前执行销毁操作,则会强制断开连接。
此函数可用于创建段句柄。创建段句柄之后,段句柄会绑定到指定的虚拟地址范围 [vaddr..vaddr+size]。此范围必须有效并基于控制器的 alignment 属性对齐。flags 参数是位掩码,可用于执行以下操作:
解除绑定段
重新绑定段
将 RSM_ALLOW_REBIND 传递给 flags
支持锁定操作
将 RSM_LOCK_OPS 传递给 flags
RSMAPI 的初始发行版中不包括 RSM_LOCK_OPS 标志。
返回值:如果成功,则返回 0。否则返回错误值。
控制器句柄无效
控制器不存在
段句柄无效
长度为零或长度超出控制器限制
地址无效
权限被拒绝
内存不足
资源不足
地址未在页边界上对齐
操作被信号中断
此函数可用于解除分配段及其可用资源。将强制断开与所有导入进程的连接。
返回值:如果成功,则返回 0。否则返回错误值。
段句柄无效
pollfd 正在使用
通过发布操作,其他互连节点可以导入内存段。一个导出段可能会在多个互连适配器上发布。
段 ID 可以在授权范围内指定或指定为零,此时 RSMAPI 框架会生成有效的段 ID 并传递回该段 ID。
段访问控制列表由多对节点 ID 和访问权限组成。对于列表中指定的每个节点 ID,关联的读/写权限会通过三个八进制数字提供给属主、组和其他用户,这与 Solaris 文件权限一样。在访问控制列表中,每个八进制数字都可以具有以下值:
写入访问。
只读访问。
读写访问。
访问权限值 0624 可指定以下访问类型:
与导出者具有相同 uid 的导入者具有读写访问权限。
与导出者具有相同 gid 的导入者仅有写入访问权限。
所有其他导入者仅有读取访问权限。
提供访问控制列表之后,未包含在此列表中的节点不能导入段。但是,如果访问列表为空,则任何节点都可导入段。所有节点的访问权限等同于导出进程的属主/组/其他用户文件创建权限。
节点应用程序负责管理段标识符的指定,从而确保导出节点的唯一性。
typedef struct { rsm_node_id_t ae_node; /* remote node id allowed to access resource */ rsm_permission_t ae_permissions; /* mode of access allowed */ }rsmapi_access_entry_t;.
返回值:如果成功,则返回 0。否则返回错误值。
段句柄无效
段已发布
访问控制列表无效
段标识符无效
段标识符正在使用
段标识符已保留
不是段的创建者
地址错误
内存不足
资源不足
授权的段 ID 范围:
0
0x0FFFFF
0x100000
0x1FFFFF
0x200000
0x2FFFFF
0x300000
0x3FFFFF
0x400000
0x4FFFFF
以下范围会保留,以便在发布值为零时由系统进行分配。
0x80000000
0xFFFFFFF
此函数可用于建立新的节点访问列表和段访问模式。这些更改仅会影响将来的导入调用,并且不会撤消已准许的导入请求。
返回值:如果成功,则返回 0。否则返回错误值。
段句柄无效
段未发布
访问控制列表无效
不是段的创建者
内存不足
资源不足
操作被信号中断
返回值:如果成功,则返回 0。否则返回错误值。
段句柄无效
段未发布
不是段的创建者
操作被信号中断
重新绑定操作可释放导出段的当前后备存储。释放导出段的当前后备存储之后,重新绑定操作将分配新的后备存储。应用程序必须首先获取分配给段的新虚拟内存。此操作对于段的导入者是透明的。
应用程序负责防止在重新绑定操作完成之前对段数据进行访问。重新绑定过程中从段中检索数据不会导致系统故障,但执行此类操作的结果是不确定的。
返回值:如果成功,则返回 0。否则返回错误值。
段句柄无效
长度无效
地址无效
不允许重新绑定
不是段的创建者
权限被拒绝
内存不足
资源不足
操作被信号中断
以下列出了导入端操作:
连接和断开连接内存段
访问导入的段内存
屏障 (barrier) 操作,用于强制设置数据访问操作顺序以及用于访问错误检测
连接操作用于创建 RSM 导入段并与导出的段形成逻辑连接。
对导入的段内存的访问由以下三个接口类别提供:
段访问。
数据传送。
段内存映射。
此函数可用于通过指定的权限 perm 连接到远程节点 node_id 上的段 segment_id。连接到段之后,此函数会返回一个段句柄。
参数 perm 用于指定导入者针对此连接请求的访问模式。要建立连接,可将导出者指定的访问权限与导入者使用的访问模式、用户 ID 和组 ID 进行比较。如果请求模式无效,则会拒绝连接请求。perm 参数限制为以下八进制值:
读取模式
写入模式
读/写模式
指定的控制器必须与用于段导出的控制器具有物理连接。
返回值:如果成功,则返回 0。否则返回错误值。
控制器句柄无效
控制器不存在
段句柄无效
权限被拒绝
未将段发布到节点
未发布此类段
无法访问远程节点
连接已中断
内存不足
资源不足
地址错误
此函数可用于断开段连接。断开段连接之后,此函数将释放段的资源。所有与断开连接的段的现有映射都将删除。句柄 im_memseg 将会释放。
返回值:如果成功,则返回 0。否则返回错误值。
段句柄无效
仍映射段
pollfd 正在使用
以下接口提供了一种机制,用于在 8 位和 64 位数据之间进行传送。get 接口使用重复计数 (rep_cnt) 来表示进程将从连续位置读取的给定大小的数据项数。这些位置从导入的段中的字节偏移 offset 开始。数据会写入从 datap 开始的连续位置。put 接口可使用重复计数 (rep_cnt)。此计数表示进程将从连续位置读取的数据项数。这些位置从 datap 开始。然后,数据会写入已导入段中的连续位置。这些位置从 offset 参数所指定的字节偏移开始。
如果源与目标具有不兼容的字节存储顺序特征,则这些接口还可提供字节交换功能。
int rsm_memseg_import_get8(rsm_memseg_import_handle_t im_memseg, off_t offset, uint8_t *datap, ulong_t rep_cnt);以下接口用于进行规模大于段访问操作所支持范围的数据传送。
int rsm_memseg_import_put(rsm_memseg_import_handle_t im_memseg, off_t offset, void *src_addr, size_t length);此函数可用于将数据从 src_addr 和 length 所指定的本地内存复制到句柄和偏移所指定的对应导入的段所在位置。
int rsm_memseg_import_get(rsm_memseg_import_handle_t im_memseg, off_t offset, void *dst_addr, size_t length);此函数类似于 rsm_memseg_import_put(),但是数据从导入的段流入 dest_vec 参数所定义的本地区域。
put 和 get 例程从参数 offset 所指定的字节偏移位置写入或读取指定的数据量。这些例程从段的基地址开始。偏移必须在相应的边界对齐。例如,rsm_memseg_import_get64() 要求 offset 和 datap 在双字界对齐,而 rsm_memseg_import_put32() 则要求偏移在单字边界对齐。
缺省情况下,段的屏障 (barrier) 模式属性为 implicit。 隐式屏障 (barrier) 模式表示调用方假设数据传送在从操作返回时已完成或失败。 由于缺省屏障 (barrier) 模式为隐式,因此应用程序必须初始化屏障 (barrier)。使用缺省模式时,应用程序会在调用 put 或 get 例程之前使用 rsm_memseg_import_init_barrier() 函数初始化屏障 (barrier)。要使用显式操作模式,调用方必须使用屏障 (barrier) 操作来强制完成传送。强制完成传送之后,调用方必须确定强制完成是否产生了任何错误。
通过在 rsm_memseg_import_map() 例程中传递偏移可以部分映射导入段。如果部分映射了导入段,则 put 或 get 例程中的 offset 参数是相对于段的基地址。用户必须确保将正确的字节偏移传递给 put 和 get 例程。
返回值:如果成功,则返回 0。否则返回错误值。
段句柄无效
地址错误
内存对齐无效
偏移无效
长度无效
权限被拒绝
未初始化屏障 (barrier)
I/O 完成错误
连接异常中止
资源不足
rsm_memseg_import_putv() 和 rsm_memseg_import_getv() 函数允许使用 I/O 请求列表来替代单个源地址和单个目标地址。
函数原型:
int rsm_memseg_import_putv(rsm_scat_gath_t *sg_io);使用分散/集中列表的 I/O 向量部分 (sg_io) 可以指定本地虚拟地址或 local_memory_handles。句柄是一种重复使用本地地址范围的有效方法。在释放句柄之前,已分配的系统资源(如已锁定的本地内存)会一直保留。句柄的支持函数包括 rsm_create_localmemory_handle() 和 rsm_free_localmemory_handle()。
可以将虚拟地址或句柄收集到向量中,以便写入单个远程段。另外,还可以将从单个远程段读取的结果分散到虚拟地址或句柄的向量中。
整个向量的 I/O 会在返回之前启动。导入段的屏障 (barrier) 模式属性可确定 I/O 是否在函数返回之前已完成。将屏障 (barrier) 模式属性设置为 implicit 可保证数据传送按照在向量中的输入顺序完成。在每个列表项开始时会执行隐式屏障 (barrier) 打开,在每个列表项结束时会执行隐式屏障 (barrier) 关闭。如果检测到错误,向量的 I/O 会终止并且函数会立即返回。剩余计数表示其 I/O 尚未完成或尚未启动的项数。
可以指定在 putv 或 getv 操作成功时,向目标段发送通知事件。要指定传送通知事件,请在 rsm_scat_gath_t 结构的 flags 项中指定 RSM_IMPLICIT_SIGPOST 值。flags 项还可以包含值 RSM_SIGPOST_NO_ACCUMULATE,该值在设置了 RSM_IMPLICIT_SIGPOST 的情况下会传递给信号传递操作。
返回值:如果成功,则返回 0。否则返回错误值。
分散/集中结构指针无效
段句柄无效
控制器句柄无效
地址错误
偏移无效
长度无效
权限被拒绝
I/O 完成错误
连接异常中止
资源不足
操作被信号中断
此函数可用于获取本地句柄,以便在后续调用 putv 或 getv 时用于 I/O 向量。尽快释放句柄可节省系统资源(特别是本地句柄占用的内存),这些资源可能会锁定。
返回值:如果成功,则返回 0。否则返回错误值。
控制器句柄无效
本地内存句柄无效
长度无效
地址无效
内存不足
此函数可用于释放与本地句柄关联的系统资源。由于进程退出时会释放属于该进程的所有句柄,因此调用此函数可节省系统资源。
返回值:如果成功,则返回 0。否则返回错误值。
控制器句柄无效
本地内存句柄无效
以下示例说明了主数据结构的定义。
typedef void *rsm_localmemory_handle_t typedef struct { ulong_t io_request_count; number of rsm_iovec_t entries ulong_t io_residual_count; rsm_iovec_t entries not completed in flags; rsm_memseg_import_handle_t remote_handle; opaque handle for import segment rsm_iovec_t *iovec; pointer to array of io_vec_t } rsm_scat_gath_t; typedef struct { int io_type; HANDLE or VA_IMMEDIATE union { rsm_localmemory_handle_t handle; used with HANDLE caddr_t virtual_addr; used with VA_IMMEDIATE } local; size_t local_offset; offset from handle base vaddr size_t import_segment_offset; offset from segment base vaddr size_t transfer_length; } rsm_iovec_t;
映射操作只能用于本机体系结构互连,如 Dolphin-SCI 或 NewLink。映射段可授予 CPU 内存操作访问该段的权限,从而节省了调用内存访问元语的开销。
int rsm_memseg_import_map(rsm_memseg_import_handle_t im_memseg, void **address, rsm_attribute_t attr, rsm_permission_t perm, off_t offset, size_t length);此函数可用于将导入的段映射成调用方地址空间。如果指定了属性 RSM_MAP_FIXED,则此函数会在 **address 中指定的值所在位置映射段。
typedef enum { RSM_MAP_NONE = 0x0, /* system will choose available virtual address */ RSM_MAP_FIXED = 0x1, /* map segment at specified virtual address */ } rsm_map_attr_t;
返回值:如果成功,则返回 0。否则返回错误值。
段句柄无效
地址无效
长度无效
偏移无效
权限无效
已映射段
未连接段
连接异常中止
映射时出现错误
地址未在页边界上对齐
此函数可用于从用户虚拟地址空间中取消映射导入的段。
返回值:如果成功,则返回 0。否则返回错误值。
段句柄无效
使用屏障 (barrier) 操作可以解决写入访问顺序内存模型问题。屏障 (barrier) 操作还可提供远程内存访问错误检测功能。
屏障 (barrier) 机制由以下操作组成:
初始化
打开
关闭
排序
打开和关闭操作定义了错误检测和排序的时间间隔。通过初始化操作,可以为每个导入的段创建屏障 (barrier) 并指定屏障 (barrier) 类型。当前支持的唯一屏障 (barrier) 类型针对每个段具有一个时间间隔范围。请使用类型参数值 RSM_BAR_DEFAULT。
成功执行关闭操作可保证成功完成所涉及的访问操作,这些操作在屏障 (barrier) 打开操作和屏障 (barrier) 关闭操作之间进行。在屏障 (barrier) 打开操作之后直到屏障 (barrier) 关闭操作之前,不会报告单个数据访问操作(读取和写入)故障。
要在屏障 (barrier) 范围内强制设置特定的写入完成顺序,请使用显式屏障 (barrier) 排序操作。在屏障 (barrier) 排序操作之前发出的写入操作会先于在屏障 (barrier) 排序操作之后发出的操作完成。给定屏障 (barrier) 范围内的写入操作会根据其他屏障 (barrier) 范围进行排序。
int rsm_memseg_import_init_barrier(rsm_memseg_import_handle_t im_memseg, rsm_barrier_type_t type, rsmapi_barrier_t *barrier);目前,RSM_BAR_DEFAULT 是唯一支持的类型。
返回值:如果成功,则返回 0。否则返回错误值。
段句柄无效
屏障 (barrier) 指针无效
内存不足
返回值:如果成功,则返回 0。否则返回错误值。
段句柄无效
屏障 (barrier) 指针无效
此函数可用于关闭屏障 (barrier) 并刷新所有存储缓冲区。此调用假设如果调用 rsm_memseg_import_close_barrier() 失败,则调用进程将重试自上次 rsm_memseg_import_open_barrier 调用以来的所有远程内存操作。
返回值:如果成功,则返回 0。否则返回错误值。
段句柄无效
屏障 (barrier) 指针无效
未初始化屏障 (barrier)
未打开屏障 (barrier)
内存访问错误
连接异常中止
此函数可用于刷新所有存储缓冲区。
返回值:如果成功,则返回 0。否则返回错误值。
段句柄无效
屏障 (barrier) 指针无效
未初始化屏障 (barrier)
未打开屏障 (barrier)
内存访问错误
连接异常中止
此函数可用于取消分配所有屏障 (barrier) 资源。
返回值:如果成功,则返回 0。否则返回错误值。
段句柄无效
屏障 (barrier) 指针无效
此函数支持可用于 put 例程的可选显式屏障 (barrier) 范围。两种有效的屏障 (barrier) 模式为 RSM_BARRIER_MODE_EXPLICIT 和 RSM_BARRIER_MODE_IMPLICIT。屏障 (barrier) 模式的缺省值为 RSM_BARRIER_MODE_IMPLICIT。在隐式模式下,隐式屏障 (barrier) 打开和屏障 (barrier) 关闭会应用于每个 put 操作。将屏障 (barrier) 模式值设置为 RSM_BARRIER_MODE_EXPLICIT 之前,请使用 rsm_memseg_import_init_barrier 例程针对导入的段 im_memseg 初始化屏障 (barrier)。
返回值:如果成功,则返回 0。否则返回错误值。
段句柄无效
此函数可用于获取 put 例程中屏障 (barrier) 范围设置的当前模式值。
返回值:如果成功,则返回 0。否则返回错误值。
段句柄无效。
通过事件操作,可以针对内存访问事件实现进程同步。如果进程无法使用 rsm_intr_signal_wait() 函数,则可以多路复用事件等待,方法是通过 rsm_memseg_get_pollfd() 获取轮询描述符并使用 poll 系统调用。
使用 rsm_intr_signal_post() 和 rsm_intr_signal_wait() 操作时需要处理对内核的 ioctl 调用。
void 指针 *memseg 可以将类型转换为导入段句柄或导出段句柄。如果 *memseg 引用导入句柄,则此函数会向导出进程发送信号。如果 *memseg 引用导出句柄,则此函数会向该段的所有导入者发送信号。如果已针对目标段暂挂此事件,则将 flags 参数设置为 RSM_SIGPOST_NO_ACCUMULATE 可废弃此事件。
返回值:如果成功,则返回 0。否则返回错误值。
段句柄无效
无法访问远程节点
void 指针 *memseg 可以将类型转换为导入段句柄或导出段句柄。进程的阻塞时间最多可达到 timeout 毫秒,或在事件发生之前一直阻塞。如果值为 -1,则进程在事件发生之前或中断之前会一直阻塞。
返回值:如果成功,则返回 0。否则返回错误值。
段句柄无效
计时器已到期
等待已中断
此函数可用于通过指定段的描述符以及 rsm_intr_signal_post() 所生成的单个固定事件初始化指定的 pollfd 结构。将 pollfd 结构用于 poll 系统调用可等待 rsm_intr_signal_post 所通知的事件。如果当前未发布内存段,则 poll 系统调用无法返回有效的 pollfd。每次成功调用都会递增指定段的 pollfd 引用计数。
返回值:如果成功,则返回 0。否则返回错误值。
段句柄无效
此调用可递减指定段的 pollfd 引用计数。如果引用计数为非零值,则取消发布、销毁或取消映射段的操作会失败。
返回值:如果成功,则返回 0。否则返回错误值。
段句柄无效
这些用法说明介绍了共享内存操作的导出端和导入端的常规注意事项。另外,这些用法说明还包含有关段、文件描述符和 RSM 可配置参数的常规信息。
系统会为每个导出操作或导入操作分配一个文件描述符,导入或导出内存的应用程序无法访问此描述符。每个进程的文件描述符分配的缺省限制为 256。导入或导出应用程序必须相应调整此分配限制。如果应用程序将文件描述符限制增加到超过 256,则为导出段和导入段分配的文件描述符值从 256 开始。选择这些文件描述符值是为了避免干扰应用程序的正常文件描述符分配。此行为允许在仅使用小于 256 的文件描述符值的 32 位应用程序中使用特定的 libc 函数。
应用程序必须防止在重新绑定操作完成之前访问段。在重新绑定过程中,访问段数据不会导致系统故障,但是数据内容结果是不确定的。当前,虚拟地址空间必须已映射并有效。
为段导入指定的控制器必须与用于段导出的控制器具有物理连接。
SUNWrsm 软件包包括 rsm.conf 文件。此文件位于 /usr/kernel/drv 中。此文件是 RSM 的配置文件。rsm.conf 文件可用于指定特定可配置 RSM 属性的值。当前在 rsm.conf 中定义的可配置属性包括 max-exported-memory 和 enable-dynamic-reconfiguration。
此属性用于指定可导出内存量的上限。此上限以可用内存总量的百分比表示。如果指定此属性值为零,则表示可导出内存量没有限制。
此属性的值表示是否启用了动态重新配置。值为零表示禁用动态重新配置。值为 1 表示可启用动态重新配置支持。此属性的缺省值为 1。
本节提供了一个简单程序来说明 RSMAPI 的用法。此程序在两个节点上运行:导出者节点和导入者节点。导出者节点用于创建和发布内存段,然后等待将消息写入段。导入者节点用于连接到导出的段,写入消息,然后通知导出者。
/* * Copyright (c) 1998 by Sun Microsystems, Inc. * All rights reserved. */ #include <stdio.h> #include <rsm/rsmpai.h> #include <errno.h> /* To run this program do the following: First node(assuming node id = 1): rsmtest -e -n 2 Second node(assuming node id = 2): rsmtest -i -n 1 The program will prompt the importer for a message at the console. Enter any message and hit return. The message will be displayed on the export console. */ typedef struct { char out; char in; char data[1]; }msg_t; #define SEG_ID 0x400000 #define EXPORT 0 #define IMPORT 1 #define BUFSIZE (1024 * 8) #define DEFAULT_SEGSZ BUFSIZE #define RSM_PERM_READ 0400 #define RSM_PERM_WRITE 0200 #define RSM_PERM_RDWR (RSM_PERM_READ|RSM_PERM_WRITE) #define RSM_ACCESS_TRUSTED 0666 rsm_topology_t *tp; int iterations = 10; int mode = EXPORT; int test = 0; char *buf; int buflen = BUFSIZE; int offset = 0; volatile char *iva; int status; rsm_memseg_id_t segid; rsmapi_controller_handle_t ctrl; rsmapi_controller_attr_t attr; rsm_memseg_export_handle_t seg; rsm_memseg_import_handle_t imseg; rsm_access_entry_t list[2]; rsm_node_id_t dest; extern void *valloc(size_t); extern void exit(); extern void sleep(); extern int atoi(const char *); /* The following function exports a segment and publishes it. */ static int export() { int i; /* allocate and clear memory */ buf = (char *)valloc(buflen); if (!buf) { (void) fprintf(stderr, "Unable to allocate memory\n"); exit (1); } for (i = 0; i < buflen; i++) buf[i] = 0; /* Create an export memory segment */ status = rsm_memseg_export_create(ctrl, &seg, (void *)buf, buflen); if (status != 0) { (void) fprintf(stderr, "unable to create an exported segment %d\n", status); exit(1); } /* Set up access list for publishing to nodes 1 and 2 */ list[0].ae_node = tp->topology_hdr.local_nodeid ; /* Allow read and write permissions */ list[0].ae_permission = RSM_ACCESS_TRUSTED; list[1].ae_node = tp->topology_hdr.local_nodeid + 1; /* Allow read and write permissions */ list[1].ae_permission = RSM_ACCESS_TRUSTED; /* Publish the created export segment */ status = rsm_memseg_export_publish(seg, &segid, list, 0); if (status != 0) { (void) fprintf(stderr, "unable to pub segment %d\n", status); exit(1); } return (0); } /* The following function is used to connect to an exported memory segment. */ static void import() { /* Connect to exported segment and set up mapping for * access through local virtual addresses. */ again: status = rsm_memseg_import_connect(ctrl, dest, segid, RSM_PERM_RDWR, &imseg); if (status != 0) { (void) fprintf(stderr, "unable to conect to segment %x err %x\n", segid, status); sleep(1); goto again; } iva = NULL; status = rsm_memseg_import_map(imseg, (void **)&iva, RSM_MAP_NONE, RSM_PERM_RDWR, 0, buflen); if (status != 0) { (void) fprintf(stderr, "unable to mmap segment %d\n", status); exit(1); } } /* Unpublish and destroy the export segment */ static void export_close() { again: status = rsm_memseg_export_unpublish(seg); if (status != 0) { (void) fprintf(stderr, "unable to create an unpub segment %d\n", status); sleep(10); goto again; } status = rsm_memseg_export_destroy(seg); if (status != 0) { (void) fprintf(stderr, "unable to destroy segment %d\n", status); exit(1); } } /* Unmap the virtual address mapping and disconnect the segment */ static void import_close() { status = rsm_memseg_import_unmap(imseg); if (status != 0) { (void) fprintf(stderr, "unable to unmap segment %d\n", status); exit(1); } status = rsm_memseg_import_disconnect(imseg); if (status != 0) { (void) fprintf(stderr, "unable to disconnect segment %d\n", status); exit(1); } } static void test0() { volatile msg_t *mbuf; /* Barrier to report error */ rsmapi_barrier_t bar; int i; if (mode == EXPORT) { (void) export(); mbuf = (msg_t *)(buf + offset); mbuf->in = mbuf->out = 0; } else { import(); mbuf = (msg_t *)(iva + offset); rsm_memseg_import_init_barrier(imseg, RSM_BARRIER_NODE, &bar); } (void) printf("Mbuf is %x\n", (uint_t)mbuf); while (iterations-- > 0) { int e; switch (mode) { case EXPORT: while (mbuf->out == mbuf->in) { (void) rsm_intr_signal_wait(seg, 1000); } (void) printf("msg [0x%x %d %d] ", (uint_t)mbuf, (int)mbuf->out, mbuf->in); for (i = 0; mbuf->data[i] != '\0' && i < buflen; i++) { (void) putchar(mbuf->data[i]); mbuf->data[i] = '?'; } (void) putchar('\n'); mbuf->out++; break; case IMPORT: (void) printf("Enter msg [0x%x %d]: ", (uint_t)mbuf, mbuf->out, mbuf->in); retry: e = rsm_memseg_import_open_barrier(&bar); if (e != 0) { (void) printf("Barrier open failed %x\n", e); exit(1); } for (i = 0; (mbuf->data[i] = getchar()) != '\n'; i++) ; mbuf->data[i] = '\0'; rsm_memseg_import_order_barrier(&bar); mbuf->in++; e = rsm_memseg_import_close_barrier(&bar); if (e != 0) { (void) printf("Barrier close failed, %d\n", e); goto retry; } (void)rsm_intr_signal_post(imseg); break; } } if (mode == IMPORT) { import_close(); } else { export_close(); } } void main(int argc, char *argv[]) { int unit = 0; char *device = "sci0"; int i; segid = SEG_ID; buflen = DEFAULT_SEGSZ; while ((i = getopt(argc, argv, "OCGeid:b:sl:n:k:t:c:u:v")) != -1) { switch (i) { case 'e': mode = EXPORT; break; case 'i': mode = IMPORT; break; case 'n': dest = atoi(optarg); if ((int)dest < 0) dest = 0; break; default: (void) fprintf(stderr, "Usage: %s -ei -n dest\n", argv[0]); exit(1); } } status = rsm_get_controller(device, &ctrl); if (status != 0) { (void) fprintf(stderr, "Unable to get controller\n"); exit(1); } status = rsm_get_controller_attr(ctrl, &attr); status = rsm_get_interconnect_topology(&tp); if (status != 0) { (void) fprintf(stderr, "Unable to get topology\n"); exit(1); } else { (void) printf("Local node id = %d\n", tp->topology_hdr.local_nodeid); } if (dest == 0) { dest = tp->topology_hdr.local_nodeid; (void) printf("Dest is adjusted to %d\n", dest); } switch (test) { case 0: test0(); break; default: (void) printf("No test executed\n"); break; } }