コグノスケ


link 未来から過去へ表示(*)  link 過去から未来へ表示

link もっと前
2022年4月20日 >>> 2022年4月11日
link もっと後

2022年4月20日

glibcのスレッドローカルストレージ - スレッドポインタ編

目次: C言語とlibc

前回はスレッドローカル変数のアドレス取得部分のコードを見て、tp(スレッドポインタ)レジスタが重要な役割をしていることがわかりました。今回はtpレジスタの初期化コードを見ます。

tpレジスタの初期化

レジスタの初期化は __libc_setup_tls() で行われます。少し長いですがコードを見ましょう。

TLSのメモリ領域確保とtpレジスタ初期化

// glibc/sysdeps/riscv/nptl/tls.h

/* The TP points to the start of the thread blocks.  */
# define TLS_DTV_AT_TP	1
# define TLS_TCB_AT_TP	0


// glibc/csu/libc-tls.c

void
__libc_setup_tls (void)
{
  void *tlsblock;

//...

  /* We have to set up the TCB block which also (possibly) contains
     'errno'.  Therefore we avoid 'malloc' which might touch 'errno'.
     Instead we use 'sbrk' which would only uses 'errno' if it fails.
     In this case we are right away out of memory and the user gets
     what she/he deserves.  */
#if TLS_TCB_AT_TP
  //...
#elif TLS_DTV_AT_TP    //★★RISC-Vではこちらのコードが使われる★★
  tcb_offset = roundup (TLS_INIT_TCB_SIZE, align ?: 1);
  tlsblock = __sbrk (tcb_offset + memsz + max_align
		     + TLS_PRE_TCB_SIZE + GLRO(dl_tls_static_surplus));
  tlsblock += TLS_PRE_TCB_SIZE;
#else
  /* In case a model with a different layout for the TCB and DTV
     is defined add another #elif here and in the following #ifs.  */
# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
#endif

//...

  /* Initialize the thread pointer.  */
#if TLS_TCB_AT_TP
  //...
#elif TLS_DTV_AT_TP    //★★RISC-Vではこちらのコードが使われる★★
  INSTALL_DTV (tlsblock, _dl_static_dtv);
  const char *lossage = TLS_INIT_TP (tlsblock);    //★★tpレジスタ初期化★★
#else
# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
#endif


// glibc/sysdeps/riscv/nptl/tls.h

register void *__thread_self asm ("tp");
# define READ_THREAD_POINTER() ({ __thread_self; })

//...

/* Code to initially initialize the thread pointer.  */
# define TLS_INIT_TP(tcbp) \
  ({ __thread_self = (char*)tcbp + TLS_TCB_OFFSET; NULL; })


// glibc/sysdeps/riscv/nptl/tls.h

/* The thread pointer tp points to the end of the TCB.
   The pthread_descr structure is immediately in front of the TCB.  */
# define TLS_TCB_OFFSET	0

メモリ領域を__sbrk() で確保してアドレスをtpに格納しています。#ifdefで多少見づらいですが、そんなに難しくないはずです。

今まではTLSに含まれるスレッドローカル変数のうち、実行時に値を初期化する変数について見てきました。スレッドローカル変数は実行時に初期化するだけではなく、起動時に初期化される変数もあります。起動時に初期化される変数は、実行ファイルに初期値が含まれていてTLSの初期化時に値がコピーされる仕組みです。

次回はTLSの初期化を見たいと思います。

編集者:すずき(2022/04/22 15:43)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2022年4月18日

glibcのスレッドローカルストレージ - 変数アドレス編

目次: C言語とlibc

前回はスレッドローカル変数宣言のコードを見ました。今回は変数のアドレス取得のコードを見ます。

スレッドローカル変数のアドレス取得

初期化の際はスレッドローカル変数のアドレスを取得する必要があります。アドレスの取得には __libc_tsd_address() というマクロを使います。

スレッドローカル変数の初期化

// glibc/ctype/ctype-info.c

void
__ctype_init (void)
{
  const uint16_t **bp = __libc_tsd_address (const uint16_t *, CTYPE_B);
  *bp = (const uint16_t *) _NL_CURRENT (LC_CTYPE, _NL_CTYPE_CLASS) + 128;
  const int32_t **up = __libc_tsd_address (const int32_t *, CTYPE_TOUPPER);
  *up = ((int32_t *) _NL_CURRENT (LC_CTYPE, _NL_CTYPE_TOUPPER) + 128);
  const int32_t **lp = __libc_tsd_address (const int32_t *, CTYPE_TOLOWER);
  *lp = ((int32_t *) _NL_CURRENT (LC_CTYPE, _NL_CTYPE_TOLOWER) + 128);
}
libc_hidden_def (__ctype_init)


// glibc/sysdeps/generic/libc-tsd.h

#define __libc_tsd_address(TYPE, KEY)		(&__libc_tsd_##KEY)
#define __libc_tsd_get(TYPE, KEY)		(__libc_tsd_##KEY)
#define __libc_tsd_set(TYPE, KEY, VALUE)	(__libc_tsd_##KEY = (VALUE))

先ほど宣言した変数のアドレスを返すだけの単純なコードです(例えばKEYがCTYPE_Bなら &__libc_tsd_CTYPE_Bを返す)。

スレッドローカル変数のアドレス取得、詳細

実際はどのようにアドレスを得るのでしょう?下記のようにコードを書き換えて、

改造後のコード(スレッドローカル変数にNULLを代入)

// glibc/ctype/ctype-info.c

void
__ctype_init (void)
{
  const uint16_t **bp = __libc_tsd_address (const uint16_t *, CTYPE_B);
  *bp = NULL;
}
libc_hidden_def (__ctype_init)

空のmain関数のみのコードをスタティックリンクでコンパイルし、実行ファイルを逆アセンブルします。

改造後のコード、逆アセンブル結果

000000000002c0c6 <__ctype_init>:

void
__ctype_init (void)
{
  const uint16_t **bp = __libc_tsd_address (const uint16_t *, CTYPE_B);
  *bp = NULL;
   2c0c6:       00045797                auipc   a5,0x45
   2c0ca:       7227b783                ld      a5,1826(a5) # 717e8 <_GLOBAL_OFFSET_TABLE_+0x70>
   2c0ce:       9792                    add     a5,a5,tp
   2c0d0:       0007b023                sd      zero,0(a5)
}
   2c0d4:       8082                    ret

逆アセンブルを見ると、GOTのエントリからロードしたオフセット+tpレジスタの値 = アドレス、という実装になっています。RISC-Vにおいてtpレジスタは「スレッドポインタ」レジスタといって、まさにスレッドローカルストレージのためのレジスタです。

次回はスレッドポインタがいつどこで初期化されるかを見たいと思います。

編集者:すずき(2022/04/22 15:44)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2022年4月15日

glibcのスレッドローカルストレージ - 変数宣言編

目次: C言語とlibc

スレッドごとに独立したデータを保持する空間をTLS(Thread Local Storage)といいます。日本語だと「スレッド局所記憶」というそうです。TLSへのアクセス方法、TLSの初期化方法などはアーキテクチャ依存ですので、今回はglibcのRISC-V版の実装を観察します。

スレッドローカル変数の宣言

変数宣言は __libc_tsd_define() マクロを使います。例として文字処理に使われるctype関連の変数を見ます。

ctype関連のスレッドローカル変数宣言

// glibc/ctype/ctype-info.c

__libc_tsd_define (, const uint16_t *, CTYPE_B)
__libc_tsd_define (, const int32_t *, CTYPE_TOLOWER)
__libc_tsd_define (, const int32_t *, CTYPE_TOUPPER)


// glibc/sysdeps/generic/libc-tsd.h

#define __libc_tsd_define(CLASS, TYPE, KEY)	\
  CLASS __thread TYPE __libc_tsd_##KEY attribute_tls_model_ie;


// glibc/include/libc-symbols.h

#define attribute_tls_model_ie __attribute__ ((tls_model ("initial-exec")))

マクロは下記のように展開されます。

スレッドローカル変数宣言マクロの展開例

// glibc/ctype/ctype-info.c

__libc_tsd_define (, const uint16_t *, CTYPE_B)
↓
__thread const uint16_t * __libc_tsd_CTYPE_B __attribute__ ((tls_model ("initial-exec")));


// glibc/include/ctype.h

__libc_tsd_define (extern, const uint16_t *, CTYPE_B)
↓
extern __thread const uint16_t * __libc_tsd_CTYPE_B __attribute__ ((tls_model ("initial-exec")));

ごちゃごちゃして見えますが、普通の変数宣言との違いは__thread指定子とtls_model("initial-exec") 属性の2つです。

スレッドローカル変数の宣言、詳細

これだけだと「そうですか」で終わってしまうので、もう少し詳しく調べます。

  • __thread: スレッドローカル変数であることを示します。
  • tls_model("initial-exec"): スレッドローカル変数のモデル、実行可能ファイルから参照可能な変数とします。

スレッドローカル変数のモデルは、Common Variable Attributes - Using the GNU Compiler Collection (GCC) を見る限り4種類あるようです。

  • global-dynamic
  • local-dynamic
  • initial-exec
  • local-exec

それぞれの意味は悲しいことにGCCのマニュアルに書いていないのです。どうして……。参考になるマニュアルとしては、-ftls-model (-qtls) - XL C/C++ for Linux - IBM Documentation もしくは スレッド固有ストレージのアクセスモデル - Oracle Solaris 11.1リンカーとライブラリガイドがわかりやすいです。

global-dynamic
すべてのスレッドローカル変数を参照できます。
local-dynamic
共有ライブラリのスレッドローカル変数を参照できますが、モジュール外からの参照は不可能です。global-dynamicより速いとのこと。
initial-exec
実行可能ファイルから共有ライブラリのスレッドローカル変数を参照できますが、実行可能ファイルと同時にロードされている必要があります(dlopenでロードしたライブラリは不可)。local-dynamicより速いとのこと。
local-exec
実行可能ファイル内のスレッドローカル変数のみ参照できます。initial-execより速いとのこと。

Solarisのリンカーのモデル名はGCCと少し違いますが、一見して対応がわかる程度の差でしょう。

次回は変数のアドレス取得について見たいと思います。

編集者:すずき(2022/04/22 03:01)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2022年4月13日

C言語とlibc - まとめリンク

目次: C言語とlibc

C言語について。

Cライブラリ(libc)について。

編集者:すずき(2024/01/13 17:22)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link もっと前
2022年4月20日 >>> 2022年4月11日
link もっと後

管理用メニュー

link 記事を新規作成

<2022>
<<<04>>>
-----12
3456789
10111213141516
17181920212223
24252627282930

最近のコメント5件

  • link 24年4月22日
    hdkさん (04/24 08:36)
    「うちのHHFZ4310は15年突破しまし...」
  • link 24年4月22日
    すずきさん (04/24 00:37)
    「ちゃんと数えてないですけど蛍光管が10年...」
  • link 24年4月22日
    hdkさん (04/23 20:52)
    「おお... うちのHHFZ4310より後...」
  • link 20年6月19日
    すずきさん (04/06 22:54)
    「ディレクトリを予め作成しておけば良いです...」
  • link 20年6月19日
    斎藤さん (04/06 16:25)
    「「Preferencesというメニューか...」

最近の記事3件

  • link 24年4月25日
    すずき (04/26 16:49)
    「[AVIFの変換] AVIFが読めないアプリケーションがたまにあるので、AVIF(AV1 Image File Format)...」
  • link 24年2月7日
    すずき (04/24 02:52)
    「[複数の音声ファイルのラウドネスを統一したい] PCやデジタル音楽プレーヤーで音楽を聞いていると、曲によって音量の大小が激しく...」
  • link 24年4月22日
    すずき (04/23 20:13)
    「[仕事部屋の照明が壊れた] いきなり仕事部屋のシーリングライトが消えました。蛍光管の寿命にしては去年(2022年10月19日の...」
link もっとみる

こんてんつ

open/close wiki
open/close Linux JM
open/close Java API

過去の日記

open/close 2002年
open/close 2003年
open/close 2004年
open/close 2005年
open/close 2006年
open/close 2007年
open/close 2008年
open/close 2009年
open/close 2010年
open/close 2011年
open/close 2012年
open/close 2013年
open/close 2014年
open/close 2015年
open/close 2016年
open/close 2017年
open/close 2018年
open/close 2019年
open/close 2020年
open/close 2021年
open/close 2022年
open/close 2023年
open/close 2024年
open/close 過去日記について

その他の情報

open/close アクセス統計
open/close サーバ一覧
open/close サイトの情報

合計:  counter total
本日:  counter today

link About www.katsuster.net
RDFファイル RSS 1.0

最終更新: 04/26 16:49