线程同步(2) - 内核模式下的线程同步

来源:互联网 发布:怎样学好历史高中知乎 编辑:程序博客网 时间:2024/05/16 19:48

转载自:http://mzf2008.blog.163.com/blog/static/355997862010112041821953/


1.内核模式下的等待

NTSTATUS 
  KeWaitForSingleObject(
    IN PVOID
  
Object,
    IN KWAIT_REASON  
WaitReason,
    IN KPROCESSOR_MODE  
WaitMode,
    IN BOOLEAN  
Alertable,
    IN PLARGE_INTEGER  
Timeout  OPTIONAL
    );

Object是一个同步对象的指针,注意这里不是句柄

WaitReason等待的原因,一般为Executive

WaitMode等待模式,一般为KernelMode

Alertable指明等待是否为“警惕”的,一般为FALSE

Timeout:等待时间,如果为NULL,就表示无限期等待,直到同步对象变为激发态

 

NTSTATUS 
  KeWaitForMultipleObjects(
    IN ULONG
  
Count,
    IN PVOID  
Object[],
    IN WAIT_TYPE  
WaitType,
    IN KWAIT_REASON  
WaitReason,
    IN KPROCESSOR_MODE  
WaitMode,
    IN BOOLEAN  
Alertable,
    IN PLARGE_INTEGER  
Timeout  OPTIONAL,
    IN PKWAIT_BLOCK
  
WaitBlockArray  OPTIONAL
    );

 

 

2.内核模式下创建线程

内核函数PsCreateSystemThread负责创建新线程。该函数可以创建两种线程,一种是用户线程,它属于当前进程中的线程。例如,如果IRP_MJ_READ的派遣函数中调用了PsCreateSystemThread,那么新创建的线程属于调用ReadFile的进程。另一种是系统线程,系统线程不属于当前用户进程,而是属于系统进程,一般PID4,名字为“System”的进程。

NTSTATUS 
  
PsCreateSystemThread(
    
OUT PHANDLE  ThreadHandle//新创建的线程句柄
    
IN ULONG  DesiredAccess, //创建的权限
    
IN POBJECT_ATTRIBUTES  ObjectAttributes  OPTIONAL,//线程的属性,一般设为NULL
    
IN HANDLE  ProcessHandle  OPTIONAL,//指定创建用户线程还是系统线程。如果为NULL,则为系统进程,如果该值是一个进程句柄,则新创建的线程属于这个指定的进程。DDK提供的NTCurrentProcess可以得到当前进程的句柄。
    
OUT PCLIENT_ID  ClientId  OPTIONAL,
    
IN PKSTART_ROUTINE  StartRoutine,//新线程的运行地址
    
IN PVOID  StartContext //新线程接收的参数
    );

注意:在内核模式下创建的线程是无法自动退出的,必须使用PsTerminateSystemThread强制结束线程。

这里介绍一个方法,可以很方便的让线程知道自己属于哪个进程。首先,使用IoGetCurrentProcess函数得到当前进程。IoGetCurrentProcess函数会得到一个PEPEOCESS数据结构。PEPROCESS结构记录进程的信息,其中包括进程名。在Windows xp Windows Server 2003中,PEPROCESS结构的0x174偏移位置记录着进程名。因此,可以用下面的方法查看当前进程信息:

PEPROCESS pEProcess = IoGetCurrentProcess();

PTSTR pString = (PTSTR)((ULONG)pEProcess + 0x174);

KdPrint(("这个线程运行在 %s 进程中!\n", pString));

 

下面代码演示了如何在驱动程序中创建线程。代码如下:

VOID SystemThread(IN PVOID lpParam)

{

         KdPrint(("进入系统线程函数!\n"));

         PEPROCESS pEProcess = IoGetCurrentProcess();

         PTSTR pString = (PTSTR)((ULONG)pEProcess + 0x174);

         KdPrint(("这个线程运行在 %s 进程中!\n", pString));

         KdPrint(("离开系统线程函数!\n"));

         //需要自己结束线程

         PsTerminateSystemThread(STATUS_SUCCESS);

}

 

VOID UserThread(IN PVOID lpParam)

