簡體   English   中英

將指針從C傳遞到程序集

[英]Passing a pointer from C to assembly

我想在我的C / C ++程序中使用“_test_and_set lock”匯編語言實現和原子交換匯編指令。

class LockImpl 
{
  public:
  static void lockResource(DWORD resourceLock )
  {
    __asm 
    {
      InUseLoop:  mov     eax, 0;0=In Use
                  xchg    eax, resourceLock
                  cmp     eax, 0
                  je      InUseLoop
    }

  }

  static void unLockResource(DWORD resourceLock )
  {
    __asm 
    {
      mov resourceLock , 1 
    }   

  }
};

這有效,但這里有一個錯誤。

問題是我想傳遞DWORD * resourceLock而不是DWORD resourceLock。

所以問題是如何將指針從C / C ++傳遞到程序集並將其取回。

提前致謝。

問候,-Jay。

PS這樣做是為了避免用戶空間和內核空間之間的上下文切換。

如果您是為Windows編寫的,則應認真考慮使用臨界區對象。 關鍵部分API函數經過優化,除非確實需要,否則它們不會轉換到內核模式,因此無爭用的正常情況下開銷很小。

你的旋轉鎖定的最大問題是,如果你在一個單獨的CPU系統而你正在等待鎖定,那么你就可以使用所有的循環,而持有鎖定的任何東西都不會有機會運行直到你的時間片結束,內核搶占你的線程。

使用臨界區比嘗試滾動自己的用戶模式自旋鎖更成功。

就實際問題而言,它非常簡單:只需更改函數頭以使用volatile DWORD *resourceLock ,並更改觸及resourceLock以使用間接的裝配線:

mov ecx, dword ptr [resourceLock]
xchg eax, dword ptr [ecx]

mov ecx, dword ptr [resourceLock]
lock mov dword ptr [ecx], 1

但請注意,您還有其他一些問題迫在眉睫:

  • 你說你在Windows上開發這個,但是想切換到Linux。 但是,您正在使用特定於MSVC的內聯匯編 - 當您轉移到Linux時,必須將其移植到gcc樣式(特別是涉及從Intel語法切換到AT&T語法)。 您將關閉與海灣合作委員會甚至在Windows上開發更好 ; 這將最大限度地減少遷移的痛苦(請參閱mingw for Windows的gcc)。

  • 格雷格·休吉爾對於無用地旋轉是絕對正確的,阻止鎖定器獲得CPU。 如果你已經旋轉太久,請考慮讓CPU產生。

  • 在多處理器x86上,您可能會遇到內存加載和存儲在鎖定周圍重新排序的問題 - 可能需要鎖定和解鎖過程中的mfence指令。


真的,如果你擔心鎖定意味着你正在使用線程,這可能意味着你已經在使用特定於平台的線程API。 因此,請使用本機同步原語,並在切換到Linux時切換到pthreads版本。

顯然,您正在使用C ++代碼中的內聯匯編塊來編譯MSVC。

作為一般性評論,您應該使用編譯器內在函數,因為內聯匯編沒有前途:在編譯x64時,我的MS編譯器不再受支持。

如果需要在匯編中對微調功能進行微調,則必須在單獨的文件中實現它們。

你應該使用這樣的東西:

volatile LONG resourceLock = 1;

if(InterlockedCompareExchange(&resourceLock, 0, 1) == 1) {
    // success!
    // do something, and then
    resourceLock = 1;
} else {
    // failed, try again later
}

請參閱InterlockedCompareExchange

該問題中原始版本的主要問題是它需要使用寄存器間接尋址並獲取引用(或指針參數)而不是鎖定DWORD的按值參數。

這是Visual C ++的可行解決方案。 編輯: 我已經與作者離線工作,我們已經驗證了這個答案中的代碼正確地在他的測試工具中工作。

但是,如果您使用的是Windows,則應該使用Interlocked API(即InterlockedExchange)。

編輯:如CAF所述,不需要lock xchg因為xchg自動斷言BusLock。

