社区导航

 
查看: 975|回复: 0

Linux线程之线程栈以及TLS

[复制链接]

19

TA的帖子

0

TA的资源

一粒金砂(中级)

Rank: 2

发表于 2012-8-18 11:44:58 | 显示全部楼层 |阅读模式
对于LinuxNPTL线程,有很多话题。本文挑选了原则上是每线程私有的地址空间来讨论,分别是线程栈和TLS。下面由卓跃教育为您介绍。
  虽然Linux将线程和进程不加区分的统一到了task_struct,但是对待其地址空间的stack还是有些区别的。对于Linux进程或者说主线程,其stack是在fork的时候生成的,实际上就是复制了父亲的stack空间地址,然后写时拷贝(cow)以及动态增长。
  何谓动态增长呢?可以看到子进程初始的size为0,然后由于复制了父亲的sp以及稍后在dup_mm中复制的所有vma。
  然而对于主线程生成的子线程而言,其stack将不再是这样的了,而是事先固定下来的,使用mmap系统调用,它不带有VM_STACK_FLAGS标记。这个可以从glibc的nptl/allocatestack.c中的allocate_stack函数中看到:
  01.mem=mmap(NULL,size,prot,
  02.MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK,-1,0);
  mem=mmap(NULL,size,prot,
  MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK,-1,0);
  此调用中的size参数的获取很是复杂,你可以手工传入stack的大小,也可以使用默认的,一般而言就是默认的。这些都不重要,重要的是,这种stack不能动态增长,一旦用尽就没了,这是和生成进程的fork不同的地方。在glibc中通过mmap得到了stack之后,底层将调用sys_clone系统调用。对于子线程的stack,它其实是在进程的地址空间中map出来的一块内存区域,原则上是线程私有的,但是同一个进程的所有线程生成的时候浅拷贝生成者的task_struct的很多字段,其中包括所有的vma,如果愿意,其它线程也还是可以访问到的,于是一定要注意。
  线程本地存储-TLS
  Linux的glibc使用GS寄存器来访问TLS,也就是说,GS寄存器指示的段指向本线程的TEB(Windows的术语),也就是TLS,这么做有个好处,那就是可以高效的访问TLS里面存储的信息而不用一次次的调用系统调用,当然使用系统调用的方式也是可以的。之所以可以这么做,是因为Intel对各个寄存器的作用的规范规定的比较松散,因此你可以拿GS,FS等段寄存器来做几乎任何事,当然也就可以做TLS直接访问了,最终glibc在线程启动的时候首先将GS寄存器指向GDT的第6个段,完全使用段机制来支持针对TLS的寻址访问,后续的访问TLS信息就和访问用户态的信息一样高效了。
  您有任何关于Linux的疑问,欢迎咨询在线老师
  在线程启动的时候,可以通过sys_set_thread_area来设置该线程的TLS信息,所有的信息都得glibc来提供fill_ldt设置GDT中第6个段描述符的基址和段限以及DPL等信息,这些信息都是从sys_set_thread_area系统调用的u_info参数中得来的。本质上,最终GDT的第6个段中描述的信息其实就是一块内存,这块内存用于存储TLS节,这块内存其实也是使用brk,mmap之类调用在主线程的堆空间申请的,只是后来调用sys_set_thread_area将其设置成了本线程的私有空间罢了,主线程或者其它线程如果愿意,也是可以通过其它手段访问到这块空间的。
此帖出自信息发布论坛


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

  • 论坛活动 E手掌握

    扫码关注
    EEWORLD 官方微信

  • EE福利  唾手可得

    扫码关注
    EE福利 唾手可得

小黑屋|手机版|Archiver|电子工程世界 ( 京ICP证 060456 )

GMT+8, 2018-10-23 02:56 , Processed in 0.087016 second(s), 16 queries , Gzip On, MemCache On.

快速回复 返回顶部 返回列表