看流星社区

 找回密码
 注册账号
查看: 1908|回复: 0

《Windows核心编程》---Interlocked原子访问系列函数

[复制链接]

该用户从未签到

发表于 2017-6-1 17:25:34 | 显示全部楼层 |阅读模式
所谓原子访问,指的是一个线程在访问某个资源的同时能够保证没有其他线程会在同一时刻访问同一资源。Interlocked系列函数提供了这样的操作。所有这些函数会以原子方式来操控一个值。
Interlocked函数的工作原理取决于代码运行的CPU平台,如果是x86系列CPU,那么Interlocked函数会在总线上维持一个硬件信号,这个信号会阻止其他CPU访问同一个内存地址。我们必须确保传给这些函数的变量地址是经过对齐的,否则这些函数可能会失败。C运行库提供了一个_aligned_malloc函数,我们可以使用这个函数来分配一块对齐过的内存:
void* _aligned_malloc(
size_t size,//要分配的字节数
size_t alignment//要对齐到的字节边界,传给alignment的值必须是2的整数幂次方
);
Interlocked函数的另一个需要注意的点是它们执行得很快。调用一次Interlocked函数通常只占用几个CPU周期(通常小于50),而且不需要在用户模式和内核模式之间进行切换(这个切换通常需要占用1000个CPU周期以上)。

1)原子加减操作InterlockedExchangeAdd函数原型如下:
LONG__cdeclInterlockedExchangeAdd(//对32位值进行操作
__inoutLONGvolatile*Addend,//需要递增的变量地址
__inLONG Value//增量值,可为负值表示减法
);

LONGLONG__cdeclInterlockedExchangeAdd64(//对64位值进行操作
__inoutLONGLONGvolatile*Addend,
__inLONGLONG Value
);

2)InterlockedExchange函数用于原子地将32位整数设为指定的值:
LONG__cdeclInterlockedExchange(
__inoutLONGvolatile*Target,//指向要替换的32位值的指针
__inLONG Value//替换的值
);
返回值是指向原先的32位整数值。

InterlockedExchangePointer函数原子地用于替换地址值:
PVOID__cdeclInterlockedExchangePointer(
__inoutPVOIDvolatile*Target,//指向要替换的地址值的指针
__inPVOID Value//替换的地址值
);
返回值是原来的地址值。
对32位应用程序来说,以上两个函数都用一个32位值替换另一个32位值,但对64位应用程序来说,InterlockedExchange替换的是32位值,而InterlockedExchangePointer替换的是64位值。

当然,还有一个函数InterlockedExchange64专门用来原子地操作64位值的:
LONGLONG__cdeclInterlockedExchange64(
__inoutLONGLONGvolatile*Target,
__inLONGLONG Value
);

在实现旋转锁时,InterlockedExchange函数极其有用:
//标识一个共享资源是否正在被使用的全局变量
BOOL g_fResourceInUse = FALSE;
...
voidASCEFunc()
{
//等待访问共享资源
while(InterlockedExchange(&g_fResourceInUse, TRUE) == TRUE)
sleep(0);
//访问共享资源
...
//结束访问
InterlockedExchange(&g_fResourceInUse, FALSE);
}
注意,在使用这项技术时要小心,因为旋转锁会耗费CPU时间。特别是在单CPU机器上应该避免使用旋转锁,如果一个线程不停地循环,那么这会浪费宝贵的CPU时间,而且会阻止其他线程改变该锁的值。

3)函数InterlockedCompareExchange函数和InterlockedCompareExchangePointer函数原型如下:
LONG__cdeclInterlockedCompareExchange(
__inoutLONGvolatile*Destination,//当前值
__inLONG Exchange,//
__inLONG Comparand//比较值
);
PVOID__cdeclInterlockedCompareExchangePointer(
__inoutPVOIDvolatile*Destination,
__inPVOID Exchange,
__inPVOID Comparand
);
这两个函数以原子方式执行一个测试和设置操作。对32位应用程序来说,这两个函数都对32位值进行操作;在64位应用程序中,InterlockedCompareExchange对32位值进行操作而InterlockedCompareExchangePointer对64位值进行操作。函数会将当前值(Destination指向的)与参数Comparand进行比较,如果两个值相同,那么函数会将*Destination修改为Exchange参数指定的值。若不等,则*Destination保持不变。函数会返回*Destination原来的值。所有这些操作都是一个原子执行单元来完成的。
当然,这两个函数的64位版本是:
LONGLONG__cdeclInterlockedCompareExchange64(
__inoutLONGLONGvolatile*Destination,
__inLONGLONG Exchange,
__inLONGLONG Comparand
);

4)Interlocked单向链表函数
InitializeSListHead函数用于创建一个空的单向链表栈:
voidWINAPI InitializeSListHead(
__inoutPSLIST_HEADER ListHead
);

InterlockedPushEntrySList函数在栈顶添加一个元素:
PSLIST_ENTRY WINAPI InterlockedPushEntrySList(
__inoutPSLIST_HEADER ListHead,
__inoutPSLIST_ENTRY ListEntry
);

InterlockedPopEntrySList函数移除位于栈顶的元素并将其返回:
PSLIST_ENTRY WINAPI InterlockedPopEntrySList(
__inoutPSLIST_HEADER ListHead
);

InterlockedFlushSList函数用于清空单向链表栈:
PSLIST_ENTRY WINAPI InterlockedFlushSList(
__inoutPSLIST_HEADER ListHead
);

QueryDepthSList函数用于返回栈中元素的数量:
USHORT WINAPI QueryDepthSList(
__inPSLIST_HEADER ListHead
);

单向链表栈中元素的结构是:
typedefstruct_SLIST_ENTRY {
struct_SLIST_ENTRY *Next;
} SLIST_ENTRY, *PSLIST_ENTRY;
注意:所有单向链表栈中的元素必须以MEMORY_ALLOCATION_ALIGNMENT方式对齐,使用_aligned_malloc函数即可。

实例如下:
#include<windows.h>
#include<malloc.h>
#include<stdio.h>

// 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.


typedefstruct_PROGRAM_ITEM {
SLIST_ENTRY ItemEntry;
ULONG Signature;
} PROGRAM_ITEM, *PPROGRAM_ITEM;

intmain( )
{
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,
&amp;(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);

return1;
}
点击按钮快速添加回复内容: 支持 高兴 激动 给力 加油 苦寻 生气 回帖 路过 感恩
您需要登录后才可以回帖 登录 | 注册账号

本版积分规则

小黑屋|手机版|Archiver|看流星社区 |网站地图

GMT+8, 2024-3-19 11:49

Powered by Kanliuxing X3.4

© 2010-2019 kanliuxing.com

快速回复 返回顶部 返回列表