3.3 同步技术 :事件对象

3.3.1 事件对象

使用事件对象是一种常见的同步方法。驱动程序可以使用事件对象同步完成IRP,或者驱动线程间同步执行某些操作。

要使用事件对象,首先需要调用KeInitializeEvent, IoCreateNotificationEvent或者IoCreateSynchronizationEvent初始化事件。KeInitializeEvent函数原型:

VOID KeInitializeEvent( IN PRKEVENT Event, IN EVENT_TYPE Type, IN BOOLEAN State);


3.3.2 信号灯对象

任何驱动程序都可以使用信号灯对象在驱动线程和处理例程间同步I/O操作。例如:当驱动程序没有未决的IRP时,驱动程序的某个线程处于等待状态;当驱动程序的IRP处理例程接受到IRP并排到未决的IRP队列中后,将增加一个信号灯,等待的线程满足等待条件,进入处理流程。

在使用信号灯之前,必须调用KeInitializeSemaphore函数初始化信号灯。KeInitializeSemaphore函数原型:

VOID KeInitializeSemaphore( IN PRKSEMAPHORE Semaphore, IN LONG Count, IN LONG Limit);


3.3.3 互斥体对象

互斥体对象是一种确保对共享资源互斥访问的同步机制。

在使用互斥体对象之前,要首先调用KeInitializeMutex函数初始化互斥体对象。KeInitializeMutex函数原型:

VOID KeInitializeMutex(IN PRKMUTEX Mutex, IN ULONG Level);

完全释放互斥体后互斥体对象才会有信号KeReleaseMutex






3.3.4 定时器对象
驱动程序可以使用定时器对象执行一个定时的操作。定时器对象包括DPC定时器和I/O定时器。I/O定时器的定时固定为1秒,不可变更;而DPC定时器的定时可以指定,更加灵活。
在使用DPC定时器之前,要首先调用KeInitializeTimer或者KeInitializeTimerEx函数初始化定时器对象。
KeInitializeTimer函数原型:VOID KeInitializeTimer(IN PKTIMER Timer);
KeInitializeTimerEx函数原型:VOID KeInitializeTimerEx(IN PKTIMER Timer, IN TIMER_TYPE Type); Type指定为NotificationTimer或SynchronizationTimer
DPC定时器典型使用方法:
1. 驱动程序的一个线程调用KeSetTimer设置定时器。KeSetTimer原型为:BOOLEAN KeSetTimer(IN PKTIMER Timer, IN LARGE_INTEGER DueTime, IN PKDPC Dpc OPTIONAL);
Timer为DPC定时器对象;DueTime指定了定时的时间;Dpc指定了一个DPC对象,为可选参数,如果设置一个DPC,那么DueTime时间到了以后。将执行该DPC;如果不指定DPC,那么该定时器对象功能表现为延迟执行,类似与KeDelayExecutionThread。
2. 驱动线程接着调用KeWaitForSignalObject等待定时器对象有信号
3. DueTime时间已过
4. 内核从定时器队列中出列一个定时器,将其设置为有信号,并将原等待线程由等待状态更改为准备执行状态
5. 内核执行线程分发,线程开始执行。


//
//头文件
//
#include "ntddk.h"








//
//驱动入口函数
//
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
    HANDLE hThread;
    OBJECT_ATTRIBUTES ObjectAttributes;
    CLIENT_ID CID;
    NTSTATUS status;




    KdPrint(("DriverEntry Cur Process:%s Cur IRQL:%d\n",
        (char*)((ULONG)IoGetCurrentProcess()+0x174),KeGetCurrentIrql()));




    //初始化定时器
    KeInitializerTimer(&Timer);




    DriverObject->DriverUnload = SyncTechUnload;




    InitializeObjectAttributes(&ObjectAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);




    //创建一个系统线程
    status = PsCreateSystemThread(
        &hThread, GENERIC_READ | GENERIC_WRITE,
        &ObjectAttributes, NtCurrentProcess(),
        &CID, (PKSTART_ROUTINE) ThreadStart, NULL);
    if (!NT_SUCESS(status))
    {
        KdPrint(("PsCreateSystenThread Failure!\n"));




        return 0;
    }




    ZwClose(hThread);




    KdPrint(("exit\n"));




    return STATUS_SUCCESS;
}








