编程接口指南

第 4 章 地址组 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)。

图 4–1 单地址组示意图

计算机中的所有 CPU 均可在相差无几的时间段内访问内存。

图 4–2 多地址组示意图

计算机的 CPU 和内存资源按限定的延迟间隔进行分组。

lgroup 分层结构的组织简化了在系统中查找最近资源的任务。创建每个线程时即为其指定了一个主 lgroup。缺省情况下,操作系统将尝试从线程的主 lgroup 为线程分配资源。例如,缺省情况下,Solaris 内核将尝试安排线程在其主 lgroup 中的 CPU 上运行,并从其主 lgroup 中分配此线程的内存。如果线程的主 lgroup 中没有所需的资源,则内核可以遍历 lgroup 分层结构,从主 lgroup 的父级中查找下一个最近的资源。如果主 lgroup 的父级中没有所需的资源,则内核将继续遍历 lgroup 分层结构,在主 lgroup 的上一级祖先 lgroup 中进行查找。根 lgroup 是计算机中所有其他 lgroup 的最后一级祖先,它包含计算机中的所有资源。

lgroup API 针对应用程序引入了 lgroup 概念,以便进行观察和性能调优。新 API 包含在名为 liblgrp 的新库中。应用程序可以使用 API 执行以下任务:

验证接口版本

在使用 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


示例 4–1 lgrp_version() 用法示例

#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 用于执行以下任务:

使用 lgrp_init()

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()

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()

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()

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()

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()

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()

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()

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()

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_RSRC_CPU

lgrp_resources() 函数返回 CPU 资源数。

LGRP_RSRC_MEM

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()

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_CONTENT_ALL

lgrp_cpus() 函数返回此 lgroup 及其后代中的 CPU 的 ID。

LGRP_CONTENT_DIRECT

lgrp_cpus() 函数只返回此 lgroup 中的 CPU 的 ID。

如果 cookie、lgroup ID 或其中一个标志无效,则 lgrp_cpus() 函数将返回 EINVAL。如果未找到指定的 lgroup ID,则 lgrp_cpus() 函数将返回 ESRCH

使用 lgrp_mem_size()

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_SZ_FREE

lgrp_mem_size() 函数返回可用内存量(以字节为单位)。

LGRP_MEM_SZ_INSTALLED

lgrp_mem_size() 函数返回已安装内存量(以字节为单位)。

content 参数可以具有以下两个值:

LGRP_CONTENT_ALL

lgrp_mem_size() 函数返回此 lgroup 及其后代中的内存量。

LGRP_CONTENT_DIRECT

lgrp_mem_size() 函数只返回此 lgroup 中的内存量。

如果 cookie、lgroup ID 或其中一个标志无效,则 lgrp_mem_size() 函数将返回 EINVAL。如果未找到指定的 lgroup ID,则 lgrp_mem_size() 函数将返回 ESRCH

地址组特征

以下 API 检索有关给定 lgroup 特征的信息。

使用 lgrp_latency_cookie()

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()

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()

madvise() 函数建议内核,在从 addr 指定的地址开始,长度等于 len 参数值的范围内,该区域的用户虚拟内存应遵循特定的使用模式。内核使用这些信息优化与指定范围关联的资源的处理和维护过程。如果使用 madvise() 函数的程序明确了解其内存访问模式,则使用此函数可以提高系统性能。

#include <sys/types.h>

#include <sys/mman.h>

int madvise(caddr_t addr, size_t len, int advice);

madvise() 函数提供了以下标志,这些标志影响 lgroup 之间线程内存的分配方式:

MADV_ACCESS_DEFAULT

此标志将指定范围的内核预期访问模式重置为缺省设置。

MADV_ACCESS_LWP

此标志通知内核,移近指定地址范围的下一个 LWP 就是将要访问此范围次数最多的 LWP。内核将相应地为此范围和 LWP 分配内存和其他资源。

MADV_ACCESS_MANY

此标志建议内核,许多进程或 LWP 将在系统内随机访问指定的地址范围。内核将相应地为此范围分配内存和其他资源。

madvise() 函数可以返回以下值:

EAGAIN

