標 題: 【分享】緩存管理器
做 者: yaolibing
時 間: 2009-07-31,21:48:35
鏈 接: http://bbs.pediy.com/showthread.php?t=94762
簡言之,就是會預先讀入文件和延遲寫入文件。當ReadFile時,會調用NtReadFile()系統調用,它會構造一個IRP下發到FSD,FSD會檢查這個IRP看是否是能夠緩存 的,是的話,若是尚未爲此文件創建緩存的話,就會調用 CcInitializeCacheMap()函數創建緩存,它裏面會調用內存管理器(VMM)函數創建一個節對象 。當用到時,會把這個節對象(和文件關聯)映射到內核空間。若是IRP是可緩存 的,則調用CcCopyRead()函數進行從緩存中讀入文件。
若是此文件尚未在內存中,則會產生頁面錯誤,交給MmAccessFault()函數處理,它會調用IoPageRead()分配一個不緩存 的IRP,可是它會走FSD,不會調用緩存的函數,而是最終調用磁盤驅動進行真實的磁盤讀寫讀入到內存。以後CcCopyRead()再不會產生錯誤了,會從緩存複製到用戶Buffer中
NtReadFile (
__in HANDLE FileHandle,
__in_opt HANDLE Event,
__in_opt PIO_APC_ROUTINE ApcRoutine,
__in_opt PVOID ApcContext,
__out PIO_STATUS_BLOCK IoStatusBlock,
__out_bcount(Length) PVOID Buffer,
__in ULONG Length,
__in_opt PLARGE_INTEGER ByteOffset,
__in_opt PULONG Key
)
status = ObReferenceObjectByHandle( FileHandle,
FILE_READ_DATA,
IoFileObjectType,
requestorMode,
(PVOID *) &fileObject,
NULL );//獲得文件對象
deviceObject = IoGetRelatedDeviceObject( fileObject );//獲得設備對象
// 若是文件已經有緩存了,直接調用
if (fileObject->PrivateCacheMap) {
IO_STATUS_BLOCK localIoStatus;
ASSERT(fastIoDispatch && fastIoDispatch->FastIoRead);
//
// Negative file offsets are illegal.
//
if (fileOffset.HighPart < 0) {
if (eventObject) {
ObDereferenceObject( eventObject );
}
IopReleaseFileObjectLock( fileObject );
ObDereferenceObject( fileObject );
return STATUS_INVALID_PARAMETER;
}
if (fastIoDispatch->FastIoRead( fileObject,
&fileOffset,
Length,
TRUE,
keyValue,
Buffer,
&localIoStatus,
deviceObject )
不然的話還要分配IRP,下發到文件系統驅動 ,(注意有三種處理用戶Buffer的方法,由於有可能FSD驅動不是在本用戶進程的地址空間中執行的,則訪問Buffer(儘管虛擬地址相同,可是通常會被映射到不一樣的物理地址),因此要作以下處理Buffer。
1,是調用irp->AssociatedIrp.SystemBuffer =
ExAllocatePoolWithQuota( NonPagedPoolCacheAligned, Length );在非分頁內存中分配內存,由於都是在內核空間,因此就算另外一個進程也能訪問。
2,是調用mdl = IoAllocateMdl( Buffer, Length, FALSE, TRUE, irp );分配一個內存描述符,再調用 MmProbeAndLockPages( mdl, requestorMode, IoWriteAccess );
把此Buffer所在的物理頁鎖定在內存中,防止換出去。
3,直接就是那個 irp->Flags = 0; irp->UserBuffer = Buffer;
調用 這個IopSynchronousServiceTail()函數下發到FSD
注意先是IopfCallDriver()調用fltMgr.sys驅動的分派函數,最後它也調用IofCallDriver()函數下發IRP到下層驅動(既ntfs.sys的NtfsFsdRead()函數)
NTSTATUS
NtfsFsdRead (
IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
IN PIRP Irp
)
最後調用NtfsCommonRead()這個函數裏面會作好多判斷,以後會創建該文件的Cache
if (FileObject->PrivateCacheMap == NULL) {
DebugTrace( 0, Dbg, ("Initialize cache mapping.\n") );
//
// Now initialize the cache map.
//
// Make sure we are serialized with the FileSizes, and
// will remove this condition if we abort.
//
if (!DoingIoAtEof) {
FsRtlLockFsRtlHeader( Header );
IrpContext->FcbWithPagingExclusive = (PFCB)Scb;
}
CcInitializeCacheMap( FileObject,
(PCC_FILE_SIZES)&Header->AllocationSize,
FALSE,
&NtfsData.CacheManagerCallbacks,
Scb );
它裏面會分配SharedCacheMap = ExAllocatePoolWithTag( NonPagedPool, sizeof(SHARED_CACHE_MAP), 'cScC' );
初始化這個結構,最後調用內存管理器(VMM) 函數SharedCacheMap->Status = MmCreateSection( &SharedCacheMap->Section,
SECTION_MAP_READ
| SECTION_MAP_WRITE
| SECTION_QUERY,
NULL,
&LocalSizes.AllocationSize,
PAGE_READWRITE,
SEC_COMMIT,
NULL,
FileObject );
創建一個共享節對象
以後FSD調用 if (!CcCopyRead( FileObject,
(PLARGE_INTEGER)&StartingVbo,
(ULONG)ByteCount,
Wait,
SystemBuffer,
&Irp->IoStatus ))進行從緩存中讀入數據
若是緩存沒有這個要讀文件的頁面,則會產生頁面異常,最終進入MmAccessFault()處理,它會調用IoPageRead()分配一個IRP_PAGING_IO | IRP _NOCACHE (沒有緩存的IRP)再次調用IoCallDriver調用FSD的函數,這裏和上面同樣,一樣進入FSD的NtfsFsdRead()-》NtfsNonCachedIo()進行沒有緩存的IRP請求。-》NtfsSingleAsync()它裏面先調用IoSetCompletionRoutine()設置一個FSD回調函數,而後調用 IoCallDriver( DeviceObject, Irp );調用1,volsnap!VolSnapRead------->2,ftdisk!FtDiskReadWrite------>3,PartMgr!PmReadWrite------->4,CLASSPNP!ClassReadWrite----->5,SCSIPORT!ScsiPortGlobalDispatch()等等會進行真正磁盤讀寫文件內容
當讀寫磁盤完成了後會產生中斷以後進入KiDispatchInterrupt()
ScsiPortCompletionDpc()一層一層的調用SCSIPORT!SpCompleteRequest()完成回調函數,最後會調用到FSD先前創建的Ntfs!NtfsSingleSyncCompletionRoutine()也算是完成了磁盤讀寫。php