Chinaunix首页 | 论坛 | 博客
  • 博客访问: 264604
  • 博文数量: 52
  • 博客积分: 1379
  • 博客等级: 大尉
  • 技术积分: 525
  • 用 户 组: 普通用户
  • 注册时间: 2006-06-18 17:34
文章分类

全部博文(52)

文章存档

2011年(48)

2010年(4)

分类: LINUX

2011-03-09 11:37:05

如果看过linux内核代码,那一定知道current为什么是当前task
current 根据esp来确定当前task, 因为内核栈和当前task有一个一一对应的关系
pthread_self和current类似,每个thread对应一个task(nptl), 每个task的gdt有3个TLS entries;

在glibc初始化时(控制权转入main), 如果支持pthread, pthread会初始化

其中会调用如下宏, 由于初始化较早,所以thrdescr来自直接的brk
把%gs直接指向用set_thread_area安装好的tls entry
这样,根据%gs就能得到一切

/* 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, secondcall) \
  ({ 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;                                  \
                                          \
     /* The 'entry_number' field.  Let the kernel pick a value.  */          \
     if (secondcall)                                  \
       _segdescr.vals[0] = TLS_GET_GS () >> 3;                      \
     else                                      \
       _segdescr.vals[0] = -1;                              \
     /* The 'base_addr' field.  Pointer to the TCB.  */                  \
     _segdescr.vals[1] = (unsigned long int) _thrdescr;                  \
     /* The 'limit' field.  We use 4GB which is 0xfffff pages.  */          \
     _segdescr.vals[2] = 0xfffff;                          \
     /* Collapsed value of the bitfield:                      \
      .seg_32bit = 1                              \
      .contents = 0                                  \
      .read_exec_only = 0                              \
      .limit_in_pages = 1                              \
      .seg_not_present = 0                              \
      .useable = 1 */                              \
     _segdescr.vals[3] = 0x51;                              \
                                          \
     /* Install the TLS.  */                              \
     asm volatile (TLS_LOAD_EBX                              \
           "int $0x80\n\t"                          \
           TLS_LOAD_EBX                              \
           : "=a" (_result), "=m" (_segdescr.desc.entry_number)          \
           : "0" (__NR_set_thread_area),                  \
             TLS_EBX_ARG (&_segdescr.desc), "m" (_segdescr.desc));    \
                                          \
     if (_result == 0)                                  \
       /* We know the index in the GDT, now load the segment register.          \
      The use of the GDT is described by the value 3 in the lower          \
      three bits of the segment descriptor value.                  \
                                          \
      Note that we have to do this even if the numeric value of          \
      the descriptor does not change.  Loading the segment register          \
      causes the segment information from the GDT to be loaded          \
      which is necessary since we have changed it.   */              \
       TLS_SET_GS (_segdescr.desc.entry_number * 8 + 3);              \
                                          \
     _result == 0 ? NULL                              \
     : "set_thread_area failed when setting up thread-local storage\n"; })












pthread_t
__pthread_self (void)
{
  return (pthread_t) THREAD_SELF;
}
strong_alias (__pthread_self, pthread_self)



# define THREAD_SELF \
  ({ struct pthread *__self;                              \
     asm ("movl %%gs:%c1,%0" : "=r" (__self)                      \
      : "i" (offsetof (struct pthread, header.self)));              \
     __self;})




set_thread_area实现很简单,找一个tls entry, 根据参数设置好GDT descc
每次任务切换,内核会根据需要是否加载tls 到gdt中, 因为每个CPU只有一个GDT

gcc支持一个__thread存储描述符, 就用到了tls

$ cat t.c
__thread int i;
int test()
{
    i = 0;
}

$ cc t.c -S
$ cat t.s
    .file    "t.c"
.globl i
    .section    .tbss,"awT",@nobits
    .align 4
    .type    i, @object
    .size    i, 4
i:
    .zero    4
    .text
.globl test
    .type    test, @function
test:
    pushl    %ebp
    movl    %esp, %ebp
    movl    $0, %gs:i@NTPOFF
    popl    %ebp
    ret
    .size    test, .-test
    .ident    "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
    .section    .note.GNU-stack,"",@progbits

NTPOFF 标示重定位为可执行文件中定义的符号的静态 TLS 偏移

比如, 所有的tls合并之后放在线程栈的某个地方,那么直接用%gs:offset就可以引用该变量了

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