中断是指在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);
….
}
转载自原文链接, 如需删除请联系管理员。
原文链接:多线程 以及多线程安全,转载请注明来源!