全部博文(2005)
分类:
2010-11-12 14:20:19
内核对线程局部存储的支持
设置
kernel/arch/arm/kernel/traps.c ==> arm_syscall[luther.gliethttp]
asmlinkage int arm_syscall(int no, struct pt_regs *regs)
{
…
case NR(set_tls):
thread->tp_value = regs->ARM_r0;// regs->ARM_r0即为用户空间传下来的线程局
部存储的虚拟地址,而thread->tp_value 极为内核为每个线程保存这个虚拟地址的地方。
#if defined(CONFIG_HAS_TLS_REG)//如果处理器有专门的寄存器用来线程局部存储,那么
好,就用这个专用寄存器
asm ("mcr p15, 0, %0, c13, c0, 3" : : "r" (regs->ARM_r0) );
#elif !defined(CONFIG_TLS_REG_EMUL)
//如果处理器没有专门的寄存器用来线程局部存储,那么好,内核就找一个靠近 4G 的地址
来存
/*
* User space must never try to access this directly.
* Expect your app to break eventually if you do so.
* The user helper at 0xffff0fe0 must be used instead.
* (see entry-armv.S for details)
*/
*((unsigned int *)0xffff0ff0) = regs->ARM_r0;//这个地址就是0xffff0ff0,内核会预先
为其建立映射,并reserve下来
#endif
return 0;
…
}
切换
既然每个线程都用了硬件专有寄存器或者专用地址-0xffff0ff0,那么内核要做的另外一件事
情就是:在线程切换时为每个被切换掉线程保存下存放在转有寄存器或者专用地址
-0xffff0ff0中的值。
arch/arm/kernel/entry-armv.S ==> __switch_to[luther.gliethttp]
struct thread_info,r2存放的是即将被切换上来的线程的 struct thread_info
add ip, r1, #TI_CPU_SAVE
ldr r3, [r2, #TI_TP_VALUE]//取到 struct thread_info 存放用户态线程局部存储虚拟地
址的内核存放地址(比较拗口,但是好像这样才比较精确)
stmia ip!, {r4 - sl, fp, sp, lr} @ Store most regs on stack
…
#if __LINUX_ARM_ARCH__ >= 6
#ifdef CONFIG_CPU_32v6K
clrex
#else
strex r5, r4, [ip] @ Clear exclusive monitor
#endif
#endif
#if defined(CONFIG_HAS_TLS_REG)
mcr p15, 0, r3, c13, c0, 3 @ set TLS register//r3 里的值在上面已经被准好,直接
将r3 的值写入寄存器
#elif !defined(CONFIG_TLS_REG_EMUL)
mov r4, #0xffff0fff
str r3, [r4, #-15] @ TLS val at 0xffff0ff0//r3里的值在上面已经被准好,直接
将r3的值写入虚拟地址0xffff0ff0, 这个地址就是上文提到的用来模拟专有寄存器的内存地
址
#endif
…
ENDPROC(__switch_to)
Device层对线程局部存储的支持
设置
void __init_tls(void** tls, void* thread)
{
…
__set_tls( (void*)tls );
}
__set_tls:
…
ldr r7, =__NR_ARM_set_tls
swi #0 //产生系统调用, 往下进入上文分析的arm_syscall的 case NR(set_tls)分支中
…
使用
int pthread_setspecific(pthread_key_t key, const void *ptr)
{
int err = EINVAL;
tlsmap_t* map;
if (TLSMAP_VALIDATE_KEY(key)) {
/* check that we're trying to set data for an allocated key */
map = tlsmap_lock();
if (tlsmap_test(map, key)) {
((uint32_t *)__get_tls())[key] = (uint32_t)ptr;
err = 0;
}
tlsmap_unlock(map);
}
return err;
}
#ifdef __arm__
# define __get_tls() ( *((volatile void **) 0xffff0ff0) )//对于 arm支持模拟的方式
#else
extern void* __get_tls( void );
#endif
对于X86,因为 x86 用gs来做线程局部存储的专用寄存器,所以就从 gs里取好了
void* __get_tls(void)
{
void* tls;
asm ( " movl %%gs:0, %0" : "=r"(tls) );
return tls;
}