2.2 Windows驱动开发:内核自旋锁结构

提到自旋锁那就必须要说链表,在上一篇《内核中的链表与结构体》文章中简单实用链表结构来存储进程信息列表,相信读者应该已经理解了内核链表的基本使用,本篇文章将讲解自旋锁的简单应用,自旋锁是为了解决内核链表读写时存在线程同步问题,解决多线程同步问题必须要用锁,通常使用自旋锁,自旋锁是内核中提供的一种高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函数初始化一个自旋锁,并通过KeAcquireSpinLockKeReleaseSpinLock函数进行加锁和解锁操作。

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/139301.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

医院等级评审,离不开医院不良事件报告系统

医院不良事件报告系统全套源码 不良事件管理系统源码 不良事件上报系统对事件的报告、处置、跟踪、评价、分析、改进、学习等进行了综合管理&#xff0c;通过双向互评机制实现临床科室与职能部门之间的进一步互动&#xff0c;加强不良事件报告处置过程中的信息互通能力。 围绕…

项目生命周期分享

第一阶段&#xff1a; 项目启动&#xff0c;2天时间即可&#xff0c;需要输出项目进度计划 1.项目组成立1天&#xff0c;用来建立项目组&#xff0c;确定工作分工和工作方法&#xff0c;指定项目总体计划&#xff08;包括前期交流&#xff0c;需求收集&#xff0c;项目立项等…

数组区域检索的优化 --- 分块,线段树,树状数组

思考 首先让我们来思考一个问题&#xff0c;给定一个数组&#xff0c;和left与right的值&#xff0c;让你求这个数组中left到right之间元素的和&#xff0c;你会怎么计算&#xff1f;最简单的当然是遍历。如果有人问你这个问题的时候&#xff0c;他决对是会让你优化的&#xff…

vue项目路由使用history模式,nginx配置,刷新页面显示404

需要在配置项中添加 try_files $uri $uri/ /index.html;

削峰填谷:居民小区电动汽车有序充电策略研究

摘 要&#xff1a;针对电动汽车在居民小区无序充电对电网系统产生严重隐患及充电间时过长问题&#xff0c;提出一种采用延迟充电的电动汽车有序充电控制策略&#xff0c;并在分析国内外电动汽车有序充电的研究现状后&#xff0c;设计了居民小区电动汽车有序充电策略的总体框架。…

【源码复现】图神经网络之PPNP/APPNH

目录 1、论文简介2、论文核心介绍2.1、现有方法局限2.2、PageRank&Personalized PageRank2.3、PPNP&APPNP 3、源码复现3.1、模型总体框架3.2、PPNP3.3、APPNP3.4、MLP(两层) 1、论文简介 论文题目——《PREDICT THEN PROPAGATE: GRAPH NEURAL NETWORKS MEET PERSONALI…

转本考前4个月,手把手教你逆袭上岸

现在离转本考试的时间还剩下4个月&#xff0c;绝大多数同学会在之后的寒假期间全力学习&#xff0c;谁在这段时期懈怠&#xff0c;谁就丢掉了一半的分数。 不管是复习了很长一段时间&#xff0c;还是刚起步的同学&#xff0c;都有必要重新规划后面的复习。下面给大家讲讲&…

【中间件篇-Redis缓存数据库07】Redis缓存使用问题及互联网运用

Redis缓存使用问题 数据一致性 只要使用到缓存&#xff0c;无论是本地内存做缓存还是使用 redis 做缓存&#xff0c;那么就会存在数据同步的问题。 我以 Tomcat 向 MySQL 中写入和删改数据为例&#xff0c;来给你解释一下&#xff0c;数据的增删改操作具体是如何进行的。 我…

个推「数据驱动运营增长」城市巡回沙龙·上海专场:哈啰出行的自动化营销之路

