windows内核下,自旋锁的初始化、用法以及链表操作

时间:2023-4-22    作者:123    分类: 博客文章


    自旋锁,简单来说就是防止多线程并发导致的错误。我们加锁后,那么当另外一个线程尝试去访问,发现已经被其他锁占用了,那么此线程就会等待锁结束后再去访问。

    使用方法:

    1.自旋锁初始化

    2.在锁开始和结束的中间,写入需要锁住的代码


    以下是示例代码:


    KSPIN_LOCK MySpinLock;//锁一般不会定义成局部变量,否则没有意义

    NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING     Reg) {

        //自旋锁初始化

        KeInitializeSpinLock(&MySpinLock);

        KIRQL irql;

        KeAcquireSpinLock(&MySpinLock,&irql);//加锁开启

        //这里写需要被加锁保护的代码

        KeReleaseSpinLock(&MySpinLock, irql);//加锁结束

        DriverObject->DriverUnload = DriverUnload;

        return STATUS_SUCCESS;

    }


    上面是一段简单的自旋锁代码,那么对于双向链表来说,肯定也是需要自旋锁的。

    官方提供了自旋锁链表操作的相关函数,以下为示例代码:



    typedef struct _Test {

        ULONG Data;

        LIST_ENTRY ListEntry;

        KSPIN_LOCK SpinLock;

    }Test,*PTest;


    KSPIN_LOCK MySpinLock = { 0 };


    NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING Reg) {

        /*

         在双向链表中使用自旋锁:

        直接使用自旋锁自带的链表操作即可完成链表的:插入,移除

        */


        Test A = { 0 };

        Test B = { 0 };

        Test C = { 0 };

        A.Data = 'A';

        B.Data = 'B';

        C.Data = 'C';


        //初始化链表和锁

        LIST_ENTRY MyListEntry = { 0 };

        InitializeListHead(&MyListEntry);

        KeInitializeSpinLock(&MySpinLock);


        //在自旋锁在干预下执行插入

        ExInterlockedInsertHeadList(&MyListEntry, &B.ListEntry, &MySpinLock);

        ExInterlockedInsertHeadList(&MyListEntry, &A.ListEntry, &MySpinLock);

        ExInterlockedInsertTailList(&MyListEntry, &C.ListEntry, &MySpinLock);

        //到此为止,双向链表为 头 A B C

        //接下来移除第一个节点

        //在自旋锁的干预下执行移除

        ExInterlockedRemoveHeadList(&MyListEntry, &MySpinLock);

        //如果要移除任意指定节点,请手动使用其他链表操作函数,手动上锁。

        //--------------------链表的遍历--------------------

        //双向链表-从前向后遍历

        KdPrint(("------------------------------------\n"));

        PLIST_ENTRY PListEntry = NULL;//定义一个指针链表

        PListEntry = MyListEntry.Flink;//因为是从前往后遍历,所以让它等于前部

        while (PListEntry != &MyListEntry) {

        PTest PTestListEntry = CONTAINING_RECORD(PListEntry, Test, ListEntry);//使用一个宏把ListEntry成员转换为结构体Test的首地址

        DbgPrint("PListEntry=%p PTestListEntry=%p  data=%c\n", PListEntry, PTestListEntry, (CHAR)PTestListEntry->Data);

        //上面输出完毕,接下来指向下一个节点的前部,因为这里PListEntry是一个指针,所以他和ListHeader是同步的,是可修改的,获取的直接是地址。

        PListEntry = PListEntry->Flink;

        }


        DriverObject->DriverUnload = DriverUnload;

        return STATUS_SUCCESS;

    }

    

    根据上面的代码,我们发现,对于双向链表的自旋锁操作,也非常简单。步骤如下:

    1.初始化自旋锁——KeInitializeSpinLock

    2.插入链表——ExInterlockedInsertHeadList或ExInterlockedInsertTailList

    3.移除链表——ExInterlockedRemoveHeadList

标签: WINDOWS内核编程