zoukankan      html  css  js  c++  java
  • 缓存管理器

    标 题: 【分享】缓存管理器
    作 者: 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()也算是完成了磁盘读写。
  • 相关阅读:
    0827IO作业
    0927集合作业
    初学集合,以及泛型
    异常课——抛出
    Python环境变量配置
    安装Python
    MySQL多表操作
    MySQL增删改查
    Group by分组详解
    MySQL常用函数
  • 原文地址:https://www.cnblogs.com/bugchecker/p/3041596.html
Copyright © 2011-2022 走看看