プログラミングインタフェース

第 4 章 ローカリティグループ API

この章では、ローカリティグループとやりとりするために、アプリケーションで使用できる API について説明します。

ローカリティグループの概要 では、ローカリティグループによる抽象化について説明します。

インタフェースバージョンの確認 では、インタフェースに関する情報を提供する関数について説明します。

ローカリティグループインタフェースの初期化 では、ローカリティグループ階層を探索し、内容を検索するために使用するインタフェース部分の初期化およびシャットダウンを実行する関数呼び出しについて説明します。

ローカリティグループ階層 では、ローカリティグループ階層を参照し、階層の特性に関する情報を取得する関数呼び出しについて説明します。

ローカリティグループの内容 では、ローカリティグループの内容に関する情報を取り出す関数呼び出しについて説明します。

ローカリティグループの特性 では、ローカリティグループの特性に関する情報を取り出す関数呼び出しについて説明します。

ローカリティグループ、スレッド、およびメモリー配置 では、スレッドのメモリー配置を制御する方法とその他のメモリー管理手法について説明します。

API の使用例 では、この章で説明した API を使用して、タスクを実行するコーディング例を示します。

ローカリティグループの概要

メモリー共有型マルチプロセッサマシンには、複数の CPU が搭載されています。 それぞれの CPU は、そのマシンのすべてのメモリーにアクセスできます。メモリー共有型マルチプロセッサには、CPU ごとに特定のメモリー領域に対して、より高速なアクセスを可能にするメモリーアーキテクチャを採用しているものがあります。

そのようなメモリーアーキテクチャのマシンで Solaris を実行した場合、特定の CPU による特定のメモリー領域への最短アクセス時間に関するカーネル情報が与えられると、システムのパフォーマンスを向上させることができます。 Solaris では、この情報を処理するためにローカリティグループ (lgroup) による抽象化が導入されています。 lgroup による抽象化は、メモリー配置の最適化 (MPO) 機能の一部です。

lgroup は CPU およびメモリーを模したデバイスの集合です。それぞれの集合内のデバイスは、決められた応答時間の間隔範囲で集合内の他のデバイスにアクセスできます。各 lgroup の応答時間値は、オペレーティングシステムによって選択されます。

lgroup は階層構造になっています。lgroup 階層は、Directed Acyclic Graph (DAG) です。ツリー構造と似ていますが、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 API は、監視と性能チューニングを目的とするアプリケーションのために lgroup の抽象化をエクスポートします。アプリケーションは API を使用して lgroup 階層を探索し、指定された lgroup の内容と特性を検出できます。また、lgroup でのスレッドとメモリー配置を操作することも可能です。 新しい API は、新規ライブラリ liblgrp に含まれています。

インタフェースバージョンの確認

lgroup API を使用する前に、この節で説明している lgrp_version() 関数を使用して、lgroup インタフェースがサポートされていることを確認する必要があります。

lgrp_version() の使用法

#include <sys/lgrp_user.h>
int lgrp_version(const int version);

lgrp_version() 関数は、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);
    }

ローカリティグループインタフェースの初期化

アプリケーションは、API を使用して lgroup 階層を参照し、内容を検出するために、lgrp_init() を呼び出す必要があります。lgrp_init() を呼び出すことにより、アプリケーションは lgroup 階層のスナップショットを取得できます。アプリケーション開発者は、特に呼び出したスレッドが利用可能なリソースだけをスナップショットに含めるか、あるいはオペレーティングシステム全般で利用可能なリソースを含めるかを指定できます。lgrp_init() 関数はクッキーを返します。クッキーは、次のタスクで使用されます。

lgrp_init() の使用法

lgrp_init() 関数は、 lgroup インタフェースを初期化し、 lgroup 階層のスナップショットを取得します。

#include <sys/lgrp_user.h>
lgrp_cookie_t lgrp_init(lgrp_view_t view);

引数 view に LGRP_VIEW_CALLER を指定して lgrp_init() 関数を呼び出す場合には、呼び出したスレッドで利用可能なリソースだけがスナップショットとして返されます。 引数 view に LGRP_VIEW_OS を指定して lgrp_init() 関数を呼び出す場合は、オペレーティングシステムで利用可能なリソースがスナップショットとして返されます。 スレッドが lgrp_init() 関数の呼び出しに成功すると、関数は lgroup 階層とやりとりするいかなる関数でも使用するクッキーを返します。

