网络文件系统中的数据缓存

这是一OSR上的技术文章,觉得可能对写文件系统过滤的朋友会有一定帮助就翻译出来放到这里了!

通常来说,文件系统过滤驱动程序要确定本地文件系统(例如NTFS 或FAT)可以简单地检查I/O请求包(IRP) 来完成,这是可能的(也是相当简单的)。IRP中的Flags域的 IRP_NOCACHE标志位会告诉文件系统(当然还有过滤驱动程序)这个文件I/O请求不要缓存。通常这是要求文件系统驱动程序这些数据不应当被缓存。

网络文件系统相比之下要复杂一点。然而它仍然使用IRP_NOCACHE标志位,它也可能需要根据自己的内部策略禁止缓存—可能由于在文件服务器上的远程文件的状态决定,也可能由于网络中的其他客户端可能正在使用这个文件。rdbss.sys实现了"mini redirector"模型的部分,它允许重定向器(redirector)(例如mrxsmb.sys,这个驱动程序实现了Windows 2000 以及更早的系统上的CIFS 或 LanManager)基于每个文件改变缓存策略。在这种情况下,一个通常的IRP_MJ_READ IRP可以被缓存,也可以解释为非缓存。

对于修改数据的过滤驱动程序来说,常用的技术是寻找并处理非缓存的I/O操作。这样能够捕获paging I/O操作以及用户层的非缓存I/O操作。但是,如果过滤驱动程序还希望过滤所有的mini redirector(例如在Windows XP上就有两个版本)就需要查询文件控制块(FCB)中的相关域。

对于大多数文件系统,文件控制块这个结构的格式绝大多数在文件系统的控制之下(除通用文件头结构以外),但是对于mini redirector 来说文件控制块的格式是由mini redirector模型定义的。具体定义参考IFS Kit 中的mrxfcb.h 。在这里对于一个过滤驱动程序来说关键的数据结构是MRX_FCB。其中的FcbState域会指示文件的当前状态是缓存或非缓存。

注意:在Windows Server 2003 IFS Kit中这个标志已经被改变因此现在叫做FCB_STATE_READCACHING_ENABLED。

尽管过滤驱动程序可以检查这个文件的当前状态,但是似乎没有任何简单的方法来让过滤驱动程序确保在两次检查之间文件系统没有改变这个状态域。因此,很可能在检查之后文件状态已经改变为禁止缓存。类似地如检查在I/O操作完成之后进行的也可能文件状态可能已经改变为再次打开缓存了。IFS Kit有示例代码(smbmrx\wnet\sys\openclos.c)演示了可能的实现模型。

要防止这个状态改变,调用者必须获得FCB资源;为了避免调用redirector的时候死锁,调用者必须独占地拥有(使用FCB中的ERESOURCE)。再次提醒,这完全依赖于实现以及在IFS Kit中公开的接口。

注意:这个同步仅仅在用户层缓存请求中需要,因为paging I/O或者用户层非缓存请求理所当然地不会被缓存。这很重要,因为在处理paging I/O的时候不能安全地获得这个锁—这会违反已有的锁定层次并引入潜在的死锁。

评论:

"Generally Not Recommended"
这里描述的技术依赖于特定的数据结构格式。现在这些结构是公开的,mini redirector的这些数据结构的接口是导出并固定的,但是即使这样也不保证这些数据结构会用于所有的redirector-但是真实的目标是使其用于RDR2并切也确实没有其他机制来确定一个文件是否会被缓存。

到现在为止,Microsoft已经改变了CIFS redirector客户端的实现,因此这个数据结构不能再使用。因此,你不能在Windows Vista中使用这个技术,因为这些数据结构不再一样了。新的格式也没有公开。也就不存在任何机制来获得这些信息。此外,过滤驱动程序现在常常挂接到MUP而不是CIFS redirector上,因为这个处理更加复杂。

我的建议是如果你希望强制通写(write-through),你可以去掉一个字节范围锁。这会引起随机锁(oplocks)被强制取消,从而引起到服务器的写操作。当然这只使用于CIFS而不是其他redirector,而且和其他基于行为的机制一样也是在将来可能改变的。
 09-Oct-07, Tony Mason


相关结构如下:
typedef struct _FSRTL_ADVANCED_FCB_HEADER {
 FSRTL_COMMON_FCB_HEADER;
 PFAST_MUTEX FastMutex;
 LIST_ENTRY FilterContexts;
} FSRTL_ADVANCED_FCB_HEADER;
typedef FSRTL_ADVANCED_FCB_HEADER *PFSRTL_ADVANCED_FCB_HEADER;

//
// ALL FIELDS IN AN FCB ARE READONLY EXCEPT Context and Context2....
// Also,Context is read only the the mini has specified RDBSS_MANAGE_FCB_EXTENSION
//

typedef struct _MRX_FCB_ {

 FSRTL_ADVANCED_FCB_HEADER Header;

 //
 // The MRX_NET_ROOT instance with which this is associated
 //
 PMRX_NET_ROOT pNetRoot;

 //
 // !!!! changes above this require realignment with fcb.h
 //

 //
 // the context fields to store additional information as deemed necessary by the
 // mini redirectors.
 //

 PVOID Context;
 PVOID Context2;

 //
 // The reference count: in a different place because we must prefix with
 // the FSRTL_COMMON_FCB_HEADER structure.
 //
 ULONG NodeReferenceCount;

 //
 // The internal state of the Fcb. THIS FIELD IS READONLY FOR MINIRDRS
 //
 ULONG FcbState;

 //
 // A count of the number of file objects that have been opened for
 // this file/directory, but not yet been cleaned up yet. This count
 // is only used for data file objects, not for the Acl or Ea stream
 // file objects. This count gets decremented in RxCommonCleanup,
 // while the OpenCount below gets decremented in RxCommonClose.
 //
 CLONG UncleanCount;

 //
 // A count of the number of file objects that have been opened for
 // this file/directory, but not yet been cleaned up yet and for which
 // cacheing is not supported. This is used in cleanup.c to tell if extra
 // purges are required to maintain coherence.
 //
 CLONG UncachedUncleanCount;

 //
 // A count of the number of file objects that have opened
 // this file/directory. For files & directories the FsContext of the
 // file object points to this record.
 //
 CLONG OpenCount;

 //
 // The outstanding locks count: if this count is nonzero, the we silently
 // ignore adding LOCK_BUFFERING in a ChangeBufferingState request. This field
 // is manipulated by interlocked operations so you only have to have the fcb
 // shared to manipulate it but you have to have it exclusive to use it.
 //
 ULONG OutstandingLockOperationsCount;

 //
 // The actual allocation length as opposed to the valid data length
 //
 ULONGLONG ActualAllocationLength;

 //
 // Attributes of the MRX_FCB,
 //
 ULONG Attributes;

 //
 // Intended for future use, currently used to round off allocation to
 // DWORD boundaries.
 //
 BOOLEAN Spare1;
 BOOLEAN fShouldBeOrphaned;
 BOOLEAN fMiniInited;

 //
 // Type of the associated MRX_NET_ROOT, intended to avoid pointer chasing.
 //
 UCHAR CachedNetRootType;

 //
 // Header for the list of srv_opens for this FCB....
 // THIS FIELD IS READONLY FOR MINIS
 //
 LIST_ENTRY SrvOpenList;

 //
 // changes whenever the list changes..prevents extra lookups
 // THIS FIELD IS READONLY FOR MINIS
 //
 ULONG SrvOpenListVersion;

} MRX_FCB, *PMRX_FCB;
