一个人在漂 发表于 2017-6-1 13:34:17

多线程 以及多线程安全

线程的运行级别(IRQL)


中断是指在CPU接到这个请求后停止手上的工作来处理我们的工作(指当出现需要时,CPU暂时停止当前程序的执行转而执行处理新情况的程序和执行过程)
中断优先级是指为使系统能及时响应并处理发生的所有中断,系统根据引起中断事件的重要性和紧迫程度,硬件将中断源分为若干个级别,称作中断优先级。


引用来自8649279
------------------------------------------------------------------------------------------------------------
IRQL级别越高,可调用的API函数就越少。
从高级别开始:




DIRQ,设备级中断,这是硬件设备的中断,只有底层的驱动程序才处理这个中断。


DISPATCH——LEVEL(DL),运行于这个级别的处理器会屏蔽除DPC以外的中断,不能访问可交换内存,所以这个级别能调用的API函数大大减少。




APC——LEVEL(AL),异步调用就运行在这个级别,这是会屏蔽APC级别的中断。在这个级别仍可访问可交换内存。当一个APC中断发生时,处理器提升到APC级别,这是,就禁止了其他的APC中断。驱动程序自己提身高APC级别,以便处理同步操作。


PASSIVE_LEVEL(PL)是最低级的IRQL,不会屏蔽任何中断。用户态应用程序的线程就运行在这个级别上,可以使用可交换的内存。
-----------------------------------------------------------------------------------------------------------------


中断级别要求
1.明白驱动中各个函数的中断级别
2.明白要调用的API的运行级别 RtlCopyUnicodeString
3.PASSIVE级别可以使用任何函数和内存
4.DISPATCH级别只能访问能运行在DISPATCH级别的API和非分页内存
5.NONPAGEPOOL内存可在任何级别使用
6.PAGEDPOOL只能在PASSIVE_LEVEL和APC_LEVEL使用
7.在PASSIVE和APC级别代码中加:PAGED_CODE()宏
#define PAGED_CODE() \
    { if (KeGetCurrentIrql() > APC_LEVEL) { \
          KdPrint(( "EX: Pageable code called at IRQL %d\n", KeGetCurrentIrql() )); \
          ASSERT(FALSE); \
       } \
    }
一般的
DriverEntry,DriverUnloadPassive级
各种分发函数Passive级
完成函数DL级
各种NDIS回调函数   DL级