lgroup 階層は、マシン上のすべての CPU とメモリーリソースを含むルート lgroup 階層によって構成されています。 ルート lgroup は、異なる応答時間間隔で定義された複数のローカリティグループを持つことができます。

lgrp_init() 関数が返すエラーは 2 種類あります。無効な引数 view が指定された場合、EINVAL を返します。 lgroup 階層のスナップショットを割り当てる十分なメモリーがない場合には、ENOMEM を返します。

lgrp_fini() の使用法

lgrp_fini() 関数は、取得したクッキーの使用を終了し、対応する lgroup 階層のスナップショットを解放します。

#include <sys/lgrp_user.h>
int lgrp_fini(lgrp_cookie_t cookie);

lgrp_fini() 関数は、前に lgrp_init () を呼び出して作成した lgroup 階層のスナップショットを表すクッキーを引数に使用します。lgrp_fini() 関数は、そのスナップショットに割り当てられたメモリーを解放します。lgrp_fini () を呼び出した後では、クッキーは無効になります。 そのクッキーを使用することはできません。

lgrp_fini() 関数に渡したクッキーが無効な場合、lgrp_fini()EINVAL を返します。

ローカリティグループ階層

この節で説明する API を使用することにより、呼び出しスレッドから lgroup 階層を参照できます。 lgroup 階層は、Directed Acyclic Graph (DAG) です。ツリー構造と似ていますが、ノードは複数の親を持つことができます。ルート lgroup は、マシン全体を表しており、システム全体で最も高い応答時間値を持っています。 子 lgroup はルート lgroup にあるハードウェアのサブセットで構成されており、低い応答時間でそれぞれくくられています。ルートに近いローカリティグループほど、多くのリソースと高い応答時間が与えられています。葉に近いローカリティグループは、リソースも少なく、応答時間も低くなります。

lgrp_cookie_stale() の使用法

lgrp_cookie_stale() 関数は、指定のクッキーで表された lgroup 階層のスナップショットが最新のものであるかどうかを判定します。

#include <sys/lgrp_user.h>
int lgrp_cookie_stale(lgrp_cookie_t cookie);

lgrp_init() 関数が返すクッキーは、そのスナップショットを取得した view 引数の種類によって、さまざまな理由により無効になる場合があります。view 引数に LGRP_VIEW_OS を指定して呼び出した lgrp_init() 関数が返すクッキーでは、動的再構成や CPU のオンライン状態の変化などが原因で lgroup 階層に変更があったときに、無効になる場合があります。view 引数に LGRP_VIEW_CALLER を指定した lgrp_init() 関数が返すクッキーでは、呼び出しスレッドのプロセッサセットの変更または lgroup 階層の変化が原因で、無効になる場合があります。無効になったクッキーは、そのクッキーで lgrp_fini() 関数を呼び出し、次に新規クッキーを生成する lgrp_init() 関数を呼び出すことによって新しくなります。

無効なクッキーを指定した場合、lgrp_cookie_stale() 関数は EINVAL を返します。

lgrp_view() の使用法

lgrp_view() 関数は、指定した lgroup 階層のスナップショットがどのビューで取得されたか判定します。

#include <sys/lgrp_user.h>
lgrp_view_t lgrp_view(lgrp_cookie_t cookie);

lgrp_view() 関数は、lgroup 階層のスナップショットを表すクッキーを引数に使用し、そのスナップショットの view を返します。LGRP_VIEW_CALLER を view に指定して取得したスナップショットには、呼び出しスレッドで使用可能なリソースのみが含まれます。LGRP_VIEW_OS で取得したスナップショットには、オペレーティングシステムで使用可能なすべてのリソースが含まれます。

無効なクッキーを指定した場合、lgrp_view() 関数は EINVAL を返します。

lgrp_nlgrps() の使用法

lgrp_nlgrps() 関数は、システムに存在するローカリティグループの数を返します。ローカリティグループが 1 つしかないシステムでは、メモリー配置の最適化による効果はありません。

#include <sys/lgrp_user.h>
int lgrp_nlgrps(lgrp_cookie_t cookie);

lgrp_nlgrps() 関数は、lgroup 階層のスナップショットを表すクッキーを引数に使用し、階層で使用可能な lgroup の数を返します。

無効なクッキーを指定した場合、lgrp_nlgrps() 関数は EINVAL を返します。

lgrp_root() の使用法

