首页 > 其他分享 >驱动开发:内核中的自旋锁结构

驱动开发:内核中的自旋锁结构

时间:2022-10-03 21:33:16浏览次数:74  
标签:LIST 自旋 list 链表 内核 ENTRY 驱动 my 节点

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

提到自旋锁那就必须要说链表,在上一篇​​《驱动开发:内核中的链表与结构体》​​文章中简单实用链表结构来存储进程信息列表,相信读者应该已经理解了内核链表的基本使用,本篇文章将讲解自旋锁的简单应用,自旋锁是为了解决内核链表读写时存在线程同步问题,解决多线程同步问题必须要用锁,通常使用自旋锁,自旋锁是内核中提供的一种高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;
}

加锁后执行效果如下:

驱动开发:内核中的自旋锁结构_flink_02

文章作者:lyshark​ (王瑞)​​
版权声明:本博客文章与代码均为学习时整理的笔记,文章 [均为原创] 作品,转载请 [添加出处] ,您添加出处是我创作的动力!
转载文章,请遵守​​《中华人民共和国著作权法》​​相关规定或遵守​《署名CC BY-ND 4.0国际》​禁止演绎规范,合理合规,携带原创出处转载。



标签:LIST,自旋,list,链表,内核,ENTRY,驱动,my,节点
From: https://blog.51cto.com/lyshark/5730498

相关文章

  • 驱动开发:应用DeviceIoContro开发模板
    内核中执行代码后需要将结果动态显示给应用层的用户,DeviceIoControl是直接发送控制代码到指定的设备驱动程序,使相应的移动设备以执行相应的操作的函数,如下代码是一个经典的......
  • 驱动开发:应用DeviceIoContro开发模板
    内核中执行代码后需要将结果动态显示给应用层的用户,DeviceIoControl是直接发送控制代码到指定的设备驱动程序,使相应的移动设备以执行相应的操作的函数,如下代码是一个经典......
  • 驱动开发:通过Async反向与内核通信
    在前几篇文章中给大家具体解释了驱动与应用层之间正向通信的一些经典案例,本章将继续学习驱动通信,不过这次我们学习的是通过运用Async异步模式实现的反向通信,反向通信机制在......
  • 驱动开发:通过Async反向与内核通信
    在前几篇文章中给大家具体解释了驱动与应用层之间正向通信的一些经典案例,本章将继续学习驱动通信,不过这次我们学习的是通过运用Async异步模式实现的反向通信,反向通信机制在......
  • JadConfig 注解驱动的java 配置管理包
    JadConfig是graylog开源的一个基于注解驱动的java配置管理包,graylogserver对于配置的管理就是使用了此包JadConfig使用比较简单,但是功能还是很强大的,配置包含了校验......
  • esxi6.7iso安装Realtek网卡驱动
    5U4TK-DML1M-M8550-XK1QP-1A052PSD:\ISO\esxi>.\ESXi-Customizer-PS.ps1-v67-pkgDir.\net\-NSC-67 指定版本,这个会自动联网下载原始iso内容-pkgDir.\net\  ......
  • 内核思维导图系列之一 —— 系统虚拟化
     ......
  • 创新实践 | SaaS增长新趋势:产品驱动增长PLG(下)
    SaaS产品增长第一步,一定是找方向,SaaS产品的北极星指标处于商业目标,用户价值,和战略选择的交点上,且一般落实在功能使用量上。与ToC产品的AARRR略有不同,ToBSaaS产品驱动......
  • Ubuntu重启后电脑分辨率变了(又名:显卡驱动去哪了;显卡去哪了)
    经多方查询,推测是因为Ubuntu内核自动更新,导致显卡驱动与显卡不匹配为了避免因为显卡松动导致的情况,这边建议把显卡重插一下(记得断电)开机后若不恢复,重装驱动即可附上Nvid......
  • 驱动开发:通过ReadFile与内核层通信
    驱动与应用程序的通信是非常有必要的,内核中执行代码后需要将其动态显示给应用层,但驱动程序与应用层毕竟不在一个地址空间内,为了实现内核与应用层数据交互......