原文地址:http://book.51cto.com/art/200912/174785.htm
在做驱动与应用层使用命名管道通讯时,不知道如何在驱动中打开命名管道的路径,原来驱动中的路径名为:"\Device\NamedPipe\MyPipe",应去层用的是"\\\\.\\Pipe\\MyPipe"
再看IoCreateFile()怎样找到命名管道驱动模块的设备对象。创建了一个命名管道以后,在对象目录中就有了一个相应的节点,例如"\Device\NamedPipe\MyPipe"。注意这里的节点"NamedPipe"是个设备对象,而设备对象提供解析函数。这样,对象管理的ObOpenObjectByName()就能根据全路径找到其具体命名管道所属的"目录""\Device\NamedPipe"(如果目标尚未创建)或代表着这个目标的对象。这二者都会有指针指向命名管道驱动模块的设备对象(如果已经装载)。下面就是驱动模块的事了。
我们看命名管道驱动模块的初始化:
- NTSTATUS STDCALL //ReactOS-0.3.3\drivers\filesystems\npfs\npfs.c
- DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
- {
- PNPFS_DEVICE_EXTENSION DeviceExtension;
- ......
- DPRINT("Named Pipe FSD 0.0.2\n");
- ASSERT (sizeof(NPFS_CONTEXT) <= FIELD_OFFSET(IRP, Tail.Overlay.DriverContext));
- ASSERT (sizeof(NPFS_WAITER_ENTRY) <=
- FIELD_OFFSET(IRP, Tail.Overlay.DriverContext));
- DriverObject->MajorFunction[IRP_MJ_CREATE] = NpfsCreate;
- DriverObject->MajorFunction[IRP_MJ_CREATE_NAMED_PIPE] =
- NpfsCreateNamedPipe;
- DriverObject->MajorFunction[IRP_MJ_CLOSE] = NpfsClose;
- DriverObject->MajorFunction[IRP_MJ_READ] = NpfsRead;
- DriverObject->MajorFunction[IRP_MJ_WRITE] = NpfsWrite;
- DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] =
- NpfsQueryInformation;
- DriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] =
- NpfsSetInformation;
- DriverObject->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION] =
- NpfsQueryVolumeInformation;
- DriverObject->MajorFunction[IRP_MJ_CLEANUP] = NpfsCleanup;
- DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = NpfsFlushBuffers;
- DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] =
- NpfsFileSystemControl;
- DriverObject->DriverUnload = NULL;
- RtlInitUnicodeString(&DeviceName, L"\\Device\\NamedPipe");
- //在对象目录中创建一个名为"\Device\NamedPipe"的设备对象
- Status = IoCreateDevice(DriverObject, sizeof(NPFS_DEVICE_EXTENSION),
- &DeviceName, FILE_DEVICE_NAMED_PIPE, 0, FALSE, &DeviceObject);
- if (!NT_SUCCESS(Status)) return Status; //创建设备对象失败
- /* initialize the device object */
- DeviceObject->Flags = DO_DIRECT_IO;
- /* initialize the device extension */
- DeviceExtension = DeviceObject->DeviceExtension;
- InitializeListHead(&DeviceExtension->PipeListHead); //所有命名管道FCB的队列
- InitializeListHead(&DeviceExtension->ThreadListHead);
- KeInitializeMutex(&DeviceExtension->PipeListLock, 0);
- DeviceExtension->EmptyWaiterCount = 0;
- /* set the size quotas */
- DeviceExtension->MinQuota = PAGE_SIZE;
- DeviceExtension->DefaultQuota = 8 * PAGE_SIZE;
- DeviceExtension->MaxQuota = 64 * PAGE_SIZE;
- return STATUS_SUCCESS;
- }
注意这里一方面使主功能为IRP_MJ_CREATE的函数指针指向NpfsCreate(),另一方面又使主功能为IRP_MJ_CREATE_NAMED_PIPE的函数指针指向NpfsCreateNamedPipe(),命名管道驱动是唯一提供后面这个主功能函数的驱动对象。
此外,这里并没有提供AddDevice函数,这说明命名管道的驱动是个"老式(Legacy)"的单块设备驱动,其设备对象不会被堆叠到别的设备对象上面。
由于路径名为"\Device\NamedPipe",IoCreateDevice()就在对象目录中的节点"\Device"下面创建了一个名为"NamedPipe"的设备对象,并使其指向所创建的命名管道设备对象。注意这里的FILE_DEVICE_NAMED_PIPE是设备类型,就像FILE_DEVICE_BEEP一样,而所创建对象的类型则是IoDeviceObjectType即设备对象。另一方面,设备对象是提供解析函数的。
设备对象的扩充部有个队列PipeListHead,队列中是所有已创建的命名管道。每个命名管道都有个"文件控制块"即NPFS_FCB数据结构,这个数据结构就挂在PipeListHead这个队列里面,这个队列就好像一个单层的目录。此外,从本质上说命名管道无非就是数据的"中转站",所以需要一定的缓冲区空间,这里还为此设置了配额。
创建了设备对象之后,内核的设备驱动框架就可以通过IRP和IoCallDriver()调用其主功能函数了。创建或打开命名管道时,就像对于别的设备一样,内核会为之创建一个代表着特定访问上下文的FILE_OBJECT,就是下面代码中的FileObject,并且系统调用所返回的句柄就代表着这个文件对象。
在设备对象的解析函数IopParseDevice()中,当文件类型为CreateFileTypeNamedPipe时所生成IRP的主功能码是IRP_MJ_CREATE_NAMED_PIPE,而在文件类型为CreateFileTypeNone时则是IRP_MJ_CREATE。也就是说,当IRP到达命名管道驱动模块的设备对象时,前者表示这是服务端的创建命名管道,而后者表示这是客户端的打开命名管道。
再看具体的主功能函数,其中最重要的是NpfsCreateNamedPipe()和NpfsCreate()。先看前者,NpfsCreateNamedPipe()是个比较大的函数,需要分段阅读。