Linux中TLS

TLS(Thread Local Storage)

线程局部存储。

在Linux操作系统中,TLS保存成GDT中描述的一个段。

   1: /*
   2:  * This creates a new process as a copy of the old one,
   3:  * but does not actually start it yet.
   4:  *
   5:  * It copies the registers, and all the appropriate
   6:  * parts of the process environment (as per the clone
   7:  * flags). The actual kick-off is left to the caller.
   8:  */
   9: static struct task_struct *copy_process(unsigned long clone_flags,
  10:                     unsigned long stack_start,
  11:                     struct pt_regs *regs,
  12:                     unsigned long stack_size,
  13:                     int __user *child_tidptr,
  14:                     struct pid *pid,
  15:                     int trace)
  16: {
  17: ......
  18: retval = copy_thread(clone_flags, stack_start, stack_size, p, regs);
  19: ......
  20: }
   1: int copy_thread(unsigned long clone_flags, unsigned long sp,
   2:     unsigned long unused,
   3:     struct task_struct *p, struct pt_regs *regs)
   4: {
   5:     struct pt_regs *childregs;
   6:     struct task_struct *tsk;
   7:     int err;
   8:  
   9:     childregs = task_pt_regs(p);
  10:     *childregs = *regs;
  11:     childregs->ax = 0;
  12:     childregs->sp = sp;
  13:  
  14:     p->thread.sp = (unsigned long) childregs;
  15:     p->thread.sp0 = (unsigned long) (childregs+1);
  16:  
  17:     p->thread.ip = (unsigned long) ret_from_fork;
  18:  
  19:     task_user_gs(p) = get_user_gs(regs);
  20:  
  21:     p->thread.io_bitmap_ptr = NULL;
  22:     tsk = current;
  23:     err = -ENOMEM;
  24:  
  25:     memset(p->thread.ptrace_bps, 0, sizeof(p->thread.ptrace_bps));
  26:  
  27:     if (unlikely(test_tsk_thread_flag(tsk, TIF_IO_BITMAP))) {
  28:         p->thread.io_bitmap_ptr = kmemdup(tsk->thread.io_bitmap_ptr,
  29:                         IO_BITMAP_BYTES, GFP_KERNEL);
  30:         if (!p->thread.io_bitmap_ptr) {
  31:             p->thread.io_bitmap_max = 0;
  32:             return -ENOMEM;
  33:         }
  34:         set_tsk_thread_flag(p, TIF_IO_BITMAP);
  35:     }
  36:  
  37:     err = 0;
  38:  
  39:     /*
  40:      * Set a new TLS for the child thread?
  41:      */
  42:     if (clone_flags & CLONE_SETTLS)
  43:         err = do_set_thread_area(p, -1,
  44:             (struct user_desc __user *)childregs->si, 0);
  45:  
  46:     if (err && p->thread.io_bitmap_ptr) {
  47:         kfree(p->thread.io_bitmap_ptr);
  48:         p->thread.io_bitmap_max = 0;
  49:     }
  50:     return err;
  51: }
   1: /*
   2:  * Set a given TLS descriptor:
   3:  */
   4: int do_set_thread_area(struct task_struct *p, int idx,
   5:                struct user_desc __user *u_info,
   6:                int can_allocate)
   7: {
   8:     struct user_desc info;
   9:  
  10:     if (copy_from_user(&info, u_info, sizeof(info)))
  11:         return -EFAULT;
  12:  
  13:     if (idx == -1)
  14:         idx = info.entry_number;
  15:  
  16:     /*
  17:      * index -1 means the kernel should try to find and
  18:      * allocate an empty descriptor:
  19:      */
  20:     if (idx == -1 && can_allocate) {
  21:         idx = get_free_idx();
  22:         if (idx < 0)
  23:             return idx;
  24:         if (put_user(idx, &u_info->entry_number))
  25:             return -EFAULT;
  26:     }
  27:  
  28:     if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX)
  29:         return -EINVAL;
  30:  
  31:     set_tls_desc(p, idx, &info, 1);
  32:  
  33:     return 0;
  34: }
   1: static void set_tls_desc(struct task_struct *p, int idx,
   2:              const struct user_desc *info, int n)
   3: {
   4:     struct thread_struct *t = &p->thread;
   5:     struct desc_struct *desc = &t->tls_array[idx - GDT_ENTRY_TLS_MIN];
   6:     int cpu;
   7:  
   8:     /*
   9:      * We must not get preempted while modifying the TLS.
  10:      */
  11:     cpu = get_cpu();
  12:  
  13:     while (n-- > 0) {
  14:         if (LDT_empty(info))
  15:             desc->a = desc->b = 0;
  16:         else
  17:             fill_ldt(desc, info);
  18:         ++info;
  19:         ++desc;
  20:     }
  21:  
  22:     if (t == &current->thread)
  23:         load_TLS(t, cpu);
  24:  
  25:     put_cpu();
  26: }
   1: static inline void fill_ldt(struct desc_struct *desc, const struct user_desc *info)
   2: {
   3:     desc->limit0        = info->limit & 0x0ffff;
   4:  
   5:     desc->base0        = (info->base_addr & 0x0000ffff);
   6:     desc->base1        = (info->base_addr & 0x00ff0000) >> 16;
   7:  
   8:     desc->type        = (info->read_exec_only ^ 1) << 1;
   9:     desc->type           |= info->contents << 2;
  10:  
  11:     desc->s            = 1;
  12:     desc->dpl        = 0x3;
  13:     desc->p            = info->seg_not_present ^ 1;
  14:     desc->limit        = (info->limit & 0xf0000) >> 16;
  15:     desc->avl        = info->useable;
  16:     desc->d            = info->seg_32bit;
  17:     desc->g            = info->limit_in_pages;
  18:  
  19:     desc->base2        = (info->base_addr & 0xff000000) >> 24;
  20:     /*
  21:      * Don't allow setting of the lm bit. It is useless anyway
  22:      * because 64bit system calls require __USER_CS:
  23:      */
  24:     desc->l            = 0;
  25: }

从上面的call_tree可以看到,在fork系统调用创建一个新的进程时,会为新的任务设置TLS。

参考:http://blog.csdn.net/dog250/article/details/7704898

fill_ldt设置GDT中第6个段描述符的基址和段限以及DPL等信息,这些信息都是从sys_set_thread_area系统调用的u_info参数中得来的。本质上,最终GDT的第6个段中描述的信息其实就是一块内存,这块内存用于存储TLS节,这块内存其实也是使用brk,mmap之类调用在主线程的堆空间申请的,只是后来调用sys_set_thread_area将其设置成了本线程的私有空间罢了,主线程或者其它线程如果愿意,也是可以通过其它手段访问到这块空间的。

因为TLS是一个对应于C/C++ Runtime库的概念,所以要深入了解TLS,需要结合glibc来理解。

原文地址:https://www.cnblogs.com/long123king/p/3501936.html