Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1810700
  • 博文数量: 241
  • 博客积分: 9862
  • 博客等级: 中将
  • 技术积分: 5206
  • 用 户 组: 普通用户
  • 注册时间: 2005-02-18 23:23
文章分类
文章存档

2011年(14)

2010年(61)

2009年(48)

2008年(118)

我的朋友

分类: LINUX

2010-06-09 17:51:21

jallen.o.y@gmail.com
20090818

    本文基于在Bionic移植过程中对TLS的一些调研,加上个人理解,所以可能会有疏漏的地方。因此,我会尽力给出信息的来源。

什么是TLS

    TLS即Thread Local Storage。支持TLS的线程有三种类型的变量:线程局部变量,线程本地变量(TLS)和全局变量。TLS的不同之处:所有线程以同一个变量名或索引访问TLS变量,但不同线程的TLS变量存储在不同的内存区域。简而言之,就是同一个名字,不同的存储。

    例如,有TLS变量errno,每个线程执行相同的语句“errno=i;”,errno在不同线程下有了不同的值。注意,这并不是在每一个线程中都声明一个名为errno的变量。这正是TLS的便利之处

TLS的接口

    从程序员的角度来看,TLS的使用方法有两种:
    1)POSIX接口
    pthread_key_create()  pthread_key_delete()

    pthread_getspecific()  pthread_setspecific()

    2)特定语言的扩展

    在gcc中,__thread int i;指定i为TLS变量

TLS的实现

    POSIX接口由线程库提供TLS支持

    语言扩展则需要编译、链接工具提供TLS支持,例如

Bionic中的TLS

    Bionic实现了关于TLS的POSIX接口,这些接口函数体系结构无关的部分定义在pthread.c文件中。

    Bionic TLS实现机制图

    下面,结合上图说明各个接口函数的使用。

    Bionic支持最多64个key

    Key Map:进程级变量,所有线程可见。该结构包含64个key的bitmap,记录所有key的使用情况

    TLS and Stack:观察pthread.c中pthread_create()函数发现,每个线程创建之前会调用calloc()函数在堆中申请一块区域, 将tls指针指向 区域最高地址-64*sizeof(tls单元)。然后将tls作为childstack传给clone系统调用。Clone系统调用childstack用于为新创建的线程指定私有栈。由于栈向低地址增长,如果在用户态能获得tls指针,向高地址寻址可得tls数据。

    pthread_key_create()

    查Key Map返回一个整数类型的未使用的key的索引

    pthread_getspecific() /pthread_setspecific() 

    参数为 index 和 val,即将索引为index的TLS变量值设为val。

    其实现通过 ((unsigned *)(__get_tls())[index]), 其中__get_tls()返回TLS数组的指针。即创建线程时传入的tls指针。

__get_tls()

    该函数是体系结构相关的。
    arm的实现位于bionic/libc/private/bionic_tls.h中,将__get_tls define为 0xffff0ff0
    其他体系结构于bioni/libc/arch-xxx/bionic/ 目录下有该函数的相应实现:
        x86 从寄存器 gs 获得需要的值
        mips 通过 "rdhwr %0,$29"指令实现
  

目前的解决方案

    由于最终的目的是获得tls指针,理论上内核通过系统调用返回该指针即可。所以我们修改了内核,加入了新的系统调用并实验。
    实验效果是:
    1)系统调用成功返回了一个值,但每次返回的值都一样,理论上calloc的地址每次应该不一样
    2)测试文件hello_world.cpp修改前打印后必段错误, 修改后平均4次成功运行出现一次段错误.
        虽然有成功运行的情况,但结果出现了不确定性.
阅读(1089) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~