TLS は、プログラムの存続中に、次の 3 つの機会に作成されます。
プログラムの起動時。
新しいスレッドの作成時。
プログラムの起動に続いて共有オブジェクトが読み込まれたあと、スレッドがはじめて TLS ブロックを参照するとき。
スレッド固有データストレージは、図 18 に示すように実行時に配置されます。
図 18 スレッド固有ストレージの実行時のレイアウト
プログラムの起動時に、実行システムはメインスレッド用の TLS を作成します。
まず実行時リンカーが、読み込まれたすべての動的オブジェクト (動的な実行可能ファイルを含む) の TLS テンプレートを論理的に結合し、単一の静的なテンプレートとしてまとめます。各動的オブジェクトの TLS テンプレートには、結合されたテンプレート内のオフセット tlsoffsetm が次のように割り当てられます。
tlsoffset1 = round(tlssize1, align1 )
tlsoffsetm+1 = round(tlsoffsetm + tlssizem+1, alignm+1)
tlssizem+1 は動的オブジェクト m の割り当てテンプレートのサイズで、 alignm+1 は整列です。ここで、1 <= m <= M であり、M は読み込まれる動的オブジェクトの合計数です。round(offset, align) 関数は、align の次の倍数に丸められたオフセットを返します。
次に、実行時リンカーは、起動時の TLS に必要な割り当てサイズの tlssizeS を計算します。このサイズは、tlsoffset M に 512 バイトを加えた値に等しくなります。この加算により、静的な TLS 参照のバックアップ予約が得られます。静的な TLS 参照を作成し、プロセスの初期化後に読み込まれる共有オブジェクトは、このバックアップ予約に割り当てられます。ただし、この予約のサイズは固定および限定されています。また、この予約が対応しているのは、初期化されていない TLS データ項目用のストレージの提供だけです。柔軟性を最大限高めるため、動的な TLS モデルを使用して、共有オブジェクトがスレッドローカル変数を参照するようにしてください。
計算された TLS サイズ tlssizeS に関連付けられる静的な TLS 領域は、スレッドポインタ tpt の直前に配置されます。TLS データに対するアクセスは、tpt からの減算にもとづいて行われます。
静的な TLS 領域は、初期化レコードのリンクリストに関連付けられます。このリスト内の各レコードは、読み込まれた動的オブジェクトごとにその TLS 初期化イメージを記述するものです。各レコードには、次のフィールドが含まれています。
TLS 初期化イメージを指すポインタ。
TLS 初期化イメージのサイズ。
オブジェクトの tlsoffsetm。
オブジェクトが静的な TLS モデルを使用するかどうかを示すフラグ。
スレッドライブラリは、この情報を使用して初期スレッドにストレージを割り当てます。このストレージが初期化され、初期スレッド用に動的な TLS ベクトルが作成されます。
初期スレッドと、新しく作成されるスレッドに対して、スレッドライブラリは読み込まれる動的オブジェクトごとに新しい TLS ブロックを割り当てます。ブロックは、個別に割り当てられることも、単一の連続ブロックとして割り当てられることもあります。
各スレッド t は関連するスレッドポインタ tpt を持ち、このポインタはスレッド制御ブロック TCB を指します。スレッドポインタ tp には、常に、現在動作しているスレッドの tpt の値が含まれます。
続いてスレッドライブラリは、現在のスレッド t のためにポインタのベクトル dtvt を作成します。各ベクトルの最初の要素には、ベクトルの拡張が必要なタイミングを決定するために使用される生成番号 gent が入ります。スレッド固有ストレージブロックの遅延割り当てを参照してください。
ベクトル内の残りの各要素 dtvt,m は、動的オブジェクト m に属する TLS 用に予約されたブロックへのポインタです。
起動後動的に読み込まれたオブジェクトについては、スレッドライブラリは TLS ブロックの割り当てを延期します。割り当ては、読み込まれたオブジェクト内で TLS 変数に対して最初の参照が行われる時に発生します。割り当てが延期されたブロックの場合、ポインタ dtvt,m は実装が定める特別な値に設定されます。
続いて、スレッドライブラリが、新しいストレージブロック内の対応する場所に初期化イメージをコピーします。
動的な TLS だけが含まれる共有オブジェクトは、プロセスの起動に続いて無制限に読み込むことができます。実行時リンカーは、初期化レコードのリストを拡張して新しいオブジェクトの初期化テンプレートを含めます。新しいオブジェクトには、インデックス m = M + 1 が与えられます。カウンタ M は 1 ずつ増えていきます。しかし、新しい TLS ブロックの割り当ては、それらが実際に参照されるまで延期されます。
動的な TLS だけが含まれる共有オブジェクトの読み込みが解除されると、その共有オブジェクトが使用している TLS ブロックは解放されます。
静的な TLS が含まれる共有オブジェクトは、プロセスの起動に続いて一定の制限内で読み込むことができます。静的な TLS 参照を満たすことができるのは、残りのバックアップ TLS 予約からだけです。プログラムの起動を参照してください。この予約のサイズは制限されています。また、この予約で提供できるのは、初期化されていない TLS データ項目用のストレージだけです。
静的な TLS を含む共有オブジェクトは、読み込み解除されません。静的な TLS 処理の結果として、共有オブジェクトには削除不可のタグが付けられます。
動的な TLS モデルでは、スレッド t がオブジェクト m の TLS ブロックにアクセスする必要が生じた場合、コードは dtv t を更新し、TLS ブロックの初期割り当てを行います。スレッドライブラリは、動的な TLS 割り当てが行えるように、次のインタフェースを提供します。
typedef struct { unsigned long ti_moduleid; unsigned long ti_tlsoffset; } TLS_index; extern void *__tls_get_addr(TLS_index *ti); (SPARC and x64) extern void *___tls_get_addr(TLS_index *ti); (32–bit x86)
tls_get_addr() の両バージョンとも、スレッドごとの生成カウンタ gent を調べ、ベクトルが更新を必要としていないかを確認します。ベクトル dtvt が古い場合、ルーチンがベクトルを更新し、必要に応じ、追加エントリ用のスペースを確保するため再割り当てを行います。続いてこのルーチンは、dtvt,m に対応する TLS ブロックがすでに割り当てられているかを調べます。ベクトルが割り当てられていない場合、このルーチンはブロックの割 り当てと初期化を行います。このルーチンは、実行時リンカーが提供する初期化レコードリスト内の情報を使用します。ポインタ dtv t,m は、割り当てられたブロックを指すように設定されます。ルーチンは、ブロック内の指定されたオフセットへのポインタを返します。