2017-05-06 18 views
-2

可以用匯編語言編寫與XCHG指令這樣鎖定一個互斥體的功能:實現向下和向上(信號)與XCGH指令

mutex_lock: 
    MOVE REGISTRO,#1  
    XCHG REGISTRO,MUTEX 
    CMP REGISTRO,#0  
    JZE ok   
    CALL thread_yield 
    JMP mutex_lock  
ok: RET   

mutex_unlock: 
    MOVE MUTEX,#0  
    RET 

¿有沒有辦法用匯編語言寫的函數啓動以及用XCHG指令關閉信號量的函數?

+0

當然,使用互斥鎖來保護信號量。 –

回答

0

我的建議是使用CMPXCHG指令。該指令基本上結合了單一操作中的比較和交換。它將源操作數與目標操作數進行比較,並相應地設置標誌。然後,如果它們相等(ZF == 1),則它將來自源的值複製到目標中。否則(ZF == 0),它將單獨離開目的地。結合LOCK前綴,這對於多線程環境中的原子操作很方便,因爲它允許您安全地更新存儲在共享內存中的值。

CMPXCHG唯一需要注意的是它需要Pentium或更高版本的處理器。今天,這顯然不是一個問題,但如果你正在做復古編程,你可能需要看別的地方。 (當然,復古程序員不必擔心多個處理器,並且通常甚至沒有多個線程。)

這是一個二進制信號量的示例實現,用僞MASM-風格的語法。二進制信號量是其值爲0(解鎖)或1(鎖定)的信號量。

; This function attempts to obtain the semaphore. 
; 
; It compares the current value of the semaphore to 0. If it is 0, that means 
; the semaphore is not currently locked (i.e., it is available), so this function 
; sets it to 1, thus marking it as locked. Otherwise, if the semaphore is already locked, 
; this function blocks (waits indefinitely) until it becomes available, 
; and then locks it as described above. 
; 
; The address of the semaphore flag to lock is passed on the stack. 
; It is the caller's responsibility to clean the stack (__cdecl). 
; 
; Clobbers: EAX, EDX 
Lock PROC 
    mov edx, DWORD PTR [esp + 4] ; get address of semaphore (passed as sole parameter on stack) 

    WaitUntilAvailable: 
    mov eax, 1 
    lock cmpxchg DWORD PTR [edx], eax 
    jz Finished 
    ; TODO: Add code to suspend the thread (yield execution) while waiting, 
    ;  for example by calling the Win32 Sleep function. 
    ;  Currently, this just spins a busy loop. 
    jmp WaitUntilAvailable 

    Finished: 
    ret 
Lock ENDP 


; This function releases the semaphore. 
; 
; If the semaphore is currently locked (has a value of 1), it is reset to 0. 
; 
; The address of the semaphore flag to lock is passed on the stack. 
; It is the caller's responsibility to clean the stack (__cdecl). 
; 
; Clobbers: EAX, EDX 
Unlock PROC 
    mov edx, DWORD PTR [esp + 4] 
    xor eax, eax 
    lock cmpxchg DWORD PTR [edx], eax 
    ret 
Unlock ENDP 

如果你願意,你可以很容易地修改這些來爲計數信號量工作。在這種情況下,由於您想實際增加/減少該值,因此可以使用XADD指令。我知道這是在486和更高版本上支持的事實,再加上它比CMPXCHG快得多。下面是代碼可能的樣子:

; Waits for a semaphore to become available. 
; 
; If the value of the semaphore variable is non-zero, decrement it by 1 and return. 
; Otherwise, block execution until the semaphore's value is greater than 
; or equal to 1 (i.e., add the caller to the semaphore's queue and wait 
; until it becomes available). 
; 
; Clobbers: EAX, EDX 
SemaphoreWait PROC 
    mov edx, DWORD PTR [esp + 4] 
    jmp Check 

    WaitUntilAvailable: 
    pause ; (or some other way to yield) 

    Check: 
    mov eax, DWORD PTR [edx] 
    test eax, eax 
    jle Check 
    mov eax, -1 
    lock xadd DWORD PTR [edx], eax 
    test eax, eax 
    jg Finished 
    lock inc DWORD PTR [edx] 
    jmp WaitUntilAvailable 

    Finished: 
    ret 
SemaphoreWait ENDP 


; Signals (releases) a semaphore, incrementing its value by 1. 
; 
; Clobbers: EAX 
SemaphoreSignal PROC 
    mov eax, DWORD PTR [esp + 4] 
    lock inc DWORD PTR [eax] 
    ret 
SemaphoreSignal ENDP