本章介绍应用程序用来与地址组进行交互的 API。
本章讨论以下主题:
地址组概述介绍地址组概念。
验证接口版本介绍用于提供接口信息的函数。
初始化地址组接口介绍用于初始化和关闭接口部分(用于遍历地址组分层结构以及搜索地址组内容)的函数调用。
地址组分层结构介绍用于导航地址组分层结构的函数调用以及获取地址组分层结构特征的函数。
地址组内容介绍用于检索地址组内容信息的函数调用。
地址组特征介绍用于检索地址组特征信息的函数调用。
地址组及线程和内存位置介绍如何影响线程及其内存的地址组位置。
API 用法示例包含使用本章介绍的 API 执行示例任务的代码。
共享内存多处理器计算机包含多个 CPU。每个 CPU 均可访问计算机中的所有内存。在某些共享内存多处理器中,内存体系结构使每个 CPU 访问某些内存区域的速度快于访问其他区域的速度。
当具有此类内存体系结构的计算机运行 Solaris 软件时,将有关给定 CPU 与给定内存区域之间最短访问时间的信息提供给内核可以提高系统性能。地址组 (lgroup) 概念就是为处理这些信息而引入的。lgroup 概念是内存位置优化 (Memory Placement Optimization, MPO) 功能的一部分。
lgroup 是指包含 CPU 类和内存类设备的集合,此集合中的每个 CPU 均可在限定的延迟间隔内访问此集合中的任何内存。延迟间隔的值表示此 lgroup 中所有 CPU 与所有内存之间的最低公用延迟。定义 lgroup 的延迟界限并未限制此 lgroup 的成员之间的最大延迟。延迟界限的值是适用于此组中所有可能的 CPU-内存对的最小延迟。
lgroup 具有分层结构。lgroup 分层结构是一个有向无环图 (Directed Acyclic Graph, DAG) 并与树类似,只不过一个 lgroup 可能具有多个父级。根 lgroup 包含系统中的所有资源,还可以包含子 lgroup。此外,根 lgroup 还有一个特点,即它的延迟值是系统内所有 lgroup 中的最大值。所有子 lgroup 的延迟值均小于根 lgroup 的延迟值。越接近根的 lgroup 的延迟值越大,越接近叶的 lgroup 的延迟值越小。
如果一台计算机中的所有 CPU 访问所有内存所需的时间相同,则可以使用一个 lgroup 表示此计算机(请参见图 4–1)。如果一台计算机中的某些 CPU 访问某些内存区域所需的时间短于访问其他内存区域所需的时间,则可以使用多个 lgroup 表示此计算机(请参见图 4–2)。
lgroup 分层结构的组织简化了在系统中查找最近资源的任务。创建每个线程时即为其指定了一个主 lgroup。缺省情况下,操作系统将尝试从线程的主 lgroup 为线程分配资源。例如,缺省情况下,Solaris 内核将尝试安排线程在其主 lgroup 中的 CPU 上运行,并从其主 lgroup 中分配此线程的内存。如果线程的主 lgroup 中没有所需的资源,则内核可以遍历 lgroup 分层结构,从主 lgroup 的父级中查找下一个最近的资源。如果主 lgroup 的父级中没有所需的资源,则内核将继续遍历 lgroup 分层结构,在主 lgroup 的上一级祖先 lgroup 中进行查找。根 lgroup 是计算机中所有其他 lgroup 的最后一级祖先,它包含计算机中的所有资源。
lgroup API 针对应用程序引入了 lgroup 概念,以便进行观察和性能调优。新 API 包含在名为 liblgrp 的新库中。应用程序可以使用 API 执行以下任务:
遍历组分层结构
搜索给定 lgroup 的内容和特征
影响 lgroup 的线程和内存位置
在使用 lgroup API 之前,必须使用 lgrp_version(3LGRP) 函数验证是否存在支持的 lgroup 接口。lgrp_version() 函数的语法如下所示:
#include <sys/lgrp_user.h> int lgrp_version(const int version);
lgrp_version() 函数使用 lgroup 接口的版本号作为参数,并返回系统支持的 lgroup 接口版本。如果 lgroup API 的当前实现支持 version 参数中的版本号,则 lgrp_version() 函数将返回此版本号。否则,lgrp_version() 函数将返回 LGRP_VER_NONE。
#include <sys/lgrp_user.h> if (lgrp_version(LGRP_VER_CURRENT) != LGRP_VER_CURRENT) { fprintf(stderr, "Built with unsupported lgroup interface %d\n", LGRP_VER_CURRENT); exit (1); }
应用程序必须调用 lgrp_init(3LGRP),才能使用 API 遍历 lgroup 分层结构并搜索 lgroup 分层结构的内容。调用 lgrp_init() 可为应用程序提供 lgroup 分层结构的一致快照。应用程序开发者可以指定快照仅包含专供调用线程使用的资源,或包含一般情况下操作系统均可使用的资源。lgrp_init() 函数返回一个 cookie,此 cookie 用于执行以下任务:
导航 lgroup 分层结构
确定 lgroup 的内容
确定快照是否最新
lgrp_init() 函数初始化 lgroup 接口并获取 lgroup 分层结构的快照。
#include <sys/lgrp_user.h> lgrp_cookie_t lgrp_init(lgrp_view_t view);
如果 lgrp_init() 函数是使用 LGRP_VIEW_CALLER 作为视图进行调用的,则此函数返回的快照仅包含可供调用线程使用的资源。如果 lgrp_init() 函数是使用 LGRP_VIEW_OS 作为视图进行调用的,则此函数返回的快照将包含可供操作系统使用的资源。如果线程成功调用 lgrp_init() 函数,则此函数将返回一个 cookie,与 lgroup 分层结构进行交互的任何函数均使用此 cookie。当线程不再需要此 cookie 时,将使用此 cookie 作为参数来调用 lgrp_fini() 函数。
lgroup 分层结构包括一个根 lgroup,其中包含计算机的所有 CPU 和内存资源。根 lgroup 可以包含其他由更小延迟限定的地址组。
lgrp_init() 函数可以返回两个错误。如果视图无效,则此函数将返回 EINVAL。如果没有足够的内存用于分配 lgroup 分层结构的快照,则此函数将返回 ENOMEM。
lgrp_fini(3LGRP) 函数终止给定 cookie 的使用,并释放相应的 lgroup 分层结构快照。
#include <sys/lgrp_user.h> int lgrp_fini(lgrp_cookie_t cookie);
lgrp_fini() 函数使用表示由先前的 lgrp_init() 调用创建的 lgroup 分层结构快照的 cookie。lgrp_fini() 函数释放分配给此快照的内存。调用 lgrp_fini() 之后,此 cookie 即失效。请不要再使用此 cookie。
如果传递给 lgrp_fini() 函数的 cookie 无效,则 lgrp_fini() 将返回 EINVAL。
使用本节介绍的 API,调用线程可以导航 lgroup 分层结构。 lgroup 分层结构是一种类似于树结构的有向无环图,只不过一个节点可能具有多个父级。根 lgroup 表示整个计算机,其中包含此计算机的所有资源。根 lgroup 是系统中具有最大延迟值的 lgroup。每个子 lgroup 均包含根 lgroup 中的硬件子集,并且由一个较小的延迟值限定。越接近根的地址组具有的资源越多,延迟也越大。越接近叶的地址组具有的资源越少,延迟也越小。lgroup 可以包含直接位于其延迟界限内的资源,还可以包含具有其自己的资源集合的叶 lgroup。叶 lgroup 的资源可供封装这些叶 lgroup 的 lgroup 使用。
lgrp_cookie_stale(3LGRP) 函数确定给定 cookie 所表示的 lgroup 分层结构快照是否最新。
#include <sys/lgrp_user.h> int lgrp_cookie_stale(lgrp_cookie_t cookie);
lgrp_init() 函数返回的 cookie 可能会因为多种原因而过时,具体取决于快照所表示的视图。当视图设置为 LGRP_VIEW_OS 时,调用 lgrp_init() 函数所返回的 cookie 可能会过时,原因包括 lgroup 分层结构的更改(如动态重新配置)或CPU 联机状态的更改。当视图设置为 LGRP_VIEW_CALLER 时,调用 lgrp_init() 函数所返回的 cookie 可能会过时,原因是调用线程处理器集的更改或 lgroup 分层结构的更改。要刷新过时的 cookie,可以使用旧 cookie 调用 lgrp_fini() 函数,然后调用 lgrp_init() 函数生成一个新 cookie。
如果给定的 cookie 无效,则 lgrp_cookie_stale() 函数将返回 EINVAL。
lgrp_view(3LGRP) 函数确定用于获取给定 lgroup 分层结构快照的视图。
#include <sys/lgrp_user.h> lgrp_view_t lgrp_view(lgrp_cookie_t cookie);
lgrp_view() 函数使用表示 lgroup 分层结构快照的 cookie 作为参数,并返回该 lgroup 分层结构的快照视图。使用视图 LGRP_VIEW_CALLER 获取的快照仅包含可供调用线程使用的资源。使用视图 LGRP_VIEW_OS 获取的快照包含可供操作系统使用的所有资源。
如果给定的 cookie 无效,则 lgrp_view() 函数将返回 EINVAL。
lgrp_nlgrps(3LGRP) 函数返回系统中的地址组数。如果系统中只有一个地址组,则内存位置优化没有任何效果。
#include <sys/lgrp_user.h> int lgrp_nlgrps(lgrp_cookie_t cookie);
lgrp_nlgrps() 函数使用表示 lgroup 分层结构快照的 cookie,并返回此分层结构中的可用 lgroup 数。
如果此 cookie 无效,则 lgrp_nlgrps() 函数将返回 EINVAL。
lgrp_root(3LGRP) 函数返回根 lgroup ID。
#include <sys/lgrp_user.h> lgrp_id_t lgrp_root(lgrp_cookie_t cookie);
lgrp_root() 函数使用表示 lgroup 分层结构快照的 cookie,并返回根 lgroup ID。
lgrp_parents(3LGRP) 函数使用表示 lgroup 分层结构快照的 cookie,并返回指定 lgroup 的父 lgroup 数。
#include <sys/lgrp_user.h> int lgrp_parents(lgrp_cookie_t cookie, lgrp_id_t child, lgrp_id_t *lgrp_array, uint_t lgrp_array_size);
如果 lgrp_array 不为 NULL 并且 lgrp_array_size 的值不为零,则 lgrp_parents() 函数将使用父 lgroup ID 填充数组,直到填满数组或者所有父 lgroup ID 均在数组中为止。根 lgroup 没有父级。如果针对根 lgroup 调用 lgrp_parents() 函数,则不会填充 lgrp_array。
如果此 cookie 无效,则 lgrp_parents() 函数将返回 EINVAL。如果未找到指定的 lgroup ID,则 lgrp_parents() 函数将返回 ESRCH。
lgrp_children(3LGRP) 函数使用表示调用线程的 lgroup 分层结构快照的 cookie,并返回指定 lgroup 的子 lgroup 数。
#include <sys/lgrp_user.h> int lgrp_children(lgrp_cookie_t cookie, lgrp_id_t parent, lgrp_id_t *lgrp_array, uint_t lgrp_array_size);
如果 lgrp_array 不为 NULL 并且 lgrp_array_size 的值不为零,则 lgrp_children() 函数将使用子 lgroup ID 填充数组,直到填满数组或者所有子 lgroup ID 均在数组中为止。
如果此 cookie 无效,则 lgrp_children() 函数将返回 EINVAL。如果未找到指定的 lgroup ID,则 lgrp_children() 函数将返回 ESRCH。
以下 API 检索有关给定 lgroup 内容的信息。
lgroup 分层结构对域资源进行了组织,以便简化查找最近资源的过程。叶 lgroup 使用具有最小延迟的资源进行定义。给定叶 lgroup 的每个上一级祖先 lgroup 均包含距离其子 lgroup 下一个最近的资源。根 lgroup 包含域中的所有资源。
给定 lgroup 的资源直接包含在此 lgroup 内,或者间接包含在此给定 lgroup 封装的叶 lgroup 内。叶 lgroup 直接包含各自的资源,而不封装其他任何 lgroup。
lgrp_resources() 函数返回指定 lgroup 中包含的资源数。
#include <sys/lgrp_user.h> int lgrp_resources(lgrp_cookie_t cookie, lgrp_id_t lgrp, lgrp_id_t *lgrpids, uint_t count, lgrp_rsrc_t type);
lgrp_resources() 函数使用表示 lgroup 分层结构快照的 cookie。此 cookie 是从 lgrp_init() 函数中获取的。lgrp_resources() 函数返回 ID 由 lgrp 参数值指定的 lgroup 中的资源数。lgrp_resources() 函数表示直接包含 CPU 或内存资源的 lgroup 集合中的资源。lgrp_rsrc_t 参数可以具有以下两个值:
lgrp_resources() 函数返回 CPU 资源数。
lgrp_resources() 函数返回内存资源数。
如果 lgrpids[] 参数中传递的值不为空并且 count 参数不为零,则 lgrp_resources() 函数将在 lgrpids[] 数组中存储 lgroup ID。此数组中存储的 lgroup ID 数最多不能超过 count 参数的值。
如果指定的 cookie、lgroup ID 或类型无效,则 lgrp_resources() 函数将返回 EINVAL。如果 lgrp_resources() 函数未找到指定的 lgroup ID,则它将返回 ESRCH。
lgrp_cpus(3LGRP) 函数使用表示 lgroup 分层结构快照的 cookie,并返回给定 lgroup 中的 CPU 数。
#include <sys/lgrp_user.h> int lgrp_cpus(lgrp_cookie_t cookie, lgrp_id_t lgrp, processorid_t *cpuids, uint_t count, int content);
如果 cpuid[] 参数不为 NULL 并且 CPU 计数不为零,则 lgrp_cpus() 函数将使用 CPU ID 填充数组,直到填满数组或者所有 CPU ID 均在数组中为止。
content 参数可以具有以下两个值:
lgrp_cpus() 函数返回此 lgroup 及其后代中的 CPU 的 ID。
lgrp_cpus() 函数只返回此 lgroup 中的 CPU 的 ID。
如果 cookie、lgroup ID 或其中一个标志无效,则 lgrp_cpus() 函数将返回 EINVAL。如果未找到指定的 lgroup ID,则 lgrp_cpus() 函数将返回 ESRCH。
lgrp_mem_size(3LGRP) 函数使用表示 lgroup 分层结构快照的 cookie,并返回给定 lgroup 中已安装内存或可用内存的大小。lgrp_mem_size() 函数报告内存大小(以字节为单位)。
#include <sys/lgrp_user.h> lgrp_mem_size_t lgrp_mem_size(lgrp_cookie_t cookie, lgrp_id_t lgrp, int type, int content)
type 参数可以具有以下两个值:
lgrp_mem_size() 函数返回可用内存量(以字节为单位)。
lgrp_mem_size() 函数返回已安装内存量(以字节为单位)。
content 参数可以具有以下两个值:
lgrp_mem_size() 函数返回此 lgroup 及其后代中的内存量。
lgrp_mem_size() 函数只返回此 lgroup 中的内存量。
如果 cookie、lgroup ID 或其中一个标志无效,则 lgrp_mem_size() 函数将返回 EINVAL。如果未找到指定的 lgroup ID,则 lgrp_mem_size() 函数将返回 ESRCH。
以下 API 检索有关给定 lgroup 特征的信息。
lgrp_latency(3LGRP) 函数返回一个 lgroup 中的 CPU 与另一个 lgroup 中的内存之间的延迟。
#include <sys/lgrp_user.h> int lgrp_latency_cookie(lgrp_cookie_t cookie, lgrp_id_t from, lgrp_id_t to. lat_between_t between);
lgrp_latency_cookie() 函数使用表示 lgroup 分层结构快照的 cookie。此 cookie 由 lgrp_init() 函数创建。lgrp_latency_cookie() 函数返回一个值,此值表示 from 参数值指定的 lgroup 中的硬件资源与 to 参数值指定的 lgroup 中的硬件资源之间的延迟。如果这两个参数指向同一个 lgroup,则 lgrp_latency_cookie() 函数将返回此 lgroup 内的延迟值。
lgrp_latency_cookie() 函数返回的延迟值由操作系统定义,并且特定于平台。此值不一定表示硬件设备之间的实际延迟,只用于在同一个域内进行比较。
如果 between 参数的值为 LGRP_LAT_CPU_TO_MEM,则 lgrp_latency_cookie() 函数将测量 CPU 资源与内存资源之间的延迟。
如果 lgroup ID 无效,则 lgrp_latency_cookie() 函数将返回 EINVAL。如果 lgrp_latency_cookie() 函数未找到指定的 lgroup ID,并且“源”lgroup 中不包含任何 CPU 或者“目标”lgroup 中没有任何内存,则 lgrp_latency_cookie() 函数将返回 ESRCH。
本节讨论用于搜索和影响与 lgroup 有关的线程和内存位置的 API。
使用 lgrp_home(3LGRP) 函数搜索线程位置。
使用 meminfo(2) 系统调用搜索内存位置。
使用 madvise(3C) 函数的 MADV_ACCESS 标志影响 lgroup 之间的内存分配。
通过设置线程与给定 lgroup 的关联,lgrp_affinity_set(3LGRP) 函数可以影响线程和内存位置。
lgroup 关联可以指定从中分配资源的 lgroup 的优先级顺序。
内核需要有关应用程序可能的内存使用模式的信息,才能有效地分配内存资源。
这些信息由 madvise() 函数及其类似共享对象 madv.so.1 提供给内核。
正在运行的进程可以使用 meminfo() 系统调用来收集其自身的内存使用信息。
lgrp_home() 函数返回指定进程或线程的主 lgroup。
#include <sys/lgrp_user.h> lgrp_id_t lgrp_home(idtype_t idtype, id_t id);
如果 ID 类型无效,则 lgrp_home() 函数将返回 EINVAL。如果调用进程的有效用户不是超级用户,并且调用进程的实际用户 ID 或有效用户 ID 与某个线程的实际用户 ID 或有效用户 ID 不匹配,则 lgrp_home() 函数将返回 EPERM。如果未找到指定的进程或线程,则 lgrp_home() 函数将返回 ESRCH。
madvise() 函数建议内核,在从 addr 指定的地址开始,长度等于 len 参数值的范围内,该区域的用户虚拟内存应遵循特定的使用模式。内核使用这些信息优化与指定范围关联的资源的处理和维护过程。如果使用 madvise() 函数的程序明确了解其内存访问模式,则使用此函数可以提高系统性能。
#include <sys/types.h> #include <sys/mman.h> int madvise(caddr_t addr, size_t len, int advice);
madvise() 函数提供了以下标志,这些标志影响 lgroup 之间线程内存的分配方式:
此标志将指定范围的内核预期访问模式重置为缺省设置。
此标志通知内核,移近指定地址范围的下一个 LWP 就是将要访问此范围次数最多的 LWP。内核将相应地为此范围和 LWP 分配内存和其他资源。
此标志建议内核,许多进程或 LWP 将在系统内随机访问指定的地址范围。内核将相应地为此范围分配内存和其他资源。
madvise() 函数可以返回以下值:
指定地址范围(从 addr 到 addr+len)中的部分或所有映射均已锁定进行 I/O 操作。
addr 参数的值不是 sysconf(3C) 返回的页面大小的倍数,指定地址范围的长度小于或等于零或者建议无效。
读写文件系统时发生 I/O 错误。
指定地址范围中的地址不在进程的有效地址空间范围内,或者指定地址范围中的地址指定了一个或多个未映射的页面。
NFS 文件句柄过时。
madv.so.1 共享对象允许为已启动的进程及其后代有选择性地配置虚拟内存建议。要使用该共享对象,环境中必须存在以下字符串:
LD_PRELOAD=$LD_PRELOAD:madv.so.1
madv.so.1 共享对象应用由 MADV 环境变量的值指定的内存建议。MADV 环境变量指定用于进程地址空间中的所有堆、共享内存和 mmap 区域的虚拟内存建议。此建议应用于所有已创建的进程。MADV 环境变量的以下值影响 lgroup 之间的资源分配:
此值将内核的预期访问模式重置为缺省设置。
此值通知内核,移近地址范围的下一个 LWP 就是将要访问此范围次数最多的 LWP。内核将相应地为此范围和 LWP 分配内存和其他资源。
此值建议内核,许多进程或 LWP 将在系统内随机访问内存。内核将相应地分配内存和其他资源。
MADVCFGFILE 环境变量的值是一个文本文件的名称,此文件中包含一个或多个形式为 exec-name:advice-opts 的内存建议配置项。
exec-name 的值是应用程序或可执行文件的名称。exec-name 的值可以是完整路径名、基本名称或模式字符串。
advice-opts 值的形式为 region=advice。advice 的值与 MADV 环境变量的值相同。可将 region 替换为以下任一合法值:
建议应用于进程地址空间中的所有堆、共享内存和 mmap(2) 区域。
堆被定义为 brk(2) 区域。建议应用于现有堆以及将来分配的任何其他堆内存。
建议应用于共享内存段。有关共享内存操作的更多信息,请参见 shmat(2)。
建议应用于使用 SHM_SHARE_MMU 标志的共享内存段。ism 选项优先于 shm。
建议应用于使用 SHM_PAGEABLE 标志的共享内存段。dsm 选项优先于 shm。
建议应用于由 mmap() 系统调用使用 MAP_SHARED 标志建立的映射。
建议应用于由 mmap() 系统调用使用 MAP_PRIVATE 标志建立的映射。
建议应用于由 mmap() 系统调用使用 MAP_ANON 标志建立的映射。当多个选项都适用时,mapanon 选项优先。
MADVERRFILE 环境变量的值是在其中记录错误消息的路径的名称。如果缺少 MADVERRFILE 位置,则 madv.so.1 共享对象将使用 syslog(3C) 记录错误,而使用 LOG_ERR 作为严重级别,使用 LOG_USER 作为功能描述符。
内存建议将被继承。子进程与其父级具有相同的建议。调用 exec(2) 之后,会将此建议重置为系统缺省建议,除非使用 madv.so.1 共享对象配置了不同级别的建议。建议仅应用于用户程序显式创建的 mmap() 区域。由运行时链接程序或执行直接系统调用的系统库建立的区域不受影响。
以下示例说明了 madv.so.1 共享对象的特定方面。
此配置将建议应用于 exec 名称以 foo 开头的应用程序的所有 ISM 段。
$ LD_PRELOAD=$LD_PRELOAD:madv.so.1 $ MADVCFGFILE=madvcfg $ export LD_PRELOAD MADVCFGFILE $ cat $MADVCFGFILE foo*:ism=access_lwp
此配置为除 ls 之外的所有应用程序设置建议。
$ LD_PRELOAD=$LD_PRELOAD:madv.so.1 $ MADV=access_many $ MADVCFGFILE=madvcfg $ export LD_PRELOAD MADV MADVCFGFILE $ cat $MADVCFGFILE ls:
由于 MADVCFGFILE 中指定的配置优先于 MADV 中设置的值,因此指定 * 作为最后一个配置项的 exec-name 相当于设置 MADV。本示例与上一个示例等效。
$ LD_PRELOAD=$LD_PRELOAD:madv.so.1 $ MADVCFGFILE=madvcfg $ export LD_PRELOAD MADVCFGFILE $ cat $MADVCFGFILE ls: *:madv=access_many
此配置为 mmap() 区域应用一类建议,为 exec() 名称以 foo 开头的应用程序的堆和共享内存区域应用不同的建议。
$ LD_PRELOAD=$LD_PRELOAD:madv.so.1 $ MADVCFGFILE=madvcfg $ export LD_PRELOAD MADVCFGFILE $ cat $MADVCFGFILE foo*:madv=access_many,heap=sequential,shm=access_lwp
meminfo() 函数为调用进程提供有关系统已分配给此进程的虚拟内存和物理内存的信息。
#include <sys/types.h> #include <sys/mman.h> int meminfo(const uint64_t inaddr[], int addr_count, const uint_t info_req[], int info_count, uint64_t outdata[], uint_t validity[]);
meminfo() 函数会返回以下类型的信息:
与给定虚拟地址对应的物理内存地址
与给定虚拟地址对应的物理页所属的 lgroup
与给定虚拟地址对应的物理页的大小
与给定虚拟地址对应的已复制物理页数
给定虚拟地址的第 n 个物理副本
给定虚拟地址的第 n 个物理副本所属的 lgroup
给定物理地址所属的 lgroup
meminfo() 函数使用以下参数:
输入地址数组。
传递给 meminfo() 的地址数。
列出所请求的信息类型的数组。
为 inaddr 数组中的每个地址请求的信息数。
meminfo() 函数用于存储结果的数组。此数组的大小等于 info_req 参数值与 addr_count 参数值的乘积。
大小等于 addr_count 参数值的数组。validity 数组中包含按位结果代码。结果代码中的第 0 位评估相应输入地址的有效性。结果代码中的每个后续位依次评估对 info_req 数组成员的响应的有效性。
如果无法写入 outdata 或 validity 数组指向的内存区域,则 meminfo() 函数将返回 EFAULT。如果无法读取 info_req 或 inaddr 数组指向的内存区域,则 meminfo() 函数将返回 EFAULT。如果 info_count 的值大于 31 或小于 1,则 meminfo() 函数将返回 EINVAL。如果 addr_count 的值小于零,则 meminfo() 函数将返回 EINVAL。
void print_info(void **addrvec, int how_many) { static const int info[] = { MEMINFO_VPHYSICAL, MEMINFO_VPAGESIZE}; uint64_t * inaddr = alloca(sizeof(uint64_t) * how_many); uint64_t * outdata = alloca(sizeof(uint64_t) * how_many * 2; uint_t * validity = alloca(sizeof(uint_t) * how_many); int i; for (i = 0; i < how_many; i++) inaddr[i] = (uint64_t *)addr[i]; if (meminfo(inaddr, how_many, info, sizeof (info)/ sizeof(info[0]), outdata, validity) < 0) ... for (i = 0; i < how_many; i++) { if (validity[i] & 1 == 0) printf("address 0x%llx not part of address space\n", inaddr[i]); else if (validity[i] & 2 == 0) printf("address 0x%llx has no physical page associated with it\n", inaddr[i]); else { char buff[80]; if (validity[i] & 4 == 0) strcpy(buff, "<Unknown>"); else sprintf(buff, "%lld", outdata[i * 2 + 1]); printf("address 0x%llx is backed by physical page 0x%llx of size %s\n", inaddr[i], outdata[i * 2], buff); } } }
为线程创建轻量级进程 (Lightweight Process, LWP) 时,内核会将此线程指定给一个地址组。此 lgroup 称为线程的主 lgroup。内核在线程的主 lgroup 中的 CPU 上运行线程,并尽量从此 lgroup 中分配内存。如果主 lgroup 中的资源不可用,则内核将从其他 lgroup 中分配资源。如果一个线程与多个 lgroup 关联,则操作系统将按关联强度选择 lgroup 并从中分配资源。lgroup 可以具有以下三个不同关联级别之一:
LGRP_AFF_STRONG 指示强关联。如果此 lgroup 是线程的主 lgroup,则操作系统将尽可能避免将其他 lgroup 重新指定为此线程的主 lgroup。有些事件(如动态重新配置、处理器脱机、处理器绑定以及处理器集绑定和处理)仍然可能导致为线程重新指定主 lgroup。
LGRP_AFF_WEAK 指示弱关联。如果此 lgroup 是线程的主 lgroup,则操作系统将在必要时为此线程重新指定主 lgroup 以便平衡负载。
LGRP_AFF_NONE 指示无关联。如果线程与任何 lgroup 均无关联,则操作系统将为此线程指定一个主 lgroup。
为给定线程分配资源时,操作系统将使用 lgroup 关联作为建议。将同时考虑此建议与其他系统约束。处理器绑定和处理器集不会更改 lgroup 关联,但是可能会限制能够运行线程的 lgroup。
lgrp_affinity_get(3LGRP) 函数返回 LWP 与给定 lgroup 的关联。
#include <sys/lgrp_user.h> lgrp_affinity_t lgrp_affinity_get(idtype_t idtype, id_t id, lgrp_id_t lgrp);
idtype 和 id 参数指定 lgrp_affinity_get() 函数检查的 LWP。如果 idtype 的值为 P_PID,则 lgrp_affinity_get() 函数将获取进程 ID 与 id 参数值匹配的进程中某个 LWP 的 lgroup 关联。如果 idtype 的值为 P_LWPID,则 lgrp_affinity_get() 函数将获取 LWP ID 与 id 参数值匹配的当前进程的 LWP 的 lgroup 关联。如果 idtype 的值为 P_MYID,则 lgrp_affinity_get() 函数将获取当前 LWP 的 lgroup 关联。
如果给定的 lgroup 或 ID 类型无效,则 lgrp_affinity_get() 函数将返回 EINVAL。如果调用进程的有效用户不是超级用户,并且调用进程的 ID 与某个 LWP 的实际用户 ID 或有效用户 ID 不匹配,则 lgrp_affinity_get() 函数将返回 EPERM。如果未找到给定的 lgroup 或 LWP,则 lgrp_affinity_get() 函数将返回 ESRCH。
lgrp_affinity_set(3LGRP) 函数设置 LWP 或 LWP 集合与给定 lgroup 的关联。
#include <sys/lgrp_user.h> int lgrp_affinity_set(idtype_t idtype, id_t id, lgrp_id_t lgrp, lgrp_affinity_t affinity);
idtype 和 id 参数指定 lgrp_affinity_set() 函数检查的 LWP 或 LWP 集合。如果 idtype 的值为 P_PID,则 lgrp_affinity_set() 函数会将进程 ID 与 id 参数值匹配的进程中所有 LWP 的 lgroup 关联设置为 affinity 参数中指定的关联级别。如果 idtype 的值为 P_LWPID,则 lgrp_affinity_set() 函数会将 LWP ID 与 id 参数值匹配的当前进程的 LWP 的 lgroup 关联设置为 affinity 参数中指定的关联级别。如果 idtype 的值为 P_MYID,则 lgrp_affinity_set() 函数会将当前 LWP 或进程的 lgroup 关联设置为 affinity 参数中指定的关联级别。
如果给定的 lgroup、关联或 ID 类型无效,则 lgrp_affinity_set() 函数将返回 EINVAL。如果调用进程的有效用户不是超级用户,并且调用进程的 ID 与某个 LWP 的实际用户 ID 或有效用户 ID 不匹配,则 lgrp_affinity_set() 函数将返回 EPERM。如果未找到给定的 lgroup 或 LWP,则 lgrp_affinity_set() 函数将返回 ESRCH。
本节包含使用本章介绍的 API 的示例任务的代码。
以下代码样例将 addr 到 addr+len 地址范围中的内存移近下一个接近此范围的线程。
#include <stdio.h> #include <sys/mman.h> #include <sys/types.h> /* * Move memory to thread */ void mem_to_thread(caddr_t addr, size_t len) { if (madvise(addr, len, MADV_ACCESS_LWP) < 0) perror("madvise"); }
本样例代码使用 meminfo() 函数确定用于在给定地址备份虚拟页的物理内存的 lgroup。然后样例代码为此 lgroup 设置一个强关联,尝试将当前线程移近该内存。
#include <stdio.h> #include <sys/lgrp_user.h> #include <sys/mman.h> #include <sys/types.h> /* * Move a thread to memory */ int thread_to_memory(caddr_t va) { uint64_t addr; ulong_t count; lgrp_id_t home; uint64_t lgrp; uint_t request; uint_t valid; addr = (uint64_t)va; count = 1; request = MEMINFO_VLGRP; if (meminfo(&addr, 1, &request, 1, &lgrp, &valid) != 0) { perror("meminfo"); return (1); } if (lgrp_affinity_set(P_LWPID, P_MYID, lgrp, LGRP_AFF_STRONG) != 0) { perror("lgrp_affinity_set"); return (2); } home = lgrp_home(P_LWPID, P_MYID); if (home == -1) { perror ("lgrp_home"); return (3); } if (home != lgrp) return (-1); return (0); }
以下样例代码遍历并列显了 lgroup 分层结构。
#include <stdio.h> #include <stdlib.h> #include <sys/lgrp_user.h> #include <sys/types.h> /* * Walk and print lgroup hierarchy from given lgroup * through all its descendants */ int lgrp_walk(lgrp_cookie_t cookie, lgrp_id_t lgrp, lgrp_content_t content) { lgrp_affinity_t aff; lgrp_id_t *children; processorid_t *cpuids; int i; int ncpus; int nchildren; int nparents; lgrp_id_t *parents; lgrp_mem_size_t size; /* * Print given lgroup, caller's affinity for lgroup, * and desired content specified */ printf("LGROUP #%d:\n", lgrp); aff = lgrp_affinity_get(P_LWPID, P_MYID, lgrp); if (aff == -1) perror ("lgrp_affinity_get"); printf("\tAFFINITY: %d\n", aff); printf("CONTENT %d:\n", content); /* * Get CPUs */ ncpus = lgrp_cpus(cookie, lgrp, NULL, 0, content); printf("\t%d CPUS: ", ncpus); if (ncpus == -1) { perror("lgrp_cpus"); return (-1); } else if (ncpus > 0) { cpuids = malloc(ncpus * sizeof (processorid_t)); ncpus = lgrp_cpus(cookie, lgrp, cpuids, ncpus, content); if (ncpus == -1) { free(cpuids); perror("lgrp_cpus"); return (-1); } for (i = 0; i < ncpus; i++) printf("%d ", cpuids[i]); free(cpuids); } printf("\n"); /* * Get memory size */ printf("\tMEMORY: "); size = lgrp_mem_size(cookie, lgrp, LGRP_MEM_SZ_INSTALLED, content); if (size == -1) { perror("lgrp_mem_size"); return (-1); } printf("installed bytes 0x%llx, ", size); size = lgrp_mem_size(cookie, lgrp, LGRP_MEM_SZ_FREE, content); if (size == -1) { perror("lgrp_mem_size"); return (-1); } printf("free bytes 0x%llx\n", size); /* * Get parents */ nparents = lgrp_parents(cookie, lgrp, NULL, 0); printf("\t%d PARENTS: ", nparents); if (nparents == -1) { perror("lgrp_parents"); return (-1); } else if (nparents > 0) { parents = malloc(nparents * sizeof (lgrp_id_t)); nparents = lgrp_parents(cookie, lgrp, parents, nparents); if (nparents == -1) { free(parents); perror("lgrp_parents"); return (-1); } for (i = 0; i < nparents; i++) printf("%d ", parents[i]); free(parents); } printf("\n"); /* * Get children */ nchildren = lgrp_children(cookie, lgrp, NULL, 0); printf("\t%d CHILDREN: ", nchildren); if (nchildren == -1) { perror("lgrp_children"); return (-1); } else if (nchildren > 0) { children = malloc(nchildren * sizeof (lgrp_id_t)); nchildren = lgrp_children(cookie, lgrp, children, nchildren); if (nchildren == -1) { free(children); perror("lgrp_children"); return (-1); } printf("Children: "); for (i = 0; i < nchildren; i++) printf("%d ", children[i]); printf("\n"); for (i = 0; i < nchildren; i++) lgrp_walk(cookie, children[i], content); free(children); } printf("\n"); return (0); }
#include <stdio.h> #include <stdlib.h> #include <sys/lgrp_user.h> #include <sys/types.h> #define INT_MAX 2147483647 /* * Find next closest lgroup outside given one with available memory */ lgrp_id_t lgrp_next_nearest(lgrp_cookie_t cookie, lgrp_id_t from) { lgrp_id_t closest; int i; int latency; int lowest; int nparents; lgrp_id_t *parents; lgrp_mem_size_t size; /* * Get number of parents */ nparents = lgrp_parents(cookie, from, NULL, 0); if (nparents == -1) { perror("lgrp_parents"); return (LGRP_NONE); } /* * No parents, so current lgroup is next nearest */ if (nparents == 0) { return (from); } /* * Get parents */ parents = malloc(nparents * sizeof (lgrp_id_t)); nparents = lgrp_parents(cookie, from, parents, nparents); if (nparents == -1) { perror("lgrp_parents"); free(parents); return (LGRP_NONE); } /* * Find closest parent (ie. the one with lowest latency) */ closest = LGRP_NONE; lowest = INT_MAX; for (i = 0; i < nparents; i++) { lgrp_id_t lgrp; /* * See whether parent has any free memory */ size = lgrp_mem_size(cookie, parents[i], LGRP_MEM_SZ_FREE, LGRP_CONTENT_ALL); if (size > 0) lgrp = parents[i]; else { if (size == -1) perror("lgrp_mem_size"); /* * Find nearest ancestor if parent doesn't * have any memory */ lgrp = lgrp_next_nearest(cookie, parents[i]); if (lgrp == LGRP_NONE) continue; } /* * Get latency within parent lgroup */ latency = lgrp_latency_cookie(lgrp, lgrp); if (latency == -1) { perror("lgrp_latency_cookie"); continue; } /* * Remember lgroup with lowest latency */ if (latency < lowest) { closest = lgrp; lowest = latency; } } free(parents); return (closest); } /* * Find lgroup with memory nearest home lgroup of current thread */ lgrp_id_t lgrp_nearest(lgrp_cookie_t cookie) { lgrp_id_t home; longlong_t size; /* * Get home lgroup */ home = lgrp_home(P_LWPID, P_MYID); /* * See whether home lgroup has any memory available in its hierarchy */ size = lgrp_mem_size(cookie, home, LGRP_MEM_SZ_FREE, LGRP_CONTENT_ALL); if (size == -1) perror("lgrp_mem_size"); /* * It does, so return the home lgroup. */ if (size > 0) return (home); /* * Otherwise, find next nearest lgroup outside of the home. */ return (lgrp_next_nearest(cookie, home)); }
本示例代码查找具有可用内存且距离给定线程的主 lgroup 最近的 lgroup。
lgrp_id_t lgrp_nearest(lgrp_cookie_t cookie) { lgrp_id_t home; longlong_t size; /* * Get home lgroup */ home = lgrp_home(); /* * See whether home lgroup has any memory available in its hierarchy */ if (lgrp_mem_size(cookie, lgrp, LGRP_MEM_SZ_FREE, LGRP_CONTENT_ALL, &size) == -1) perror("lgrp_mem_size"); /* * It does, so return the home lgroup. */ if (size > 0) return (home); /* * Otherwise, find next nearest lgroup outside of the home. */ return (lgrp_next_nearest(cookie, home)); }