首页 > 系统相关 >多进程(线程)访问设备的一些疑惑

多进程(线程)访问设备的一些疑惑

时间:2022-11-07 20:34:46浏览次数:70  
标签:疑惑 Header 0x000 访问 线程 FILE DISPATCHER 设备


    同事在看设备驱动同步时,问了我一个事:如果驱动程序创建了一个设备,在应用层是否允许多个进程同时打开这个设备;如果允许,这种方式应用层和驱动的通信方式是否会相互影响?我不是很确定,写了个测试代码并把结果记录下来。

1.我们在DriverEntry/AddDevice中调用IoCreateDevice创建设备对象。这是IoCreateDevice的接口,倒数第二个参数用于设置设备是否支持独占式访问。一般情况下我们都会传入FALSE(MSDN也建议使用这种方式)。

NTSTATUS IoCreateDevice(
_In_ PDRIVER_OBJECT DriverObject,
_In_ ULONG DeviceExtensionSize,
_In_opt_ PUNICODE_STRING DeviceName,
_In_ DEVICE_TYPE DeviceType,
_In_ ULONG DeviceCharacteristics,
_In_ BOOLEAN Exclusive,
_Out_ PDEVICE_OBJECT *DeviceObject
);

对于FALSE的情况,设备支持多个进程访问:


这部分是驱动创建设备的片段,驱动在响应IRP_MJ_CREATE的派遣函数中设置了一个定时器。

NTSTATUS SampleCharAddDevice(PDRIVER_OBJECT drvObj, PDEVICE_OBJECT pdo)
{
NTSTATUS status = STATUS_SUCCESS;

RtlInitUnicodeString(&fdoName, L"\\Device\\SampleChar");
RtlInitUnicodeString(&fdoSymName, L"\\DosDevices\\SampleChar");

status = IoCreateDevice(drvObj, sizeof(SampleCharDevContext),
&fdoName,
FILE_DEVICE_UNKNOWN,
FILE_DEVICE_SECURE_OPEN,
FALSE, &fdo);
if (!NT_SUCCESS(status))
{
return status;
}
...
}

这部分是应用层打开设备的片段:

int main()
{
GetDeviceInterface(interfaceBuff);

hDev = CreateFileA(interfaceBuff,
GENERIC_ALL,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL);
if (hDev == INVALID_HANDLE_VALUE)
{
lstErr = GetLastError();
return 0;
}

while (1)
{
Sleep(1000);
}
}

这是同时运行两个测试程序打开设备的截图。从截图中可以看出,两个进程在打开设备时都没有从失败的分支退出。

多进程(线程)访问设备的一些疑惑_windows


2.修改代码,试图以独占方式访问设备。如果单独在调用IoCreateDevice时把Exclusive改为TRUE,并不能实现这个功能,还需要修改inf文件设备安装节AddReg部分:

多进程(线程)访问设备的一些疑惑_局部变量_02

AddReg中"HKR,,Exclusive,0x10001,1"的行为将会为设备的注册表硬件键(HKLM\SYSTEM\CurrentControl\Set\Enum\设备总线\设备号)添加一个值为1的Exclusive键。

多进程(线程)访问设备的一些疑惑_局部变量_03

之后再用两个进程去打开设备,后调用CreateFile的进程会调用失败,并且驱动程序的IRP_MJ_CREATE派遣函数也没有得到执行(换句话说是文件系统让CreateFile调用失败的,而不是驱动程序在派遣函数中做了特殊处理)。

多进程(线程)访问设备的一些疑惑_初始化_04

3.进程间相互影响.个人认为,因为派遣函数运行在线程上下文中,必然用了不同线程的内核栈。因此,在不同线程的内核栈中定义的局部变量,如:buf/spinlock/Kevent等,不会相互影响;如果派遣函数中要用到全局变量,则这些全局变量的初始化应该尽早在DriverEntry/AddDevice中做初始化,避免在派遣函数中重复初始化。至于R0和R3之间通信的IRP及IRP的参数,这是IO管理器为每次IO请求新创建的,因此也不会有影响。