{

         KdPrint(("进入用户线程函数!\n"));

         PEPROCESS pEProcess = IoGetCurrentProcess();

         PTSTR pString = (PTSTR)((ULONG)pEProcess + 0x174);

         KdPrint(("这个线程运行在 %s 进程中!\n", pString));

 

         KdPrint(("离开用户线程函数!\n"));

         PsTerminateSystemThread(STATUS_SUCCESS);

}

 

 VOID CreateSystemThread()

 {

         HANDLE hSystemThreadhUserThread;

         NTSTATUS status = PsCreateSystemThread(&hSystemThread, 0, NULLNULL,

NULLSystemThreadNULL);

         status = PsCreateSystemThread(&hUserThread, 0, NULLNtCurrentProcess(),

NULLUserThreadNULL);

 

         ZwClose(hSystemThread);

         ZwClose(hUserThread);

 }

线程同步(1) - 内核模式下的线程同步 - Fly - 从C开始

  

 

3.内核模式下的事件对象

在内核中,用KEVENT数据结构表示一个事件对象。在使用前,需要进行初始化。

VOID 
  KeInitializeEvent(
    IN PRKEVENT
  
Event//要初始化的KEVENT结构对象
    IN EVENT_TYPE  
Type//事件的类型。

    IN BOOLEAN  State //如果为真,初始为激发态
   );

Type事件的类型,有两类,一类是“通知事件”,对应参数为NotificationEvent,当事件变为激发态时,需要手动将其该为未激发状态。另一类为“同步事件”,对应参数为SynchronizationEvent,当事件为激发态时,如果遇到KeWaitFor*的函数,事件对象则自动变为未激发状态。

 

示例代码:

VOID SystemThread(IN PVOID lpParam)

{

         KdPrint(("进入系统线程函数!\n"));

         KEVENT kEvent = *(PKEVENT)lpParam;

         KeSetEvent(&kEventIO_NO_INCREMENTFALSE);

         KdPrint(("离开系统线程函数!\n"));

         PsTerminateSystemThread(STATUS_SUCCESS);

}

 

VOID CreateSystemThread()

 {

         KdPrint(("进入主线程!\n"));

         HANDLE hSystemThread;

         KEVENT kEvent;

         //初始化Event

         KeInitializeEvent(&kEventSynchronizationEventFALSE);

         //创建线程

         NTSTATUS status = PsCreateSystemThread(&hSystemThread, 0, NULLNULLNULL,

 SystemThread, &kEvent);

         KeWaitForSingleObject(&kEventExecutiveKernelModeFALSENULL);

         ZwClose(hSystemThread);

         KdPrint(("离开主线程!\n"));

 }

 

 

4.驱动程序与应用程序交互事件对象

应用程序中创建的事件和在内核模式下创建的事件对象,本质上是同一个东西。在用户模式下,它用句柄代表,在内核模式下,它用KEVENT数据结构代表。

在应用程序中,所有内核对象都不会被用户看到,用户看到的只是代表内核对象的对象句柄。

我们要将用户模式下创建的事件传递给驱动程序,可以用DeviceIoControl API函数。DDK提供了内核函数将句柄转化为指针,该函数如下:

NTSTATUS 
  ObReferenceObjectByHandle(
    IN HANDLE
  
Handle,
    IN ACCESS_MASK  
DesiredAccess,
    IN POBJECT_TYPE  
ObjectType  OPTIONAL,
    IN KPROCESSOR_MODE
  
AccessMode,
    OUT PVOID  *
Object,
    OUT POBJECT_HANDLE_INFORMATION  
HandleInformation  OPTIONAL
    );

ObReferenceObjectByHandle函数在得到指针的同时,会为对象的指针维护一个计数。每次调用ObReferenceObjectByHandle函数时会使计数加1.因此为了计数平衡,在使用完ObReferenceObjectByHandle函数后,需要调用如下函数:

VOID 
  ObDereferenceObject(
    IN PVOID
  
Object
    );

ObDereferenceObject函数使计数减一。

 

示例代码:

用户模式下代码:

#include <windows.h>

#include <stdio.h>

#include <winioctl.h>

#include "../NT_Driver/ioctl.h"

 

int main(void)

