TLS 数据地址在所有线程中相同

TLS Data Address the Same in All Threads

我使用 GDB 追踪了 glibc-2.27。在sysdeps/unix/sysv/linux/getsysstats.c中的178行,存在一个thread local storage访问,如下图:

while (l < re && isspace (*l))

IIUC,isspace()好像访问了一个table映射ASCII字符到符号键入以快速确定当前字符是否为 space。这个table好像是一个TLS。相关反汇编如下:

0x7f8f9ef480de <__GI___get_nprocs+318>  mov    0x2cbd1b(%rip),%rax        # 0x7f8f9f213e00                                                                        
0x7f8f9ef480e5 <__GI___get_nprocs+325>  mov    %fs:(%rax),%rdi                                                                                                    

rax 包含 0xffffffffffffff98,IIUC 表示 table 的 地址,对于 each thread,使用以下等式计算:$fs_base + 0xffffffffffffff98。当我使用这个等式为每个线程找到 table 地址时,它们都是 return 相同的 0x00007f8f9732b82c。如下所示:

(gdb) thread apply all x/2x $fs_base + 0xffffffffffffff98

Thread 47 (Thread 22457.22471):
0x7f8f75dfc698: 0x9732b82c  0x00007f8f

Thread 46 (Thread 22457.22470):
0x7f8f768fd698: 0x9732b82c  0x00007f8f

Thread 45 (Thread 22457.22469):
0x7f8f773fe698: 0x9732b82c  0x00007f8f

Thread 44 (Thread 22457.22468):
0x7f8f77eff698: 0x9732b82c  0x00007f8f

Thread 43 (Thread 22457.22467):
0x7f8f80a53698: 0x9732b82c  0x00007f8f

Thread 37 (Thread 22457.22465):
0x7f8f81c55698: 0x9732b82c  0x00007f8f

Thread 36 (Thread 22457.22464):
0x7f8f82456698: 0x9732b82c  0x00007f8f

Thread 35 (Thread 22457.22463):
0x7f8f8e6b0698: 0x9732b82c  0x00007f8f

Thread 34 (Thread 22457.22461):
0x7f8f8f480698: 0x9732b82c  0x00007f8f

Thread 33 (Thread 22457.22460):
0x7f8f94824698: 0x9732b82c  0x00007f8f

Thread 32 (Thread 22457.22459):
0x7f8f9649f698: 0x9732b82c  0x00007f8f

Thread 31 (Thread 22457.22458):
0x7f8f96ea0698: 0x9732b82c  0x00007f8f

Thread 30 (Thread 22457.22457):
0x7f8fa2570a18: 0x9732b82c  0x00007f8f

Thread 29 (Thread 22457.22466):
0x7f8f81454698: 0x9732b82c  0x00007f8f

我认为 TLSeach 线程 exclusive,但是,在这里,all 线程在 0x00007f8f9732b82c 处使用 相同的 变量。为什么会这样?似乎链接器 识别 变量是 read-only 并且 保存 一些 space?

你已经证明每个线程都有一个不同的指针变量,在
的TLS存储中 0x7f8f75dfc698 对比
0x7f8f768fd698

它们都指向相同的 table 这一事实是完全正常的,除非您使用 uselocale(3) 在不同的线程中使用不同的语言环境。

我认为 glibc 有针对不同语言环境的静态常量 (.section .rodata) 字符映射 table,它会根据语言环境设置一个指向右侧 table 的指针。为每个线程复制整个 table 会非常低效,浪费更多的 L3 缓存空间。如果它要这样做,您会期望整个 table 就在那里,没有任何间接级别。

看看 glibc 如何实现 isupper() 之类的功能 - 通过索引字符属性标志数组,并检查该数组元素中的特定位。 (即每个数组元素都是标志位图)。

https://code.woboq.org/userspace/glibc/ctype/ctype.h.html和相关的.c文件在同一目录下。