汇编 - 线程安全局部变量
我试图在汇编程序中使用线程安全的局部变量。 我在网上搜索过,但没有找到任何简单的东西。
我目前正在使用 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
,因为我正在使用系统 V ABI 对此进行测试。
汇编代码非常简单,我想保持这种方式,这样它就可以很容易维护。
问题是:如何使 stack0 和 stack1 变量线程安全。
I'm trying to have thread-safe local variables in an assembly program.
I've searched on the net, but I haven't found anything simple.
I'm currently using GCC assembler, as the program is a mix of C code and assembly, but the final program will contain code for multiple-platforms / calling conventions.
For now, I've declared my variables using the .lcomm
pseudo-op.
As I understand it, those variables will be placed in the .bss
section.
So I guess they will be shared by all threads.
Is there a way to have a kind of TLS variables directly in assembly, or should I use platform-specific implementations, like pthread
or __declspec
on Windows?
Hope it's clear enough. Don't hesitate to ask if more information is needed.
Thanks to everyone,
EDIT
Here's the code in question:
.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
Basically, it's used to redirect a call to a C function.
The C prototype is:
extern uint64_t XSRuntime_CallMethod( void ( *m )( void * self, ... ), ... );
It takes a function pointer as first argument, hence the callq *%rdi
, as I'm testing this with system V ABI.
The assembly code is very simple, and I'd like to keep it that way, so it can be easily maintainable.
The question is: how to make the stack0
and stack1
variables thread safe.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
对汇编程序不太熟悉,所以:
它是这样工作的,是吗?
如果是这样,直接跳转到间接过程而不是调用它不是更容易吗?然后,您不需要任何额外的变量来保存调用者标志/返回&间接过程直接返回给调用者。
只是一个建议 - 自从我做汇编以来。
如果您必须将调用者地址存储在某处,请对 SP 进行 dec(输入?),然后使用堆栈帧。其他任何事情在某些时候都可能是线程不安全的。
平均值,
Martin
嗯,使用 TLS 也许不是线程不安全的,但是递归调用呢?您最终会在 TLS 中使用另一个堆栈来解决此问题,因此您也可以使用“SP”堆栈
Martin
Not that familiar with assembler so:
Is this how it works, yes??
If so, would it not be easier to just jump to the indirect procedure, rather than calling it? You then don't need any extra variables to hold the caller flags/return & the indirected procedure returns directly to the caller.
Just a suggestion - while since I did assembler.
If you have to store the caller address somewhere, dec the SP, (enter?), and use the stack frame. Anything else is likely to be thread-unsafe at some point.
Rgds,
Martin
Well, with a TLS maybee not thread-unsafe, but what about any recursive calls? You end up with another stack in the TLS to cover this, so you may as well use the 'SP' stack
Martin
您认为编译器如何实现线程局部变量?尝试使用 -S 或 /FAs 编译这样的程序,您就会看到。提示:它必须依赖特定于操作系统的 API 或其他详细信息才能访问 TLS 存储。有时,准备步骤隐藏在 CRT 中,但没有单一的方法可以完成。
例如,最近的 MSVC 是这样做的:
正如您所看到的,它使用由 CRT 初始化的特殊变量。
在最近的 Linux 上,GCC 可以使用特定于 TLS 的重定位:
如果您想要可移植性,最好不要依赖此类特定于操作系统的细节,而是使用通用 API,例如 pthread 或使用 Martin 提出的基于堆栈的方法。但我想如果你想要可移植性你就不会使用汇编器:)
How do you think the compiler implements thread-local variables? Try compiling such a program with -S or /FAs and you'll see. Hint: it has to rely on OS-specific APIs or other details to get access to TLS storage. Sometimes the preparation steps are hidden in the CRT but there's no single way to do it.
For example, here's how recent MSVC does it:
As you can see, it uses special variables which are initialized by the CRT.
On recent Linux, GCC can use TLS-specific relocations:
If you want portability, it's best not to rely on such OS-specific details but use a generic API such as pthread or use stack-based approach proposed by Martin. But I guess if you wanted portability you wouldn't use assembler :)
?? “经典”局部变量,即。通过堆栈偏移访问的参数/变量/结果本质上是线程安全的。
如果您需要与平台无关的“TLS”,请在恢复所有线程之前将一些合适的结构/类实例传递到所有线程中,作为创建参数,在线程字段中,将第一条消息传递到线程输入队列或其他任何内容。 ..
Rgds,
马丁
?? 'Classic' local variables, ie. parameters/variables/results accessed by stack offsets, are inherently thread-safe.
If you need a 'TLS' that is platform-agnostic, pass some suitable struct/class instance into all threads, either as the creation parameter, in a thread field before resuming all the threads, first message to the threads input queue or whatever...
Rgds,
Martin
如前所述,局部变量(基于堆栈)本质上是线程安全的,因为每个线程都有自己的堆栈。
所有线程都可以访问的线程安全变量(不是基于堆栈的)可能最好使用自旋锁(或 Windows NT 引擎中的等效项,关键部分)来实现。这样的变量必须在访问前锁定,访问后再解锁。一种变体可能是读取是免费的,但写入必须通过锁定/解锁来构建。
据我所知,只有编译器本身不实现线程安全变量。相反,它们提供了访问所需操作系统功能的库函数。
As previously mentioned, local variables (stack-based) are inherently thread-safe because each thread has its own stack.
A thread-safe variable which is reachable by all threads (not stack-based) is probably best implemented using a spin-lock (or the equivalent in the Windows NT engine, a critical section). Such a variable must be locked before access, accessed and then unlocked. One variant could be that reads are free, but writes must be framed by locking/unlocking.
AFAIK only compilers themselves don't implement thread-safe variables. Instead they provide lib functions which access the required OS functionality.
您可能应该使用(调用)
TlsAlloc
&TlsFree
(或其他操作系统的等效项)来执行类似的操作。返回的索引可以一次性存储在全局集合中,只读变量以便于使用。根据变量保存的内容以及使用它们的代码的作用,您可能能够摆脱原子操作,但这可能会产生其自身的问题。
You should probably be using(placing calls to) the
TlsAlloc
&TlsFree
(or their other OS equivalents) to do something like this. the returned indices can be stored in global set once, read-only vars for easy use.Depending on what the vars hold and what the code using them does, you might be able to get away with atomic ops, but that might yield problems of its own.