{

    HANDLE hFile = CreateFile("\\\\.\\HelloDDK", 0, 0,

NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

    if (hFile == INVALID_HANDLE_VALUE)

    {

       printf("error:%d", GetLastError());

       return -1;

    }

    printf("创建Event\n");

    //创建一个自动重置的,初始为未激发的事件对象

    HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

    DWORD dwRet;

    //调用DeviceIoControl把事件句柄传进内核

    DeviceIoControl(hFile, CLT_TRANSMIT_EVENT,

&hEvent, sizeof(hEvent), NULL, 0, &dwRet, NULL);

    //等待事件被激发

    WaitForSingleObject(hEvent, INFINITE);

    printf("程序退出!\n");

    return 0;

}

 

内核代码:

NTSTATUS FuckDeviceControl(IN PDEVICE_OBJECT pDeviceObj,

IN PIRP pIrp)

{

    KdPrint(("进入IRP_MJ_DEVICE_CONTROL处理函数!\n"));

   

    NTSTATUS status = STATUS_SUCCESS;

    PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);

    ULONG inLength =

              stack->Parameters.DeviceIoControl.InputBufferLength;

    ULONG outLength =

           stack->Parameters.DeviceIoControl.OutputBufferLength;

    ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;

 

    switch(code)

    {

       case CLT_TRANSMIT_EVENT:

       {

           //获得传递进来的事件句柄

HANDLE hEvent =

                  *(PHANDLE)pIrp->AssociatedIrp.SystemBuffer;

           PKEVENT pkEvent;

           //把句柄转化为KEvent结构

           status = ObReferenceObjectByHandle(hEvent,

              EVENT_MODIFY_STATE, *ExEventObjectType, KernelMode,

              (PVOID*)&pkEvent, NULL);

           //使事件激发

           KeSetEvent(pkEvent, IO_NO_INCREMENT, FALSE);

           //递减计数

           ObDereferenceObject(pkEvent);

           break;

       }

       default:

           status = STATUS_INVALID_VARIANT;

    }

 

    pIrp->IoStatus.Information = outLength;

    pIrp->IoStatus.Status = status;

    IoCompleteRequest(pIrp, IO_NO_INCREMENT);

    KdPrint(("离开IR_MJ_DEVICE_CONTROL处理函数!\n"));

 

    return STATUS_SUCCESS;

}

 

 

5.驱动程序与驱动程序交互对象

有时候,还需要驱动程序与驱动程序之间交互事件对象。例如,驱动程序A的某个派遣函数要与驱动程序B的派遣函数进行同步,就需要两个驱动程序间交互事件对象。

    最简单的方法是驱动程序B创建一个有名字的事件对象,这样,驱动程序A就可以根据名字找到事件对象的指针。

创建有名字的事件可以通过IoCreateSynchronizationEventIoCreateNotificationEvent函数。为了进一步得到事件内核对象的指针,可以使用前面介绍的ObReferenceObjectByHandle函数。

 

 

6.内核模式下的信号灯

在内核模式下,信号灯对象用KSEMAPHORE数据结构表示。在使用信号灯对象钱,需要对其进行初始化,函数如下:

VOID 
  KeInitializeSemaphore(
    IN PRKSEMAPHORE
  
Semaphore,
    IN LONG  
Count//初始化计数
    IN LONG  
Limit //最大计数
    );

 

KeReadStateSemaphore函数可以读取信号灯当前计数。

LONG 
  KeReadStateSemaphore(
    IN PRKSEMAPHORE
  
Semaphore
    );

 

释放信号灯会增加信号灯计数,它对应内核函数KeReleaseSemaphore函数

LONG 
  KeReleaseSemaphore(
    IN PRKSEMAPHORE
  
Semaphore,
    IN KPRIORITY  
Increment,
    IN LONG  
Adjustment,
    IN BOOLEAN  
Wait
    );

 

示例代码:

void UserThreadProc(IN PVOID lpParam)

{

    KdPrint(("进入新建的线程!\n"));

    //得到信号灯

    KSEMAPHORE kSemaphore = *(PKSEMAPHORE)lpParam;

    //增加信号灯计数

    KeReleaseSemaphore(&kSemaphore, IO_NO_INCREMENT, 1, FALSE);

    KdPrint(("离开新建的线程!\n"));

PsTerminateSystemThread(STATUS_SUCCESS);

}

 

VOID Test()

{

    KdPrint(("进入主线程!\n"));

    HANDLE hUserThread;

    ULONG count;

    KSEMAPHORE kSemaphore;

    //初始化信号灯

    KeInitializeSemaphore(&kSemaphore, 2, 2);

    //两次等待,用信号灯处于未激发状态

    KeWaitForSingleObject(&kSemaphore, Executive, KernelMode,

FALSE, NULL);

    KeWaitForSingleObject(&kSemaphore, Executive, KernelMode,

FALSE, NULL);

    //读取现有的信号灯计数,为0

    count = KeReadStateSemaphore(&kSemaphore);

    KdPrint(("信号灯计数为:%d\n", count));

    //创建新线程

    NTSTATUS status = PsCreateSystemThread(&hUserThread,

THREAD_ALL_ACCESS, NULL, NtCurrentProcess(), NULL,

UserThreadProc, &kSemaphore);

    //等待信号灯被激发

    KeWaitForSingleObject(&kSemaphore, Executive, KernelMode,

 FALSE, NULL);

    KdPrint(("离开主线程!\n"));

}

 

 

