驅動開發:核心中的自旋鎖結構

2022-09-28 12:01:05

提到自旋鎖那就必須要說連結串列,在上一篇《驅動開發:核心中的連結串列與結構體》文章中簡單實用連結串列結構來儲存程序資訊列表,相信讀者應該已經理解了核心連結串列的基本使用,本篇文章將講解自旋鎖的簡單應用,自旋鎖是為了解決核心連結串列讀寫時存線上程同步問題,解決多執行緒同步問題必須要用鎖,通常使用自旋鎖,自旋鎖是核心中提供的一種高IRQL鎖,用同步以及獨佔的方式存取某個資源。

首先以簡單的連結串列為案例,連結串列主要分為單向連結串列與雙向連結串列,單向連結串列的連結串列節點中只有一個連結串列指標,其指向後一個連結串列元素,而雙向連結串列節點中有兩個連結串列節點指標,其中Blink指向前一個連結串列節點Flink指向後一個節點,以雙向連結串列為例。

#include <ntifs.h>
#include <ntstrsafe.h>

/*
// 連結串列節點指標
typedef struct _LIST_ENTRY
{
  struct _LIST_ENTRY *Flink;   // 當前節點的後一個節點
  struct _LIST_ENTRY *Blink;   // 當前節點的前一個結點
}LIST_ENTRY, *PLIST_ENTRY;
*/

typedef struct _MyStruct
{
  ULONG x;
  ULONG y;
  LIST_ENTRY lpListEntry;
}MyStruct,*pMyStruct;

VOID UnDriver(PDRIVER_OBJECT driver)
{
  DbgPrint("驅動解除安裝成功 \n");
}

// By: LyShark
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
  DbgPrint("By:LyShark \n");
  DbgPrint("Email:[email protected] \n");
  // 初始化頭節點
  LIST_ENTRY ListHeader = { 0 };
  InitializeListHead(&ListHeader);

  // 定義連結串列元素
  MyStruct testA = { 0 };
  MyStruct testB = { 0 };
  MyStruct testC = { 0 };

  testA.x = 100;
  testA.y = 200;

  testB.x = 1000;
  testB.y = 2000;

  testC.x = 10000;
  testC.y = 20000;

  // 分別插入節點到頭部和尾部
  InsertHeadList(&ListHeader, &testA.lpListEntry);
  InsertTailList(&ListHeader, &testB.lpListEntry);
  InsertTailList(&ListHeader, &testC.lpListEntry);

  // 節點不為空 則 移除一個節點
  if (IsListEmpty(&ListHeader) == FALSE)
  {
    RemoveEntryList(&testA.lpListEntry);
  }

  // 輸出連結串列資料
  PLIST_ENTRY pListEntry = NULL;
  pListEntry = ListHeader.Flink;

  while (pListEntry != &ListHeader)
  {
    // 計算出成員距離結構體頂部記憶體距離
    pMyStruct ptr = CONTAINING_RECORD(pListEntry, MyStruct, lpListEntry);
    DbgPrint("節點元素X = %d 節點元素Y = %d \n", ptr->x, ptr->y);

    // 得到下一個元素地址
    pListEntry = pListEntry->Flink;
  }

  Driver->DriverUnload = UnDriver;
  return STATUS_SUCCESS;
}

連結串列輸出效果如下:

如上所述,核心連結串列讀寫時存線上程同步問題,解決多執行緒同步問題必須要用鎖,通常使用自旋鎖,自旋鎖是核心中提供的一種高IRQL鎖,用同步以及獨佔的方式存取某個資源。

#include <ntifs.h>
#include <ntstrsafe.h>

/*
// 連結串列節點指標
typedef struct _LIST_ENTRY
{
struct _LIST_ENTRY *Flink;   // 當前節點的後一個節點
struct _LIST_ENTRY *Blink;   // 當前節點的前一個結點
}LIST_ENTRY, *PLIST_ENTRY;
*/

typedef struct _MyStruct
{
	ULONG x;
	ULONG y;
	LIST_ENTRY lpListEntry;
}MyStruct, *pMyStruct;

// 定義全域性連結串列和全域性鎖
LIST_ENTRY my_list_header;
KSPIN_LOCK my_list_lock;

// 初始化
void Init()
{
	InitializeListHead(&my_list_header);
	KeInitializeSpinLock(&my_list_lock);
}

// 函數內使用鎖
void function_ins()
{
	KIRQL Irql;

	// 加鎖
	KeAcquireSpinLock(&my_list_lock, &Irql);

	DbgPrint("鎖內部執行 \n");

	// 釋放鎖
	KeReleaseSpinLock(&my_list_lock, Irql);
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
	DbgPrint("驅動解除安裝成功 \n");
}

// By: LyShark
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	DbgPrint("By:LyShark \n");
	DbgPrint("Email:[email protected] \n");

	// 初始化連結串列
	Init();

	// 分配連結串列空間
	pMyStruct testA = (pMyStruct)ExAllocatePool(NonPagedPoolExecute, sizeof(pMyStruct));
	pMyStruct testB = (pMyStruct)ExAllocatePool(NonPagedPoolExecute, sizeof(pMyStruct));

	// 賦值
	testA->x = 100;
	testA->y = 200;

	testB->x = 1000;
	testB->y = 2000;

	// 向全域性連結串列中插入資料
	if (NULL != testA && NULL != testB)
	{
		ExInterlockedInsertHeadList(&my_list_header, (PLIST_ENTRY)&testA->lpListEntry, &my_list_lock);
		ExInterlockedInsertTailList(&my_list_header, (PLIST_ENTRY)&testB->lpListEntry, &my_list_lock);
	}

	function_ins();

	// 移除節點A並放入到remove_entry中
	PLIST_ENTRY remove_entry = ExInterlockedRemoveHeadList(&testA->lpListEntry, &my_list_lock);

	// 輸出連結串列資料
	while (remove_entry != &my_list_header)
	{
		// 計算出成員距離結構體頂部記憶體距離
		pMyStruct ptr = CONTAINING_RECORD(remove_entry, MyStruct, lpListEntry);
		DbgPrint("節點元素X = %d 節點元素Y = %d \n", ptr->x, ptr->y);

		// 得到下一個元素地址
		remove_entry = remove_entry->Flink;
	}

	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}

加鎖後執行效果如下: