多线程 以及多线程安全

线程的运行级别(IRQL)


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


引用来自http://blog.csdn.net/fengkuangfj/article/details/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,DriverUnload   Passive级
各种分发函数   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_NAME    L"\\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
{
    HANDLE  hParentId;
    HANDLE  hProcessId;
    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[256] = {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
{
    HANDLE  hParentId;
    HANDLE  hProcessId;
    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[i] = CommonDispatch;
	}

	DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = 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;


	PVOID  objtowait[2] = {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[0],
		NULL
		); 
	if (!NT_SUCCESS(ntStatus))
	{
		return;
	}


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


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


	ObDereferenceObject(objtowait[0]);
	ObDereferenceObject(objtowait[1]);


	//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_LOCK  g_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;


	PVOID  objtowait[2] = {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[0],
		NULL
		); 
	if (!NT_SUCCESS(ntStatus))
	{
		return;
	}


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


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


	ObDereferenceObject(objtowait[0]);
	ObDereferenceObject(objtowait[1]);


	//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);
    ….
}






















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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值