内核多线程创建演示
VOID DoFind(IN PVOID pContext)
{
}
void StartThread()
{
          HANDLE hThread         = NULL;
          PVOID objtowait        = 0;
          NTSTATUS dwStatus =
        PsCreateSystemThread(
        &hThread,
        0,
        NULL,
        (HANDLE)0,
        NULL,
        DoFind,
        NULL
        );
        if (!NT_SUCCESS(dwStatus)
       {
                           return;
        }
        if ((KeGetCurrentIrql())!=PASSIVE_LEVEL)
       {
                dwStatus=KfRaiseIrql(PASSIVE_LEVEL);
        }
        if ((KeGetCurrentIrql())!=PASSIVE_LEVEL)
       {
                return;
        }       
        ObReferenceObjectByHandle(
                hThread,
                THREAD_ALL_ACCESS,
                NULL,
                KernelMode,
                &objtowait,
                NULL
                );
        KeWaitForSingleObject(objtowait,Executive,KernelMode,FALSE,NULL);
        <span style="font-family: 'Courier New'; line-height: 18px; white-space: pre-wrap;">ObDereferenceObject</span>(objtowait);
        return;
}


同步互斥
互斥(A和B只能一个访问)
KSPIN_LOCK
ERESOURCE
FAST_MUTEX
同步(A告诉B发生了什么事)
KEVENT
KSEMAPHORE
KMUTEX
KeWaitForSingleObject
KEVENT
KMUTEX/KMUTANT
KPROCESS
KQUEUE
KSEMAPHORE
KTHREAD
KTIMER(都带dispatcher header)
fileobject/driverobject等不行


KeWaitForSingleObject演示:
LARGE_INTEGER TimeOut ={0};
TimeOut.QuadPart = -10 * 10000000i64;//10s


KeWaitForSingleObject(         &kSemaphore,
        Executive,
        KernelMode,
        FALSE,
        &TimeOut//0,不等;NULL,无限等待
        );
同步:
一.KEVENT
用于线程同步
Event两个状态:
Signaled
Non-signaled
Event两个类别:
Notification事件:不自动恢复
synchronization事件:自动恢复


创建:IoCreateNotificationEvent //创建一个有名字的事件 供其它进程 或R3 R0通信

KEVENT waitEvent;
KeInitializeEvent(&waitEvent,
               NotificationEvent,
               FALSE );
//开灯
KeSetEvent(&waitEvent,
        IO_NO_INCREMENT,
        FALSE );


//等灯
KeWaitForSingleObject(
                  &waitEvent,
                  Executive,
                  KernelMode,
                  FALSE,
                  NULL );//0,立即返回;
                              //NULL无限等待


//关灯
KeClearEvent(&waitEvent);
KeResetEvent(&waitEvent);


例子:进程创建监视:R0到R3同步通信 这个例子在xp和win7中的效果是不同的,xp上只要得到了信号就会退出,win7不会 原因:
-------------------------------------------------------------------------------------------------------
在XP中创建一个进程就退出 原因是WaitForSingleObject 等待成功后返回0
而在win7中 WaitForSingleObject 等待成功后返回0xffffffff;

------------------------------------------------------------------------------------------------------
Event创建:

L\\BaseNamedObjects\\ProcEvent
IoCreateNotificationEvent
数据存放:使用设备扩展 详细使用方法见下面完整代码
定义
typedef struct _DEVICE_EXTENSION
{
HANDLE       hProcessHandle;    // 事件对象句柄
PKEVENT      ProcessEvent;      // 用户和内核通信的事件对象指针
HANDLE       hParentId;          // 在回调函数中保存进程信息
HANDLE       hProcessId;
BOOLEAN   bCreate;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
IoCreateDevice( DriverObject, sizeof(DEVICE_EXTENSION), &ustrDeviceName,...);
访问:
// 获得DEVICE_EXTENSION结构
PDEVICE_EXTENSION deviceExtension =
(PDEVICE_EXTENSION)g_pDeviceObject->DeviceExtension;
// 保存信息
deviceExtension->hParentId = hParentId;
deviceExtension->hProcessId = PId;
deviceExtension->bCreate = bCreate;
事件触发
// 触发事件,通知应用程序
KeSetEvent(deviceExtension->ProcessEvent, 0, FALSE);
KeClearEvent(deviceExtension->ProcessEvent);


R3
#define EVENT_NAMEL"\\Global\\ProcEvent"


// 打开内核事件对象
HANDLE hProcessEvent = ::OpenEventW(SYNCHRONIZE, FALSE, EVENT_NAME);


while (::WaitForSingleObject(hProcessEvent, INFINITE))
{
//bugs to fix here
//发送DeviceIoControl下去拿数据
}


当然是做监控 最好不要采用这种方式,原因是有个死循环 虽然通过sleep可以减低cpu占用率 但这个思路还是错的 只为实验而已
R3:

// ProcWatchClientConsole.cpp : Defines the entry point for the console application.
//

//#include "stdafx.h"

#include "windows.h"
#include "winioctl.h"
#include "stdio.h"

BOOL LoadDriver(char* lpszDriverName,char* lpszDriverPath);
BOOL UnloadDriver(char * szSvrName);


#define EVENT_NAME    L"ProcEvent"
#define DRIVER_NAME          "ProcWatch"
#define DRIVER_PATH          ".\\ProcWatch.sys"

#pragma comment(lib,"Advapi32.lib")

#define                CTRLCODE_BASE 0x8000
#define                MYCTRL_CODE(i) \
CTL_CODE(FILE_DEVICE_UNKNOWN,CTRLCODE_BASE + i, METHOD_BUFFERED, FILE_ANY_ACCESS)

#define                IOCTL_PROCWATCH                MYCTRL_CODE(0)

typedef struct _ProcMonData
{
    HANDLEhParentId;
    HANDLEhProcessId;
    BOOLEAN bCreate;
}ProcMonData, *PProcMonData;


int main(int argc, char* argv[])
{

        ProcMonData pmdInfoNow = {0};
        ProcMonData pmdInfoBefore = {0};

        if (!LoadDriver(DRIVER_NAME, DRIVER_PATH))
        {
                printf("LoadDriver Failed:%x\n", GetLastError());
                return -1;
        }
    // 打开驱动设备对象
    HANDLE hDriver = ::CreateFile(
                "\\\\.\\ProcWatch",
                GENERIC_READ | GENERIC_WRITE,
                0,
                NULL,
                OPEN_EXISTING,
                FILE_ATTRIBUTE_NORMAL,
                NULL);
    if (hDriver == INVALID_HANDLE_VALUE)
    {
      printf("Open device failed:%x\n", GetLastError());
                UnloadDriver(DRIVER_NAME);
      return -1;
    }
    // 打开内核事件对象
    HANDLE hProcessEvent = ::OpenEventW(SYNCHRONIZE, FALSE, EVENT_NAME);

    //while (TRUE)
    //{
      //DWORD    dwRet        = 0;
      //BOOL   bRet        = FALSE;

      //::WaitForSingleObject(hProcessEvent, INFINITE);
        //WAIT_OBJECT_0
                //WAIT_TIMEOUT
    while (1)
    {
                ULONG test = ::WaitForSingleObject(hProcessEvent, INFINITE);
                Sleep(100);
      DWORD    dwRet        = 0;
      BOOL   bRet        = FALSE;


      bRet = ::DeviceIoControl(
                        hDriver,
                        IOCTL_PROCWATCH,
                        NULL,
                        0,
                        &pmdInfoNow,
                        sizeof(pmdInfoNow),
                        &dwRet,
                        NULL);
      if (bRet)
      {
            if (pmdInfoNow.hParentId != pmdInfoBefore.hParentId || \
                pmdInfoNow.hProcessId != pmdInfoBefore.hProcessId || \
                pmdInfoNow.bCreate != pmdInfoBefore.bCreate)
            {
                if (pmdInfoNow.bCreate)
                {
                  printf("ProcCreated,PID = %d\n", pmdInfoNow.hProcessId);
                }
                else
                {
                  printf("ProcTeminated,PID = %d\n", pmdInfoNow.hProcessId);
                }
                pmdInfoBefore = pmdInfoNow;
            }
      }
      else
      {
            printf("Get Proc Info Failed!\n");
            break;
      }
    }

    ::CloseHandle(hDriver);
        UnloadDriver(DRIVER_NAME);

    return 0;
}

//装载NT驱动程序
BOOL LoadDriver(char* lpszDriverName,char* lpszDriverPath)
{
        char szDriverImagePath = {0};
        //得到完整的驱动路径
        GetFullPathName(lpszDriverPath, 256, szDriverImagePath, NULL);

        BOOL bRet = FALSE;

        SC_HANDLE hServiceMgr=NULL;//SCM管理器的句柄
        SC_HANDLE hServiceDDK=NULL;//NT驱动程序的服务句柄

        //打开服务控制管理器
        hServiceMgr = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );

        if( hServiceMgr == NULL )
        {
                //OpenSCManager失败
                printf( "OpenSCManager() Faild %d ! \n", GetLastError() );
                bRet = FALSE;
                goto BeforeLeave;
        }
        else
        {
                ////OpenSCManager成功
                printf( "OpenSCManager() ok ! \n" );
        }

        //创建驱动所对应的服务
        hServiceDDK = CreateService( hServiceMgr,
                lpszDriverName, //驱动程序的在注册表中的名字
                lpszDriverName, // 注册表驱动程序的 DisplayName 值
                SERVICE_ALL_ACCESS, // 加载驱动程序的访问权限
                SERVICE_KERNEL_DRIVER,// 表示加载的服务是驱动程序
                SERVICE_DEMAND_START, // 注册表驱动程序的 Start 值
                SERVICE_ERROR_IGNORE, // 注册表驱动程序的 ErrorControl 值
                szDriverImagePath, // 注册表驱动程序的 ImagePath 值
                NULL,//GroupOrder HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\GroupOrderList
                NULL,
                NULL,
                NULL,
                NULL);

        DWORD dwRtn;
        //判断服务是否失败
        if( hServiceDDK == NULL )
        {
                dwRtn = GetLastError();
                if( dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_EXISTS )
                {
                        //由于其他原因创建服务失败
                        printf( "CrateService() Faild %d ! \n", dwRtn );
                        bRet = FALSE;
                        goto BeforeLeave;
                }
                else
                {
                        //服务创建失败,是由于服务已经创立过
                        printf( "CrateService() Faild Service is ERROR_IO_PENDING or ERROR_SERVICE_EXISTS! \n" );
                }

                // 驱动程序已经加载,只需要打开
                hServiceDDK = OpenService( hServiceMgr, lpszDriverName, SERVICE_ALL_ACCESS );
                if( hServiceDDK == NULL )
                {
                        //如果打开服务也失败,则意味错误
                        dwRtn = GetLastError();
                        printf( "OpenService() Faild %d ! \n", dwRtn );
                        bRet = FALSE;
                        goto BeforeLeave;
                }
                else
                {
                        printf( "OpenService() ok ! \n" );
                }
        }
        else
        {
                printf( "CrateService() ok ! \n" );
        }

        //开启此项服务
        bRet= StartService( hServiceDDK, NULL, NULL );
        if( !bRet )
        {
                DWORD dwRtn = GetLastError();
                if( dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_ALREADY_RUNNING )
                {
                        printf( "StartService() Faild %d ! \n", dwRtn );
                        bRet = FALSE;
                        goto BeforeLeave;
                }
                else
                {
                        if( dwRtn == ERROR_IO_PENDING )
                        {
                                //设备被挂住
                                printf( "StartService() Faild ERROR_IO_PENDING ! \n");
                                bRet = FALSE;
                                goto BeforeLeave;
                        }
                        else
                        {
                                //服务已经开启
                                printf( "StartService() Faild ERROR_SERVICE_ALREADY_RUNNING ! \n");
                                bRet = TRUE;
                                goto BeforeLeave;
                        }
                }
        }
        bRet = TRUE;
//离开前关闭句柄
BeforeLeave:
        getchar();
        if(hServiceDDK)
        {
                CloseServiceHandle(hServiceDDK);
        }
        if(hServiceMgr)
        {
                CloseServiceHandle(hServiceMgr);
        }
        return bRet;
}

//卸载驱动程序
BOOL UnloadDriver( char * szSvrName )
{
        BOOL bRet = FALSE;
        SC_HANDLE hServiceMgr=NULL;//SCM管理器的句柄
        SC_HANDLE hServiceDDK=NULL;//NT驱动程序的服务句柄
        SERVICE_STATUS SvrSta;
        //打开SCM管理器
        hServiceMgr = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
        if( hServiceMgr == NULL )
        {
                //带开SCM管理器失败
                printf( "OpenSCManager() Faild %d ! \n", GetLastError() );
                bRet = FALSE;
                goto BeforeLeave;
        }
        else
        {
                //带开SCM管理器失败成功
                printf( "OpenSCManager() ok ! \n" );
        }
        //打开驱动所对应的服务
        hServiceDDK = OpenService( hServiceMgr, szSvrName, SERVICE_ALL_ACCESS );

        if( hServiceDDK == NULL )
        {
                //打开驱动所对应的服务失败
                printf( "OpenService() Faild %d ! \n", GetLastError() );
                bRet = FALSE;
                goto BeforeLeave;
        }
        else
        {
                printf( "OpenService() ok ! \n" );
        }
        //停止驱动程序,如果停止失败,只有重新启动才能,再动态加载。
        if( !ControlService( hServiceDDK, SERVICE_CONTROL_STOP , &SvrSta ) )
        {
                printf( "ControlService() Faild %d !\n", GetLastError() );
        }
        else
        {
                //打开驱动所对应的失败
                printf( "ControlService() ok !\n" );
        }
        //动态卸载驱动程序。
        if( !DeleteService( hServiceDDK ) )
        {
                //卸载失败
                printf( "DeleteSrevice() Faild %d !\n", GetLastError() );
        }
        else
        {
                //卸载成功
                printf( "DelServer:deleteSrevice() ok !\n" );
        }
        bRet = TRUE;
BeforeLeave:
//离开前关闭打开的句柄
        if(hServiceDDK)
        {
                CloseServiceHandle(hServiceDDK);
        }
        if(hServiceMgr)
        {
                CloseServiceHandle(hServiceMgr);
        }
        return bRet;       
}

R0:


#include "ntddk.h"
#include "windef.h"

#define EVENT_NAME    L"\\BaseNamedObjects\\ProcEvent"
#define DEVICE_NAME          L"\\Device\\ProcWatch"
#define LINK_NAME          L"\\DosDevices\\ProcWatch"

#define                CTRLCODE_BASE 0x8000
#define                MYCTRL_CODE(i) \
        CTL_CODE(FILE_DEVICE_UNKNOWN,CTRLCODE_BASE + i, METHOD_BUFFERED, FILE_ANY_ACCESS)

#define                IOCTL_PROCWATCH                MYCTRL_CODE(0)

typedef struct _ProcMonData
{
    HANDLEhParentId;
    HANDLEhProcessId;
    BOOLEAN bCreate;
}ProcMonData, *PProcMonData;


VOID DriverUnload(IN PDRIVER_OBJECT DriverObject);

NTSTATUS CommonDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
NTSTATUS IoctrlDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);