7.内核模式下的互斥体

互斥体在内核模式下的结构体为KMUTEX,使用前需要进行初始化:

VOID 
  KeInitializeMutex(
    IN PRKMUTEX
  
Mutex,
    IN ULONG  
Level //保留值,一般为0
    );

 

释放互斥体使用KeReleaseMutex内核函数:

LONG 
  KeReleaseMutex(
    IN PRKMUTEX
  
Mutex,
    IN BOOLEAN  
Wait
    );

 

示例代码:

void UserThreadProc1( IN PVOID lpParam)

{

    //获得互斥体

    PKMUTEX pMutex = (PKMUTEX)lpParam;

    //等待互斥体

    KeWaitForSingleObject(pMutex, Executive, KernelMode,

FALSE, NULL);

    KdPrint(("进入新建的线程1\n"));

    //强迫停止50ms

    KeStallExecutionProcessor(50);

    KdPrint(("离开新建的线程1\n"));

    //释放互斥体

    KeReleaseMutex(pMutex, FALSE);

    PsTerminateSystemThread(STATUS_SUCCESS);

}

 

void UserThreadProc2( IN PVOID lpParam)

{

    PKMUTEX pMutex = (PKMUTEX)lpParam;

    KeWaitForSingleObject(pMutex, Executive, KernelMode,

FALSE, NULL);

    KdPrint(("进入新建的线程2\n"));

    KeStallExecutionProcessor(50);

    KdPrint(("离开新建的线程2\n"));

    KeReleaseMutex(pMutex, FALSE);

    PsTerminateSystemThread(STATUS_SUCCESS);

}

 

VOID Test()

{

    HANDLE hUserThread1, hUserThread2;

    KMUTEX kMutex;

    //初始化互斥体

    KeInitializeMutex(&kMutex, 0);

    //创建两个新线程

    NTSTATUS status = PsCreateSystemThread(&hUserThread1, 0,

NULL, NtCurrentProcess(), NULL, UserThreadProc1, &kMutex);

    status = PsCreateSystemThread(&hUserThread2, 0, NULL,

               NtCurrentProcess(), NULL, UserThreadProc2, &kMutex);

   

    PVOID ThreadArray[2];

    //把线程句柄转换为可以等待的指针

    ObReferenceObjectByHandle(hUserThread1, 0, NULL,

KernelMode, &ThreadArray[0], NULL);

    ObReferenceObjectByHandle(hUserThread2, 0, NULL,

KernelMode, &ThreadArray[1], NULL);

    //等待2个新建线程执行完毕

    KeWaitForMultipleObjects(2, ThreadArray, WaitAll,

                         Executive, KernelMode, FALSE, NULL, NULL);

    //减少引用计数

    ObDereferenceObject(ThreadArray[0]);

    ObDereferenceObject(ThreadArray[1]);

    //关闭线程句柄

    ZwClose(hUserThread1);

    ZwClose(hUserThread2);

}

线程同步(1) - 内核模式下的线程同步 - Fly - 从C开始

  

 

8.快速互斥体

快速互斥体是DDK提供的另外一种内核同步对象,他的特征类似于前面介绍的普通互斥体。他们两的作用完全一样,之所以被称为是快速互斥体,是因为它执行的速度比普通的互斥体速度快(这里指的是获取和释放的速度)。然而,快速互斥体对象不能被递归获取。

普通互斥体在内核中用KMUTEX数据结构表示,而快速互斥体在内核中用FAST_MUTEX数据结构描述。

除此之外,对快速互斥体的初始化,获取和释放对应的内核函数也和普通互斥体不同。

初始化:

VOID 
  ExInitializeFastMutex(
    IN PFAST_MUTEX
  
FastMutex
    );

获取:

VOID 
  ExAcquireFastMutex(
    IN PFAST_MUTEX
  
FastMutex
    );

释放:

VOID 
  ExReleaseFastMutex(
    IN PFAST_MUTEX
  
FastMutex
    );

 

示例代码:

void UserThreadProc1( IN PVOID lpParam)

