自旋锁

自旋锁是内核中提供的一种高IRQL锁,用同步以及独占的方式访问某个资源。

KSPIN_LOCK my_spin_lock;
KeInitializeSpinLock(&my_spin_lock); //函数无返回值

自旋锁的使用方法:

KIRQL irql;		//KIRQL是一个中断级
KeAcquireSpinLock(&my_spin_lock,&irql);
// To do something ...
KeReleaseSpinLock(&my_spin_lock,irql);

KeAcquireSpinLock 和 KeReleaseSpinLock 之间的代码是只有单线程执行的,其他的线程会停留在 KeAcquireSpinLock 等候,直到 KeReleaseSpinLock 被调用。换句话说,只有一个线程能够获得自旋锁。

注意: 锁一般不会定义成局部变量,可以使用静态变量、全局变量,或者分配在池(POOL)中。因为每个线程来执行的时候都会重新初始化一个锁。只有所有的线程共用一个锁,锁才有意义。

/*正确使用自旋锁的例子*/
KSPIN_LOCK my_spin_lock = {0};
Void InitSpinLock()
{
KeInitializeSpinLock(&my_spin_lock);
}
Void MySafeFunction()
{
KIRQL irql;
KeAcquireSpinlock(&my_spin_lock,&irql);
// To do something...
KeRealeaseSpinLock(&my_spin_lock,irql);
}

双向链表自旋锁

在双向链表中使用自旋锁的目的是保证多线程安全。在操作链表之前,调用KeAcquireSpinLock 来获取锁,在操作完成之后,调用 KeReleaseSpinLock 来释放锁。

链表中初始化自旋锁。LIST_ENTRY有一系列操作,这些操作并不需要使用者自己调用获取与释放锁,只需要为每个链表定义并初始化一个锁即可。

typedef struct _FILE_INFO
{
LIST_ENTRY m_ListEntry;
WCHAR m_strFileName[260];
// other member
}FILE_INFO, *PFLIE_INFO;

LIST_ENTRY my_list_head; //链表头
KSPIN_LOCK my_list_lock; //链表的值

//初始化链表
void MyFileInforInit()
{
InitializeListHead(&my_list_head);
KeInitalizeSpinLock(&my_list_lock);
}

正常情况下链表插入一个节点:

FILE_INFO my_file_infor={0};
// 对 my_file_infor 进行初始化
InsertHeadList(&my_list_head,(PLIST_ENTRY)& my_file_infor);

加锁的情况下链表插入一个节点:

ExInterlockInsertHeadList(
&my_list_head,
(PLIST_ENTRY)& my_file_infor,
&my_list_lock
);

加锁的情况下链表移除一个节点:

PLSIT_ENTRY pRemoveEntry = NULL;
pRemoveEntry = ExInterlockInsertHeadList(
&my_list_head,
&my_list_lock
);

注意: 锁一般不会定义成局部变量。可以使用静态变量、全局变量,或者分配在堆中。

队列自旋锁

队列自旋锁遵守“谁先等待,谁先获取自旋锁”的原则。其过程和队列的“First inFirst out”特点非常类似,正是由于这个原因,这种自旋锁被称为“队列自旋锁”。

队列自旋锁的获取和释放

VOID KeAcquireInStackQueueSpinlock(
IN PKSPIN_LOCK SpinLock,
IN PKSPIN_QUEUE_HANDLE LockHandle
);

VOID KeReleaseInStackQueueSpinLock(
IN PKLOCK_QUEUE_HANDLE LockHandle
);

队列自旋锁初始化

KSPIN_LOCK my_Queue_SpinLock = {0};
KeInitializeSpinLock( &my_Queue_SpinLock );

队列自旋锁的获取和释放

KLOCK_QUEUE_HANDLE my_lock_queue_handle;
KeAcquireInStackQueuedSpinLock(&my_Queue_SpinLock,&my_lock_queue_handle );//do something
KeReleaseInStackQueueSpinLock( &my_Queue_SpinLock );

注意

  1. 队列自旋锁的使用增加了一个 KLOCK_QUEUE_HANDLE 数据结构,这个数据结构唯一地表示一个队列自旋锁。
  2. 普通自旋锁和队列自旋锁虽然都是使用 KeInitializeSpinLock 函数来初始化的,但是对于一个初始化后的自旋锁,要么按普通自旋锁方式来使用,要么按队列自旋锁方式来使用,绝对不能混用。
⬆︎TOP