lgrp_root() 関数は、ルート lgroup ID を返します。

#include <sys/lgrp_user.h>
lgrp_id_t lgrp_root(lgrp_cookie_t cookie);

lgrp_root() 関数は、lgroup 階層のスナップショットを表すクッキーを引数に使用し、ルート lgroup ID を返します。

lgrp_parents() の使用法

lgrp_parents() 関数は、lgroup 階層のスナップショットを表すクッキーを引数に使用し、指定した lgroup の親 lgroups の数を返します。

#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 には親はありません。ルート lgroup に対して lgrp_parents() 関数が呼び出された場合は、lgrp_array には何も返されません。

無効なクッキーを指定した場合、lgrp_parents() 関数は EINVAL を返します。指定した lgroup ID が存在しない場合、lgrp_parents() 関数は ESRCH を返します。

lgrp_children() の使用法

lgrp_children() 関数は、呼び出しスレッドの lgroup 階層のスナップショットを表すクッキーを引数に使用し、指定した lgroup の子 lgroups の数を返します。

#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 を配列に入れて返します。

無効なクッキーを指定した場合、lgrp_children() 関数は EINVAL を返します。指定した lgroup ID が存在しない場合は、lgrp_children() 関数は ESRCH を返します。

ローカリティグループの内容

次に示す API では、指定した lgroup の内容に関する情報を取り出します。

lgrp_cpus() の使用法

lgrp_cpus() 関数は、lgroup 階層のスナップショットを表すクッキーを引数に使用し、指定した 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 を配列に入れて返します。

content 引数には、次の 2 つの値が指定できます。

LGRP_CONTENT_HIERARCHY

lgrp_cpus() 関数は、この lgroup と下位ノードにある CPU の ID を返す

LGRP_CONTENT_DIRECT

lgrp_cpus() 関数は、この lgroup にある CPU の ID だけを返す

指定したクッキー、lgroup ID、または上記フラグのいずれか 1 つが無効な場合、lgrp_cpus() 関数は EINVAL を返します。指定した lgroup ID が存在しない場合は、lgrp_cpus() 関数は ESRCH を返します。

lgrp_mem_size() の使用法

lgrp_mem_size() 関数は、lgroup 階層のスナップショットを表すクッキーを引数に使用し、指定した 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 引数には、次の 2 つの値が指定できます。

LGRP_CONTENT_HIERARCHY

lgrp_mem_size() 関数は、この lgroup と下位ノードのメモリーサイズの総量を返す

LGRP_CONTENT_DIRECT

lgrp_mem_size() 関数は、この lgroup のメモリーのサイズのみを返す

指定したクッキー、lgroup ID、またはフラグのいずれか 1 つが無効な場合、lgrp_mem_size() 関数は EINVAL を返します。指定した lgroup ID が存在しない場合は、lgrp_mem_size() 関数は ESRCH を返します。

ローカリティグループの特性

次に示す API では、指定した lgroup の特性に関する情報を取り出します。

lgrp_latency() の使用法

lgrp_latency() 関数は、ある lgroup の CPU が別の lgroup のメモリーにアクセスするときの応答時間を返します。

#include <sys/lgrp_user.h>
int lgrp_latency(lgrp_id_t from, lgrp_id_t to);

lgrp_latency() 関数は、from 引数で指定した lgroup の CPU が to 引数で指定した lgroup のメモリーにアクセスするときの応答時間を返します。2 つの引数が同じ lgroup を指している場合、lgrp_latency() 関数は、同一 lgroup での応答時間値を返します。


注 –

lgrp_latency() 関数が返す応答時間値は、オペレーティングシステムで定義されたもので、プラットフォーム固有の数値です。 この値は、実際のハードウェアデバイスの応答時間を表しているとは限りません。あるドメイン内部での比較のために使用する数値である場合があります。


無効な lgroup を指定した場合、lgrp_latency() 関数は EINVAL を返します。lgrp_latency() 関数で指定された lgroup ID が存在しないとき、from 引数で指定された lgroup に CPU が存在しないとき、または to 引数で指定された lgroup にメモリーが存在しないときには、lgrp_latency() 関数は ESRCH を返します。

ローカリティグループ、スレッド、およびメモリー配置

