-
set_tls_desc(p, idx, &info, 1);
-
return 0;
-
}
glibc或者bionic都会调用set_thread_area来设置线程的数据的,bionic是通过__set_tls来调用的。
其实线程有很大一部分是在glibc/bionic中实现的,不全是在内核中的。
上述代码中idx和entry_number表示gs指向gdt中的第几个描述符,如果上层调用者没有指定idx和entry_number的话,由内核自己动态分配。
x86可能的值为6,7,8,x86_64可能的值为12,13,14。但是一般来说,x86上的gs的值是0x33,对应的idx为6,使用gdt中的第6个描述符。
线程切换时,修改的是gdt[6]中的东西,不会去修改gs的,gdt[6]指向了什么东西呢?glibc时gdt[6]中的是线程的tcbhead_t的指针,bionic时gdt[6]是一个指向TLS数组的指针。
线程通过gs,找到gdt中的描述符,然后找到tcbhead_t *或者TLS数组,然后在glibc和bionic中使用不同的实现,可以获得线程的tid,errno,TLS,等等信息。
==========================================================================
设置gs的值,以及gdt[6]中的地址,是在如下代码中进行设置的:
-
/* Code to initially initialize the thread pointer. This might need
-
special attention since 'errno' is not yet available and if the
-
operation can cause a failure 'errno' must not be touched. */
-
# define TLS_INIT_TP(thrdescr) \
-
({ void *_thrdescr = (thrdescr); \
-
tcbhead_t *_head = _thrdescr; \
-
union user_desc_init _segdescr; \
-
int _result; \
-
\
-
_head->tcb = _thrdescr; \
-
/* For now the thread descriptor is at the same address. */ \
-
_head->self = _thrdescr; \
-
/* New syscall handling support. */ \
-
INIT_SYSINFO; \
-
\
-
/* Let the kernel pick a value for the 'entry_number' field. */ \
-
tls_fill_user_desc (&_segdescr, -1, _thrdescr); \
-
\
-
/* Install the TLS. */ \
-
INTERNAL_SYSCALL_DECL (err); \
-
_result = INTERNAL_SYSCALL (set_thread_area, err, 1, &_segdescr.desc); \
-
/*........*/
tls_fill_user_desc第二个参数为-1,表示动态申请gdt中的位置,一般为6,所以gs=0x33。
在_segdescr中保存了tcbhead_t的地址,后续在set_thread_area系统调用里将tc