我還添加了一個更快的版本,在嘗試執行xchg之前執行非鎖定讀取。 這顯着減少了內存接口上的BusLock爭用。 通過對持有很長時間的鎖進行退避(yield,然后睡眠),可以加快算法(在一個有爭議的多線程情況下)。 對於單線程CPU情況,使用立即在掛鎖上休眠的OS鎖定將是最快的。

class LockImpl
{
    // This is a simple SpinLock
    //  0 - in use / busy
    //  1 - free / available
public:
    static void lockResource(volatile DWORD &resourceLock )
    {
        __asm 
        {
            mov     ebx, resourceLock
InUseLoop:
            mov     eax, 0           ;0=In Use
            xchg    eax, [ebx]
            cmp     eax, 0
            je      InUseLoop
        }

    }

    static void lockResource_FasterVersion(DWORD &resourceLock )
    {
        __asm 
        {
            mov     ebx, resourceLock
InUseLoop:
            mov     eax, [ebx]    ;// Read without BusLock 
            cmp     eax, 0
            je      InUseLoop     ;// Retry Read if Busy

            mov     eax, 0
            xchg    eax, [ebx]    ;// XCHG with BusLock
            cmp     eax, 0
            je      InUseLoop     ;// Retry if Busy
        }
    }

    static void unLockResource(volatile DWORD &resourceLock)
    {
        __asm 
        {
            mov     ebx, resourceLock
            mov     [ebx], 1 
        }       

    }
};

// A little testing code here
volatile DWORD aaa=1;
void test()
{
 LockImpl::lockResource(aaa);
 LockImpl::unLockResource(aaa);
}

查看編譯器文檔以了解如何為函數打印生成的匯編語言。

打印此功能的匯編語言:

static void unLockResource(DWORD resourceLock )
{
  resourceLock = 0;
  return;
}

這可能不起作用,因為編譯器可以優化函數並刪除所有代碼。 您應該更改上面的函數以傳遞指向resourceLock的指針,然后讓該函數設置鎖定。 打印此工作功能的組件。

我已經提供了一個工作版本,它回答了原始海報的問題,如何獲取ASM中傳遞的參數以及如何使其鎖定正常工作。

許多其他答案都質疑使用ASM的明智性,並提到應該使用內在函數或C OS調用。 以下也適用,是我的ASM答案的C ++版本。 如果您的平台不支持InterlockedExchange(),那么只需要使用ASM片段。

class LockImpl
{
    // This is a simple SpinLock
    //  0 - in use / busy
    //  1 - free / available
public:
#if 1
    static DWORD MyInterlockedExchange(volatile DWORD *variable,DWORD newval)
    {
        // InterlockedExchange() uses LONG / He wants to use DWORD
        return((DWORD)InterlockedExchange(
            (volatile LONG *)variable,(LONG)newval));
    }
#else
    // You can use this if you don't have InterlockedExchange()
    // on your platform. Otherwise no ASM is required.
    static DWORD MyInterlockedExchange(volatile DWORD *variable,DWORD newval)
    {
        DWORD old;
        __asm 
        {
            mov     ebx, variable
            mov     eax, newval
            xchg    eax, [ebx]  ;// XCHG with BusLock
            mov     old, eax
        }
        return(old);
    }
#endif
    static void lockResource(volatile DWORD &resourceLock )
    {
        DWORD oldval;
        do 
        {
            while(0==resourceLock)
            {
                // Could have a yield, spin count, exponential 
                // backoff, OS CS fallback, etc. here
            }
            oldval=MyInterlockedExchange(&resourceLock,0);
        } while (0==oldval);
    }
    static void unLockResource(volatile DWORD &resourceLock)
    {
        // _ReadWriteBarrier() is a VC++ intrinsic that generates
        // no instructions / only prevents compiler reordering.
        // GCC uses __sync_synchronize() or __asm__ ( :::"memory" )
        _ReadWriteBarrier();
        resourceLock=1;
    }
};

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM