この節では、各 lgroup のスレッドとメモリー配置を検出し、制御するために使用する API について説明します。lgrp_home() 関数は、スレッドの配置を検出するために使用します。メモリー配置の検出には、meminfo (2) システムコールを使用します。lgroup 間でのメモリー配置を制御するには、madvise (3C) 関数に MADV_ACCESS フラグを設定して使用します。lgrp_affinity_set() 関数では、指定した lgroup のスレッドのアフィニティーを設定することにより、スレッドとメモリー配置を制御できます。ある 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 が、その領域に最も頻繁にアクセスする LPW であることをカーネルに指定する。それに応じて、カーネルはメモリーや他のリソースをこの領域と LWP に割り当てる
このフラグは、多くのプロセスまたは 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 は、起動されたプロセスやその子プロセスに対して選択された仮想メモリーの構成を実現します。共有オブジェクトを使用するには、環境変数に次の文字列を指定する必要があります。
LD_PRELOAD=$LD_PRELOAD:madv.so.1
madv.so.1 共有オブジェクトは、 MADV 環境変数の値に従ってメモリーのアドバイス情報を適用します。MADV 環境変数は、プロセスのアドレス空間におけるすべてのヒープ、共有メモリー、および mmap 領域のために使用する仮想メモリーのアドバイス情報を指定します。この情報は生成されたすべてのプロセスに適用されます。次に示す MADV 環境変数値は、複数の lgroup 間でのリソースの割り当てに影響を与えます。
この値は、カーネルに期待されるアクセスパターンをデフォルトに戻す
この値は、アドレス範囲に次回アクセスする LWP が、その領域に最も頻繁にアクセスする LPW であることをカーネルに指定する。それに応じて、カーネルはメモリーや他のリソースをこの領域と LWP に割り当てる
この値は、多くのプロセスまたは LPW が、ランダムにシステム全域からメモリーにアクセスしていることをカーネルに指定する。それに応じて、カーネルはメモリーや他のリソースを割り当てる
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 より優先される
アドバイス情報は、MAP_SHARED フラグを使用した mmap() システムコールにより作成されたマッピングに適用される
アドバイス情報は、MAP_PRIVATE フラグを使用した mmap() システムコールにより作成されたマッピングに適用される
アドバイス情報は、MAP_ANON フラグを使用した mmap() システムコールにより作成されたマッピングに適用される。複数のオプションが指定された場合は、mapanon オプションが優先される
MADVERRFILE 環境変数値は、エラーメッセージが記録されるファイル名です。MADVERRFILE による指定がない場合は、 madv.so.1 共有オブジェクトは syslog(3C) を使用してエラーを記録します。重要度は LOG_ERR 、機能記述子は LOG_USER になります。
メモリーに関するアドバイス情報は継承されます。子プロセスには親と同じアドバイス情報が適用されます。madv.so.1 共有オブジェクトにより異なるレベルのアドバイスを設定しない限り、exec(2) が呼び出された後には、アドバイスはシステムデフォルトの設定に戻されます。 アドバイス情報は、ユーザープログラムによって作成された 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); } } }
スレッドの軽量プロセス (LWP) が作成されるとき、カーネルはスレッドにローカリティグループを割り当てます。そのような lgroup は、スレッドのホーム lgroup と呼ばれます。カーネルは、スレッドをホーム lgroup の CPU で実行し、可能な限り lgroup からメモリーを割り当てます。ホーム lgroup のリソースが使用可能でない場合は、他の lgroup からリソースを割り当てます。スレッドに 1 つまたは複数の lgroup に対するアフィニティーがある場合は、オペレーティングシステムはアフィニティーの強さに応じて lgroup から順にリソースを割り当てます。アフィニティーには 3 つのレベルがあります。
LGRP_AFF_STRONG は強いアフィニティーを示します。この lgroup がスレッドのホーム lgroup であるとき、オペレーティングシステムは、そのスレッドのホーム lgroup を変更するリホーミングをできるだけ避けようとします。その場合でも、動的再構成、プロセッサのオフライン化、プロセッサ割り当て、プロセッサセットの割り当ておよび操作などのイベントは、スレッドのリホーミングにつながる可能性があります。
LGRP_AFF_WEAK は、弱いアフィニティーを示します。この lgroup がスレッドのホーム lgroup であるとき、オペレーティングシステムは、負荷均衡の必要に応じてスレッドのリホーミングを行います。
LGRP_AFF_NONE はアフィニティーを持たないことを示します。スレッドがどの lgroup にもアフィニティーを持たないとき、オペレーティングシステムはそのスレッドにホーム lgroup を割り当てます。
指定されたスレッドにリソースを割り当てるときに、オペレーティングシステムは lgroup のアフィニティーをアドバイスとして使用します。このアドバイスは他のシステム制限とともに考慮されます。プロセッサ割り当ておよびプロセッサセットによって lgoup のアフィニティーが影響を受けることはありませんが、スレッドが実行される lgroup を制限する場合があります。
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 アフィニティーを取得します。idtype に P_LWPID を指定した場合は、lgrp_affinity_get () 関数は、実行中のプロセスにある、id 引数の値と一致する LWP ID を持つ LWP について lgroup アフィニティーを取得します。idtype に P_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() 関数は、指定した 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 引数で指定したアフィニティーレベルに設定します。idtype に P_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 を返します。