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

glibc-2.27使用GDB. 在178in行sysdeps/unix/sysv/linux/getsysstats.c,存在一个thread local storage访问,如下图:

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

IIUC,isspace()似乎访问了一个字符映射到符号类型的 ,以快速确定当前字符是否为空格。这张桌子似乎是一个. 相关拆解如下: ASCIITLS

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

rax包含0xffffffffffffff98,其中,IIUC,表示地址,对于每个线程,使用以下等式计算:$fs_base + 0xffffffffffffff98。当我使用这个等式来查找每个线程的表地址时,它们都返回相同的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

我以为TLS独家每个线程,但是,在这里,所有线程使用相同的变量0x00007f8f9732b82c。为什么会这样?似乎链接器识别了变量 isread-only节省了一些空间?

回答

您已经展示了每个线程在vs.等的TLS 存储中都有一个不同的指针变量。
0x7f8f75dfc698
0x7f8f768fd698

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

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

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

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

  • @TheAhmad: I think it's the other way around. There is a single global copy of the Turkish table, and a single global copy of the French table, and so on (loaded into memory by mmap when the process starts, not linked in). Each thread has, in its thread-local storage, a pointer to whichever table it intends to use. If Thread A changes its locale from Turkish to French, it simply changes its thread-local pointer to point to the (global) French table instead.
  • @TheAhmad No, Peter is saying that there's one copy of the "C" locale data, and one copy of the "UTF-8" locale data, and one copy of the Turkish locale data. If all threads are using the same locale then all the thread locale pointers will point to to same locale data. There's no linker magic going on here. The C library has just set the locale pointers to the same value. The locale pointers themselves are in thread local storage, but the locale data they point to is in normal static storage.

以上是TLS数据在所有线程中寻址相同的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>