在软件中实现线程本地存储
Implementing Thread Local Storage in Software
我们正在将一个嵌入式应用程序从Windows CE移植到不同的系统上。目前的处理器是STM32F4。我们当前的代码库大量使用TLS。新的原型机正在运行KEIL CMSIS RTOS,它的功能非常少。
在http://www.keil.com/support/man/docs/armcc/arm CC_chr1359124216560.htm上,它说自5.04以来支持线程本地存储。现在我们使用的是5.04。问题是,当链接我们的程序与__thread int a;
的变量定义时,链接器找不到对我有意义的__aeabi_read_tp
。
我的问题是:是否有可能实现__aeabi_read_tp
,它将工作或有更多的?
如果这对我们来说是不可能的:有没有一种方法只在软件中实现TLS ?我们现在先不讨论性能。
编辑我尝试通过查看旧的freeBSD源代码和其他源代码来实现__aeabi_read_tp
。虽然该函数主要是在汇编中实现的,但我发现C中的一个版本可以归结为:
extern "C"
{
extern osThreadId svcThreadGetId(void);
void *__aeabi_read_tp()
{
return (void*)svcThreadGetId();
}
}
这基本上是给我当前正在执行的线程的ID (void*)。如果我没理解错的话,这就是我们想要的。这可行吗?
不考虑性能和不进入CMIS RTOS细节(我不知道),您可以为变量分配所需的空间-无论是在堆上还是作为静态或全局变量-我建议有一个结构数组。然后,在创建线程时,将指向下一个未使用结构体的指针传递给线程函数。
在静态或全局变量的情况下,最好知道有多少线程并行工作,以限制预分配内存的大小。
EDIT:添加了基于pthreads的TLS实现示例:
#include <pthread.h>
#define MAX_PARALLEL_THREADS 10
static pthread_t threads[MAX_PARALLEL_THREADS];
static struct tls_data tls_data[MAX_PARALLEL_THREADS];
static int tls_data_free_index = 0;
static void *worker_thread(void *arg) {
static struct tls_data *data = (struct tls_data *) arg;
/* Code omitted. */
}
static int spawn_thread() {
if (tls_data_free_index >= MAX_PARALLEL_THREADS) {
// Consider increasing MAX_PARALLEL_THREADS
return -1;
}
/* Prepare thread data - code omitted. */
pthread_create(& threads[tls_data_free_index], NULL, worker_thread, & tls_data[tls_data_free_index]);
}
不那么令人印象深刻的解决方案是std::map<threadID, T>
。需要用互斥锁包装以允许新线程。
要了解更复杂的内容,请参见
我相信这是可能的,但可能有点棘手。
这是一篇描述__thread
或thread_local
在ELF图像中的行为的论文(尽管它没有讨论AEABI的ARM架构):
执行摘要如下:
- 链接器在生成的可执行文件中创建
- 在运行时,每个线程控制块(TCB)都有一个指向d动态t thread-local v指针表(本文中的
dtv
),该表包含该线程的线程本地存储。它是在线程第一次尝试访问线程局部变量时惰性分配和初始化的。(大概是__aeabi_read_tp()
) - Initialization将原型
.tdata
和memset
的图像复制到分配的存储中。 - 当源代码访问线程局部变量时,编译器生成代码从
__aeabi_read_tp()
中读取线程指针,并执行所有适当的间接操作以获取该线程局部变量的存储。
.tbss
和/或.tdata
节,以提供每个线程所需的线程本地数据的原型图像。编译器和链接器正在做你期望它做的所有工作,但是你需要初始化并返回一个"线程指针",这个指针的结构和填充方式是编译器期望的,因为它直接生成指令来跟随跳转。
如本文所述,有几种访问TLS变量的方法,这些方法可能完全适用于您的编译器和体系结构,也可能不完全适用:http://www.fsfla.org/lxoliva/那样/TLS/RFC-TLSDESC-x86.txt
但是,问题大致相同。当您有运行时加载的库时,可能会带来自己的.tbss
和.tdata
部分,这就变得更加复杂了。对于突然试图访问一个变量的线程,您必须扩展线程本地存储,该变量是在初始化该线程的存储后加载的库引入的。编译器必须根据声明TLS变量的位置生成不同的访问代码。您需要处理和测试您想要支持的所有用例。
这是年,所以你可能已经解决或没有解决你的问题。在这种情况下,直接使用操作系统的TLS API可能是最简单的。
- 将线程中的数据存储到全局容器的最佳方法?
- C++ 多线程原子加载/存储
- 混合 c++ 和 .net 的线程本地存储
- 存储/传递 v8 承诺解析器供以后使用的最佳实践?(结合C++线程)
- 线程上下文上的静态存储对象优化
- 在容器中存储指向对象的指针时的线程安全
- 多线程存储到文件中
- 如何使一个线程中的内存存储"promptly"在其他线程中可见?
- 创建一个以存储线程并调用它们的类
- Apache 崩溃并显示错误 R6016 没有足够的空间来存储线程数据
- Valgrind:在多线程程序中发现冲突的存储/加载
- C 静态与线程存储持续时间破坏顺序
- 线程特定数据与线程本地存储
- 多个动态链接库(DLL)是否可以从静态库(LIB)共享线程本地存储
- 线程本地存储(TLS)和OpenMP
- 线程本地存储构造函数 g++
- 在C++中将所有TLS(线程本地存储)变量设置为新的单个值
- 如何确保变量在不同的线程读取它们之前存储到内存中
- 动态提升线程运行存储在具有共享指针的向量中的对象的方法
- 选择 STL 容器来存储线程