{

    PFAST_MUTEX pFastMutex = (PFAST_MUTEX)lpParam;

    ExAcquireFastMutex(pFastMutex);

    KdPrint(("进入新建的线程1\n"));

    KeStallExecutionProcessor(50);

    KdPrint(("离开新建的线程1\n"));

    ExReleaseFastMutex(pFastMutex);

    PsTerminateSystemThread(STATUS_SUCCESS);

}

 

void UserThreadProc2( IN PVOID lpParam)

{

    //获得快速互斥体

    PFAST_MUTEX pFastMutex = (PFAST_MUTEX)lpParam;

    ExAcquireFastMutex(pFastMutex);

    KdPrint(("进入新建的线程2\n"));

    //强迫停止50ms

    KeStallExecutionProcessor(50);

    KdPrint(("离开新建的线程2\n"));

    //释放互斥体

    ExReleaseFastMutex(pFastMutex);

    PsTerminateSystemThread(STATUS_SUCCESS);

}

 

void Test()

{

    HANDLE hUserThread1, hUserThread2;

    //初始化快速互斥体

    FAST_MUTEX fastMutex;

    ExInitializeFastMutex(&fastMutex);

    //创建两个新线程

    NTSTATUS status = PsCreateSystemThread(&hUserThread1, 0,

NULL, NtCurrentProcess(), NULL, UserThreadProc1, &fastMutex);

    status = PsCreateSystemThread(&hUserThread2, 0, NULL,

           NtCurrentProcess(), NULL, UserThreadProc2, &fastMutex);

   

    PVOID ThreadArray[2];

    //把线程句柄转换为可以等待的指针

    ObReferenceObjectByHandle(hUserThread1, 0, NULL,

KernelMode, &ThreadArray[0], NULL);

    ObReferenceObjectByHandle(hUserThread2, 0, NULL,

KernelMode, &ThreadArray[1], NULL);

    //等待2个新建线程执行完毕

    KeWaitForMultipleObjects(2, ThreadArray, WaitAll,

              Executive, KernelMode, FALSE, NULL, NULL);

    //减少引用计数

    ObDereferenceObject(ThreadArray[0]);

    ObDereferenceObject(ThreadArray[1]);

    //关闭线程句柄

    ZwClose(hUserThread1);

    ZwClose(hUserThread2);  

}

 

 

9.使用自旋锁进行同步

如果自旋锁已经被锁住,这时有程序申请“获取”这个自旋锁时,程序则处于“自旋”状态。所谓自旋状态,就是不停的询问是否可以“获取”自旋锁。

自旋锁不同于其他的等待事件。在线程中如果等待某个事件(Event),操作系统会让这个线程进入睡眠状态,CPU会转而运行其他线程。而自旋锁则不同,它不会切换到别的线程,而是一直让这个线程“自旋”。因此,对自旋锁占用时间不宜过长,否则会导致申请自旋锁的其他线程处于自旋,这会浪费宝贵的CPU时间。

自旋锁的作用一般是使各派遣函数之间同步。尽量不要将自旋锁放在全局变量中,而应该将自旋锁放在设备扩展里面。自旋锁用KSPIN_LOCK数据结构表示。

 

如下定义:

typedef struct _DEVICE_EXTENSION{

KSPIN_LOCK My_SpinLock; //在设备扩展中定义自旋锁

}DEVICE_EXTENSION,*PDEVICE_EXTENSION;

 

在使用自旋锁前需要对它进行初始化,一般在DriverEntry或者AddDevice函数中初始化自旋锁:

VOID 
  KeInitializeSpinLock(
    IN PKSPIN_LOCK
  
SpinLock
    );

 

申请自旋锁可以使用内核函数KeAcquireSpinLock,它有两个参数,一个是自旋锁的指针,另一个用来记录在获得自旋锁前的IRQL级别。

VOID 
  KeAcquireSpinLock(
    IN PKSPIN_LOCK
  
SpinLock,
    OUT PKIRQL  
OldIrql
    );

一般用法如下:

PDEVICE_EXTENSION pdx =

                  (PDEVICE_EXTENSION)pDevObj->DeviceExtension;

KIRQL oldirql;

KeAcquireSpinLock(&pdx->My_SpinLock, &oldirql);

 

释放自旋锁函数如下:

VOID 
  KeReleaseSpinLock(
    IN PKSPIN_LOCK
  
SpinLock,
    IN KIRQL  
NewIrql
    );

 