指定地址范围(从 addraddr+len)中的部分或所有映射均已锁定进行 I/O 操作。

EINVAL

addr 参数的值不是 sysconf(3C) 返回的页面大小的倍数,指定地址范围的长度小于或等于零或者建议无效。

EIO

读写文件系统时发生 I/O 错误。

ENOMEM

指定地址范围中的地址不在进程的有效地址空间范围内,或者指定地址范围中的地址指定了一个或多个未映射的页面。

ESTALE

NFS 文件句柄过时。

使用 madv.so.1

madv.so.1 共享对象允许为已启动的进程及其后代有选择性地配置虚拟内存建议。要使用该共享对象,环境中必须存在以下字符串:

LD_PRELOAD=$LD_PRELOAD:madv.so.1

madv.so.1 共享对象应用由 MADV 环境变量的值指定的内存建议。MADV 环境变量指定用于进程地址空间中的所有堆、共享内存和 mmap 区域的虚拟内存建议。此建议应用于所有已创建的进程。MADV 环境变量的以下值影响 lgroup 之间的资源分配:

access_default

此值将内核的预期访问模式重置为缺省设置。

access_lwp

此值通知内核,移近地址范围的下一个 LWP 就是将要访问此范围次数最多的 LWP。内核将相应地为此范围和 LWP 分配内存和其他资源。

access_many

此值建议内核,许多进程或 LWP 将在系统内随机访问内存。内核将相应地分配内存和其他资源。

MADVCFGFILE 环境变量的值是一个文本文件的名称,此文件中包含一个或多个形式为 exec-name:advice-opts 的内存建议配置项。

exec-name 的值是应用程序或可执行文件的名称。exec-name 的值可以是完整路径名、基本名称或模式字符串。

advice-opts 值的形式为 region=adviceadvice 的值与 MADV 环境变量的值相同。可将 region 替换为以下任一合法值:

madv

建议应用于进程地址空间中的所有堆、共享内存和 mmap(2) 区域。

heap

堆被定义为 brk(2) 区域。建议应用于现有堆以及将来分配的任何其他堆内存。

shm

建议应用于共享内存段。有关共享内存操作的更多信息,请参见 shmat(2)

ism

建议应用于使用 SHM_SHARE_MMU 标志的共享内存段。ism 选项优先于 shm

dsm

建议应用于使用 SHM_PAGEABLE 标志的共享内存段。dsm 选项优先于 shm

mapshared

建议应用于由 mmap() 系统调用使用 MAP_SHARED 标志建立的映射。

mapprivate

建议应用于由 mmap() 系统调用使用 MAP_PRIVATE 标志建立的映射。

mapanon

建议应用于由 mmap() 系统调用使用 MAP_ANON 标志建立的映射。当多个选项都适用时,mapanon 选项优先。

MADVERRFILE 环境变量的值是在其中记录错误消息的路径的名称。如果缺少 MADVERRFILE 位置,则 madv.so.1 共享对象将使用 syslog(3C) 记录错误,而使用 LOG_ERR 作为严重级别,使用 LOG_USER 作为功能描述符。

内存建议将被继承。子进程与其父级具有相同的建议。调用 exec(2) 之后,会将此建议重置为系统缺省建议,除非使用 madv.so.1 共享对象配置了不同级别的建议。建议仅应用于用户程序显式创建的 mmap() 区域。由运行时链接程序或执行直接系统调用的系统库建立的区域不受影响。

madv.so.1 用法示例

以下示例说明了 madv.so.1 共享对象的特定方面。


示例 4–2 为应用程序集合设置建议

此配置将建议应用于 exec 名称以 foo 开头的应用程序的所有 ISM 段。

$ LD_PRELOAD=$LD_PRELOAD:madv.so.1

$ MADVCFGFILE=madvcfg

$ export LD_PRELOAD MADVCFGFILE

$ cat $MADVCFGFILE

        foo*:ism=access_lwp


示例 4–3 从建议中排除应用程序集合

此配置为除 ls 之外的所有应用程序设置建议。

$ LD_PRELOAD=$LD_PRELOAD:madv.so.1

$ MADV=access_many

$ MADVCFGFILE=madvcfg