//
//线程设置Timer等候执行DPC
//
VOID ThreadStart(__in PVOID StartContext)
{
    LARGE_INTEGER DueTime;
    KDPC Dpc;




    KdPrint(("Cur Process: %s IRQL:%d\n",
        (char*)((ULONG)IoGetCurrentProcess()+0x174),
        KeGetCurrentIrql()));




    DueTime = RtlConvertLongToLargeInteger(-1000000);




    //初始化一个DPC
    KeInitializeDpc(&Dpc,(PKDEFERRED_ROUTINE)CustomDpc,NULL);




    //设置DPC定时器
    KeSetTime(&Timer,DueTime,&Dpc);




    //等待定时器
    KeWaitForSignalObject(&Timer,Executive,KernelMode,FALSE,NULL);




    KdPrint(("ThreadStart time expire!\n"));




    return;
}








//
//简单输出进程名和当前的IRQL,注意该函数运行在dispatch级别
//
VOID CustonDpc(
               __in struct _KDPC *Dpc,
               __in_opt PVOID DeferredContext,
               __in_opt PVOID SystemArgument1,
               __in_opt PVOID SystemArgument2)
{
    KdPrint(("CustomDpc Process: %s IRQL:%d\n",
        (char*)((ULONG)IoGetCurrentProcess()+0x174),//使用了硬编码
        KeGetCurrentIrql()));




    return;
}


上述代码中,驱动程序创建了一个系统线程,在此系统线程中设置了一个定时器,该定时器绑定了一个DPC,如果设置的时间到了就会激发DPC的交付


3.3.5 自旋锁
自旋锁是一种内核模式专用的同步机制。使用自旋锁可以在并行访问时保护共享数据及资源。
使用前调用KeInitializeSpinLock函数初始化自旋锁,调用KeAcquireSpinLock获取自旋锁,在访问完共享数据后调用函数KeReleaseSpinLock函数释放自旋锁。


3.3.6 回调对象
内核的回调机制为驱动程序提供了一种通用的通知方法:当某条件满足时,驱动程序A向驱动程序B通知某一消息。驱动程序A首先需要创建一个回调对象,其它的驱动程序(如驱动程序B)可以向该回调对象注册回调函数,当条件满足时,驱动程序A将调用所以向其注册的回调函数。创建一个回调对象:
1. 首先需求调用InitializeObjectAttributes函数初始回调对象的属性,注意必须为回调对象赋予名字,而且Attributes参数必须设置OBJ_PERMANENT标记,
2. 然后调用ExCreateCallback函数创建回调对象,ExCreateCallback函数原型如下:
NTSTATUS ExCreateCallback(OUT PCALBACK_OBJECT *CallbackObject, IN POBJECT_ATTRIBUTES ObjectAttributes, IN BOOLEAN Create, IN BOOLEAN ALLowMultipleCallbacks);
3. 其它驱动向该回调对象注册回调函数,这个回调函数在条件满足时被调用。注册回调函数使用的函数是ExRegisterCallback,函数原型如下:
PVOID ExRegisterCallback(IN PCALLBACK_OBJECT CallbackObject, IN PCALLBACK_FUCTION CallbackFunction, IN PVOID CallbackContext);
其中VOID (*PCALLBACK_FUNCTION)(IN PVOID CallbackContext, IN PVOID Argument1, IN PVOID Argument2);


//Callback驱动程序//
//
//头文件
//
#include "ntddk.h"




NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
    UNICODE_STRING DeviceName,Win32Device;
    PDEVICE_OBJECT DeviceObject = NULL;
    NTSTATUS status;
    unsigned i;




    UNICODE_STRING DemoCallback;
    OBJECT_ATTRIBUTES oa;




    RtlInitializeUnicodeString(&DeviceName,L"\\Device\\Callback0");
    RtlInitializeUnicodeString(&Win32Device,L"\\DosDevices\\Callback0");




    for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
    {
        DriverObject->MajorFunction[i] = CallbackDefaultHandler;
    }




    DriverObject->MajorFunction[IRP_MJ_CREATE] = CallbackCreateClose;
    DriverObject->MajorFunction[IRP_MJ_CLOSE] = CallbackCreateClose;




    DriverObject->DriverUnload = CallbackUnload;
    status = IoCreateDevice(DriverObject,
        0,&DeviceName,FILE_DEVICE_UNKNOWN,0,&FALSE,&DeviceObject);
    if (!NT_SUCCESS(status))
    {
        return status;
    }
    if (!DeviceObject)
    {
        return STATUS_UNEXPECTED_IO_ERROR;
    }




    DeviceObject->Flags |= DO_DIRECT_IO;




    status = IoCreateSymbolicLink(&Win32Device,&DeviceName);




    DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;




    //创建一个回调对象
    //注意回调对象都放在\Callback目录中
    RtlInitUnicodeString(&DemoCallback,L"\\Callback\\DemoCallback");




    //注意一定要指定OBJ_PERMANENT,否则ExCreateCallback无法成功创建一个带有名字的回调函数
    InitializeObjectAttributes(
        &oa,&DemoCallback,
        OBJ_CASE_INSENSITIVE|OBJ_PERMANENT,
        NULL,NULL);




    status = ExCreateCallback(&pCallback, &oa, TRUE, TRUE);
    if (!NT_SUCCESS(status))
    {
        KdPrint(("Callback ExCreateCallback Failure!status: 0x%08x\n",status));
    }




    KdPrint(("Callback --pCallback: 0x%08x\n",pCallback));




    return STATUS_SUCCESS;
    
}




