この節では、各 lgroup のスレッドとメモリー配置を検出し、制御するために使用する API について説明します。
lgrp_home(3LGRP) 関数は、スレッドの配置を検出するために使用します。
メモリー配置の検出には、meminfo(2) システムコールを使用します。
lgroup 間でのメモリー配置を制御するには、madvise(3C) 関数に MADV_ACCESS フラグを設定して使用します。
lgrp_affinity_set(3LGRP) 関数では、指定した 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 が、ランダムにシステム全域から指定の領域にアクセスしていることをカーネルに指定します。それに応じて、カーネルはメモリーやほかの資源をこの領域に割り当てます。
madvise() 関数は、次の値を返します。
addr から addr+len までのアドレス範囲で指定されたマッピング領域の全体または一部が、入出力操作によりロックされている場合。
addr パラメータの値が sysconf(3C) で返されるページサイズの倍数ではない、指定したアドレス範囲の長さがゼロ以下、またはアドバイスが無効の場合。
ファイルシステムに対する読み取りや書き込み中に入出力エラーが発生した場合。
指定したアドレス範囲のアドレスが、プロセスの有効なアドレス空間の範囲外、またはマップされていないページが指定されている場合。
NFS ファイルハンドルが無効の場合。
共有オブジェクト madv.so.1 は、起動されたプロセスやその子プロセスに対して選択された仮想メモリーの構成を実現します。共有オブジェクトを使用するには、環境変数に次の文字列を指定する必要があります。
LD_PRELOAD=$LD_PRELOAD:madv.so.1
madv.so.1 共有オブジェクトは、 MADV 環境変数の値に従ってメモリーのアドバイス情報を適用します。MADV 環境変数は、プロセスのアドレス空間におけるすべてのヒープ、共有メモリー、および mmap 領域のために使用する仮想メモリーのアドバイス情報を指定します。この情報は生成されたすべてのプロセスに適用されます。次に示す MADV 環境変数値は、複数の lgroup 間での資源の割り当てに影響を与えます。
この値は、カーネルに期待されるアクセスパターンをデフォルトに戻します。
この値は、アドレス範囲に次回アクセスする LWP が、その領域にもっとも頻繁にアクセスする LPW であることをカーネルに指定します。それに応じて、カーネルはメモリーやほかの資源をこの領域と LWP に割り当てます。
この値は、多くのプロセスまたは LPW が、ランダムにシステム全域からメモリーにアクセスしていることをカーネルに指定します。それに応じて、カーネルはメモリーやほかの資源を割り当てます。
MADVCFGFILE 環境変数の値は、1 つまたは複数のメモリーのアドバイス情報構成エントリが 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 から順に資源を割り当てます。lgroup は、次の 3 つのアフィニティーレベルのうち 1 つを持つことができます。
LGRP_AFF_STRONG は強いアフィニティーを示します。この lgroup がスレッドのホーム lgroup であるとき、オペレーティングシステムは、そのスレッドのホーム lgroup を変更する再ホーミングをできるだけ避けようとします。その場合でも、動的再構成、プロセッサのオフライン化、プロセッサ割り当て、プロセッサセットの割り当ておよび操作などのイベントは、スレッドの再ホーミングにつながる可能性があります。
LGRP_AFF_WEAK は、弱いアフィニティーを示します。この lgroup がスレッドのホーム lgroup であるとき、オペレーティングシステムは、負荷均衡の必要に応じてスレッドの再ホーミングを行います。
LGRP_AFF_NONE はアフィニティーを持たないことを示します。スレッドがどの lgroup にもアフィニティーを持たないとき、オペレーティングシステムはそのスレッドにホーム lgroup を割り当てます。
指定されたスレッドに資源を割り当てるときに、オペレーティングシステムは lgroup のアフィニティーをアドバイスとして使用します。このアドバイスはほかのシステム制限とともに考慮されます。プロセッサ割り当ておよびプロセッサセットによって lgroup のアフィニティーが影響を受けることはありませんが、スレッドが実行される lgroup を制限する場合があります。
lgrp_affinity_get(3LGRP) 関数は、指定した lgroup に対して 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 を指定します。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(3LGRP) 関数は、指定した 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 を返します。