マルチスレッドのプログラミング

スタックについて

通常、スレッドスタックはページ境界で始まり、指定した大きさは次のページ境界まで切り上げられます。アクセス権のないページがスタックの一番上に付加されることにより、ほとんどのスタックオーバーフローで、違反したスレッドに SIGSEGV シグナルが送られるようになります。呼び出し側によって割り当てられるスレッドスタックは、そのまま使われます。

スタックを指定するときは、スレッドを PTHREAD_CREATE_JOINABLE として生成してください。このスタックは、そのスレッドに対する pthread_join(3T) 呼び出しが戻るまで解放できません。これは、そのスレッドのスタックは、そのスレッドが終了するまで解放できないからです。スレッドが終了したかどうかを確実に知るには、pthread_join(3T) を使用してください。

通常、スレッド用にスタック空間を割り当てる必要はありません。スレッドライブラリが、各スレッドのスタックとして 1M バイトの仮想記憶を割り当てます。このときスワップ空間は確保されません。(このライブラリは、mmap()MAP_NORESERVE オプションを使って割り当てを行います。)

スレッドライブラリで生成される各スレッドスタックには、レッドゾーンがあります。スレッドライブラリはレッドゾーンとして、スタックオーバーフローを補足するためのページをスタックの一番上に付加します。このページは無効で、アクセスされるとメモリーフォルトになります。レッドゾーンは、自動的に割り当てられるすべてのスタックに付加されます。これは、その大きさがアプリケーションで指定されたかデフォルトの大きさかに関係なく行われます。


注 -

実行時のスタック要件は一定ではないので、指定したスタックがライブラリの呼び出しと動的リンクに必要な実行時要件を確実に満足するようにしなければなりません。


スタックとスタックの大きさの一方または両方を指定するのが適正であることはほとんどありません。専門家であっても、適切な大きさを指定したかどうかを判断するのは困難です。これは、ABI 準拠のプログラムでもスタックの大きさを静的に判定できないからです。スタックの大きさは、プログラムが実行される、それぞれの実行環境に左右されます。

独自のスタックを構築する

スレッドスタックの大きさを指定するときは、呼び出される関数に必要な割り当てを計算してください。これには、呼び出し手続きで必要とされる量、局所変数、情報構造体が含まれます。

デフォルトスタックと少し違うスタックが必要になることがあります。たとえば、スレッドで 1M バイトを超えるスタック空間が必要になる場合です。また、少し分かりにくいケースですが、デフォルトスタックが大きすぎる場合もあります。何千ものスレッドを生成するとすれば、デフォルトスタックでは合計サイズが数 G バイトにもなるため、仮想メモリが足りず、それだけのスタック空間を扱えないかもしれないからです。

スタックの大きさの上限は明らかであることが多いのですが、下限はどうでしょうか。スタックにプッシュされるスタックフレームを、その局所変数などを含めて、すべて扱えるだけのスタック空間が必要です。

マクロ PTHREAD_STACK_MIN を呼び出すと、スタックの大きさの絶対最小値が得られます。このマクロは、NULL 手続きを実行するスレッドに必要なスタック空間の大きさを戻します。実用的なスレッドに必要なスタック空間はもっと大きいので、スタックサイズを小さくするときは十分注意してください。


#include <pthread.h>

pthread_attr_t tattr;
pthread_t tid;
int ret;

int size = PTHREAD_STACK_MIN + 0x4000;

/* デフォルト属性で初期化する */
ret = pthread_attr_init(&tattr);

/* スタックの大きさも設定する */
ret = pthread_attr_setstacksize(&tattr, size);

/* tattr に大きさのみを指定する */
ret = pthread_create(&tid, &tattr, start_routine, arg); 

独自のスタックを割り当てるときは、その終わりにレッドゾーンを付加するために必ず mprotect(2) を呼び出してください。