求詳解該InterlockedIncrement的實現?

該實現如下:

INT32 InterlockedIncrement(INT32* lpAddend){

INT32 i = 1;

__asm__ __volatile__(

"xaddl %0,%1"

:"+r" (i),"+m" (*lpAddend)

::"memory"

);

return *lpAddend;

}

"+m" (*lpAddend)和"memory"是什麼意思?該函數是如何保證返回的是遞增前的值的?謝謝!


這個問題應該是vczh群裡面的那個同學問的吧?這個問題在群里我應該回答了。

不過我這裡還是想再講講GNU Extended ASM(也稱為Inline ASM),我做過這塊兒的編譯器的實現,然後這一塊兒也巨複雜,我順便來簡單總結一下(也只能簡單),也當普及一下,我覺得不理解這一塊兒的應該不是少數。

之前我認為Inline ASM應該很少見,但是在編譯一些開源項目的時候,結果發現用的很多,比如Kernel,database,包括一些編程語言。比如Ruby的代碼實現,裡面的Inline ASM就用的異常的風騷,我們編譯器編譯的時候,直接ICE了,目前在Fix中,咳咳...

對於Inline ASM來說,其分為幾塊:

asm [volatile] ( asm instructions: Output Operands: Input Operands: Clobbers);

其中,若你有Input,但是Output為空的話,你也需要保留 : ,而Clobbers是可以為空的,如果有內容的話,Input Operands後面需要跟著 : 分割與Clobber的內容。

其實GCC還有針對asm goto的,但是目前只有GCC做了,Clang和我們編譯器都沒有支持這樣的寫法,其語法為

asm [volatile] goto ( asm instruction : Input Operands : Clobbers : Goto Labels);

這裡面的話, asm instructions 包含了兩部分,一部分是常規的彙編語句,一部分是指向Output Operands, Input Operands. 比如題主的例子:

__asm__ __volatile__(
"xaddl %0,%1"
:"+r" (i),"+m" (*lpAddend)
::"memory"

);

這裡面的xaddl是彙編指令,%0和%1就是指向的output的東西。

那麼具體來說 %0指向的就是 "+r"(i),%1就是"+m"(*lpAddend)這裡。

對於Output Operands格式為 "[+][=] Register Constraint" (Operands),其中+表示可讀可寫, =表示可寫。這隻能用於Output,不能用於Input。然而對於Clang來說,它會把+翻譯為=,也就是不區分 + = 。而Register Constaint就跟具體的平台有關了,但是也有一些通用的,如 r, m, i等。r 代表的是 允許一個 regiser operand,把它放在General Purpose Register中,而m代表著允許一個memory operand, 而i代表著的是允許一個immediate number operand,如 3 這樣的。

而Input Operands的格式與Output大同小異,但是不能加 = 這樣的,之前也說過了。但是Input也有一些特殊的修飾,這裡就不細談了,我說過細節很多,編譯器實現巨複雜 -_-||

然後就是 Clobber部分,這部分分為常規的Clobber和特殊的Clobber,你的例子的memory就屬於特殊的Clobber。而memory的作用是什麼呢?就是告訴編譯器可能會進行memory的讀寫操作。而編譯器識別到memory的時候,就會置上類似usememory這樣的Flag,然後在執行ASM語句之前就會去Flush寄存器的值。

然後你這個實現返回的並非是之前的值,而是遞增後的值。因為按照ATT彙編語法,第一個是源操作數,第二個是目標操作數。所以,這句話是把i加到*lpAddend這裡來。

而對於m寄存器來說,在編譯器實現其實是很特殊的。當它出現在Output的時候,會被我們移動到Input這裡來,相關的索引號都會變化。

所以,寫代碼其實是一件很幸福的事情,而做編譯器來實現這些特性其實是一件受虐的事情。


https://en.wikipedia.org/wiki/Fetch-and-add#x86_implementation


推薦閱讀:

word2vec多線程優化時不加鎖的做法合理么?
ASIO + HTTP 如何打造高性能伺服器?
為什麼`atomic::fetch_add()`可以 relaxed memory order?
make 多線程編譯會出錯么?
CLion 鏈接庫?如 lpthread 怎麼設置?

TAG:操作系統 | Linux | GNU | 並行計算 | 多線程 |