この節では、各 lgroup のスレッドとメモリー配置を検出し、制御するために使用する API について説明します。lgrp_home() 関数は、スレッドの配置を検出するために使用します。メモリー配置の検出には、meminfo (2) システムコールを使用します。lgroup 間でのメモリー配置を制御するには、madvise (3C) 関数に MADV_ACCESS フラグを設定して使用します。lgrp_affinity_set() 関数では、指定した lgroup のスレッドのアフィニティーを設定することにより、スレッドとメモリー配置を制御できます。ある lgroup のアフィニティーが、リソースをどの lgroup から割り当てるか優先順位を決定するのに影響を与える可能性があります。カーネルが効率的なメモリーの割り当てを行うには、アプリケーションのメモリー使用パターンについての情報が必要になります。madvise() 関数およびその共有オブジェクト版である madv.so.1 により、この情報をカーネルに提供します。実行中のプロセスが meminfo() システムコールを使用して自分自身のメモリー使用に関する情報を収集できます。

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 が、その領域に最も頻繁にアクセスする LPW であることをカーネルに指定する。それに応じて、カーネルはメモリーや他のリソースをこの領域と LWP に割り当てる

MADV_ACCESS_MANY

このフラグは、多くのプロセスまたは LPW が、ランダムにシステム全域から指定の領域にアクセスしていることをカーネルに指定する。それに応じて、カーネルはメモリーや他のリソースをこの領域に割り当てる

addr から addr+len までのアドレス範囲で指定されたマッピング領域の全体または一部が、入出力操作によりロックされている場合、madvise() 関数は EAGAIN を返します。addr パラメータの値が sysconf(3C) で返されるページサイズの倍数ではない場合、madvise() 関数は EINVAL を返します。指定したアドレス範囲の長さがゼロ以下の場合、madvise() 関数は EINVAL を返します。カーネルに指定したアドバイス情報が無効だった場合、madvise() 関数は EINVAL を返します。ファイルシステムに対する読み取りや書き込み中に入出力エラーが発生した場合は、madvise() 関数は EIO を返します。指定したアドレス範囲のアドレスが、プロセスの有効なアドレス空間の範囲外、またはマップされていないページが指定されている場合には、madvise() 関数は ENOMEM を返します。NFS ファイルハンドルが無効の場合は、madvise() 関数はESTALE を返します。

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 が、その領域に最も頻繁にアクセスする LPW であることをカーネルに指定する。それに応じて、カーネルはメモリーや他のリソースをこの領域と LWP に割り当てる

access_many

この値は、多くのプロセスまたは LPW が、ランダムにシステム全域からメモリーにアクセスしていることをカーネルに指定する。それに応じて、カーネルはメモリーや他のリソースを割り当てる

MADVCFGFILE 環境変数値は、メモリー構成のアドバイス情報エントリが <exec-name>:<advice-opts> の書式で記述されているテキストファイルの名前です。

<exec-name> の値は、アプリケーションまたは実行プログラムの名前です。<exec-name> には、フルパス名、基本名、またはパターン文字列による指定が可能です。

<advice-opts> の値は、 <region>=<advice> の書式で記述します。 <advice> の値は、MADV 環境変数の値と同じです。<region> には、次のいずれかの規定された値を指定します。

madv

プロセスのアドレス空間のすべてのヒープ、共有メモリー、および mmap(2) 領域に、アドバイス情報が適用される

heap

ヒープは、brk(2) 領域として定義される。アドバイス情報は、既存のヒープにも将来割り当てられる追加ヒープメモリーにも適用される

shm

アドバイス情報は、共有メモリーセグメントに適用される。共有メモリー操作に関する詳細は、shmat(2) を参照

ism

アドバイス情報は、SHM_SHARE_MMU フラグを使用している共有メモリーセグメントに適用される。 ism オプションは、shm より優先される

dsm

アドバイス情報は、SHM_PAGEABLE フラグを使用している共有メモリーセグメントに適用される。dsm オプションは、shm より優先される

mapshared

アドバイス情報は、MAP_SHARED フラグを使用した mmap() システムコールにより作成されたマッピングに適用される

mapprivate

アドバイス情報は、MAP_PRIVATE フラグを使用した mmap() システムコールにより作成されたマッピングに適用される

mapanon

アドバイス情報は、MAP_ANON フラグを使用した mmap() システムコールにより作成されたマッピングに適用される。複数のオプションが指定された場合は、mapanon オプションが優先される

MADVERRFILE 環境変数値は、エラーメッセージが記録されるファイル名です。MADVERRFILE による指定がない場合は、 madv.so.1 共有オブジェクトは syslog(3C) を使用してエラーを記録します。重要度は LOG_ERR 、機能記述子は LOG_USER になります。

