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

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

この節では、各 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 を返します。