-
int gscope_flag;
-
#ifndef __ASSUME_PRIVATE_FUTEX
-
int private_futex;
-
#else
-
int __glibc_reserved1;
-
#endif
-
/* Reservation of some values for the TM ABI. */
-
void *__private_tm[4];
-
/* GCC split stack support. */
-
void *__private_ss;
-
} tcbhead_t;
如何去获得上述代码保存的tcbhead_t,以pthread_self的实现为例:
-
# define THREAD_SELF \
-
({ struct pthread *__self; \
-
asm ("movl %%gs:%c1,%0" : "=r" (__self) \
-
: "i" (offsetof (struct pthread, header.self))); \
-
__self;})
PS:x86时,pthread结构体的第一个元素就是tcbhead_t,所以他们的地址相同:
-
struct pthread
-
{
-
union
-
{
-
#if !TLS_DTV_AT_TP
-
/* This overlaps the TCB as used for TLS without threads (see tls.h). */
-
tcbhead_t header;
-
#else
-
/*........*/
glibc中的TLS,可以分为两类,三种。
第一类通过dtv_t *dtv实现,这是一个数组,数组里面每一项都是dtv_t联合体。
-
typedef union dtv
-
{
-
size_t counter;
-
struct
-
{
-
void *val;
-
bool is_static;
-
} pointer;
-
} dtv_t;
dtv[-1]为申请的数组的大小,dtv[0]是max generation number,不知道表示什么。这两个都是counter类型的,之后的都是pointer类型的。
每个pointer类型的dtv_t联合体,都和一个被打开的有__thread变量的.so相关(dtv[1]除外,表示程序本身)。其val指向一个数组,也就是该.so中的保存所有__thread变量的一段连续空间。dtv数组的下标是l_tls_modid,表示被打开的有__thread变量的.so的序号。
保存__thread变量的连续空间的大小在编译时就确定好了,已初始化的__thread保存在.tdata段,未初始化的__thread保存在.tbss段,类似于.data和.bss的概念。
可以readelf -S 看看.tdata和.tbss的信息。
pointer类型的dtv_t联合体有静态和动态两种。
在线程创建之前被打开的.so对应的dtv_t是静态的,具体的位置在tcbhead_t前面的内存中。
在线程创建后被dlopen打开的.so对应的dtv_t是动态的,动态申请内存,具体位置在线程栈中。
gdb调试验证可以看:http://codemacro.com/2014/10/07/pthread-tls-bug/
第