2011-05-24 87 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函數。