メモリーに関するアドバイス情報は継承されます。子プロセスには親と同じアドバイス情報が適用されます。madv.so.1 共有オブジェクトにより異なるレベルのアドバイスを設定しない限り、exec(2) が呼び出された後には、アドバイスはシステムデフォルトの設定に戻されます。 アドバイス情報は、ユーザープログラムによって作成された 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_reqaddr_count パラメータに指定した値の積になる

validity

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 を返します。


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

ローカリティグループのアフィニティー

スレッドの軽量プロセス (LWP) が作成されるとき、カーネルはスレッドにローカリティグループを割り当てます。そのような lgroup は、スレッドのホーム lgroup と呼ばれます。カーネルは、スレッドをホーム lgroup の CPU で実行し、可能な限り lgroup からメモリーを割り当てます。ホーム lgroup のリソースが使用可能でない場合は、他の lgroup からリソースを割り当てます。スレッドに 1 つまたは複数の lgroup に対するアフィニティーがある場合は、オペレーティングシステムはアフィニティーの強さに応じて lgroup から順にリソースを割り当てます。アフィニティーには 3 つのレベルがあります。

  1. LGRP_AFF_STRONG は強いアフィニティーを示します。この lgroup がスレッドのホーム lgroup であるとき、オペレーティングシステムは、そのスレッドのホーム lgroup を変更するリホーミングをできるだけ避けようとします。その場合でも、動的再構成、プロセッサのオフライン化、プロセッサ割り当て、プロセッサセットの割り当ておよび操作などのイベントは、スレッドのリホーミングにつながる可能性があります。

  2. LGRP_AFF_WEAK は、弱いアフィニティーを示します。この lgroup がスレッドのホーム lgroup であるとき、オペレーティングシステムは、負荷均衡の必要に応じてスレッドのリホーミングを行います。

  3. LGRP_AFF_NONE はアフィニティーを持たないことを示します。スレッドがどの lgroup にもアフィニティーを持たないとき、オペレーティングシステムはそのスレッドにホーム lgroup を割り当てます。

指定されたスレッドにリソースを割り当てるときに、オペレーティングシステムは lgroup のアフィニティーをアドバイスとして使用します。このアドバイスは他のシステム制限とともに考慮されます。プロセッサ割り当ておよびプロセッサセットによって lgoup のアフィニティーが影響を受けることはありませんが、スレッドが実行される lgroup を制限する場合があります。

lgrp_affinity_get() の使用法

lgrp_affinity_get() 関数は、指定した lgroup に対して LWP または LWP のセットが持つアフィニティーを返します。

#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 または LWPのセットを指定します。idtype の値に P_PID を指定した場合、lgrp_affinity_get() 関数は、id 引数の値と一致するプロセス ID を持つプロセスにある LWP のいずれかについて lgroup アフィニティーを取得します。idtypeP_LWPID を指定した場合は、lgrp_affinity_get () 関数は、実行中のプロセスにある、id 引数の値と一致する LWP ID を持つ LWP について lgroup アフィニティーを取得します。idtypeP_MYID を指定すると、lgrp_affinity_get() 関数は、実行中の LPW またはプロセスについて lgroup アフィニティを取得します。

指定した lgroup、または ID タイプが有効でない場合は、lgrp_affinity_get() 関数は EINVAL を返します。呼び出しプロセスの実効ユーザーがスーパーユーザーではなく、呼び出しプロセスの ID がどの LPW の実ユーザー ID または実効ユーザー ID とも一致しない場合、lgrp_affinity_get() 関数は EPERM を返します。指定した lgroup または LPW が存在しない場合は、lgrp_affinity_get() 関数は ESRCH を返します。

lgrp_affinity_set() の使用法

lgrp_affinity_set() 関数は、指定した lgroup に対して LWP または LWP のセットが持つアフィニティーを設定します。

#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() 関数は、id 引数の値が一致する LWP ID を持つ実行プロセス中の LWP について、lgroup アフィニティーを affinity 引数で指定したアフィニティーレベルに設定します。idtypeP_MYID を指定した場合は、lgrp_affinity_set() 関数は、実行中の LWP またはプロセスについて、lgroup アフィニティーを affinity 引数で指定したアフィニティーレベルに設定します。

