2011-05-24 102 views
2

我试图在装配程序中拥有线程安全的局部变量。 我在网上搜索过,但我还没有找到任何简单的东西。装配 - 线程安全局部变量

我目前使用GCC汇编程序,因为程序是C代码和程序集的混合,但最终程序将包含多平台/调用约定的代码。

现在,我已经使用.lcomm伪操作声明了我的变量。 据我了解,这些变量将被放置在.bss部分。 所以我想他们将被所有线程共享。

有没有办法在程序集中直接生成一种TLS变量,还是应该使用平台特定的实现,例如Windows上的pthread__declspec

希望它很清楚。不要犹豫,询问是否需要更多信息。

感谢大家,

编辑

这里是有问题的代码:

.lcomm stack0, 8 
.lcomm stack1, 8 

.globl _XSRuntime_CallMethod 
_XSRuntime_CallMethod: 

    pushq %rbp 
    movq %rsp, %rbp 

    xor  %rax, %rax 

    popq stack0(%rip) 
    popq stack1(%rip) 

    callq *%rdi 

    pushq stack1(%rip) 
    pushq stack0(%rip) 

    leave 
    ret 

基本上,它是用来重定向到一个C函数的调用。

的C原型为:

extern uint64_t XSRuntime_CallMethod(void (*m)(void * self, ...), ...); 

它需要一个函数指针作为第一个参数,因此callq *%rdi,如我测试这与System V ABI。

汇编代码非常简单,我想保留它,所以它可以很容易地维护。

现在的问题是:如何使stack0stack1变量线程安全。

+1

为什么在编写汇编程序时,担心平台特定的问题? – 2011-05-24 13:29:48

+0

因为拥有一种“跨平台”的方式会更容易,而且更易于维护。 – Macmade 2011-05-24 13:41:32

+0

请参阅编辑... – Macmade 2011-05-24 13:46:40

回答

1

不那么熟悉汇编这样:

.lcomm stack0, 8 
.lcomm stack1, 8 

.globl _XSRuntime_CallMethod 
_XSRuntime_CallMethod: 

    pushq %rbp // save BP 
    movq %rsp, %rbp // load BP with SP 

    xor  %rax, %rax // clear AX 

    popq stack0(%rip) // pop return address into STACK0 
    popq stack1(%rip) // pop flags into stack1 

    callq *%rdi // call the indirect procedure, so putting flags/return to   XSRuntime_CallMethod onto stack 

    pushq stack1(%rip) // put caller flags onto stack 
    pushq stack0(%rip) // put caller return onto stack 

    leave // clean passed parameters from stack 
    ret // and back to caller 

这是它是如何工作的,是?

如果是这样,跳转到间接过程而不是调用它会不会更容易?然后你不需要任何额外的变量来保存调用者标志/返回&该间接程序直接返回给调用者。

只是一个建议 - 而因为我做了汇编程序。

如果您必须在某处存储调用者地址,请使用SP(输入?)并使用堆栈帧。其他任何事情在某个时候都可能是线程不安全的。

RGDS, 马丁

好,用TLS梅比不是线程安全的,但对于任何递归调用?你最终与另一组在TLS来覆盖这一点,所以你不妨使用“SP”堆栈

马丁

+0

一个jmp实际上是解决方案...非常感谢; ) – Macmade 2011-05-24 14:20:28

0

?? '经典'局部变量,即。通过堆栈偏移量访问的参数/变量/结果本质上是线程安全的。

如果您需要与平台无关的'TLS',请将一些合适的结构/类实例传递到所有线程中,作为创建参数,在线程字段中恢复所有线程之前,向线程输入第一条消息

+0

感谢您的回复。请参阅编辑...我使用'.lcomm'使用两个变量。所以他们不在筹码中。 – Macmade 2011-05-24 13:48:06

0

你或许应该使用(拨打电话到)TlsAlloc & (或他们的其他OS当量),从而做这样的事情排队或任何...

RGDS, 马丁。返回的索引可以存储在全局集中一次,只读变量易于使用。

根据变量的含义以及使用它们的代码的作用,你可能能够摆脱原子操作,但这可能会产生它自己的问题。

1

您如何看待编译器实现线程局部变量?尝试使用-S或/ FA编译这样的程序,您会看到。提示:它必须依赖操作系统特定的API或其他细节才能访问TLS存储。有时准备步骤隐藏在CRT中,但没有办法做到这一点。

例如,这里是MSVC怎么最近做的:

_TLS SEGMENT 
[email protected]@3HA DD 01H DUP (?)    ; number 
_TLS ENDS 
EXTRN __tls_array:DWORD 
EXTRN __tls_index:DWORD 
_TEXT SEGMENT 
[...] 
mov eax, DWORD PTR __tls_index 
mov ecx, DWORD PTR fs:__tls_array 
mov edx, DWORD PTR [ecx+eax*4] 
mov eax, DWORD PTR [email protected]@3HA[edx] 

正如你所看到的,它使用了由CRT初始化特殊变量。

在最近的Linux,GCC可以使用特定的TLS-重定位:

.globl number 
    .section .tbss,"awT",@nobits 
number: 
    .zero 4 
    .text 
    [...] 
    movl %gs:[email protected]OFF, %eax 

如果你想可移植性,最好不要依赖这种操作系统的具体细节,但使用一个通用的API,如并行线程或使用堆栈基于马丁提出的方法。但我想,如果你想可移植性,你不会用汇编:)

0

正如前面提到的,局部变量(基于堆栈的)本质上是线程安全的,因为每个线程都有自己的堆栈。

所有线程都可以访问的线程安全变量(不是基于堆栈)最好使用自旋锁(或Windows NT引擎中的等效项,临界区)来实现。这样的变量在访问,访问和解锁之前必须锁定。一种变体可能是读取是免费的,但写入必须通过锁定/解锁来构建。

AFAIK只有编译器本身没有实现线程安全变量。相反,他们提供访问所需操作系统功能的lib函数。