NT缓存管理器(四)

CcInitializeCacheMap

缓存管理器维护一个文件的cache map,以跟踪对文件执行的活动。第一个文件打开操作导致一个public cache map的生成。更多地,每个打开文件的实例同样有自己的一张cache map用来跟踪在其文件对象上执行的操作的信息。

一般地,cache map的生成被推迟直到第一个I/O发生。这保证了文件系统不能生成和删除cache map对一般的不需要IO的操作,因为这些操作对于Win32应用来说太常见了。

一旦对指定文件执行了I/O操作,文件系统建立cache map。这通过CcInitalizeCacheMap调用实现:

           NTKERNELAPI VOID CcInitializeCacheMap (

               IN PFILE_OBJECT FileObject,

               IN PCC_FILE_SIZES FileSizes,

               IN BOOLEAN PinAccess,

               IN PCACHE_MANAGER_CALLBACKS Callbacks,

               IN PVOID LazyWriteContext

               );

这些参数大部分不言自明-文件大小通常来自头文件,Callbacks作为文件系统定义的函数集。LazyWriteContext是传给callback的参数,所以就允许你来指定信息(回传给callback函数以做进一步处理)。

PinAccess参数被缓存管理器用来决定 由这个内存区表达的数据 是否被文件系统锁在内存上。NT文件系统用这个来锁住内存,作为 内存映射它们自己的数据结构的 一种机制。然而,为了保证特别重要的数据结构常驻内存并且不可释放,缓存管理器允许文件系统把数据锁在内存中(在关键操作的执行期间)。BCB描述了这样的锁部分(pinned sections)

从而,一般用户数据不支持锁访问。

注意:cache map通常不会为这样的文件(被unbuffered I/O操作访问的文件)而进行初始化,被打开的文件FILE_NO_INTERMEDIATE_BUFFERING位被设定了。

一旦CcInitializeCacheMap被你的文件系统调用了,你可能接收到对文件的快速I/O操作。缓存管理器在文件对象中设了PrivateCacheMap域来指向一个分配数据结构CacheManager,I/O管理器决定是否能利用I/O通道基于这个域上的值。

接下来的代码样例来自较早版本的OSR FSDK:

         NTSTATUS OwInitializeCacheMap(POW_IRP_CONTEXT IrpContext)

          {

        // Make sure that this thing can even be cached.

         OwAssert(IrpContext->IrpSp->FileObject->SectionObjectPointer);

         OwAssert(OwIsResourceAcquiredExclusive(&IrpContext->Fcb-

         >Resource));

         OwAssert(!IrpContext->FileObject->PrivateCacheMap);

         OwAssert(IrpContext->Fcb->CommonHeader.AllocationSize.QuadPart >=

         IrpContext->Fcb->CommonHeader.FileSize.QuadPart);

         CcInitializeCacheMap(IrpContext->FileObject,

          (PCC_FILE_SIZES) &IrpContext->Fcb->CommonHeader.AllocationSize,

         FALSE, // access for pinning?

         &OwCallbacks,

         IrpContext->Fcb);

         OSR_TRACE1(IrpContext);

         return (STATUS_SUCCESS);

         }

Microsoft IFS kit里的每个文件系统同样包含cache map初始化的例子:

                    File System      File                Routine

                    FAT              read.c               FatCommonRead

                    CDFS             write.c              CdCommonWrite

                    RDR2             rdbss/fileinfo.c     RxSetAllocationInfo

其它类似的例程也位于相同的文件里。

CcIsThereDirtyData

这个例程用来决定 在给定的物理media volume上是否有脏数据,由它的VPB结构指定。例程原型如下:

         NTKERNELAPI BOOLEAN CcIsThereDirtyData (

              IN PVPB Vpb

              );

缓存管理器缓存的数据用Section Objects来描述。反过来,Section Object指的是一些存储它的File Object.File Object表明它位于哪个Volume上(对于物理媒体文件系统来讲)。因此,缓存管理器能够通过调用这个例程来断定一个给定的物理多媒体卷上是否存有脏数据。

不包含VPB结构的文件系统,比如网络文件系统,不能调用这个。

CcMapData

这个例程被文件系统用来建立数据映射,用这种方式它就能被文件系统控制啦(通过BCB)。典型地,内存映射它自己的文件系统数据结构(这句翻地我自己都晕了)。

         NTKERNELAPI BOOLEAN CcMapData (

              IN PFILE_OBJECT FileObject,

              IN PLARGE_INTEGER FileOffset,

              IN ULONG Length,

              IN BOOLEAN Wait,

              OUT PVOID *Bcb,

               OUT PVOID *Buffer

               );

调用完这个例程后,文件系统在真正访问数据前要锁定buffer.访问cache中没锁上的数据可能导致无法预期的错误。

CcMdlRead

这个例程被文件系统用来获得一个描述cache buffer的MDL。因为MDL只能被kernel模式下的组件使用,所以这个只能被kernel模式下的应用程序使用,比如file server.通过用MDL来描述cache,常驻kernel的代码能够避免buffer和cache间的数据拷贝。

函数原型如下:

         NTKERNELAPI VOID CcMdlRead (

               IN PFILE_OBJECT FileObject,

               IN PLARGE_INTEGER FileOffset,

               IN ULONG Length,

               OUT PMDL *MdlChain,

               OUT PIO_STATUS_BLOCK IoStatus

               );

