glibcのTunablesのパラメータを静観する

# はじめに
glibcにはパラメータチューニングのために設定値を変更することのできる Tunables という仕組みを持っている。この機能の概要について述べる。なお、glibc-2.40 のコードを見つつ、Ubuntu-24.04の環境(Linux-6.8 based、glibc-2.39 based)で動作させている。暗黙にLinux前提での話になってしまっている点についてはご了承いただきたい。

# Tunablesの概要
### 概要
Tunablesはディストリビューションやユーザによってglibcをカスタマイズすることのできるパラメータを統一的に扱うための仕組みとなっている。モノによっては専用の環境変数や[**mallopt(3)**](https://man7.org/linux/man-pages/man3/mallopt.3.html)で変更することもできるが、glibc実装者がいろんな設定の入口を意識しなくても済むような統一的な仕組みを提供している。扱うパラメータの特性上、セキュリティに直結するものもあるので慎重に取り扱う必要があり、現に最近[CVE-2023-4911](https://jvndb.jvn.jp/ja/contents/2023/JVNDB-2023-013913.html)でお騒がせをしたりなどしている。っという感じの内容が[LWN.net](https://lwn.net/Articles/947736/)のサイトに書いてあった。

### パラメータの確認方法
具体的にどんなパラメータを持っているのかについては、ld.soを実行することで確認できる。[glibcのマニュアルに記載](https://www.gnu.org/software/libc/manual/html_node/Tunables.html)されている。

```bash
[rarul@tina ~]$ /lib64/ld-linux-x86-64.so.2 --list-tunables |sort
glibc.cpu.hwcap_mask: 0x6 (min: 0x0, max: 0xffffffffffffffff)
glibc.cpu.hwcaps:
glibc.cpu.plt_rewrite: 0 (min: 0, max: 2)
glibc.cpu.prefer_map_32bit_exec: 0 (min: 0, max: 1)
glibc.cpu.x86_data_cache_size: 0x8000 (min: 0x0, max: 0xffffffffffffffff)
glibc.cpu.x86_ibt:
glibc.cpu.x86_non_temporal_threshold: 0x300000 (min: 0x4040, max: 0xfffffffffffffff)
glibc.cpu.x86_rep_movsb_threshold: 0x2000 (min: 0x100, max: 0xffffffffffffffff)
glibc.cpu.x86_rep_stosb_threshold: 0x800 (min: 0x1, max: 0xffffffffffffffff)
glibc.cpu.x86_shared_cache_size: 0x600000 (min: 0x0, max: 0xffffffffffffffff)
glibc.cpu.x86_shstk:
glibc.elision.enable: 0 (min: 0, max: 1)
glibc.elision.skip_lock_after_retries: 3 (min: 0, max: 2147483647)
glibc.elision.skip_lock_busy: 3 (min: 0, max: 2147483647)
glibc.elision.skip_lock_internal_abort: 3 (min: 0, max: 2147483647)
glibc.elision.skip_trylock_internal_abort: 3 (min: 0, max: 2147483647)
glibc.elision.tries: 3 (min: 0, max: 2147483647)
glibc.gmon.maxarcs: 1048576 (min: 50, max: 2147483647)
glibc.gmon.minarcs: 50 (min: 50, max: 2147483647)
glibc.malloc.arena_max: 0x0 (min: 0x1, max: 0xffffffffffffffff)
glibc.malloc.arena_test: 0x0 (min: 0x1, max: 0xffffffffffffffff)
glibc.malloc.check: 0 (min: 0, max: 3)
glibc.malloc.hugetlb: 0x0 (min: 0x0, max: 0xffffffffffffffff)
glibc.malloc.mmap_max: 0 (min: 0, max: 2147483647)
glibc.malloc.mmap_threshold: 0x0 (min: 0x0, max: 0xffffffffffffffff)
glibc.malloc.mxfast: 0x0 (min: 0x0, max: 0xffffffffffffffff)
glibc.malloc.perturb: 0 (min: 0, max: 255)
glibc.malloc.tcache_count: 0x0 (min: 0x0, max: 0xffffffffffffffff)
glibc.malloc.tcache_max: 0x0 (min: 0x0, max: 0xffffffffffffffff)
glibc.malloc.tcache_unsorted_limit: 0x0 (min: 0x0, max: 0xffffffffffffffff)
glibc.malloc.top_pad: 0x20000 (min: 0x0, max: 0xffffffffffffffff)
glibc.malloc.trim_threshold: 0x0 (min: 0x0, max: 0xffffffffffffffff)
glibc.mem.decorate_maps: 0 (min: 0, max: 1)
glibc.mem.tagging: 0 (min: 0, max: 255)
glibc.pthread.mutex_spin_count: 100 (min: 0, max: 32767)
glibc.pthread.rseq: 1 (min: 0, max: 1)
glibc.pthread.stack_cache_size: 0x2800000 (min: 0x0, max: 0xffffffffffffffff)
glibc.pthread.stack_hugetlb: 1 (min: 0, max: 1)AT_SYSINFO
glibc.rtld.dynamic_sort: 2 (min: 1, max: 2)
glibc.rtld.nns: 0x4 (min: 0x1, max: 0x10)
glibc.rtld.optional_static_tls: 0x200 (min: 0x0, max: 0xffffffffffffffff)
```

### 環境変数から設定する
プロセス起動時に環境変数からTunablesパラメータを変更することができる。これも[glibcのマニュアルに記載](https://www.gnu.org/software/libc/manual/html_node/Tunables.html)に記載されている。

```bash
GLIBC_TUNABLES=glibc.malloc.trim_threshold=128:glibc.malloc.check=3
export GLIBC_TUNABLES
```

んー、本当に設定されたかどうかをユーザプログラム内から確認する方法が見当たらんぞ？


### glibcのソースコードの箇所
Tunablesのパラメータの定義は.listファイルで行われている。主に[elf/dl-tunables.list](https://github.com/bminor/glibc/blob/release/2.40/master/elf/dl-tunables.list)と[sysdeps/nptl/dl-tunables.list](https://github.com/bminor/glibc/blob/release/2.40/master/sysdeps/nptl/dl-tunables.list)と[sysdeps/x86/dl-tunables.list](https://github.com/bminor/glibc/blob/release/2.40/master/sysdeps/x86/dl-tunables.list)と[sysdeps/x86_64/64/dl-tunables.list](https://github.com/bminor/glibc/blob/release/2.40/master/sysdeps/x86_64/64/dl-tunables.list)とを見ればよいかと思う。aarch64 ? ここから先はぜひ君の目で確かめてくれ！

glibcのソースコードから参照している箇所は、コード全体を **TUNABLE_GET**でgrepすれば概ね確認できるかと思う。例えば**glibc.rtld.optional_static_tls**の場合、[elf/dl-tls.c の _dl_tls_static_surplus_init() 関数](https://github.com/bminor/glibc/blob/release/2.40/master/elf/dl-tls.c#L1119)で参照している。

```c:elf/dl-tls.c
/* This value is chosen so that with default values for the tunables,
   the computation of dl_tls_static_surplus in
   _dl_tls_static_surplus_init yields the historic value 1664, for
   backwards compatibility.  */
#define LEGACY_TLS (1664 - tls_static_surplus (DEFAULT_NNS, OPTIONAL_TLS))

/* Calculate the size of the static TLS surplus, when the given
   number of audit modules are loaded.  Must be called after the
   number of audit modules is known and before static TLS allocation.  */
void
_dl_tls_static_surplus_init (size_t naudit)
{
  size_t nns, opt_tls;

  nns = TUNABLE_GET (nns, size_t, NULL);
  opt_tls = TUNABLE_GET (optional_static_tls, size_t, NULL);
  if (nns > DL_NNS)
    nns = DL_NNS;
  if (DL_NNS - nns < naudit)
    _dl_fatal_printf ("Failed loading %lu audit modules, %lu are supported.\n",
		      (unsigned long) naudit, (unsigned long) (DL_NNS - nns));
  nns += naudit;

  GL(dl_tls_static_optional) = opt_tls;
  assert (LEGACY_TLS >= 0);
  GLRO(dl_tls_static_surplus) = tls_static_surplus (nns, opt_tls) + LEGACY_TLS;
}
```

# Auxiliary Vector(補助ベクタ)との兼ね合い
Tunablesはパラメータが扱う内容上どうしてもセキュリティ面を気にせざるを得ない。**Auxiliary Vector(補助ベクタ)** の **AT_SECURE(==23)** が **TUNABLE_SECLEVEL_SXID_ERASE**(==0) の場合、環境変数の値を反映させないようになっている。このあたりは[こちらの記事](https://qiita.com/takeoverjp/items/d829193c133a28843394)に詳しい。ただし、先のCVE-2023-4911の影響か、上記の記事が対象にするglibc-2.27と本記事で見ているglibc-2.30とで、このあたりのコードがちょこちょこ変わっていっている模様。

なお[getauxval(3)](https://man7.org/linux/man-pages/man3/getauxval.3.html)に記載のある通り、「LD_SHOW_AUXV=1 」にすると設定されているAuxiliary Vectorのリストを起動時に表示することができる。下記では「???」になってしまっているけど、[elf/elf.h](https://github.com/bminor/glibc/blob/release/2.40/master/elf/elf.h)によれば、0x1c(==28)は **AT_RSEQ_ALIGN**、0x20(==32)は **AT_SYSINFO**、らしい。

```bash
[rarul@tina ~]$ LD_SHOW_AUXV=1 sleep 1
AT_SYSINFO_EHDR:      0x7ffed9535000
AT_MINSIGSTKSZ:       2032
AT_HWCAP:             bfebfbff
AT_PAGESZ:            4096
AT_CLKTCK:            100
AT_PHDR:              0x5921a0064040
AT_PHENT:             56
AT_PHNUM:             13
AT_BASE:              0x75496baf2000
AT_FLAGS:             0x0
AT_ENTRY:             0x5921a0066b80
AT_UID:               1000
AT_EUID:              1000
AT_GID:               1000
AT_EGID:              1000
AT_SECURE:            0
AT_RANDOM:            0x7ffed9473ae9
AT_HWCAP2:            0x2
AT_EXECFN:            /usr/bin/sleep
AT_PLATFORM:          x86_64
AT_??? (0x1b): 0x1c
AT_??? (0x1c): 0x20
```

ちなみに、[secure_getenv(3)](https://man7.org/linux/man-pages/man3/getenv.3.html)という関数があるらしい、初めて知った。

# Environemtn Variable(環境変数)との兼ね合い
先の通り、特にmalloc()に関するパラメータの変更については、[**mallopt(3)**](https://man7.org/linux/man-pages/man3/mallopt.3.html)であったり、専用の環境変数を持っていたりする。mallopt()のmanにある通り、こんな環境変数が用意されている。アンダーバーのありなしが非常にややこしいので注意したい。

- MALLOC_ARENA_MAX
- MALLOC_ARENA_TEST
- MALLOC_CHECK_
- MALLOC_MMAP_MAX_
- MALLOC_MMAP_THRESHOLD_
- MALLOC_PERTURB_
- MALLOC_TOP_PAD_
- MALLOC_TRIM_THRESHOLD_

 なお[こちらの記事](https://qiita.com/kaityo256/items/fc9a27d0df1371246951)で環境変数による動作の差分を詳細に解説している。

# Memory Allocation Tunables
ここから先は具体的なパラメータリストをさらりと眺めていく。詳細はそれぞれのglibcのドキュメント、ここの場合は[38.2 Memory Allocation Tunables (The GNU C Library)](https://www.gnu.org/software/libc/manual/html_node/Memory-Allocation-Tunables.html)を見てほしい。

### Tunable: glibc.malloc.check
環境変数 MALLOC_CHECK_ と同じ。mallopt()のM_CHECK_ACTIONと同じ。デフォルトは 0。1にするとmalloc()やfree()などのときにメモリ不正の簡易チェックが走る。2にすると不正時にabort()を呼ぶ。3にするとその両方。[masamiさんの記事](https://kernhack.hatenablog.com/entry/2013/10/17/212801)にあるように、プログラマならば MALLOC_CHECK_=3 を常用しておこう。

### glibc.malloc.top_pad
環境変数 MALLOC_TOP_PAD_ と同じ。mallopt()のM_TOP_PADと同じ。設定しない場合のデフォルトは 131072 (128KiB)。arenaを増減させるときの一度に処理するサイズ。先の[malloc(環境変数編)記事](https://qiita.com/kaityo256/items/fc9a27d0df1371246951)に詳しい。

### glibc.malloc.perturb
環境変数 MALLOC_PERTURB_ と同じ。mallopt()のM_PERTURBと同じ。デフォルトは 0。mallocやfree時にこの値で埋めることでメモリ破壊を検出しやすくする。先の[malloc(環境変数編)記事](https://qiita.com/kaityo256/items/fc9a27d0df1371246951)に詳しい。

### glibc.malloc.mmap_threshold
環境変数 MALLOC_MMAP_THRESHOLD_ と同じ。mallopt()のM_MMAP_THRESHOLDと同じ。設定しない場合のデフォルトは 131072 (128KiB)。この値以上の要求の場合はヒープで処理せずに直接mmap()で確保する。先の[malloc(環境変数編)記事](https://qiita.com/kaityo256/items/fc9a27d0df1371246951)に詳しい。

### glibc.malloc.trim_threshold
環境変数 MALLOC_TRIM_THRESHOLD_ と同じ。mallopt()のM_TRIM_THRESHOLDと同じ。設定しない場合のデフォルトは 131072 (128KiB)で開始しつつ動的に値を変更する。設定があった場合は動的な変更が走らない。

私も話を追いきれているわけではないが、glibc mallocがメモリをガメる話にこれが関わっていることが多い模様。[このへん](http://mkosaki.blog46.fc2.com/blog-entry-472.html)やら[このへんの「What causes Ruby memory bloat? - Hongli Lai」](https://techlife.cookpad.com/entry/2019/07/10/083318)の項目やらを参考に。

### glibc.malloc.mmap_max
環境変数 MALLOC_MMAP_MAX_ と同じ。mallopt()のM_MMAP_MAXと同じ。設定しない場合のデフォルトは、65536(個)。mmap()経由でメモリを確保する個数の上限値。先の[malloc(環境変数編)記事](https://qiita.com/kaityo256/items/fc9a27d0df1371246951)に詳しい。

### glibc.malloc.arena_max
環境変数 MALLOC_ARENA_MAX と同じ。mallopt()のM_ARENA_MAXと同じ。arena_maxもarena_testも設定しない場合、オンラインのCPU個数の8倍数(sizeof(long)が4じゃないシステム)、もしくは2倍(sizeof(long)が4のシステム)になる。この値が新規に作成するarenaの個数の上限とする。ググってもらうとわかる通り、ランタイム系言語でフラグメンテーション問題でよく槍玉に上がる。個人的な意見だが、デフォルトの「8倍」が過剰じゃないだろうかと思っている。

### glibc.malloc.arena_test
環境変数 MALLOC_ARENA_TEST と同じ。mallopt()のM_ARENA_TESTと同じ。arena_maxが設定されていない場合にのみ有効。arenaの個数がこの値を超えない限りは新しいarenaを作ろうとする。超えた後はarena_maxのデフォルト値に従う。glibcのドキュメントの記載内容とコードとが一致していない気がする。いずれにしても仕様がややこしいので通常は使わないほうが良い。[malloc/arena.cのarena_get2()関数](https://github.com/bminor/glibc/blob/release/2.40/master/malloc/arena.c#L817)より、

```c:malloc/arena.c
  static size_t narenas_limit;

  a = get_free_list ();
  if (a == NULL)
    {
      /* Nothing immediately available, so generate a new arena.  */
      if (narenas_limit == 0)
        {
          if (mp_.arena_max != 0)
            narenas_limit = mp_.arena_max;
          else if (narenas > mp_.arena_test)
            {
              int n = __get_nprocs ();

              if (n >= 1)
                narenas_limit = NARENAS_FROM_NCORES (n);
              else
                /* We have no information about the system.  Assume two
                   cores.  */
                narenas_limit = NARENAS_FROM_NCORES (2);
            }
        }
```

narenas_limitがstatic変数でありかつ一度でも確定すると二度と更新されない点に注意。それに連動し、値を途中で変更するようなことをさせた場合の挙動が非常に理解しづらい。arena_maxやarena_testに限らないが、malloc()を使い始めるよりも先に設定を確定させること。特にC++でmain()関数に来る前にいろんな初期化をしてしまうシステムで問題を踏むことがある。

### glibc.malloc.tcache_max
設定しない場合のデフォルトは 1032(64bit)もしくは516(32bit)。per-thread cache(tcache)でキャッシュするチャンクの上限サイズ。tcacheはスレッドごとにキャッシュするのでスレッド数が多いシステムで注意したほうが良い点になる。

### glibc.malloc.tcache_count
設定しない場合のデフォルトは 7。0にするとtcacheは無効になる。サイズごとのリストでキャッシュするチャンクの個数の上限。ドキュメントによれば、デフォルトでは236KiB(64bit)または118KiB(32bit)がtcacheでキャッシュする上限となるらしい。

### glibc.malloc.tcache_unsorted_limit
設定しない場合のデフォルトは 0 で制限なしになる。メモリ確保時にtcacheから見つからなかった場合にarenaから取得するが、このとき追加でarenaからtcacheに持ってくる処理で、unsortedはリストをたどるのに時間がかかるから、それを制限する、らしい。

### glibc.malloc.mxfast
mallopt()のM_MXFASTと同じ。デフォルトではfastbinは、160バイト(64bit)または80バイト(32bit)まで扱うが、これを制限するためのパラメータ。

### glibc.malloc.hugetlb
デフォルトは0でHuge Pageを使用しない。1にすると、mmap()で確保したあとにmadvise()でMADV_HUGEPAGEを設定する。2にすると、mmap()時にMAP_HUGETLBを指定する。

# Dynamic Linking Tunables
ドキュメント [Dynamic Linking Tunables (The GNU C Library)](https://www.gnu.org/software/libc/manual/html_node/Dynamic-Linking-Tunables.html)

### glibc.rtld.nns
デフォルトは4。[dlmopen(3)](https://man7.org/linux/man-pages/man3/dlopen.3.html)で使うnamespaceの上限になる。dlmopen()は、dlopen()の派生関数で、使用する関数の名前をLmid_tのnamespaceに制限するもの、らしい。知らんかった。

### glibc.rtld.optional_static_tls
デフォルトは512。起動時に確保するTLS(thread local storage)のサイズ。あとから増えると間接参照のコストが大きいから使いそうなサイズを予め確保しておいたほうが良い、みたいな話かな。

### glibc.rtld.dynamic_sort
デフォルトは2で、1以外ならば新しいアルゴリズム(トポロジカルソート)を使う。1なら旧来のソートを用いる。dynamic link libraryの使用を開始したりやめたりするときに管理のためにソートしている模様。

# Elision Tunables
glibcマニュアル [Elision Tunables (The GNU C Library)](https://www.gnu.org/software/libc/manual/html_node/Elision-Tunables.html)

### glibc.elision.enable
デフォルトは0で無効？Hardware Lock Elision機能を有効にするかどうか。Intel系CPUの場合、Haswell世代から入ったTSX機能の1つで、lock処理を楽観的に実行しあとで衝突を検知したらロールバックするというのが基本的な内容。[この辺の記事](https://ascii.jp/elem/000/000/726/726151/)に詳しい。結局あまり使われていない？Intel系以外では、powerとs390で対応が入っている模様。

### glibc.elision.skip_lock_busy
デフォルトは3。elisionによるロックに何回失敗したら普通のlockにフォールバックするか。

### glibc.elision.skip_lock_internal_abort
デフォルトは3。スレッド競合以外の原因で失敗した回数がこれ以上になったらelisionを使うのをやめる。

### glibc.elision.skip_lock_after_retries
デフォルトは3。powerとs390のみ有効。スレッド競合により失敗した回数がこれ以上になったら普通のlockにフォールバックする。...一時的なフォールバックか恒久的に無効にするのかが分かりづらい。

### glibc.elision.tries
デフォルトは3。他のトランザクションが完了するとわかっているときに何回elisionを試みるかの値？ドキュメント見てもコード見てもわからない...

### glibc.elision.skip_trylock_internal_abort
他スレッドがメモリアクセス以外の要因で失敗した場合に通常のロックを避ける回数の値？ドキュメント見てもコード見てもわからない...

# POSIX Thread Tunables
glibcマニュアル [POSIX Thread Tunables (The GNU C Library)](https://www.gnu.org/software/libc/manual/html_node/POSIX-Thread-Tunables.html)

### glibc.pthread.mutex_spin_count
デフォルトは100。PTHREAD_MUTEX_ADAPTIVE_NP な pthread mutex のときに kernelでfutex待ちする前に何回spin lockでトライするかどうか。ただ毎回spinで試しているわけではなくて、TCPの受信待ちに似せたadaptiveな方法で atomic_spin_nop()関数(=="pause"命令)で待っている模様。[nptl/pthread_mutex_lock.c](https://github.com/bminor/glibc/blob/release/2.40/master/nptl/pthread_mutex_lock.c#L133)より、

```c:nptl/pthread_mutex_lock.c
 else if (__builtin_expect (PTHREAD_MUTEX_TYPE (mutex)
			  == PTHREAD_MUTEX_ADAPTIVE_NP, 1))
    {
      if (LLL_MUTEX_TRYLOCK (mutex) != 0)
	{
	  int cnt = 0;
	  int max_cnt = MIN (max_adaptive_count (),
			     mutex->__data.__spins * 2 + 10);
	  int spin_count, exp_backoff = 1;
	  unsigned int jitter = get_jitter ();
	  do
	    {
	      /* In each loop, spin count is exponential backoff plus
		 random jitter, random range is [0, exp_backoff-1].  */
	      spin_count = exp_backoff + (jitter & (exp_backoff - 1));
	      cnt += spin_count;
	      if (cnt >= max_cnt)
		{
		  /* If cnt exceeds max spin count, just go to wait
		     queue.  */
		  LLL_MUTEX_LOCK (mutex);
		  break;
		}
	      do
		atomic_spin_nop ();
	      while (--spin_count > 0);
	      /* Prepare for next loop.  */
	      exp_backoff = get_next_backoff (exp_backoff);
	    }
	  while (LLL_MUTEX_READ_LOCK (mutex) != 0
		 || LLL_MUTEX_TRYLOCK (mutex) != 0);

	  mutex->__data.__spins += (cnt - mutex->__data.__spins) / 8;
	}
      assert (mutex->__data.__owner == 0);
    }
```

### glibc.pthread.stack_cache_size
デフォルトは 41943040 (==40MiB)。Glibc-2.34から登場した。スレッドのスタックのメモリをkernelに返すしきい値。私の記憶が確かならば、これが入る前は一度確保したスタックはキャッシュしっぱなしだったはず。コードを見た感じだとスレッドを作るときと破棄するときにチェックが走る模様。

### glibc.pthread.rseq
デフォルトは1で有効。Linux固有の「Restartable sequences」をglibc内で使用させるかどうか。無効にするとユーザプロゴラムから直接利用することになる。Linux-4.18からGlibc-2.35から入った。Restartable sequencesとは、ユーザランドでクリティカルセクションを処理途中にpreemptionが発生したことを検知して特定の箇所へジャンプしたりすることっぽい。[このへん](https://lwn.net/Articles/883104/)に詳しい。

### glibc.pthread.stack_hugetlb
デフォルトは1で有効。Glibc-2.38から入った。スレッドのスタックをHuge Page可にするかどうか。1にするとシステムデフォルトのまま、0にするとmadvise()でMADV_NOHUGEPAGEを呼び無効にする。マニュアルによれば、スタックにガードページをはさみたいときメモリ使用量のために無効がいい、みたいに書かれている。

# Hardware Capability Tunables
マニュアル [Hardware Capability Tunables (The GNU C Library)](https://www.gnu.org/software/libc/manual/html_node/Hardware-Capability-Tunables.html)

### glibc.cpu.hwcap_mask
環境変数 LD_HWCAP_MASK より優先される。Auxiliary Vector の AT_HWCAPで CPU特有の拡張命令セットの情報を得るが、それをこのマスクで無効化する。Intel系CPUの命令セット事情は詳しくないので詳細は省略する。

### glibc.cpu.hwcaps
CPU特有の拡張命令セットを手動で有効/無効するためのものの模様。

### glibc.cpu.cached_memopt
デフォルト0。power系のみ有効。1にするとメモリすべてがcacheable(non-device)であるとglibcが仮定する。コードを見た感じ、memcpy()をCPU archごとに最適化したものを選ぶ模様。

### glibc.cpu.name
aarch64でのみ有効。特定のCPU archであると設定する。デフォルト空でkernelから取得する？コードを見る限りmemmove()/memset()/memcpy()で最適化した関数へ分岐しているっぽい。

### glibc.cpu.x86_data_cache_size
L1 dcacheのサイズの指定を上書きする。x64/x86_64でのみ有効。コードを見る限り、memset()やmemcpy()でこのサイズを意識して転送サイズを調整する模様。

### glibc.cpu.x86_shared_cache_size
sharedなCPUのキャッシュサイズを上書きする。Core i7-7500だと6MiBになったのでL3キャッシュサイズになってる模様。コードを見る限り、memset()やmemcpy()でこのサイズを意識して転送サイズを調整する模様。

### glibc.cpu.x86_non_temporal_threshold
データを移動させるときにDRAMに行かずにCPUキャッシュ内で閉じると思われるサイズを指定する。memmove()やmemcpy()で使う模様。手元 Core i5-7500だと0x300000(==3MiB)になってるけど、CPU archにも依存したなかなか謎な計算をしている模様。

### glibc.cpu.x86_rep_movsb_threshold
memmove()系で "rep movsb"命令を使い始めるサイズのしきい値。マニュアルには「デフォルト 2048」と書いてあるけど、コード見る限りCPU archに依存して値を決めてるように見える。

### libc.cpu.x86_rep_stosb_threshold
memset()系で "rep stosb"命令を使い始めるサイズのしきい値。マニュアルには「デフォルト 2048」と書いてあるけど、Zen3+のときだけSIZE_MAXにしている模様。

### glibc.cpu.x86_ibt
デフォルトは 0(==cet_elf_property)だけどpermissive と同じ動作になりシステム設定任せ。他に on と off が選べる。IBT(Indirect branch Tracking)を有効にするかどうか。IBTはTiger Lake(第11世代core)から入った。[このへん](https://lwn.net/Articles/889475/)に詳しい。

### glibc.cpu.x86_shstk
デフォルトは 0(==cet_elf_property)でELFバイナリのfeatureに書かれた通りの動きになる？他に on と off と permissive が選べる。シャドウスタック機能(SHSTK)を使うかどうか。onだと常に有効、offだと常にoff。0かつELFバイナリで有効化されてる場合はdlopen()に失敗する。permissive だと自動で無効になって成功する？

# Memory Related Tunables
マニュアル [Memory Related Tunables (The GNU C Library)](https://www.gnu.org/software/libc/manual/html_node/Memory-Related-Tunables.html)

### glibc.mem.tagging
デフォルトは0で無効。aarch64のMTE(Memory Tagging Extension)が使える環境でのみ有効。ビットマスクで機能の有効を指定する。MTEについては[このへん](https://source.android.com/docs/security/test/memory-safety/arm-mte?hl=ja)が概要として良い。

### glibc.mem.decorate_maps
デフォルト0。1にするとprctl(PR_SET_VMA)でPR_SET_VMA_ANON_NAMEを呼べるようになる。Linux-5.17で入った「naming anonymous virtual memory areas」機能のこと。[このへん](https://lwn.net/Articles/867818/)に詳しい。簡単に書くと、anonymousメモリの使用量が問題になりがちだけどanonymousのままだとどの領域で使いすぎてるかわからんからあらかじめメモリ領域に名前を設定しておくと /proc/[PID]/maps から確認しやすくなるよ、という感じ。

Glibc-2.40で追加されたと発表されてるけど、実はGlibc-2.39から入っていてノートに載せ忘れただけ？現にUbunut-24.024ではTunablesのリストに表示されてる。オンラインマニュアルには今は表示されていない。

# gmon Tunables
マニュアル [gmon Tunables (The GNU C Library)](https://www.gnu.org/software/libc/manual/html_node/gmon-Tunables.html)が、namespaceが「glibc.gmon」ではなくて「glibc.mem」と間違った記載になっている。gmonは、gprofの一部で call graph を生成する機能。生成するためのメモリ量をヒューリスティックに決めるが、それが外れることがあるので、指定できるようになっている。

### glibc.gmon.minarcs
デフォルトの MINARCS は 50。小さいプログラムにも関わらず巨大な call graph を作ろうとするとメモリ不足になるのでこれで指定する。

### glibc.gmon.maxarcs
デフォルトの MAXARCS は 1048576 (1MiB)。大きいプログラムを動かすときに課題にメモリ確保しようとするのをこれで指定して抑制する。

# おわりに
思ったりたくさんあってすべてを確認するのに時間がかかって年越してしまって疲れたので「いいね」ください。

# 参考サイト
### Tunablesそのもの周り
- [Toward safer GNU C Library tunable handling [LWN.net]](https://lwn.net/Articles/947736/)
- [Tunables (The GNU C Library)](https://www.gnu.org/software/libc/manual/html_node/Tunables.html) -- Glibcのマニュアル
- [mallocのパラメータが環境変数から設定される流れ - Qiita](https://qiita.com/takeoverjp/items/d829193c133a28843394)
- [getauxval(3)](https://man7.org/linux/man-pages/man3/getauxval.3.html)

### Tunables出でてくる項目がらみ
- [ASCII.jp：Haswellで導入された新機能「TSX」とは？ IDF 2012より](https://ascii.jp/elem/000/000/726/726151/)
- [Restartable sequences in glibc [LWN.net]](https://lwn.net/Articles/883104/)
- [Indirect branch tracking for Intel CPUs [LWN.net]](https://lwn.net/Articles/889475/)
- [Arm メモリタグ付け拡張機能 &nbsp;|&nbsp; Android Open Source Project](https://source.android.com/docs/security/test/memory-safety/arm-mte?hl=ja)
- [Not-so-anonymous virtual memory areas [LWN.net]](https://lwn.net/Articles/867818/)
- [dlmopen(3)](https://man7.org/linux/man-pages/man3/dlopen.3.html)


### malloc編
ここを語りたいわけではないけどどうしても触れずにいられないので。
- [Ruby: mallocでマルチスレッドプログラムのメモリが倍増する理由（翻訳）｜TechRacho by BPS株式会社](https://techracho.bpsinc.jp/hachi8833/2022_06_23/50109)
- [The 67th Yokohama kernel reading party - YouTube](https://www.youtube.com/watch?v=0-vWT-t0UHg)
- - 伝説のmalloc()解説の動画です。
- [Glibc malloc internal | PPT](https://www.slideshare.net/slideshow/glibc-malloc/8555290)
- - 伝説のmalloc()解説の資料です。
- [mallocの動作を追いかける(環境変数編) - Qiita](https://qiita.com/kaityo256/items/fc9a27d0df1371246951)
- [革命の日々！ MALLOC_TRIM_THRESHOLD_ と MALLOC_MMAP_MAX_ パラメタについて](http://mkosaki.blog46.fc2.com/blog-entry-472.html)
- [**mallopt(3)**](https://man7.org/linux/man-pages/man3/mallopt.3.html)