VOID ProcessCreateMon( IN HANDLE hParentId, IN HANDLE PId, IN BOOLEAN bCreate);


typedef struct _DEVICE_EXTENSION
{
    HANDLE             hProcessHandle;      // 事件对象句柄
    PKEVENT            ProcessEvent;          // 用户和内核通信的事件对象指针
    HANDLE             hParentId;             // 在回调函数中保存进程信息
    HANDLE             hProcessId;
    BOOLEAN            bCreate;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

PDEVICE_OBJECT g_pDeviceObject = NULL;

// 驱动入口
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath )

{
        UNICODE_STRING        ustrDeviceName = {0};
        UNICODE_STRING        ustrLinkName = {0};
        PDEVICE_OBJECT        deviceObject = NULL;
        NTSTATUS                status = STATUS_SUCCESS;
        int                                i = 0;
        UNICODE_STRING        ustrEventStr = {0};       
        PDEVICE_EXTENSION pDeviceExtension = NULL;
        //建立设备

        RtlInitUnicodeString( &ustrDeviceName, DEVICE_NAME );
        status = IoCreateDevice( DriverObject,
          sizeof(DEVICE_EXTENSION),
          &ustrDeviceName,
          FILE_DEVICE_UNKNOWN,
          0,
          FALSE,
          &deviceObject
          );
       
        if (!NT_SUCCESS( status ))
        {
                return status;
        }

        deviceObject->Flags |= DO_BUFFERED_IO;

        g_pDeviceObject = deviceObject;

        // 创建事件对象与应用层通信
        RtlInitUnicodeString(&ustrEventStr, EVENT_NAME);
        pDeviceExtension = (PDEVICE_EXTENSION)deviceObject->DeviceExtension;
   
        pDeviceExtension->ProcessEvent =
                IoCreateNotificationEvent(&ustrEventStr,
                &pDeviceExtension->hProcessHandle);
        KeClearEvent(pDeviceExtension->ProcessEvent);            // 设置为非受信状态

        RtlInitUnicodeString( &ustrLinkName, LINK_NAME);
        status = IoCreateSymbolicLink(&ustrLinkName, &ustrDeviceName);

        if (!NT_SUCCESS( status ))
        {
                IoDeleteDevice(DriverObject->DeviceObject);
                return status;
        }

        status = PsSetCreateProcessNotifyRoutine(ProcessCreateMon, FALSE);
        if (!NT_SUCCESS( status ))
        {
                  IoDeleteDevice(DriverObject->DeviceObject);
                  DbgPrint("PsSetCreateProcessNotifyRoutine()\n");
                  return status;
        }

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

        DriverObject->MajorFunction = IoctrlDispatch;

        DriverObject->DriverUnload = DriverUnload;

        return STATUS_SUCCESS;
}

