《Windows核心編程》---Interlocked原子訪問(wèn)系列函數(shù)
所謂原子訪問(wèn),指的是一個(gè)線程在訪問(wèn)某個(gè)資源的同時(shí)能夠保證沒(méi)有其他線程會(huì)在同一時(shí)刻訪問(wèn)同一資源。Interlocked系列函數(shù)提供了這樣的操作。所有這些函數(shù)會(huì)以原子方式來(lái)操控一個(gè)值。
Interlocked函數(shù)的工作原理取決于代碼運(yùn)行的CPU平臺(tái),如果是x86系列CPU,那么Interlocked函數(shù)會(huì)在總線上維持一個(gè)硬件信號(hào),這個(gè)信號(hào)會(huì)阻止其他CPU訪問(wèn)同一個(gè)內(nèi)存地址。我們必須確保傳給這些函數(shù)的變量地址是經(jīng)過(guò)對(duì)齊的,否則這些函數(shù)可能會(huì)失敗。C運(yùn)行庫(kù)提供了一個(gè)_aligned_malloc函數(shù),我們可以使用這個(gè)函數(shù)來(lái)分配一塊對(duì)齊過(guò)的內(nèi)存:
void?* _aligned_malloc(
????size_t size,??//要分配的字節(jié)數(shù)
????size_t alignment?//要對(duì)齊到的字節(jié)邊界,傳給alignment的值必須是2的整數(shù)冪次方
);
Interlocked函數(shù)的另一個(gè)需要注意的點(diǎn)是它們執(zhí)行得很快。調(diào)用一次Interlocked函數(shù)通常只占用幾個(gè)CPU周期(通常小于50),而且不需要在用戶模式和內(nèi)核模式之間進(jìn)行切換(這個(gè)切換通常需要占用1000個(gè)CPU周期以上)。
?
1)原子加減操作InterlockedExchangeAdd函數(shù)原型如下:
LONG?__cdecl?InterlockedExchangeAdd(?//對(duì)32位值進(jìn)行操作
??__inout??LONG?volatile?*Addend,?//需要遞增的變量地址
??__in?????LONG Value?//增量值,可為負(fù)值表示減法
);
?
LONGLONG?__cdecl?InterlockedExchangeAdd64(?//對(duì)64位值進(jìn)行操作
??__inout??LONGLONG?volatile?*Addend,
??__in?????LONGLONG Value
);
?
2)InterlockedExchange函數(shù)用于原子地將32位整數(shù)設(shè)為指定的值:
LONG?__cdecl?InterlockedExchange(
??__inout??LONG?volatile?*Target,?//指向要替換的32位值的指針
??__in?????LONG Value?//替換的值
);
返回值是指向原先的32位整數(shù)值。
?
InterlockedExchangePointer函數(shù)原子地用于替換地址值:
PVOID?__cdecl?InterlockedExchangePointer(
??__inout??PVOID?volatile?*Target,?//指向要替換的地址值的指針
??__in?????PVOID Value?//替換的地址值
);
返回值是原來(lái)的地址值。
對(duì)32位應(yīng)用程序來(lái)說(shuō),以上兩個(gè)函數(shù)都用一個(gè)32位值替換另一個(gè)32位值,但對(duì)64位應(yīng)用程序來(lái)說(shuō),InterlockedExchange替換的是32位值,而InterlockedExchangePointer替換的是64位值。
?
當(dāng)然,還有一個(gè)函數(shù)InterlockedExchange64專門用來(lái)原子地操作64位值的:
LONGLONG?__cdecl?InterlockedExchange64(
??__inout??LONGLONG?volatile?*Target,
??__in?????LONGLONG Value
);
?
在實(shí)現(xiàn)旋轉(zhuǎn)鎖時(shí),InterlockedExchange函數(shù)極其有用:
//標(biāo)識(shí)一個(gè)共享資源是否正在被使用的全局變量
BOOL g_fResourceInUse = FALSE;
...
void?ASCEFunc()
{
?????????//等待訪問(wèn)共享資源
?????????while(InterlockedExchange(&g_fResourceInUse, TRUE) == TRUE)
???????????????????sleep(0);
?????????//訪問(wèn)共享資源
?????????...
?????????//結(jié)束訪問(wèn)
?????????InterlockedExchange(&g_fResourceInUse, FALSE);
}
注意,在使用這項(xiàng)技術(shù)時(shí)要小心,因?yàn)樾D(zhuǎn)鎖會(huì)耗費(fèi)CPU時(shí)間。特別是在單CPU機(jī)器上應(yīng)該避免使用旋轉(zhuǎn)鎖,如果一個(gè)線程不停地循環(huán),那么這會(huì)浪費(fèi)寶貴的CPU時(shí)間,而且會(huì)阻止其他線程改變?cè)撴i的值。
?
3)函數(shù)InterlockedCompareExchange函數(shù)和InterlockedCompareExchangePointer函數(shù)原型如下:
LONG?__cdecl?InterlockedCompareExchange(
??__inout??LONG?volatile?*Destination,?//當(dāng)前值
??__in?????LONG Exchange,?//
??__in?????LONG Comparand?//比較值
);
PVOID?__cdecl?InterlockedCompareExchangePointer(
??__inout??PVOID?volatile?*Destination,
??__in?????PVOID Exchange,
??__in?????PVOID Comparand
);
這兩個(gè)函數(shù)以原子方式執(zhí)行一個(gè)測(cè)試和設(shè)置操作。對(duì)32位應(yīng)用程序來(lái)說(shuō),這兩個(gè)函數(shù)都對(duì)32位值進(jìn)行操作;在64位應(yīng)用程序中,InterlockedCompareExchange對(duì)32位值進(jìn)行操作而InterlockedCompareExchangePointer對(duì)64位值進(jìn)行操作。函數(shù)會(huì)將當(dāng)前值(Destination指向的)與參數(shù)Comparand進(jìn)行比較,如果兩個(gè)值相同,那么函數(shù)會(huì)將*Destination修改為Exchange參數(shù)指定的值。若不等,則*Destination保持不變。函數(shù)會(huì)返回*Destination原來(lái)的值。所有這些操作都是一個(gè)原子執(zhí)行單元來(lái)完成的。
當(dāng)然,這兩個(gè)函數(shù)的64位版本是:
LONGLONG?__cdecl?InterlockedCompareExchange64(
??__inout??LONGLONG?volatile?*Destination,
??__in?????LONGLONG Exchange,
??__in?????LONGLONG Comparand
);
?
4)Interlocked單向鏈表函數(shù)
InitializeSListHead函數(shù)用于創(chuàng)建一個(gè)空的單向鏈表?xiàng)#?/p>
void?WINAPI InitializeSListHead(
??__inout??PSLIST_HEADER ListHead
);
?
InterlockedPushEntrySList函數(shù)在棧頂添加一個(gè)元素:
PSLIST_ENTRY WINAPI InterlockedPushEntrySList(
??__inout??PSLIST_HEADER ListHead,
??__inout??PSLIST_ENTRY ListEntry
);
?
InterlockedPopEntrySList函數(shù)移除位于棧頂?shù)脑夭⑵浞祷兀?/p>
PSLIST_ENTRY WINAPI InterlockedPopEntrySList(
??__inout??PSLIST_HEADER ListHead
);
?
InterlockedFlushSList函數(shù)用于清空單向鏈表?xiàng)#?/p>
PSLIST_ENTRY WINAPI InterlockedFlushSList(
??__inout??PSLIST_HEADER ListHead
);
?
QueryDepthSList函數(shù)用于返回棧中元素的數(shù)量:
USHORT WINAPI QueryDepthSList(
??__in??PSLIST_HEADER ListHead
);
?
單向鏈表?xiàng)V性氐慕Y(jié)構(gòu)是:
typedef?struct?_SLIST_ENTRY {
??struct?_SLIST_ENTRY *Next;
} SLIST_ENTRY, *PSLIST_ENTRY;
注意:所有單向鏈表?xiàng)V械脑乇仨氁訫EMORY_ALLOCATION_ALIGNMENT方式對(duì)齊,使用_aligned_malloc函數(shù)即可。
?
實(shí)例如下:
#include?
#include?
#include?
?
// Structure to be used for a list item; the first member is the
// SLIST_ENTRY structure, and additional members are used for data.
// Here, the data is simply a signature for testing purposes.
?
?
typedef?struct?_PROGRAM_ITEM {
????SLIST_ENTRY ItemEntry;
????ULONG Signature;
} PROGRAM_ITEM, *PPROGRAM_ITEM;
?
int?main( )
{
????ULONG Count;
????PSLIST_ENTRY pFirstEntry, pListEntry;
????PSLIST_HEADER pListHead;
????PPROGRAM_ITEM pProgramItem;
?
????// Initialize the list header to a MEMORY_ALLOCATION_ALIGNMENT boundary.
????pListHead = (PSLIST_HEADER)_aligned_malloc(sizeof(SLIST_HEADER),
???????MEMORY_ALLOCATION_ALIGNMENT);
????if( NULL == pListHead )
????{
????????printf("Memory allocation failed./n");
????????return?-1;
????}
????InitializeSListHead(pListHead);
?
????// Insert 10 items into the list.
????for( Count = 1; Count <= 10; Count += 1 )
????{
????????pProgramItem = (PPROGRAM_ITEM)_aligned_malloc(sizeof(PROGRAM_ITEM),
????????????MEMORY_ALLOCATION_ALIGNMENT);
????????if( NULL == pProgramItem )
????????{
????????????printf("Memory allocation failed./n");
????????????return?-1;
????????}
????????pProgramItem->Signature = Count;
????????pFirstEntry = InterlockedPushEntrySList(pListHead,
???????????????????????&(pProgramItem->ItemEntry));
????}
?
????// Remove 10 items from the list and display the signature.
????for( Count = 10; Count >= 1; Count -= 1 )
????{
????????pListEntry = InterlockedPopEntrySList(pListHead);
?
????????if( NULL == pListEntry )
????????{
????????????printf("List is empty./n");
????????????return?-1;
????????}
?
????????pProgramItem = (PPROGRAM_ITEM)pListEntry;
????????printf("Signature is %d/n", pProgramItem->Signature);
?
????// This example assumes that the SLIST_ENTRY structure is the
????// first member of the structure. If your structure does not
????// follow this convention, you must compute the starting address
????// of the structure before calling the free function.
?
????????_aligned_free(pListEntry);
????}
?
????// Flush the list and verify that the items are gone.
????pListEntry = InterlockedFlushSList(pListHead);
????pFirstEntry = InterlockedPopEntrySList(pListHead);
????if?(pFirstEntry != NULL)
????{
????????printf("Error: List is not empty./n");
????????return?-1;
????}
?
????_aligned_free(pListHead);
?
????return?1;
}