提到自旋锁那就必须要说链表,在上一篇《内核中的链表与结构体》文章中简单实用链表结构来存储进程信息列表,相信读者应该已经理解了内核链表的基本使用,本篇文章将讲解自旋锁的简单应用,自旋锁是为了解决内核链表读写时存在线程同步问题,解决多线程同步问题必须要用锁,通常使用自旋锁,自旋锁是内核中提供的一种高IRQL锁,用同步以及独占的方式访问某个资源。

在了解自旋锁之前需简单介绍一下内核中如何分配内存,一般而言分配内存有两个函数来实现ExAllocatePool可实现分配不带有任何标签的内存空间,而ExAllocatePoolWithTag则可分配带标签的,两者在使用上没有任何区别与之对应的就是释放ExFreePool用于释放非标签内存,而ExFreePoolWithTag则用于通过传入的标签释放对应的内存。

此处的分页属性常用的有三种,NonPagedPool用于分配非分页内存,PagedPool是分页内存,NonPagedPoolExecute是带有执行权限的非分页内存。

  • NonPagedPool: 用于分配非分页内存,该内存不会被交换到磁盘上,并且可以直接被内核访问。适用于需要快速访问的内存,例如驱动程序的代码、中断处理程序、系统调用等。

  • PagedPool: 用于分配分页内存,该内存可能会被交换到磁盘上,需要通过分页机制进行访问。适用于占用空间较大、访问频率较低的内存,例如缓存、数据结构等。

  • NonPagedPoolExecute: 是带有执行权限的非分页内存,适用于需要执行代码的情况,例如一些特殊的驱动程序。

需要注意的是,使用NonPagedPoolExecute分配内存存在一定的安全风险,因为恶意软件可能会利用该内存进行攻击。因此,建议仅在必要时使用该分页属性。

#include <ntifs.h>

typedef struct _MyStruct
{
ULONG x;
ULONG y;
}MyStruct, *pMyStruct; VOID UnDriver(PDRIVER_OBJECT driver)
{
DbgPrint("驱动已卸载 \n");
} NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("hello lyshark \n"); // 用于分配与释放非标签内存
PVOID buffer = ExAllocatePool(NonPagedPool, 1024); DbgPrint("[*] 分配内存地址 = %p \n", buffer); ExFreePool(buffer); // 用于分配带有标签的内存
pMyStruct ptr_buffer = (pMyStruct)ExAllocatePoolWithTag(NonPagedPoolExecute, sizeof(pMyStruct),"lyshark"); ptr_buffer->x = 100;
ptr_buffer->y = 200; DbgPrint("[*] 分配内存 x = %d y = %d \n", ptr_buffer->x, ptr_buffer->y); ExFreePoolWithTag(ptr_buffer, "lyshark"); UNICODE_STRING dst = { 0 };
UNICODE_STRING src = RTL_CONSTANT_STRING(L"hello lyshark"); dst.Buffer = (PWCHAR)ExAllocatePool(NonPagedPool, src.Length);
if (dst.Buffer == NULL)
{
DbgPrint("[-] 分配空间错误 \n");
} dst.Length = dst.MaximumLength = src.Length; RtlCopyUnicodeString(&dst, &src); DbgPrint("[*] 输出拷贝 = %wZ \n", dst); Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}

代码输出效果如下图所示;

接着步入正题,以简单的链表为案例,链表主要分为单向链表与双向链表,单向链表的链表节点中只有一个链表指针,其指向后一个链表元素,而双向链表节点中有两个链表节点指针,其中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");
} NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("hello lyshark \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锁,用于同步和独占地访问某个资源。

  • 自旋锁的基本思想是:当一个线程尝试获取锁时,如果锁已经被占用,则该线程不断循环(即自旋),直到锁被释放。自旋锁适用于锁的持有时间较短,且竞争者较少的情况下,可以避免进程上下文的切换和调度开销。

Windows内核提供了多种类型的自旋锁,例如KSPIN_LOCK、KIRQL等。其中,KSPIN_LOCK是最常用的自旋锁类型,可以通过KeInitializeSpinLock函数初始化一个自旋锁,并通过KeAcquireSpinLock和KeReleaseSpinLock函数进行加锁和解锁操作。

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

#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");
} NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("hello lyshark \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;
}

加锁后执行效果如下图所示;