指定した lgroup、アフィニティー、または ID タイプが有効でないとき、lgrp_affinity_set() 関数は EINVAL を返します。呼び出しプロセスの実効ユーザーがスーパーユーザーではなく、呼び出しプロセスの ID がどの LPW の実ユーザー ID または実効ユーザー ID とも一致しない場合、lgrp_affinity_set() 関数は EPERM を返します。指定した lgroup または LPW が存在しない場合は、lgrp_affinity_set() 関数は ESRCH を返します。

API の使用例

この節では、この章で説明した API を使ったタスクを実行するコーディング例を示します。


例 4–7 スレッドへのメモリーの移動

次のコーディング例では、addr で指定されたアドレスから addr+len で指定されるアドレスまでの範囲のメモリーを、 MADV_ACCESS_LWP で指定されるスレッドに移動します。

#include <sys/mman.h>
#include <sys/types.h>

/*
 * スレッドへのメモリーの移動
 */
mem_to_thread(caddr_t addr, size_t len)
{
	if (madvise(addr, len, MADV_ACCESS_LWP) < 0)
		perror("madvise");
}


例 4–8 メモリーへのスレッドの移動

このコーディング例では、meminfo() 関数を使用して指定されたメモリーページの lgroup を取得し、lgrp_affinity_set function() 関数によって、該当する lgroup への指定されたスレッドのアフィニティーを上げます。

#include <sys/lgrp_user.h>
#include <sys/mman.h>
#include <sys/types.h>


/*
 * メモリーへのスレッドの移動
 */
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 <stdlib.h>
#include <sys/lgrp_user.h>
#include <sys/types.h>


/*
 * 指定された lgroup からそのすべての下位ノードを巡回し、
 * lgroup 階層を出力
 */
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;

	/*
	 * 指定された lgroup、呼び出しプロセスの lgroup へのアフィニティー、
	 * および目的とする内容情報の出力
	 */
	printf("LGROUP #%d:\n", lgrp);

	aff = lgrp_affinity_get(P_MYID, P_MYID, lgrp);
	if (aff == -1)
		perror ("lgrp_affinity_get");
	printf("\tAFFINITY: %d\n", aff);

	printf("CONTENT %d:\n", content);

	/*
	 * CPU 情報を取得する
	 */
	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");

	/*
	 * メモリーサイズを取得する
	 */
	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);

	/*
	 * 親 lgroup を取得する
	 */
	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");

	/*
	 * 子 lgroup を取得する
	 */
	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 <stdlib.h>
#include <sys/lgrp_user.h>
#include <sys/types.h>

#define	INT_MAX	2147483647

/*
 * 指定された lgroup 以外で利用可能なメモリーを持つ、次に最も近い lgroup の検索
 */
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;


	/*
	 * 親 lgroup の数を取得する
	 */
	nparents = lgrp_parents(cookie, from, NULL, 0);
	if (nparents == -1) {
		perror("lgrp_parents");
		return (LGRP_NONE);
	}

	/*
	 * 親 lgroup がない場合は現在の lgroup が最も近い
	 */
	if (nparents == 0) {
		return (from);
	}

	/*
	 * 親 lgroup を取得する
	 */
	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);
        }

	/*
	 * 最も近い位置の親 lgroup を検索 (最も低い応答時間を持つ親 lgroup など)
	 */
	closest = LGRP_NONE;
	lowest = INT_MAX;
	for (i = 0; i < nparents; i++) {
		lgrp_id_t	lgrp;

		/*
		 * 空きメモリー領域を持つ親 lgroup を調べる
		 */
		size = lgrp_mem_size(cookie, parents[i], LGRP_MEM_SZ_FREE,
		    LGRP_CONTENT_HIERARCHY);
		if (size> 0)
			lgrp = parents[i];
		else {
			if (size == -1)
				perror("lgrp_mem_size");

			/*
			 * 親 lgroup にメモリーがないときは、
			 * 最も近い上位ノードを検索する
			 */
			lgrp = lgrp_next_nearest(cookie, parents[i]);
			if (lgrp == LGRP_NONE)
				continue;
		}

		/*
		 * 親 lgroup の応答時間を取得する
		 */
		latency = lgrp_latency(lgrp, lgrp);
		if (latency == -1) {
			perror("lgrp_latency");
			continue;
		}

		/*
		 * 最も低い応答時間の lgroup を記憶する
		 */
		if (latency < lowest) {
			closest = lgrp;
			lowest = latency;
		}
	}

	free(parents);
	return (closest);
}


