NT缓存管理器(三)

本文详细介绍了虚拟内存管理系统如何处理数据修改速度超过写回磁盘速度的情况,包括关键操作CcCanIWrite的作用,以及如何通过CcDeferWrite实现写操作节流等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CcCanIWrite

因为应用程序能够在内存中以大于写回磁盘的速度修改数据,虚拟内存系统能用数据填满。这反过来在VM系统中能够导致致命的out-of-memory情况。缓存管理器提供的一个支持这个的关键操作是CcCanIWrite.这种回调的原型是:

         NTKERNELAPI BOOLEAN CcCanIWrite (

              IN PFILE_OBJECT FileObject,

              IN ULONG BytesToWrite,

              IN BOOLEAN Wait,

              IN BOOLEAN Retrying

              );

如果这个回调返回False,那么FSD需要延迟把脏数据写进cache以防止出现out-of-memory情况。这种out-of-memory的典型症状就是NO_PAGES_AVAILABLE这样的停止代码。

FSD必须处理推迟和接下来的重试写操作。FSD能够推迟写操作,或者通过内部推迟机制,或者通过例程。CcDeferWrite.

例程FsRtlCopyWrite能被你的FSD使用而不是直接访问cache.这种情况下,推迟IO操作在函数内部被操作了。

CcCopyRead

一旦一个文件系统建立了caching(通过CcInitializeCacheMap调用),它使用FsRtl例程或这个例程。典型地,FsRtlCopyRead被用来实现快速读的I/O路径,这个例程被用来实现IRP_MJ_READ.回调原型是:

          NTKERNELAPI BOOLEAN CcCopyRead (

               IN PFILE_OBJECT FileObject,

               IN PLARGE_INTEGER FileOffset,

               IN ULONG Length,

               IN BOOLEAN Wait,

               OUT PVOID Buffer,

               OUT PIO_STATUS_BLOCK IoStatus

               );

FileObject包含了一个指向SectionObjectPointer的指针,该指针被缓存管理器使用,当从cache往用户缓存区里拷贝数据的时候。从而有个假设:caching之前就被初始化好了。

Length说明读操作的长度。Buffer被假设为足够地大以容纳从cache拷过来的数据量。

参数Wait表明调用者是否愿意在一段不确定的时间里阻塞,例如当必须获得一个锁的时候可能被请求。这个参数应该被视作”hint”(暗示)而不是一个保证。例如,如果一个磁盘I/O被需要来结束这个操作,那么操作将会继续尽管Wait值是False.

Buffer指的是调用者提供的缓冲区。它不需要是合法的,这样的话这个例程将会抛出异常。FSD应该设陷阱捕捉异常并返回一个错误给用户程序。

IoStatus块会被设置来表明操作的结束状态和总共读了多少个字节。

注意,缓存管理器可能被用来默认分页地将数据导入cache。那种情况下,FSD会被重新进入以处理实际的分页I/O操作。

CcCopyWrite

一旦一个文件系统建立了caching(通过CcInitializeCacheMap调用),它使用FsRtl例程(FsRtlCopyWrite)或这个例程。典型地,FsRtlCopyWrite被用来实现快速读的I/O路径,这个例程被用来实现IRP_MJ_WRITE.回调原型是:

          NTKERNELAPI BOOLEAN CcCopyWrite (

               IN PFILE_OBJECT FileObject,

               IN PLARGE_INTEGER FileOffset,

               IN ULONG Length,

               IN BOOLEAN Wait,

               IN PVOID Buffer

               );

FileObject包含了一个指向SectionObjectPointer的指针,该指针被缓存管理器使用,当从用户缓存区往cache里拷贝数据的时候。从而有个假设:caching之前就被初始化好了。

Length说明写操作的长度。Buffer被假设为足够地大以容纳拷进cache的数据。

参数Wait表明调用者是否愿意在一段不确定的时间里阻塞,例如当必须获得一个锁的时候可能被请求。这个参数应该被视作”hint”(暗示)而不是一个保证。例如,如果一个磁盘I/O被需要来结束这个操作,那么操作将会继续尽管Wait值是False.

Buffer指的是调用者提供的缓冲区。它不需要是合法的,这样的话这个例程将会抛出异常。FSD应该设陷阱捕捉异常并返回一个错误给用户程序。

注意,这个操作可能要求cached页的一部分被写。如果是那种情况,那么那一页的内容将会首先从磁盘上读过来,然后再对页中被写的那一部分进行修改。因此,这个回调有可能导致重新进入FSD来处理读页错误。

因为数据能被非常快地写入VM系统,FSD必须实现一个write-throttling机制,通过使用CcCanIWrite和CcDeferWrite。

CcDeferWrite

为了简化实现write-throttling的过程,缓存管理器提供一个简单的机制:排队写操作直到VM系统能够接纳它们。这通过一个Deferred Write回调实现当调用CcCanIWrite返回False的时候。

回调函数的原型是:

          Typedef VOID (*PCC_POST_DEFERRED_WRITE) (

               IN PVOID Context1,

               IN PVOID Context2

               );

The context pointers are typically specified by your file system as part of establishing the deferred write

processing.

