Chinaunix首页 | 论坛 | 博客
  • 博客访问: 15531043
  • 博文数量: 2005
  • 博客积分: 11986
  • 博客等级: 上将
  • 技术积分: 22535
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-17 13:56
文章分类

全部博文(2005)

文章存档

2014年(2)

2013年(2)

2012年(16)

2011年(66)

2010年(368)

2009年(743)

2008年(491)

2007年(317)

分类:

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]

ENTRY(__switch_to)//在进入__switch_to 函数之前,r1 存放的是即将被切换掉的线程的
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; 
}

阅读(1721) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~