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实现的一部分。