近日&#xff0c;以“数据增能&#xff0c;高效提升用户运营价值”为主题的个推「数据驱动运营增长」城市巡回沙龙上海专场圆满举行。活动现场&#xff0c;哈啰出行基础算法负责人贾立从APP的营销场景切入&#xff0c;将“智能圈人->智能创意->智能活动->智能补贴->…

什么是指纹浏览器?——社媒营销多账号的管理神器

对于跨境卖家来说&#xff0c;通过海外社媒平台进行引流推广是不错的选择&#xff0c;但在实际操作中我们总会遇到很多问题。比如老手们肯定都经历过多个账号被封禁的情况&#xff0c;如果你也跟以前的东哥一样困扰怎么在一台电脑登录同平台多个账号&#xff0c;那今天这篇文章…

Go利用反射实现一个ini文件的解析器程序

package mainimport ("bufio" // 逐行读取配置文件"fmt""log""os""reflect""strconv""strings" )type Config struct { // 定义配置结构体Section1 Section1 ini:"section1" // 嵌套结构体1…

SAP系统供应商预付款请求和预付账款业务

最近搞清帐&#xff01; 在SAP中处理客户或供应商的预收/预付款相关业务流程操作说明, 首先由业务部门(销售或采购)下达销售/采购订单,同时基于订单提交预收/预付申请,客户/供应商款项到账时,由财务部门在SAP中勾选申请单来收付款;最后在财务转应收/应付转发票时自动核销。预付…

立体库堆垛机水平电机输出控制程序功能

###############水平电机输出控制程序################# #############水平变频器输出控制程序################# *******************水平速度曲线建立*********************** 列距离差值&#xff0c;建立与速度的关系式&#xff1a;VX/k MW220为K系数 水平速度控制K系数 列…

RK3568驱动指南|第七期-设备树-第66章of操作函数实验:获取设备树节点

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

圆形承重钢管用在线直线度测量仪实时检测品质!

关于直线度尺寸的检测&#xff0c;相信你听说过很多诸如直线法、直尺法、激光准直法等的离线检测方式&#xff0c;那你听说过在线直线度测量仪吗&#xff1f;它是可安装在产线上进行实时在线检测的设备。本文就跟随小编一起来简单的了解一下。 在线直线度测量仪的测量方法 直…

Notepad++,搜索窗口独立后,恢复

双击一下find result框&#xff0c;恢复到原来的模式。

使用cmd运行控制面板工具

如何通过键入命令运行“控制面板”工具 - Microsoft 支持 windows自带管理工具&#xff08;exe/cpl/msc&#xff09;-CSDN博客 CMD下打开系统各面板_cmd打开轻松使用面板-CSDN博客 示例&#xff1a; rundll32.exe shell32.dll,Control_RunDLL powercfg.cpl 替换powercfg.…

光计算1周2篇Nature,英伟达的时代彻底结束!

近期&#xff0c;光计算领域连续发出重量级文章&#xff0c;刊登在学术界的顶级期刊上。一时间&#xff0c;各大媒体纷纷转发&#xff0c;读者们也纷纷感叹&#xff1a;中国芯片取代英伟达的机会来了&#xff01;今天&#xff0c;光子盒用这篇万字长文为大家梳理光计算的背景、…

FL Studio最新版本号21.2发行更新啦

Image Line宣布发布FL Studio 21.2。更新带来了许多改进&#xff0c;但主要功能是引入了新的词干分离功能和FL Cloud&#xff0c;这是一个新的在线平台&#xff0c;直接与DAW集成&#xff0c;为用户提供从循环和样本到母带和发行功能的一切。 词干分离与FL云 随着最新更新的发…

记一次线上问题引发的对 Mysql 锁机制分析

背景 最近双十一开门红期间组内出现了一次因 Mysql 死锁导致的线上问题&#xff0c;当时从监控可以看到数据库活跃连接数飙升&#xff0c;导致应用层数据库连接池被打满&#xff0c;后续所有请求都因获取不到连接而失败 整体业务代码精简逻辑如下&#xff1a; Transaction p…