只要写过多线程应用的程序员都知道,多线程访问公共资源的时候需要同步。在用户模式下,经常使用事件,互斥,信号量等对象来控制公共资源的访问。内核模式下,也是有相应的事件,互斥,信号量等内核对象,还有自旋锁。如果不是用同步对象进行控制,那么当多线程访问的时候就会产生一些不可预测的问题了。
不使用同步对象
看下面的代码:
NTSTATUS Encoding(IN PDEVICE_OBJECT fdo, IN PIRP Irp)
{
KdPrint(("Start to Encode\n"));
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
//得到输入缓冲区大小
ULONG cbin = stack->Parameters.DeviceIoControl.InputBufferLength;
//得到输出缓冲区大小
ULONG cbout = stack->Parameters.DeviceIoControl.OutputBufferLength;
//获取输入缓冲区,IRP_MJ_DEVICE_CONTROL的输入都是通过buffered io的方式
char* inBuf = (char*)Irp->AssociatedIrp.SystemBuffer;
//假如需要将数据放到一个公共资源中,然后再进行操作,比如这里是亦或编码,那么就需要考虑同步的问题。
//不然在多线程调用的时候,公共资源的访问将会有不可预测的问题。
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
RtlCopyMemory(pdx->buffer, inBuf, cbin);
//模拟延时3秒
KdPrint(("Wait 3s\n"));
KEVENT event;
KeInitializeEvent(&event, NotificationEvent, FALSE);
LARGE_INTEGER timeout;
timeout.QuadPart = -3 * 1000 * 1000 * 10;//负数表示从现在开始计数,KeWaitForSingleObject的timeout是100ns为单位的。
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, &timeout);//等待3秒
for (ULONG i = 0; i < cbin; i++)//将输入缓冲区里面的每个字节和m亦或
{
pdx->buffer[i] = pdx->buffer[i] ^ 'm';
}
//获取输出缓冲区,这里使用了直接方式,见CTL_CODE的定义,使用了METHOD_IN_DIRECT。所以需要通过直接方式获取out buffer
KdPrint(("user address: %x, this address should be same to user mode addess.\n", MmGetMdlVirtualAddress(Irp->MdlAddress)))