文件大小信息
文件系统和内存管理器每个都维护了文件的大小信息。无何何时只要文件系统对一个文件建立映射,它就指明文件的当前大小。任何接下来对文件大小的改变就由缓存管理器来说明了。
缓存管理器用三个值来说明当前的文件大小:
typedef struct _CC_FILE_SIZES {
LARGE_INTEGER AllocationSize;
LARGE_INTEGER FileSize;
LARGE_INTEGER ValidDataLength;
} CC_FILE_SIZES, *PCC_FILE_SIZES;
这些域的名字可能很迷惑。例如AllocationSize不是用来说明给文件分配的实际物理空间,而是用来说明能放进目前分配好的空间的数据量。对于一些文件系统,这两个值可能是一样的。然而对于支持压缩和扩张的文件系统,这个值仅代表了能适应(能放进去的)的数据大小。
缓存管理器回调
文件系统和缓存管理器的交互是由一系列回调函数实现的。这些回调函数被登记(在每个文件的基础上)并被缓存管理器利用以确保数据结构在执行文件系统操作之前被锁上。
WindowsNT假设文件系统、缓存管理器、内存管理器之间的资源分配有严格的顺序。如果遵循,这个顺序能确保死锁不会发生。如果不遵循,死锁能够/将会发生。具体来说,第一个申请的是文件系统的资源。然后是缓存管理器资源。最后是内存管理器资源。
从而,这些回调函数被缓存管理器用来纪念这个层次。这些回调是:
typedef BOOLEAN (*PACQUIRE_FOR_LAZY_WRITE) (
IN PVOID Context,
IN BOOLEAN Wait
);
typedef VOID (*PRELEASE_FROM_LAZY_WRITE) (
IN PVOID Context
);
typedef BOOLEAN (*PACQUIRE_FOR_READ_AHEAD) (
IN PVOID Context,
IN BOOLEAN Wait
);
typedef VOID (*PRELEASE_FROM_READ_AHEAD) (
IN PVOID Context
);
typedef struct _CACHE_MANAGER_CALLBACKS {
PACQUIRE_FOR_LAZY_WRITE AcquireForLazyWrite;
PRELEASE_FROM_LAZY_WRITE ReleaseFromLazyWrite;
PACQUIRE_FOR_READ_AHEAD AcquireForReadAhead;
PRELEASE_FROM_READ_AHEAD ReleaseFromReadAhead;
} CACHE_MANAGER_CALLBACKS, *PCACHE_MANAGER_CALLBACKS;
FileSize代表了文件数据中最后一个合法的字节,逻辑上它是“End of File”的标志。
ValidDataLength代表了内存数据中最后一个合法的字节。因此,文件能被扩展在数据被写回磁盘之前。
注意CC_FILE_SIZES结构与FSRTL_COMMON_FCB_HEADER结构有同样的域布局。典型地,一个文件系统不包含一个独立的CC_FILE_SIZES数据结构而只是传递重叠域的地址。
文件的AllocationSize被内存管理器用来表示”section object”(节对象、片对象)的大小。因为section对象随后要被用来决定文件如何被映射入内存,所以AllocationSize总是大于等于文件大小这点很关键哦。
缓存管理器和内存管理器并不检测AllocationSize小于文件大小的这种情况,否则系统将会因为数据结构的不一致而崩溃掉。
注意,回调用于缓存管理器的两个不同的部分。第一,the lazy writer,负责把dirty cached data写回文件系统。第二个是预读处理-读数据在任何实际的用户调用之前。
首先,在设计的时候必需注意到你保护文件系统是为了防什么。没有理由序列化cached I/O操作…(这句有些然)。然而,你确实有必要来防范non-cached I/O操作和改变文件大小的用户操作。
NT文件系统通过使用两个ERESOURCE结构来实现这一点。这两个也能被操作系统的其它组件使用,通过使用共同的头文件-特别是共同头文件里的Resource和PagingIoResource域。缓存管理器并不直接获取资源-而是请求进入文件系统来获取必要的资源。
注意,这些登程必须由你的文件系统提供-它们不是可选地,如果不提供它们系统会崩溃掉地。
接下来的代码是老版本OSR FSDK里的一个样例实现(基于回调管理例程的实现):
static BOOLEAN OwAcquireForLazyWrite(PVOID Context, BOOLEAN Wait)
{
POW_FCB fcb = (POW_FCB) Context;
BOOLEAN result;
// Take out the lock on the file.
result = OwAcquireResourceExclusiveExp(&fcb->Resource, Wait);
if (!result) {
// We did not acquire the resource.
return (result);
}
// We did acquire the resource. We need to:
// (1) Store away the thread id of this thread (for the release)
// (2) Set top level irp to a pseudo value
// In both cases, the previous value should be zero.
OwAssert(!fcb->ResourceThread);
fcb->ResourceThread = OwGetCurrentResourceThread();
return (TRUE);
}
Microsoft IFS kit里的每个文件系统也包含了类似的样例程序。对于Lazy Writer,这些例程位于以下这些地方:
File System File Routine
FAT resrcsup.c FatAcquireFcbForLazyWrite
CDFS resrcsup.c CdAcquireForCache
RDR2 rxce/resrcsup.c RxAcquireFcbForLazyWrite
其它类似例程也在这些文件夹里。