Android Linux的TLS(Thread
Local Storage)实现由内核和用户两部分模块配合完成的。内核对TLS需要做的事情是能够让用户态程序在某个时刻能够设置线程唯一的基址值到内核的线程信息结构内。 用户态程序需要能在主线程运行时设置线程唯一的基址到内核。对于bionic的实现,是在动态链接器启动之前就要把主线程的struct
pthread对象的基址使用上述的系统调用设置进内核。否则将会影响errno——一个位于TLS的全局变量的使用。从而导致动态链接器的启动。因为没有errno会导致很多动态链接库无法使用。之后在创建新的线程的时候,会调用clone系统调用。在clone_flags填入CLONE_SETTLS告诉内核需要为新的线程设置新的线程唯一的基址到新的内核线程信息结构内,然后在第六个参数里面填入相应的基址信息。这个基址就是新创建的struct pthread对象的地址。 ARM
CPU没有段的概念,所以Linux arm家族使用aeabi来获得线程唯一的基址。
# ifdef LIBC_STATIC
/* Use the kernel helper in static C library. */
typedef volatile void* (__kernel_get_tls_t)(void);
# define __get_tls() (*(__kernel_get_tls_t *)0xffff0fe0)()
# else /* !LIBC_STATIC */
/* Use optimized code path.
* Note that HAVE_ARM_TLS_REGISTER is build-specific
* (it must match your kernel configuration)
*/
# ifdef HAVE_ARM_TLS_REGISTER
/* We can read the address directly from a coprocessor
* register, which avoids touching the data cache
* completely.
*/
# define __get_tls() \
({ register unsigned int __val asm("r0"); \
asm ("mrc p15, 0, r0, c13, c0, 3" : "=r"(__val) ); \
(volatile void*)__val; })
# else /* !HAVE_ARM_TLS_REGISTER */
/* The kernel provides the address of the TLS at a fixed
* address of the magic page too.
*/
# define __get_tls() ( *((volatile void **) 0xffff0ff0) )
# endif
# endif /* !LIBC_STATIC */
#else /* !ARM */
extern void* __get_tls( void );
#endif /* !ARM *
__kuser_get_tls定义在0xffff0ff0,它能够返回用户调用set_tls的tls基地址。它目前有两种实现,其一是从cp15寄存器读取那个基地址;其二是从0xffff0ff0读取那个基地址。这个基地址都是在切换任务的时候设置到cp15或者0xffff0ff0的。(见entry-armv.S)因此arm版的Linux获取tls地址也没有系统调用