2.2 Windows驱动开发:内核自旋锁结构的更多相关文章

  1. Windows驱动开发-内核常用内存函数

    搞内存常用函数 C语言 内核 malloc ExAllocatePool memset RtlFillMemory memcpy RtlMoveMemory free ExFreePool

  2. C++第三十三篇 -- 研究一下Windows驱动开发(一)内部构造介绍

    因为工作原因,需要做一些与网卡有关的测试,其中涉及到了驱动这一块的知识,虽然程序可以运行,但是不搞清楚,心里总是不安,觉得没理解清楚.因此想看一下驱动开发.查了很多资料,看到有人推荐Windows驱动 ...

  3. Windows驱动开发(中间层)

    Windows驱动开发 一.前言 依据<Windows内核安全与驱动开发>及MSDN等网络质料进行学习开发. 二.初步环境 1.下载安装WDK7.1.0(WinDDK\7600.16385 ...

  4. [Windows驱动开发](一)序言

    笔者学习驱动编程是从两本书入门的.它们分别是<寒江独钓——内核安全编程>和<Windows驱动开发技术详解>.两本书分别从不同的角度介绍了驱动程序的制作方法. 在我理解,驱动程 ...

  5. windows驱动开发推荐书籍

    [作者] 猪头三 个人网站 :http://www.x86asm.com/ [序言] 很多人都对驱动开发有兴趣,但往往找不到正确的学习方式.当然这跟驱动开发的本土化资料少有关系.大多学的驱动开发资料都 ...

  6. windows 驱动开发入门——驱动中的数据结构

    最近在学习驱动编程方面的内容,在这将自己的一些心得分享出来,供大家参考,与大家共同进步,本人学习驱动主要是通过两本书--<独钓寒江 windows安全编程> 和 <windows驱动 ...

  7. Windows驱动——读书笔记《Windows驱动开发技术详解》

    =================================版权声明================================= 版权声明:原创文章 谢绝转载  请通过右侧公告中的“联系邮 ...

  8. Windows驱动开发-IRP的完成例程

    <Windows驱动开发技术详解 >331页, 在将IRP发送给底层驱动或其他驱动之前,可以对IRP设置一个完成例程,一旦底层驱动将IRP完成后,IRP完成例程立刻被处罚,通过设置完成例程 ...

  9. C++第三十八篇 -- 研究一下Windows驱动开发(二)--WDM式驱动的加载

    基于Windows驱动开发技术详解这本书 一.简单的INF文件剖析 INF文件是一个文本文件,由若干个节(Section)组成.每个节的名称用一个方括号指示,紧接着方括号后面的就是节内容.每一行就是一 ...

  10. 【分析笔记】Linux 内核自旋锁的理解和使用原则

    自旋锁简单说明: 自旋锁主要解决在竞态并发下,保护执行时间很短的临界区.它只允许一个执行单位进入临界区,在该执行单位离开前,其它的执行单位将会在进入临界区前不停的循环等待(即所谓的自旋),直至该执行单 ...

随机推荐

  1. 题解 CF1388A 【Captain Flint and Crew Recruitment】(思维、贪心)

    AC代码: #include<bits/stdc++.h> using namespace std; void solve() { int n; cin >> n; if (n ...

  2. L3-001. 凑零钱-PAT团体程序设计天梯赛GPLT(01背包,动态规划)

    韩梅梅喜欢满宇宙到处逛街.现在她逛到了一家火星店里,发现这家店有个特别的规矩:你可以用任何星球的硬币付钱,但是绝不找零,当然也不能欠债.韩梅梅手边有 104 枚来自各个星球的硬币,需要请你帮她盘算一下 ...

  3. 【SpringBoot】 启动后会调用执行的方法的 (五种方式)

    在 SpringBoot 工程 启动后, 会调用执行方法的五种方式: 亲自测试, 按照执行顺序如下: 第一种: @Component public class SpringContext1 { @Po ...

  4. VueTreeselect

    https://www.vue-treeselect.cn/ 官网简介

  5. idea 解决git更新冲突

    转载请注明出处: 对使用idea工具解决git冲突,最近有发现不同的解决冲突的方法,都很快捷方便,记录一下. 1.先commit 再pull,然后手动进行merge 左边部分是本地仓库的代码,右边部分 ...

  6. MySQL-多表查询练习

    首先创建练习所需要的数据表 储备:建表操作: CREATE TABLE `t_dept` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `deptName` VARC ...

  7. [转帖]Oracle中unicode的几种不同字符编码模式

    https://zhuanlan.zhihu.com/p/668340691#   在Oracle中unicode字符集中,存在以下几种不同unicode字符集的编码模式 AL32UTF8 UTF8 ...

  8. [转帖]SQL Server数据库存储总结

    SQL Server数据库存储文件类型:数据文件和日志文件.数据文件以页面作为存储单元存储数据. 页面:即数据页面,数据页(Page).是系统在磁盘间中分配的一段大小为8k的连续空间. 文件头(Fil ...

  9. MySQL数据库页存储结构学习与了解

    MySQL数据库页存储结构学习与了解 背景 MySQL总是出现奇奇怪怪的问题. 想着自己能够学习与提高一下. 最近看了很多文档.关于MySQL数据库相关的. 想着总结和提炼一下, 希望能够给未来的工作 ...

  10. [转帖]Debian9换源(阿里源)(Linux子系统)

    http://www.taodudu.cc/news/show-5410026.html?action=onClick 默认你已经装好Linux子系统. Step 0: 换源核心就是把/etc/apt ...