VOID DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
        UNICODE_STRING ustrLinkName;
        PsSetCreateProcessNotifyRoutine(ProcessCreateMon, TRUE);
        RtlInitUnicodeString(&ustrLinkName, LINK_NAME);
        IoDeleteSymbolicLink(&ustrLinkName);
        IoDeleteDevice(DriverObject->DeviceObject);
}

//处理设备对象操作
NTSTATUS CommonDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)

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

NTSTATUS IoctrlDispatch(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)
{
        NTSTATUS                        ntStatus = STATUS_SUCCESS;
        PVOID                                pUserBuffer = NULL;
        ULONG                                ulInputSize = 0;
        ULONG                                ulOutputSize = 0;
        PIO_STACK_LOCATION        pIrpStack = NULL;
        ULONG                                ulIoCtrlCode = 0;
        PProcMonData                pProcMonData = NULL;
        PDEVICE_EXTENSION        pDeviceExtension= NULL;

        pIrpStack = IoGetCurrentIrpStackLocation(pIrp);

        pUserBuffer = pIrp->AssociatedIrp.SystemBuffer;

        pProcMonData = (PProcMonData)pUserBuffer;

        ulIoCtrlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
        ulInputSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
        ulOutputSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;

        switch(ulIoCtrlCode)
        {
        case IOCTL_PROCWATCH:

                pDeviceExtension = (PDEVICE_EXTENSION)g_pDeviceObject->DeviceExtension;

                pProcMonData->bCreate = pDeviceExtension->bCreate;
                pProcMonData->hParentId = pDeviceExtension->hParentId;
                pProcMonData->hProcessId = pDeviceExtension->hProcessId;

                break;
        default:
                ntStatus = STATUS_INVALID_PARAMETER;
                ulOutputSize = 0;
                break;
        }

        pIrp->IoStatus.Status = ntStatus;
        pIrp->IoStatus.Information = ulOutputSize;

        IoCompleteRequest(pIrp, IO_NO_INCREMENT);

        return ntStatus;
}

