• 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来理解。

  • 相关阅读:
    css3新特性
    线程间通信的三种方法
    硬件相关知识
    time.h
    ldr指令总结
    你不知道的100个小秘密
    ARM学习日记
    C中位域的使用
    《编程之美》第2刷勘误
    排序2
  • 原文地址:https://www.cnblogs.com/long123king/p/3501936.html
Copyright © 2020-2023  润新知