本节讨论用于搜索和影响与 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。