1.
kd> ?? devCtx->timer
struct _KTIMER
+0x000 Header : _DISPATCHER_HEADER
+0x010 DueTime : _ULARGE_INTEGER 0x0
+0x018 TimerListEntry : _LIST_ENTRY [ 0x0 - 0x0 ]
+0x020 Dpc : (null)
+0x024 Period : 0
2.
kd> ?? devCtx->timer
struct _KTIMER
+0x000 Header : _DISPATCHER_HEADER
+0x010 DueTime : _ULARGE_INTEGER 0x00000001`5ecb0982
+0x018 TimerListEntry : _LIST_ENTRY [ 0x86e86f80 - 0x82b34f94 ]
+0x020 Dpc : 0x85381564 _KDPC
+0x024 Period : 0
3.
kd> ?? devCtx->timer
struct _KTIMER
+0x000 Header : _DISPATCHER_HEADER
+0x010 DueTime : _ULARGE_INTEGER 0x00000001`65ffd3f7
+0x018 TimerListEntry : _LIST_ENTRY [ 0x851544c0 - 0x82b3503c ]
+0x020 Dpc : 0x85381564 _KDPC
+0x024 Period
4.
kd> ?? devCtx->timer
struct _KTIMER
+0x000 Header : _DISPATCHER_HEADER
+0x010 DueTime : _ULARGE_INTEGER 0x00000001`760536ef
+0x018 TimerListEntry : _LIST_ENTRY [ 0x863e2e90 - 0x82b349c4 ]
+0x020 Dpc : 0x85381564 _KDPC
+0x024 Period : 0

5.
kd> ?? &timeEvt
struct _KEVENT * 0x8a8eba48
+0x000 Header : _DISPATCHER_HEADER

6.
kd> ?? &timeEvt
struct _KEVENT * 0x8cec6a48
+0x000 Header : _DISPATCHER_HEADER

解释一下:1-4是进入SampleCharCreateClose时,调用KeSetTimer设置定时器的windbg的输出,这是一个全局变量,存放在设备扩展区;5-6是局部变量KEVENT timeEvt;的变量地址。

1处 进入SampleCharCreateClose时定时器对象时,KTIMER仅做过初始化,各个域值都是0;调用KeSetTimer后再次进入SampleCharCreateClose,可以看到每次定时器的定时间隔都非0,且每次改动都会被保存到下一次进入SampleCharCreateClose前。这可以说明多线程环境下,设备对象扩展区中的变量会相互影响;

5.6处 进入SampleCharCreateClose操作的KEVENT对象都是栈变量,且每次生成的栈变量的地址都是不同的。因此,在一个线程上下文中的局部变量不会影响其他线程上下文中的局部变量。换言之,如果要做线程同步,绝对不能在派遣函数中新分配一个spinlock/KEVENT,这根本起不到同步的作用


参考:

MSDN:​​Setting Device Object Properties in the Registry​

MSDN:​​INF AddReg Directive​

MSDN:​​Specifying Exclusive Access to Device Objects​

标签:疑惑,Header,0x000,访问,线程,FILE,DISPATCHER,设备
From: https://blog.51cto.com/u_13927568/5831325

相关文章

  • 多线程详解
    1.多线程快速入门1.1进程与线程什么是进程?CPU从硬盘中读取一段程序到内存中,该执行程序的实例就叫做进程。一个程序如果被CPU多次读取到内存中,则变成多个独立的进程......
  • 多线程同步之条件变量
    对于多线程程序来说,同步是指在一定的时间内只允许某一个线程访问某个资源。而在此时间内,不允许其他的线程访问该资源。同步资源的方式:互斥锁、条件变量、读写锁、信号......
  • 多线程同步之互斥锁
    对于多线程程序来说,同步是指在一定的时间内只允许某一个线程访问某个资源。而在此时间内,不允许其他的线程访问该资源。同步资源的方式:互斥锁、条件变量、读写锁、信号......
  • 多线程同步之读写锁
    对于多线程程序来说,同步是指在一定的时间内只允许某一个线程访问某个资源。而在此时间内,不允许其他的线程访问该资源。同步资源的方式:互斥锁、条件变量、读写锁、信号......
  • 多线程同步之信号量
    对于多线程程序来说,同步是指在一定的时间内只允许某一个线程访问某个资源。而在此时间内,不允许其他的线程访问该资源。同步资源的方式:互斥锁、条件变量、读写锁、信号......
  • C# 多线程访问之 SemaphoreSlim(信号量)【C# 进阶】
    SemaphoreSlim是对可同时访问某一共享资源或资源池的线程数加以限制的Semaphore的轻量替代,也可在等待时间预计很短的情况下用于在单个进程内等待。由于SemaphoreSlim......
  • 线程池中多余的线程是如何回收的?
    最近阅读了JDK线程池​​ThreadPoolExecutor​​的源码,对线程池执行任务的流程有了大体了解,实际上这个流程也十分通俗易懂,就不再赘述了,别人写的比我好多了。不过,我倒是对线......
  • java 获取IP地址 无法获取到真实的IP地址springboot 获取访问接口的请求的IP地址
    工具类:springboot获取访问接口的请求的IP地址问题:无法获取到真实IP地址  获取出来全是 192.xxx.xxx.xxx开头或者 172.xxx.xxx.xxx 开头 解决方案:nginx代理需......
  • 计算机网络:信道划分介质访问控制
    计算机网络:信道划分介质访问控制介质访问控制所要完成的主要任务是:为使用介质的每个结点隔离来自同一信道上其他结点所传送的信号,以协调活动结点的传输。用来决定广播信道中......
  • JUC并发编程第二章之Java线程
    JUC并发专题系列JUC并发编程第一章之进程与线程JUC并发编程第二章之Java线程JUC并发编程第三章之共享模型之管程JUC并发编程第四章之共享模型之内存JUC并发编程第五章......