緩存管理器

標 題: 【分享】緩存管理器
做 者: 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

相關文章
相關標籤/搜索