典型地,FSD将会使用这个例程和一个IRP_MN_MDL函数来满足一个IRP_MJ_READ请求。这个例程返回的MDL接下来被CcMdlReadComplete或FsRtlMdlReadCompleteDev释放。因为在使用这些值时有潜在影响,程序员们需要仔细思考他们的需求以选择哪个函数。

MDL在IRP中被返回给调用者。

CcMdlReadComplete

This routine is used as the compliment of CcMdlRead. The prototype for this function is:

         NTKERNELAPI VOID CcMdlReadComplete (

               IN PFILE_OBJECT FileObject,

               IN PMDL MdlChain

               );

FSD使用这个和IRP_MN_MDL_COMPLETE函数来响应IRP_MJ_READ请求。

注意,在WindowsNT4.0到Service Pack 3,这个调用通过调用快速I/O入口点MdlRead而被实现。如果这个例程没有实现,那么FsRtlMdlReadCompleteDev将会被调用。如果这个例程实现了,其返回值被忽略。这将导致分层过滤驱动的问题,比如包含在Microsoft IFS kit里的那些例子。

将要被释放的MDL通常是作为IRP里的MdlAddress域提供给FSD。

CcMdlWriteComplete

这个例程作为CcMdlWrite的结束部分。函数原型如下:

         NTKERNELAPI VOID CcMdlWriteComplete (

              IN PFILE_OBJECT FileObject,

              IN PLARGE_INTEGER FileOffset,

              IN PMDL MdlChain

              );

FSD使用这个和IRP_MN_MDL_COMPLETE函数来响应IRP_MJ_WRITE请求。

注意,在WindowsNT4.0到Service Pack 3,这个调用通过调用快速I/O入口点MdlWrite而被实现。如果这个例程没有实现,那么FsRtlMdlReadCompleteDev将会被调用。如果这个例程实现了,其返回值被忽略。这将导致分层过滤驱动的问题,比如包含在Microsoft IFS kit里的那些例子。

将要被释放的MDL通常是作为IRP里的MdlAddress域提供给FSD。

CcPinMappedData

这个调用用来保证 通过调用CcMapData而被映射入内存的数据被锁在了内存里,从而可以被文件系统使用。调用原型如下:

         NTKERNELAPI BOOLEAN CcPinMappedData (

              IN PFILE_OBJECT FileObject,

              IN PLARGE_INTEGER FileOffset,

              IN ULONG Length,

              IN BOOLEAN Wait,

              IN OUT PVOID *Bcb

              );

FileObject,FileOffset和Length参数指明了什么被锁在了内存里。Wait参数表明调用者是否愿意阻滞当进行调用的时候。如果Wait值是假,锁又不能很快获得,那么这个调用将会返回false给调用者,调用者会尝试在另一个时间或上下文里进行调用。

Bcb参数是通过早期调用CcMapData而返回给FSD的BCB指针。FSD负责释放BCB一旦它完成了访问被锁的数据。

CcPinRead

这个调用用来读、映射、将数据锁进cache在一个单独的操作里。原型是:

         NTKERNELAPI BOOLEAN CcPinRead (

              IN PFILE_OBJECT FileObject,

              IN PLARGE_INTEGER FileOffset,

              IN ULONG Length,

              IN BOOLEAN Wait,

              OUT PVOID *Bcb,

              OUT PVOID *Buffer

              );

从函数功能上讲,这等价于调用CcMapData和CcPinMappedData在一个单独的操作里。

FSD必须解锁数据,一旦buffer不再需要的时候。

CcPrepareMdlWrite

这个例程被文件系统用来获得一个描述cache buffer的MDL。因为MDL只能被kernel模式下的组件使用,所以这个只能被kernel模式下的应用程序使用,比如file server.通过用MDL来描述cache,常驻kernel的代码能够避免buffer和cache间的数据拷贝。函数原型如下:

         NTKERNELAPI VOID CcPrepareMdlWrite (

              IN PFILE_OBJECT FileObject,

              IN PLARGE_INTEGER FileOffset,

              IN ULONG Length,

              OUT PMDL *MdlChain,

              OUT PIO_STATUS_BLOCK IoStatus

              );

典型地,FSD将会使用这个例程和一个IRP_MN_MDL函数来满足一个IRP_MJ_WRITE请求。注意数据是要被写地,所以它只能从磁盘上读取如果需要的话。这样也是必要地,就是比如,offset和length表明一个物理内存页的一部分被修改了。现在在那个文件区里的数据是从磁盘上获得的。

Because of this, data need not be present in the buffer when this call returns.

就因为这个,数据需要呈现在buffer里,如果这个调用返回的话。

CcPreparePinWrite

这个调用用来映射、将数据锁入cache在一个单独的操作中(操作接下来就会被修改了)。

         NTKERNELAPI BOOLEAN CcPreparePinWrite (

              IN PFILE_OBJECT FileObject,

              IN PLARGE_INTEGER FileOffset,

              IN ULONG Length,

              IN BOOLEAN Zero,

              IN BOOLEAN Wait,

              OUT PVOID *Bcb,

              OUT PVOID *Buffer

              );

因为页要被修改了,它们不需要从磁盘上读,除非页的一部分已经被修改了。

调用者负责释放Bcb,一旦对数据做了修改的话。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值