概述:Windows 内核自旋锁了解与使用。自旋锁

相关参考:

内核自旋锁 KSPIN_LOCK

Windows 内核自旋锁(Spin Locks)是内核定义的、仅在内核模式下使用的同步机制,用于保护共享数据或资源免受同时访问的影响。以下是关于 Windows 内核自旋锁的一些关键信息和使用指南:

  1. 自旋锁类型

    • KSPIN_LOCK:这是最常用的自旋锁类型,是一个不透明的类型,用于同步对共享资源的访问。
    • 执行自旋锁(Executive Spin Locks):任何类型的驱动程序都可能使用一个或多个执行自旋锁。例如,大多数文件系统使用执行自旋锁来保护文件系统驱动程序(FSD)设备扩展中的交错工作队列,该队列存储由文件系统的工作者线程回调例程和 FSD 处理的 IRP。
    • 中断自旋锁(Interrupt Spin Locks):每个具有 ISR(Interrupt Service Routine)的驱动程序都使用中断自旋锁来保护其 ISR 和通常从其 StartIo 和 DpcForIsr 例程调用的 SynchCritSection 例程之间共享的任何数据或硬件。
  2. 自旋锁的获取与释放

    • 在 IRQL(Interrupt Request Level)小于或等于 DISPATCH_LEVEL 时,驱动程序可以使用 KeAcquireInStackQueuedSpinLockKeReleaseInStackQueuedSpinLock 来获取和释放自旋锁作为队列自旋锁。
    • 对于在 IRQL 大于或等于 DISPATCH_LEVEL 的调用者,可以调用 KeAcquireSpinLockAtDpcLevelKeReleaseSpinLockFromDpcLevel 以获得更好的驱动程序性能。
  3. 队列自旋锁(Queued Spin Locks)

    • 队列自旋锁在多处理器机器上的性能优于普通自旋锁,特别是在高争用锁的情况下。
    • 队列自旋锁允许处理器在锁上排队,释放时,锁会传递给排队的处理器,等待的处理器在本地标志上自旋。
  4. 使用自旋锁的指南

    • 任何由自旋锁保护的数据或资源以及相应的自旋锁的存储必须在驻留系统空间内存(非分页池)中提供。
    • 驱动程序必须提供其使用的任何执行自旋锁的存储。但是,设备驱动程序不需要为其中断自旋锁提供存储,除非它具有多向量 ISR 或多个 ISR。
  5. 注意事项

    • 使用自旋锁时需要注意避免死锁和优先级反转问题,因此在实际应用中需要谨慎使用。

如上所示为 Windows 中提供的集中自旋锁,根据特性使用在不同的场景,尤其需要注意 死锁 优先级反转 等问题,谨慎使用。

使用示例

// #include <ntddk.h>
 
#include <ntifs.h>
 
typedef struct _MyStruct
{
	ULONG x;
	ULONG y;
	LIST_ENTRY lpListEntry;
}MyStruct, * PMyStruct;
 
// 定义全局链表和全局锁
LIST_ENTRY g_ListHeader;
KSPIN_LOCK g_kSpinLock;
 
void Init()
{
	InitializeListHead(&g_ListHeader);
	KeInitializeSpinLock(&g_kSpinLock);
}
 
void function_ins()
{
	KIRQL Irql;
 
	// 加锁
	KeAcquireSpinLock(&g_kSpinLock, &Irql);
 
	DbgPrint("[*] 锁内部执行 \n");
 
	KeReleaseSpinLock(&g_kSpinLock, Irql);
}
 
NTSTATUS UnloadDriver(PDRIVER_OBJECT pDriver)
{
	DbgPrint("Bye, Driver\n");
 
	return STATUS_SUCCESS;
}
 
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{
	DbgPrint("Hello World, Driver\n");
	DbgPrint("PDRIVER_OBJECT-> [0x%08x]\n", pDriver);
	DbgPrint("PUNICODE_STRING->[%ws]\n", pReg->Buffer);
 
	// 初始化链表
	Init();
 
	// 分配链表空间
	PMyStruct pStuA = (PMyStruct)ExAllocatePool(NonPagedPoolExecute, sizeof(PMyStruct));
	PMyStruct pStuB = (PMyStruct)ExAllocatePool(NonPagedPoolExecute, sizeof(PMyStruct));
 
	// 赋值
	if (pStuA)
	{
		pStuA->x = 10;
		pStuA->y = 20;
	}
 
	if (pStuB)
	{
		pStuB->x = 100;
		pStuB->y = 200;
	}
 
	if (pStuA && pStuB)
	{
		ExInterlockedInsertHeadList(&g_ListHeader, (PLIST_ENTRY)&pStuA->lpListEntry, &g_kSpinLock);
		ExInterlockedInsertTailList(&g_ListHeader, (PLIST_ENTRY)&pStuB->lpListEntry, &g_kSpinLock);
	}
 
	function_ins();
 
	int i = 0;
 
	// 移除节点A并放入到 remove_entry 中
	PLIST_ENTRY pRemoveEntry = ExInterlockedRemoveHeadList(&pStuA->lpListEntry, &g_kSpinLock);
 
	while (pRemoveEntry != &g_ListHeader)
	{
		// 计算出成员距离结构体顶部内存距离
		PMyStruct ptr = CONTAINING_RECORD(pRemoveEntry, MyStruct, lpListEntry);
		DbgPrint("节点元素x = %d, 节点元素y = %d\n", ptr->x, ptr->y);
 
		if (i++ > 10)
		{
			break;
		}
		// 获取下一个链表地址
		pRemoveEntry = pRemoveEntry->Flink;
	}
 
	pDriver->DriverUnload = UnloadDriver;
 
	return STATUS_SUCCESS;
}

输出:

Hello World, Driver
PDRIVER_OBJECT-> [0xfdd2fe50]
PUNICODE_STRING-> [\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services\KmdfKSpinLock]
[*] 锁内部执行
节点元素 x = 100, 节点元素 y = 200
Bye, Driver