//
//当有程序打开或者关闭本驱动的时候,将通知所以向回调对象注册的函数
//
NTSTATUS CallbackCreateClose(IN PDEVIE_OBJECT DeviceObject, IN PIRP Irp)
{
    PIO_STACK_LOCATION pSP = IoGetCurrentIrpStackLoaction(Irp);
    char* pImageName = NULL;




    if (pCallback)
    {
        //获取进程名,使用硬编码:x174
        pImageName = (char*)((ULONG)PsGetCurrentProcess()+0x174);




        //
        //通知注册函数
        //arg1:主功能码(IRP_MJ_READ或者IRP_MJ_WRITE))
        //arg2:当前进程的进程名
        //
        ExNotifyCallback(pCallback,(PVOID)pSP->MajorFunction,pImageName);
    }




    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest(Irp,IO_NO_INCREMENT);
    return STATUS_SUCCESS;
}


//Client驱动程序//
//
//头文件
//
#include "ntddk.h"




NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
    NTSTATUS status;
    UNICODE_STRING DemoCallback;
    OBJECT_ATTRIBUTES oa;




    DriverObject->DriverUnload = ClientUnload;




    do 
    {
        //打开回调对象
        RtlInitUnicodeString(&DemoCallback,L"\\Callback\\DemoCallback");




        InitializeObjectAttributes(
            &oa, &DemoCallback,
            OBJ_CASE_INSENSITIVE|OBJ_PERMANENT,
            NULL,NULL);




        //指定ExCreateCallback的Create参数为FALSE,即打开回调对象而不是创建
        status = ExCreateCallback(&pCallback,&oa,FALSE,TRUE);
        if (!NT_SUCCESS(status))
        {
            KdPrint(("Client:ExCreateCallback Failure!Status:0x%08x\n",
                status));
            
            break;
        }




        KdPrint(("Client --pCallback:0x%08x\n",pCallback));




        //向该回调对象注册一个回调函数
        pRegisterHandle = ExRegisterCallback(
            pCallback, (PCALLBACK_FUNCTION)CallbackFunction, NULL);
        if (NULL == pRegisterHandle)
        {
            KdPrint(("Client:ExRegisterCall Failure!\n"));




            ObDereferenceObject(pCallback);




            break;
        }




        //不需要回调函数时,减少引用次数(减少到0时,将会被删除)
        ObDereferenceObject(pCallback);




    } while ();




    return STATUS_SUCCESS;
}




//
//回调函数
//当有程序打开或者关闭Callback驱动程序时,Callback驱动将调用此函数
//本函数只是简单地输出Argument1和Argument2。Argument1和Argument2的
//语义由创建回调对象的驱动程序(本例中即Callback)定义
//Argument1:IRP的主功能码(IRP_MJ_CREATE或者IRP_MJ_CLOSE)
//Argument2:打开或关闭callback驱动的进程名
//
VOID CallbackFunction(
                      IN PVOID CallbackContext,
                      IN PVOID Argument1,
                      IN PVOID Argument2)
{
    KdPrint(("Client CallbackFunction--Argument1:%s Argument:%s\n",
        ((LONG)Argument1 == IRP_MJ_CREATE)?"IRP_MJ_CREATE":(((LONG)Argument1 == IRP_MJ_CLOSE)?
        "IRP_MJ_CLOSE":"exception!"),Argument2));




    return;
}


//CallbackUser应用程序//
#include <windows.h>




int main()
{
    HANDLE hFile;




    hFile = CreateFileA(
        "\\\\.\\Callback0",
        GENERIC_READ|GENERIC_WRITE,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL);




    if (INVALID_HANDLE_VALUE == hFile)
    {
        printf("CreateFile Failure!err:%d\n",
            GetLastError());




        return 0;
    }




    CloseHandle(hFile);




    return 0;
}
 加载Callback驱动程序,创建一个回调对象;加载Client驱动程序,向回调对象注册回调函数;最后执行CallbackUser,打开和关闭Callback驱动程序,从而触发回调条件,Callback驱动程序将通知所以向回调对象注册的回调函数


3.3.7 原子操作
原子操作保证代码执行的原子性,不会被打断。常用的原子操作包括:InterlockedIncrement、InterlockedDecrement、InterlockedCompareExchang等


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值