$ export LD_PRELOAD MADV MADVCFGFILE

$ cat $MADVCFGFILE

        ls:


示例 4–4 配置文件中的模式匹配

由于 MADVCFGFILE 中指定的配置优先于 MADV 中设置的值,因此指定 * 作为最后一个配置项的 exec-name 相当于设置 MADV。本示例与上一个示例等效。

$ LD_PRELOAD=$LD_PRELOAD:madv.so.1

$ MADVCFGFILE=madvcfg

$ export LD_PRELOAD MADVCFGFILE

$ cat $MADVCFGFILE

        ls:

        *:madv=access_many


示例 4–5 针对多个区域的建议

此配置为 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()

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() 函数会返回以下类型的信息:

MEMINFO_VPHYSICAL

与给定虚拟地址对应的物理内存地址

MEMINFO_VLGRP

与给定虚拟地址对应的物理页所属的 lgroup

MEMINFO_VPAGESIZE

与给定虚拟地址对应的物理页的大小

MEMINFO_VREPLCNT

与给定虚拟地址对应的已复制物理页数

MEMINFO_VREPL|n

给定虚拟地址的第 n 个物理副本

MEMINFO_VREPL_LGRP|n

给定虚拟地址的第 n 个物理副本所属的 lgroup

MEMINFO_PLGRP

给定物理地址所属的 lgroup

meminfo() 函数使用以下参数:

inaddr

输入地址数组。

addr_count

传递给 meminfo() 的地址数。

info_req

列出所请求的信息类型的数组。

info_count

inaddr 数组中的每个地址请求的信息数。

outdata

meminfo() 函数用于存储结果的数组。此数组的大小等于 info_req 参数值与 addr_count 参数值的乘积。

validity

大小等于 addr_count 参数值的数组。validity 数组中包含按位结果代码。结果代码中的第 0 位评估相应输入地址的有效性。结果代码中的每个后续位依次评估对 info_req 数组成员的响应的有效性。

如果无法写入 outdatavalidity 数组指向的内存区域,则 meminfo() 函数将返回 EFAULT。如果无法读取 info_reqinaddr 数组指向的内存区域,则 meminfo() 函数将返回 EFAULT。如果 info_count 的值大于 31 或小于 1,则 meminfo() 函数将返回 EINVAL。如果 addr_count 的值小于零,则 meminfo() 函数将返回 EINVAL


示例 4–6 使用 meminfo() 列显与虚拟地址集合对应的物理页和页面大小

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 可以具有以下三个不同关联级别之一:

  1. LGRP_AFF_STRONG 指示强关联。如果此 lgroup 是线程的主 lgroup,则操作系统将尽可能避免将其他 lgroup 重新指定为此线程的主 lgroup。有些事件(如动态重新配置、处理器脱机、处理器绑定以及处理器集绑定和处理)仍然可能导致为线程重新指定主 lgroup。

  2. LGRP_AFF_WEAK 指示弱关联。如果此 lgroup 是线程的主 lgroup,则操作系统将在必要时为此线程重新指定主 lgroup 以便平衡负载。

  3. LGRP_AFF_NONE 指示无关联。如果线程与任何 lgroup 均无关联,则操作系统将为此线程指定一个主 lgroup。

为给定线程分配资源时,操作系统将使用 lgroup 关联作为建议。将同时考虑此建议与其他系统约束。处理器绑定和处理器集不会更改 lgroup 关联,但是可能会限制能够运行线程的 lgroup。

使用 lgrp_affinity_get()

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);

idtypeid 参数指定 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()

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);

idtypeid 参数指定 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 用法示例

本节包含使用本章介绍的 API 的示例任务的代码。


示例 4–7 将内存移动到线程

以下代码样例将 addraddr+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");

}


示例 4–8 将线程移动到内存

本样例代码使用 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);

}


示例 4–9 遍历 lgroup 分层结构

以下样例代码遍历并列显了 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);

}


示例 4–10 查找位于给定 lgroup 外具有可用内存的最近 lgroup

#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));

}


示例 4–11 查找具有可用内存的最近 lgroup

本示例代码查找具有可用内存且距离给定线程的主 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));

}