原文地址: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()是個比較大的函數,需要分段閱讀。