VOID ProcessCreateMon ( IN HANDLE hParentId, IN HANDLE PId,IN BOOLEAN bCreate )
{
    // 获得DEVICE_EXTENSION结构
    PDEVICE_EXTENSION deviceExtension =
                (PDEVICE_EXTENSION)g_pDeviceObject->DeviceExtension;
    // 保存信息
    deviceExtension->hParentId = hParentId;
    deviceExtension->hProcessId = PId;
    deviceExtension->bCreate = bCreate;
    // 触发事件,通知应用程序
    KeSetEvent(deviceExtension->ProcessEvent, 0, FALSE);
    KeClearEvent(deviceExtension->ProcessEvent);
}


注意了 上面这个工程中有个循环 ,循环中有个变量时test 这个变量在xp下 一般返回0 在win7 下返回0xffffffff;
这个代码没有改之前是

while (::WaitForSingleObject(hProcessEvent, INFINITE))
{
                ....
}这会影响循环是否keep runing



2.KSEMAPHORE
比如工作者与消费者之间 工作者增加一个资源 消费者才能买一个 消费者买了一个 就减少一个资源
用于同步与多个资源共享访问
KSEMAPHORE kSemaphore;
KeInitializeSemaphore(
    &kSemaphore,
    1,//信号量的初始值
    2 //信号量的最大值
    );
LARGE_INTEGER waitTime = {0};
waitTime.QuadPart = -1*10000000i64;
KeWaitForSingleObject(&kSemaphore, Executive, KernelMode, FALSE, &waitTime);//0,立即返回;NULL,无限等待
KeReleaseSemaphore(&kSemaphore ,IO_NO_INCREMENT , 1 , FALSE );

例子:工作者 与 消费者
#include <ntddk.h>


ULONG        g_ulTotal = 0;
KSEMAPHORE g_kSemaphore;



VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
        DbgPrint("Goodbye, driver\n");
}


VOID Worker(IN PVOID pContext)
{
        ULONG i = 0;
        LARGE_INTEGER waitTime = {0};
        waitTime.QuadPart = -3*10000000i64;


        while(i < 10)
        {
                g_ulTotal++;
                KeReleaseSemaphore(&g_kSemaphore ,IO_NO_INCREMENT , 1 , FALSE );//增加一个资源semaphore
                DbgPrint("Worker:produced 1, total:%x\n", g_ulTotal);
                i++;
                KeDelayExecutionThread(KernelMode, FALSE, &waitTime);//延迟3秒
        }


}


VOID Consumer(IN PVOID pContext)
{
        ULONG i = 10;
        LARGE_INTEGER waitTime = {0};
        waitTime.QuadPart = -3*10000000i64;


        while(i > 0)
        {
               
                KeWaitForSingleObject(&g_kSemaphore, Executive, KernelMode, FALSE, NULL);//等待资源并减少一个semaphore(等待成功后减少一个)
                g_ulTotal--;
                DbgPrint("Consumer:consumed 1, total:%x\n", g_ulTotal);
                i--;
                KeDelayExecutionThread(KernelMode, FALSE, &waitTime);//延迟3秒


        }


}


void StartThreads()
{
        HANDLE hThread1        = NULL;
        HANDLE hThread2        = NULL;


        PVOIDobjtowait = {NULL};
        NTSTATUS ntStatus =
                PsCreateSystemThread(//创建工作者线程
                &hThread1,
                0,
                NULL,
                (HANDLE)0,
                NULL,
                Worker,
                NULL
                );
        if (!NT_SUCCESS(ntStatus))
        {
                return;
        }


        ntStatus =
                PsCreateSystemThread(//创建消费者线程
                &hThread2,
                0,
                NULL,
                (HANDLE)0,
                NULL,
                Consumer,
                NULL
                );
        if (!NT_SUCCESS(ntStatus))
        {
                return;
        }


        if ((KeGetCurrentIrql())!=PASSIVE_LEVEL)
        {
                ntStatus = KfRaiseIrql(PASSIVE_LEVEL);
        }
        if ((KeGetCurrentIrql())!=PASSIVE_LEVEL)
        {
                return;
        }       
        ntStatus = ObReferenceObjectByHandle(
                hThread1,
                THREAD_ALL_ACCESS,
                NULL,
                KernelMode,
                &objtowait,
                NULL
                );
        if (!NT_SUCCESS(ntStatus))
        {
                return;
        }


        ntStatus = ObReferenceObjectByHandle(
                hThread2,
                THREAD_ALL_ACCESS,
                NULL,
                KernelMode,
                &objtowait,
                NULL
                );
        if (!NT_SUCCESS(ntStatus))
        {
                ObDereferenceObject(objtowait);
                return;
        }


        KeWaitForMultipleObjects(//等待两个线程结束
                2,
                objtowait,
                WaitAll,
                Executive,
                KernelMode,
                FALSE,
                NULL,
                NULL);


        ObDereferenceObject(objtowait);
        ObDereferenceObject(objtowait);


        //KeWaitForSingleObject(objtowait,Executive,KernelMode,FALSE,NULL);
        return;
}






NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegPath)
{
        pDriverObject->DriverUnload = DriverUnload;
        KeInitializeSemaphore(
                &g_kSemaphore,
                0,//信号量的初始值
                10 //信号量的最大值
                );




        StartThreads();




        return STATUS_SUCCESS;
}




二.互斥


1.KSPIN_LOCK


KSPIN_LOCK与Mutex的区别:
1.KSPIN_LOCK是忙等,不会阻塞系统,对忙等线程的调度则该线程会占着CPU不放,一直轮循
因此,Spinlock适用于等待时间不会太长(不超过25微妙)的情况.
而Mutex不是,它会系统阻塞请求线程,如果需要长时间范围一个对线,那么首先考虑使用互斥而不是自循锁


2.Spinlock请求成功之后,CPU的执行级别会提升到DL,Mutex不会.


3.DL及一线级别都可以使用Spinlock,而Mutex通常在PL请求,如果要在DL上则TIMEOUT需要设为0(只能试探一下)


4.Spinlock是“非递归锁” 不能递归获得该锁,而Mutex是“递归锁”


5.Spinlock主要用于多CUP,但效率不高,使用ERESOURCE较好.


KSPIN_LOCK 使用:
用于线程互斥
使用注意事项:
多CPU共享安全
提升IRQL到DPC
禁止访问分页内存
获得时间越短越好
使用方法:
//定义
KIRQL                   OldIrql;
KSPIN_LOCK           mySpinLockProc;
//获得
KeAcquireSpinLock(
                &mySpinLockProc,
                &OldIrql);
//对数据进行操作和访问
……
//释放
KeReleaseSpinLock(
                &mySpinLockProc,
                OldIrql);





2.ERESOURCE(读写共享锁)
typedef struct _MY_LOCK
{
    ERESOURCE        m_Lock;//用于互斥
}MY_LOCK;


VOID __stdcall LockWrite(MY_LOCK* lpLock)
{
    KeEnterCriticalRegion();
    ExAcquireResourceExclusiveLite(&lpLock->m_Lock, TRUE);
}


VOID __stdcall UnLockWrite(MY_LOCK* lpLock)
{
    ExReleaseResourceLite(&lpLock->m_Lock);
    KeLeaveCriticalRegion();
}
VOID __stdcall LockRead(MY_LOCK* lpLock)
{
    KeEnterCriticalRegion();
    ExAcquireResourceSharedLite(&lpLock->m_Lock, TRUE);
}


VOID __stdcall LockReadStarveWriter(MY_LOCK* lpLock)
{
    KeEnterCriticalRegion();
    //读优先
    ExAcquireSharedStarveExclusive(&lpLock->m_Lock, TRUE);
    //写优先
//ExAcquireSharedWaitForExclusive
}
VOID __stdcall UnLockRead(MY_LOCK* lpLock)
{
    ExReleaseResourceLite(&lpLock->m_Lock);
    KeLeaveCriticalRegion();
}


VOID __stdcall InitLock(MY_LOCK* lpLock)
{
    ExInitializeResourceLite(&lpLock->m_Lock);
}


VOID __stdcall DeleteLock(MY_LOCK* lpLock)
{
    ExDeleteResourceLite(&lpLock->m_Lock);
}


//ERESOURCE的使用
LIST_ENTRY g_WaitList;
MY_LOCKg_WaitListLock;
//DriverEntry里
InitLock(&g_WaitListLock);


//多线程环境里
LockWrite(&g_WaitListLock);
lpWaitEntry = LookupWaitEntryByID(&g_WaitList, lpReply->m_ulWaitID);                                               
if (lpWaitEntry != NULL)
{
                lpWaitEntry->m_bBlocked = lpReply->m_ulBlocked;
                KeSetEvent(&lpWaitEntry->m_ulWaitEvent, 0, FALSE);               
                return;
}                                       
UnLockWrite(&g_WaitListLock);


//DriverUnload里
DeleteLock(&g_WaitListLock);

3.FAST_MUTEX
用于互斥
大于APC_LEVEL就不能使用了(PL AL可用)
不能递归获得锁

FAST_MUTEX        gSfilterAttachLock
ExInitializeFastMutex( &gSfilterAttachLock );


ExAcquireFastMutex( &gSfilterAttachLock );
ExAcquireFastMutex( &gSfilterAttachLock );//错 递归获得锁
//Do something here
……
ExReleaseFastMutex( &gSfilterAttachLock );
ExReleaseFastMutex( &gSfilterAttachLock );

例子:
#include <ntddk.h>


ULONG        g_ulTotal = 0;
FAST_MUTEX g_fmLock;


VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
        DbgPrint("Goodbye, driver\n");
}


