2017-08-06 51 views
0

我需要一些幫助來理解Linux的`try_cmpxchg語義和實現。Linux try_cmpxchg神祕的內聯彙編

#define __raw_try_cmpxchg(_ptr, _pold, _new, size, lock)  \ 
({                \ 
    bool success;            \ 
    __typeof__(_ptr) _old = (_pold);       \ 
    __typeof__(*(_ptr)) __old = *_old;       \ 
    __typeof__(*(_ptr)) __new = (_new);       \ 
    switch (size) {            \ 
    case __X86_CASE_B:           \ 
    {               \ 
     volatile u8 *__ptr = (volatile u8 *)(_ptr);    \ 
     asm volatile(lock "cmpxchgb %[new], %[ptr]"    \ 
       CC_SET(z)          \ 
       : CC_OUT(z) (success),       \ 
        [ptr] "+m" (*__ptr),       \ 
        [old] "+a" (__old)       \ 
       : [new] "q" (__new)       \ 
       : "memory");         \ 
     break;             \ 
    }               \ 
    case __X86_CASE_W:           \ 
    {               \ 
     volatile u16 *__ptr = (volatile u16 *)(_ptr);   \ 
     asm volatile(lock "cmpxchgw %[new], %[ptr]"    \ 
       CC_SET(z)          \ 
       : CC_OUT(z) (success),       \ 
        [ptr] "+m" (*__ptr),       \ 
        [old] "+a" (__old)       \ 
       : [new] "r" (__new)       \ 
       : "memory");         \ 
     break;             \ 
    }               \ 
    case __X86_CASE_L:           \ 
    {               \ 
     volatile u32 *__ptr = (volatile u32 *)(_ptr);   \ 
     asm volatile(lock "cmpxchgl %[new], %[ptr]"    \ 
       CC_SET(z)          \ 
       : CC_OUT(z) (success),       \ 
        [ptr] "+m" (*__ptr),       \ 
        [old] "+a" (__old)       \ 
       : [new] "r" (__new)       \ 
       : "memory");         \ 
     break;             \ 
    }               \ 
    case __X86_CASE_Q:           \ 
    {               \ 
     volatile u64 *__ptr = (volatile u64 *)(_ptr);   \ 
     asm volatile(lock "cmpxchgq %[new], %[ptr]"    \ 
       CC_SET(z)          \ 
       : CC_OUT(z) (success),       \ 
        [ptr] "+m" (*__ptr),       \ 
        [old] "+a" (__old)       \ 
       : [new] "r" (__new)       \ 
       : "memory");         \ 
     break;             \ 
    }               \ 
    default:             \ 
     __cmpxchg_wrong_size();         \ 
    }               \ 
    if (unlikely(!success))          \ 
     *_old = __old;           \ 
    likely(success);           \ 
}) 

#define __try_cmpxchg(ptr, pold, new, size)  \ 
    __raw_try_cmpxchg((ptr), (pold), (new), (size), LOCK_PREFIX) 

#define try_cmpxchg(ptr, pold, new)     \ 
    __try_cmpxchg((ptr), (pold), (new), sizeof(*(ptr))) 

我很好奇那些CC_SETCC_OUT手段:在內核源代碼,因爲它是實現的。他們被定義爲:

/* 
* Macros to generate condition code outputs from inline assembly, 
* The output operand must be type "bool". 
*/ 
#ifdef __GCC_ASM_FLAG_OUTPUTS__ 
# define CC_SET(c) "\n\t/* output condition code " #c "*/\n" 
# define CC_OUT(c) "[email protected]" #c 
#else 
# define CC_SET(c) "\n\tset" #c " %[_cc_" #c "]\n" 
# define CC_OUT(c) [_cc_ ## c] "=qm" 
#endif 

而且,這將是巨大的,如果你能解釋的try_cmpxchg的精確語義(不是很明白如何能在原子cmpxchg失敗...)

+0

'CC_SET'和'CC_OUT'是宏。他們處理條件碼;具體而言,通過擴展的內聯彙編語法告訴編譯器,內聯彙編塊中的指令如何影響條件代碼(即CPU標誌)。 'CMPXCHG'指令的[documentation](http://x86.renejeschke.de/html/file_module_x86_id_41.html)告訴你它是如何影響標誌的。 –

回答

3

較新版本的gcc(我相信從版本6)支持特定的標誌輸出。如果可用的話,這些宏可以使用這種支持,否則通過執行setCC指令和臨時輸出回退到舊的方式。

至於如何cmpxchg可以「失敗」:它進行比較,所以如果比較失敗,則失敗,在這種情況下,目標不變並且當前值從內存中獲取。有關詳細信息,請參閱指令集參考。

+0

啊......我知道'cmpxchg'可能會失敗,但'cmpxchg()'和'try_cmpxchg()'有什麼區別? – walkerlala

+0

請參閱[此提交訊息](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/arch/x86/include/asm/cmpxchg.h?id= a9ebf306f52c756c4f9e50ee9a60cd6389d71344)。 TL; DR:'try'版本返回布爾指示,另一個則必須測試舊值。 'try'更好更易讀,另一個保持兼容性。 – Jester