/*
 * 現在のスレッドのホーム lgroup に最も近い、メモリーのある lgroup を検索する
 */
lgrp_id_t
lgrp_nearest(lgrp_cookie_t cookie)
{
	lgrp_id_t	home;
	longlong_t	size;

	/*
	 * ホーム lgroup を取得する
	 */
	home = lgrp_home(P_LWPID, P_MYID);

	/*
	 * ホーム lgroup が自分の階層に利用可能なメモリーを持っているかどうかを調べる
	 */
	size = lgrp_mem_size(cookie, home, LGRP_MEM_SZ_FREE,
	    LGRP_CONTENT_HIERARCHY);
	if (size == -1)
		perror("lgrp_mem_size");

	/*
	 * ホーム lgroup が利用可能なメモリーを持っていればこのホーム lgroup を返す
	 */
	if (size> 0)
		return (home);

	/*
	 * そうでなければ次に近いホーム lgroup 以外の lgroup を検索する 
	 */
	return (lgrp_next_nearest(cookie, home));
}


例 4–11 空きメモリー領域を持つ最も近い lgroup の検索

このコーディング例では、指定されたスレッドのホーム lgroup に最も近い、空きメモリー領域を持つ lgroup を検索します。

#include <stdlib.h>
#include <sys/lgrp_user.h>
#include <sys/types.h>


#define	INT_MAX	2147483647


/*
 * 指定された lgroup 以外で、利用可能なメモリーを持つ次に最も近い lgroup の検索
 */
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;


	/*
	 * 親 lgroup の数を取得する
	 */
	nparents = lgrp_parents(cookie, from, NULL, 0);
	if (nparents == -1) {
		perror("lgrp_parents");
		return (LGRP_NONE);
	}

	/*
	 * 親 lgroup がない場合は、現在の lgroup が最も近い
	 */
	if (nparents == 0) {
		return (from);
	}

	/*
	 *  親 lgroup を取得する
	 */
	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);
        }

	/*
	 * 最も近い親 lgroup を検索する (最も低い応答時間を持つ)
	 */
	closest = LGRP_NONE;
	lowest = INT_MAX;
	for (i = 0; i < nparents; i++) {
		lgrp_id_t	lgrp;

		/*
		 * 親 lgroup が空きメモリーを持っているかどうか調べる
		 */
		size = lgrp_mem_size(cookie, parents[i], LGRP_MEM_SZ_FREE,
		    LGRP_CONTENT_HIERARCHY);
		if (size> 0)
			lgrp = parents[i];
		else {
			if (size == -1)
				perror("lgrp_mem_size");

			/*
			 * 親 lgroup がメモリーを持たない場合は、
			 * 最も近い上位ノードを検索する
			 */
			lgrp = lgrp_next_nearest(cookie, parents[i]);
			if (lgrp == LGRP_NONE)
				continue;
		}

		/*
		 * 親 lgroup の応答時間を取得する
		 */
		latency = lgrp_latency(lgrp, lgrp);
		if (latency == -1) {
			perror("lgrp_latency");
			continue;
		}

		/*
		 * 最も低い応答時間を持つ lgroup を記憶する
		 */
		if (latency < lowest) {
			closest = lgrp;
			lowest = latency;
		}
	}

	free(parents);
	return (closest);
}


/*
 * 現在のスレッドのホーム lgroup に最も近い、メモリーを持つ lgroup を検索する
 */
lgrp_id_t
lgrp_nearest(lgrp_cookie_t cookie)
{
	lgrp_id_t	home;
	longlong_t	size;

	/*
	 * ホーム lgroup を取得する
	 */
	home = lgrp_home(P_LWPID, P_MYID);

	/*
	 * ホーム lgroup が自分の階層に利用可能なメモリーを持っているかどうかを調べる
	 */
	size = lgrp_mem_size(cookie, home, LGRP_MEM_SZ_FREE,
	    LGRP_CONTENT_HIERARCHY);
	if (size == -1)
		perror("lgrp_mem_size");

	/*
	 * ホーム lgroup が利用可能なメモリーを持っていればこのホーム lgroup を返す
	 */
	if (size> 0)
		return (home);

	/*
	 * そうでなければ次に近いホーム lgroup 以外の lgroup を調べる
	 */
	return (lgrp_next_nearest(cookie, home));
}