VOID ThreadProc1(IN PVOID pContext)
{
        ULONG i = 0;
        ExAcquireFastMutex(&g_fmLock);


        g_ulTotal++;
        DbgPrint("ThreadProc1:%x\n", g_ulTotal);




        ExReleaseFastMutex(&g_fmLock);


}


VOID ThreadProc2(IN PVOID pContext)
{
        ULONG i = 0;
        ExAcquireFastMutex(&g_fmLock);


        g_ulTotal++;
        DbgPrint("ThreadProc2:%x\n", g_ulTotal);


        ExReleaseFastMutex(&g_fmLock);
       
}


void StartThreads()
{
        HANDLE hThread1        = NULL;
        HANDLE hThread2        = NULL;


        PVOIDobjtowait = {NULL};
        NTSTATUS ntStatus =
                PsCreateSystemThread(
                &hThread1,
                0,
                NULL,
                (HANDLE)0,
                NULL,
                ThreadProc1,
                NULL
                );
        if (!NT_SUCCESS(ntStatus))
        {
                return;
        }


        ntStatus =
                PsCreateSystemThread(
                &hThread2,
                0,
                NULL,
                (HANDLE)0,
                NULL,
                ThreadProc2,
                NULL
                );
        if (!NT_SUCCESS(ntStatus))
        {
                return;
        }


        if ((KeGetCurrentIrql())!=PASSIVE_LEVEL)
        {
                ntStatus = KfRaiseIrql(PASSIVE_LEVEL);
        }
        if ((KeGetCurrentIrql())!=PASSIVE_LEVEL)
        {
                return;
        }       
        ntStatus = ObReferenceObjectByHandle(
                hThread1,
                THREAD_ALL_ACCESS,
                NULL,
                KernelMode,
                &objtowait,
                NULL
                );
        if (!NT_SUCCESS(ntStatus))
        {
                return;
        }


        ntStatus = ObReferenceObjectByHandle(
                hThread1,
                THREAD_ALL_ACCESS,
                NULL,
                KernelMode,
                &objtowait,
                NULL
                );
        if (!NT_SUCCESS(ntStatus))
        {
                ObDereferenceObject(objtowait);
                return;
        }


        KeWaitForMultipleObjects(
                2,
                objtowait,
                WaitAll,
                Executive,
                KernelMode,
                FALSE,
                NULL,
                NULL);


        ObDereferenceObject(objtowait);
        ObDereferenceObject(objtowait);


        //KeWaitForSingleObject(objtowait,Executive,KernelMode,FALSE,NULL);
        return;
}






NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegPath)
{
        pDriverObject->DriverUnload = DriverUnload;


        ExInitializeFastMutex(&g_fmLock);


        StartThreads();




        return STATUS_SUCCESS;
}


4.KMUTEX
基本淘汰,使用FAST_MUTEX取代
特点:
FAST_MUTEX无法递归,KMUTEX可以
FAST_MUTEX无法WAIT,KMUTEX可以
FAST_MUTEX 在APC LEVE,KMUTEX任意LEVEL
用法:
KMUTEX mutex;
KeInitializeMutex(
            & mutex,
            0
                );
KeWaitForSingleObject(& mutex, Executive, KernelMode, FALSE, &MmOneSecond);
KeReleaseMutex(& mutex, FALSE);
总结:
DISPATCH_LEVEL:
SpinLock
APC/PASSIVE:
互斥:ERESOURCE/FAST_MUTEX
同步:KEVENT/KSEMAPHORE
R3/R0同步通信:
KEVENT
整数增减赋值:
InterlockedExchange 函数把第一个参数指向的内存地址的值,以原子的方式替换为第二个参数的值。并返回原来的值。InterlockedExchange函数还有8位,16位和64位的版本;
InterlockedIncrement/InterlockedDecrement 增加1/减少1 //在FAST_MUTEX的例子中可以用这个替换掉

R3多线程演示:

ULONG WINAPI ThreadProc(void* arg)
{
    return 1;
}


VOID CreateThread()
{
        HANDLE hThread = NULL;


        hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
       
        WaitForSingleObject(hThread, INFINITE);
        CloseHandle(hThread);
        hThread = NULL;
}






UINT WINAPI ThreadProc(LPVOID lpParameter)
{
            return 0;
}


VOID Beginthreadex()
{
        unsigned tid = 0;
        HANDLE hThread = (HANDLE)_beginthreadex( NULL, 0, ThreadProc,NULL, 0, &tid);
        WaitForSingleObject( hThread, INFINITE );
        CloseHandle(hThread);
        hThread = NULL;
}






UINT threadProc(LPVOID v)
{
        AfxEndThread(0);
}
       
VOID AfxbeginThread()
{
       CWinThread *pThreadR=         AfxBeginThread(threadProc,(LPVOID)param);
        pThreadR->SuspendThread();
        pThreadR->m_bAutoDelete = FALSE;
        pThreadR->ResumeThread();


        if (WaitForSingleObject(pThreadR->m_hThread,         2*1000)==WAIT_TIMEOUT)
        {
                TerminateThread(pThreadR->m_hThread, 0);
        }
        delete (pThreadR);
   pThreadR = NULL;
}



