IRQL

每一个硬件中断都与一个优先级相联系,这个优先级就是IRQL。

IRQL:中断请求级别。一般情况下处理器的IRQL = 0,在用户模式代码执行的时候,他一直是0;内核模式下一般是0,但是也有例外。数字越大,等级越高(中断线路IRQ:当电脑内的周边硬件需要处理器去执行某些工作时,该硬件就会发出一个硬件信号,通知处理器工作,而这个信号就是IRQ。)

比如:磁盘完成执行IO操作之后,磁盘驱动器通过请求中断通知系统操作已经完成,这个中断连接到中断控制器这个硬件,然后将请求发给cpu处理,由指定的线程来执行相关的中断服务例程(ISR)

自旋锁 spinlock

​ 自旋锁是为了解决内核链表读写时存在线程同步问题,解决多线程同步问题必须要用锁,通常使用自旋锁,自旋锁是内核中提供的一种高IRQL锁,用同步以及独占的方式访问某个资源。

​ 并在 SMP 计算机中以 IRQL >= DISPATCH_LEVEL 执行。

​ 这个程序实现了自旋锁保护下的对链表的操作。但是实现的功能很简单,不需要上锁也能完成;主要是展示怎么枷锁,加锁后怎么操作。但是没有多线程实操。

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

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);
}

NTSTATUS unload(PDRIVER_OBJECT driver)
{
    DbgPrint("Driver unload success..");
    return STATUS_SUCCESS;
}

NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING RegistryPath)
{
    driver->DriverUnload = unload;
    DbgPrint("3 Hello World\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 (testA != NULL && testB != NULL)
    {
        // 在锁的保护下安全的移除节点
        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;
    }
    return STATUS_SUCCESS;
}

初始化

KeInitializeSpinLock(&my_list_lock);用这个api进行初始化,参数就是定义的自旋锁变量。

KeReleaseSpinLock(&my_list_lock, IRQL);用这个api去释放锁。

使用

自旋锁使用其实就三个步骤

  1. 初始化自旋锁(KeinitializeSpinlock)

  2. 获得自旋锁 (KeAcquireSpinlock)

  3. 释放自旋锁 (keReleaseSpinLock)

结合链表

自旋锁可以单独使用也可以配合链表一起使用。这里参考Windows 驱动开发 - 自旋锁,队列自旋锁,链表自旋锁的使用.-腾讯云开发者社区-腾讯云 (tencent.com)这里的实例代码:

typedef struct _IBINARY_INFO 
{
    UNICODE_STRING m_blobLinks;
    LIST_ENTRY m_listentry;
    //other
}IBINARY_INFO,*PIBINARY_INFO;
// 定义锁和链表
LIST_ENTRY m_list_head;
KSPIN_LOCK g_spinlock;
// 初始化锁和链表
NTSTATUS init() 
{
    InitializeListHead(&m_list_head);
    KeInitializeSpinLock(&g_spinlock);
    return STATUS_SUCCESS;
}
// 假设是线程函数,或者是可能会发生访问冲突的函数
NTSTATUS Insert()
{
    // 在非分页池分配IBINARY_INFO结构体
    PIBINARY_INFO info1 = reinterpret_cast<PIBINARY_INFO>(
        ExAllocatePool(NonPagedPoolExecute, sizeof(IBINARY_INFO)));
    if (nullptr != info1)
    {
        RtlUnicodeStringInit(&info1->m_blobLinks, L"spinlink");
        //InsertHeadList(&m_list_head, &info1->m_listentry); // 一般的插入操作
        ExInterlockedInsertHeadList(&m_list_head, &info1->m_listentry, &g_spinlock);;  //此位置
    }
    //测试
    PIBINARY_INFO pCur = CONTAINING_RECORD(m_list_head.Flink ,IBINARY_INFO, m_listentry);
    UNICODE_STRING ustr = pCur->m_blobLinks;
    DbgPrint("the bloblink is %wZ \r\n", ustr);
    //KeReleaseSpinLock(&g_spinlock, Irql);
    ExInterlockedRemoveHeadList(&m_list_head, &g_spinlock);  //此位置
    return STATUS_SUCCESS;
}

为什么在非分页池分配IBINARY_INFO结构体呢?

  • 自旋锁用于保护的临界区数据通常需要稳定的内存地址,内存抖动(分页池(PagedPool)所分配的内存位于系统地址空间,会随着进程地址空间的换页而移动位置,这会导致内存抖动。)会引起各种问题。
  • 在DISPATCH_LEVEL或者更高的IRQL获得的自旋锁,不能在分页池分配内存,只能使用非分页池。

锁操作和链表看起来并没有发生交集,代码上面也没有发现什么关联。但是通过我写的代码中的:

    // 向全局链表中插入数据
    if (testA != NULL && testB != NULL)
    {
        // 在锁的保护下安全的移除节点
        ExInterlockedInsertHeadList(&my_list_header, (PLIST_ENTRY)&testA -> lpListEntry, &my_list_lock);
        ExInterlockedInsertTailList(&my_list_header, (PLIST_ENTRY)&testB -> lpListEntry, &my_list_lock);
    }

使用了wdk集成的ExInterlockedInsertHeadListapi来实现的,还有很多类似的api ExInterlocked*************

没有版权,随便复制,免费的知识应该共享 all right reserved,powered by Gitbook该文章修订时间: 2023-08-29 22:16:00

results matching ""

    No results matching ""