注意:如果在DISPATHC_LEVEL级别申请自旋锁,那么不会改变IRQL级别。这时,申请和释放自旋锁可以简单的使用KeAcquireSpinLockAtDpcLevel KeReleaseSpinLockFromDpcLevel内核函数。

 

示例代码:

定义设备扩展:

typedef struct _DEVICE_EXTENSION {

    PDEVICE_OBJECT pDevice;

    UNICODE_STRING ustrDeviceName;  //设备名称

    UNICODE_STRING ustrSymLinkName; //符号链接名

    KSPIN_LOCK My_SpinLock;            //自旋锁

} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

 

DriverEntry中初始化自旋锁:

PDEVICE_EXTENSION pDevExt =

                  (PDEVICE_EXTENSION)pDevObj->DeviceExtension;

……

KeInitializeSpinLock(&pDevExt->My_SpinLock);

 

IRP_MJ_DEVICE_CONTROL派遣函数:

NTSTATUS FuckTest(IN PDEVICE_OBJECT pDevObj,

                IN PIRP pIrp)

{

    //为了避免多个派遣函数并行运行,所以进行同步

    //DeviceIoControl调用来源自用户线程,因此处于PASSIVE_LEVEL

    ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);

    KdPrint(("进入IRP_MJ_DEVICE_CONTROL处理函数!\n"));

   

    PDEVICE_EXTENSION pdx =

                  (PDEVICE_EXTENSION)pDevObj->DeviceExtension;

    KIRQL oldIrql;

    //请求获得自旋锁

    KeAcquireSpinLock(&pdx->My_SpinLock, &oldIrql);

 

    KdPrint(("获得自旋锁!\n"));

    //获得自旋锁后,IRQL提升至DISPATCH_LEVEL

    ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);

    KdPrint(("IRQL提升至DISPATCH_LEVEL\n"));

 

    pIrp->IoStatus.Information = 0;

    pIrp->IoStatus.Status = STATUS_SUCCESS;

    IoCompleteRequest(pIrp, IO_NO_INCREMENT);

    //释放自旋锁

    KeReleaseSpinLock(&pdx->My_SpinLock, oldIrql);

    KdPrint(("释放互斥锁!\n"));

    KdPrint(("离开IRP_MJ_DEVICE_CONTROL处理函数!\n"));

 

    return STATUS_SUCCESS;

}

 

测试程序代码:

#include <windows.h>

#include <winioctl.h>

#include <stdio.h>

#include <process.h>

#include "../Driver/ioctl.h"

 

UINT WINAPI ThreadProc1(LPVOID lpParam)

{

    HANDLE hFile = *(PHANDLE)lpParam;

    BOOL bRet = DeviceIoControl(hFile, TEST_CTL, NULL,

0, NULL, 0, NULL, NULL);

    return 0;

}

 

int main(void)

{

    HANDLE hFile = CreateFile("\\\\.\\HelloDDK",

           GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING,

FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);

    if (hFile == INVALID_HANDLE_VALUE)

    {

       printf("打开设备失败!\n");

       return -1;

    }

 

    HANDLE hThread1 = (HANDLE)_beginthreadex(NULL, 0,

ThreadProc1, &hFile, 0, 0);

    HANDLE hThread2 = (HANDLE)_beginthreadex(NULL, 0,

ThreadProc1, &hFile, 0, 0);

    HANDLE hThread[2] = {hThread1, hThread2};

 

    WaitForMultipleObjects(2, hThread, TRUE, INFINITE);

 

    CloseHandle(hThread1);

    CloseHandle(hThread2);

    CloseHandle(hFile);

    return 0;

}

 

  

线程同步(1) - 内核模式下的线程同步 - Fly - 从C开始

 

 

10.使用互锁操作进行同步

DDK提供了两类互锁操作来提供简单的同步处理。一类是InterlockedXX函数,另一类是ExInterlockedXX函数。

其中InterlockedXX函数不是通过自旋锁实现的,内部不会提升IRQL,因此既可以操作分页数据又可以操作非分页数据。而ExInterlockedXX函数是通过自旋锁实现的,在使用的时候需要我们提供一个自旋锁。内部依靠这个自旋锁实现同步,因此它不能操作分页内存。

DDK提供的函数和功能如下图:

线程同步(2) - 内核模式下的线程同步 - Fly - 从C开始

 

线程同步(2) - 内核模式下的线程同步 - Fly - 从C开始

原创粉丝点击