三个函数的区别
CreateThread:是Windows的API函数(SDK函数的标准形式,直截了当的创建方式,任何场合都可以使用),提供操作系统级别的创建线程的操作,且仅限于工作者线程。不调用MFC和RTL的函数时,可以用CreateThread,其它情况不要使用。因为:
C Runtime中需要对多线程进行纪录和初始化,以保证C函数库工作正常。
MFC也需要知道新线程的创建,也需要做一些初始化工作。
有些CRT的函数象malloc(),fopen(),_open(),strtok(),ctime(),或localtime()等函数需要专门的线程局部存储的数据块,这个数据块通常需要在创建线程的时候就建立,如果使用CreateThread,这个数据块就没有建立,但函数会自己建立一个,然后将其与线程联系在一起,这意味着如果你用CreateThread来创建线程,然后使用这样的函数,会有一块内存在不知不觉中创建,而且这些函数并不将其删除,而CreateThread和ExitThread也无法知道这件事,于是就会有Memory
Leak,在线程频繁启动的软件中,迟早会让系统的内存资源耗尽。
_beginthreadex:MS对C Runtime库的扩展SDK函数,首先针对C Runtime库做了一些初始化的工作,以保证C Runtime库工作正常。然后,调用CreateThread真正创建线程。
AfxBeginThread:MFC中线程创建的MFC函数,首先创建了相应的CWinThread对象,然后调用CWinThread::CreateThread,在CWinThread::CreateThread中,完成了对线程对象的初始化工作,然后,调用_beginthreadex(AfxBeginThread相比较更为安全)创建线程。它让线程能够响应消息,可用于界面线程,也可以用于工作者线程。




三个函数的应用条件
AfxBeginThread:在MFC中用,工作者线程/界面线程
_beginthreadex: 调用了C运行库的,应该用这个,但不能用在MFC中。
CreateThread:工作者线程,MFC中不能用,C Runtime中不能用。所以任何时候最好都不要用。



R3多线程总结:
Critical Section/Mutex/Semap
Critical_sectionhore/Event
1. Critical Section与Mutex作用非常相似,但Mutex是可以命名的,也就是说它可以跨越进程使用。所以创建互斥量需要的资源更多,如果只为了在进程内部使用的话,使用临界区会带来速度上的优势并能够减少资源占用量。因为互斥量是跨进程的,互斥量一旦被创建,就可以通过名字打开它。
2.互斥量(Mutex),信号量(Semaphore),事件(Event)都可以跨越进程来进行同步数据操作(一个进程创建之后,另外的进程可以通过名字打开它,从而用于进程间的数据同步)
3.通过Mutex可以指定资源被独占的方式使用,但如果一个资源允许N(N>1)个进程或者线程访问,这时候如果利用Mutex就没有办法完成这个要求, Semaphore可以,是一种资源计数器。


Critical_section
struct RTL_CRITICAL_SECTION
{
PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
LONG LockCount;
LONG RecursionCount;
HANDLE OwningThread;
HANDLE LockSemaphore;
ULONG_PTR SpinCount;
};
DebugInfo 此字段包含一个指针,指向系统分配的伴随结构,该结构的类型为
RTL_CRITICAL_SECTION_DEBUG
LockCount 这是临界区中最重要的一个字段。它被初始化为数值 -1;此数值等于或大于 0 时,表示此临界区被占用。当其不等于 -1 时,OwningThread 字段包含了拥有此临界区的线程 ID。此字段与 (RecursionCount -1) 数值之间的差值表示有多少个其他线程在等待获得该临界区。
RecursionCount 此字段包含所有者线程已经获得该临界区的次数。如果该数值为零,下一个尝试获取该临界区的线程将会成功。
OwningThread 此字段包含当前占用此临界区的线程的线程标识符。此线程 ID 与 GetCurrentThreadId 之类的 API 所返回的 ID 相同。
LockSemaphore 它实际上是一个自复位事件,而不是一个信号。它是一个内核对象句柄,用于通知操作系统:该临界区现在空闲。操作系统在一个线程第一次尝试获得该临界区,但被另一个已经拥有该临界区的线程所阻止时,自动创建这样一个句柄。应当调用 DeleteCriticalSection(它将发出一个调用该事件的 CloseHandle 调用,并在必要时释放该调试结构),否则将会发生资源泄漏。
SpinCount 仅用于多处理器系统。在多处理器系统中,如果该临界区不可用,调用线程将在对与该临界区相关的信号执行等待操作之前,旋转 dwSpinCount 次。如果该临界区在旋转操作期间变为可用,该调用线程就避免了等待操作。旋转计数可以在多处理器计算机上提供更佳性能,其原因在于在一个循环中旋转通常要快于进入内核模式等待状态。此字段默认值为零,但可以用InitializeCriticalSectionAndSpinCount API 将其设置为一个不同值。




三.实现自动加锁 使用C++的构造和析构函数


CAutoLocker

class CLock
{
public:
        void Lock() {EnterCriticalSection(&m_sec);}
        void Unlock() {LeaveCriticalSection(&m_sec);}
        CLock () {InitializeCriticalSection(&m_sec);}
        ~ CLock () {DeleteCriticalSection(&m_sec);}       
private:
    CRITICAL_SECTION m_sec;
};


class CAutoLock
{
public:
      CAutoLock(CLock* lpLock) :
      m_pLock (lpLock)
      {
          m_pLock ->Lock();
      }
      ~CAutoLock()
      {
          m_pLock ->Unlock();
      }
private:
    CLock * m_pLock;
};





使用例子:
{
    CLock m_lock;
    CAutoLock(&m_lock);
    ….
}
页: [1]
查看完整版本: 多线程 以及多线程安全