CcDeferWrite的原型是:

          NTKERNELAPI VOID CcDeferWrite (

               IN PFILE_OBJECT FileObject,

               IN PCC_POST_DEFERRED_WRITE PostRoutine,

               IN PVOID Context1,

               IN PVOID Context2,

               IN ULONG BytesToWrite,

                IN BOOLEAN Retrying

                );

FileObject表明调用者要写的对象文件。

PostRoutine是FSD提供的回调函数,它将会被缓存管理器调当VM状态改变(更多的写操作被允许)的时候。

Context1和Context2指针是FSD定义的,被传递给FSD提供的回调函数,一旦对文件的写操作被允许。

BytesToWrite参数表明这个操作将向文件写入的字节数。VM系统利用这个信息来决定它是否写地安全。

Retrying参数表明这是否是第一次尝试。

CcGetDirtyPages

为了完整性把这个例程放在这里。它被用在文件系统里,利用WindowsNT内部的登录机制。它对文件系统并不都是有用地。函数原型如下:

          NTKERNELAPI LARGE_INTEGER CcGetDirtyPages (

                IN PVOID LogHandle,

                IN PDIRTY_PAGE_ROUTINE DirtyPageRoutine,

                IN PVOID Context1,

                IN PVOID Context2

                );

CcGetFileObjectFromBcb

BCB还包含了一个指向文件对象的指针,被VM系统用来跟踪文件的cache信息。如果需要的话,文件对象的信息能从一个给定的BCB里提取出来。

原型如下:

          NTKERNELAPI PFILE_OBJECT CcGetFileObjectFromBcb (

                IN PVOID Bcb

                );

CcGetFileObjectFromSectionPtrs

当caching首次建立的时候,缓存管理器使用FileObject参数来CcInitializeCacheMap以生成新的Section对象(用以caching文件数据)。只要cached数据被缓存管理器维护着,最初的FileObject被VM系统用来进行多种必要的I/O操作。

给定一个SectionObjectPointer结构,这个例程能够告诉文件系统VM系统正在使用的实际的文件对象。

The prototype for this call is:

这个调用的原型是:

         NTKERNELAPI PFILE_OBJECT CcGetFileObjectFromSectionPtrs (

               IN PSECTION_OBJECT_POINTERS SectionObjectPointer

               );

An interesting side-effect of this implementation model (where the SectionObjectPointer field refers to a particular section object that in turn refers to a particular file object) is that a given FileObject may remain

Valid for a considerable period of time – far beyond the point when the file has been closed by the application program.

这个实现模型的一个有趣的副作用是:给定的FileObject可能在相当长一段时间内保持合法-远远超过文件已经被应用程序关闭的时间点。

CcGetLsnForFileObject

为了完整性把这个例程放在这里。它被用在文件系统里,利用WindowsNT内部的登录机制。它对文件系统并不都是有用地。

函数原型如下:

         NTKERNELAPI LARGE_INTEGER CcGetLsnForFileObject(

               IN PFILE_OBJECT FileObject,

               OUT PLARGE_INTEGER OldestLsn OPTIONAL

               );

CcFastCopyRead

这个例程被这样的文件系统使用:不支持文件偏移量大于4GB的。它相当于一个替代的对CcCopyRead的调用,基本上能以相同形式调用。原型如下:

         NTKERNELAPI VOID CcFastCopyRead (

               IN PFILE_OBJECT FileObject,

               IN ULONG FileOffset,

               IN ULONG Length,

               IN ULONG PageCount,

               OUT PVOID Buffer,

               OUT PIO_STATUS_BLOCK IoStatus

               );

FileObject表明正被读的文件。

Length表明被拷进调用者提供的Buffer的字节数。

PageCount表明用户提供的Buffer占用的物理页数。

IoStatus包含读操作的结束状态以及读了多少字节。

CcFastCopyWrite

这个例程被这样的文件系统使用:不支持文件偏移量大于4GB的。它相当于一个替代的对CcCopyWrite的调用,基本上能以相同形式调用。原型如下:

         NTKERNELAPI VOID CcFastCopyWrite (

               IN PFILE_OBJECT FileObject,

               IN ULONG FileOffset,

               IN ULONG Length,

               IN PVOID Buffer

               );

FileObject表明正被读的文件。

Length表明被拷进调用者提供的Buffer的字节数。

PageCount表明用户提供的Buffer占用的物理页数。

IoStatus包含读操作的结束状态以及读了多少字节。

就像CcCopyWrite那样,使用这个例程的文件系统也必须利用CcCanIWrite实现write-throttling。

CcFlushCache

这个例程被FSD利用来保证任何正被cached的dirty数据能被写回磁盘。原型如下:

          NTKERNELAPI VOID CcFlushCache (

               IN PSECTION_OBJECT_POINTERS SectionObjectPointer,

               IN PLARGE_INTEGER FileOffset OPTIONAL,

               IN ULONG Length,

               OUT PIO_STATUS_BLOCK IoStatus OPTIONAL

               );

这个例程被FSD用来保证所有的脏/污点数据被写回磁盘。

如果FileOffset参数是空,那么整个文件被冲掉。

如果FileOffset参数被设置,从那个偏移开始的长Length字节的文件将被冲掉。

注意,这个调用可能导致I/O操作从而从新进入FSD。这个调用通常被FSD作为IRP_MJ_FLUSH_BUFFERS实现的一部分。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值