Windows 的Cache管理器

Cache的四个接口

File Stream Manipulation Functions
Copy Interface
MDL Interface
Pinning Interface

注意:Cache管理器的Pin接口和另外两个接口:Copy接口以及MDL接口不能
同时使用。

重要:Cache管理器并解释被缓存的数据内容,它只是理解File Object以及
相关的Stream对象。

Cache管理器的客户类型:文件系统驱动、过滤器驱动、网络文件重
定向器、网络文件服务器。

HSM:Hierarchical Storage Management。

FILE_OBJECT中大部分字段是由IO管理器负责维护,但有几个例外:
FsContext指向一个CommonFCBHeader结构,简称FCB,也就是Unix系统
中的vnode结构,或许就是DOS下的FCB。注意FILE_OBJECT是Per
handle的,而FCB则是per file stream的。

一个重要的结构:

//
// The following structure is pointed to by the SectionObject
// pointer field
// of a file object, and is allocated by the various NT file systems.
//

typedef struct _SECTION_OBJECT_POINTERS {
    PVOID DataSectionObject;
    PVOID SharedCacheMap;
    PVOID ImageSectionObject;
} SECTION_OBJECT_POINTERS; typedef SECTION_OBJECT_POINTERS
*PSECTION_OBJECT_POINTERS;

和FCB一样,这也是一个per file stream的对象,其三个成员都是
Cache管理器的私有结构,甚至在IFSKits中都没有公开。
Cache Map
一个复杂的对象,结构未知!

理解Cache子系统的关键在于了解Io管理器、文件系统、Cache管理器、
Memory管理器之间的关系,用户读写文件时首先经过Io管理器,Io管
理器视情况而定可能会直接向文件系统发出请求,也可能绕过文件系统
直接和Cache管理器打交道,对于后者来说,Cache管理器将利用Memory
管理器的功能实现数据的读写—-事实上只是一个简单的拷贝。

问题:如果Io管理器向文件系统发出请求,那么是不是文件系统(在做了必要
的初始化工作之后)一定会回头调用Cache管理器?

Cache管理器为每个File
Stream开了一个或者多个窗口,每个窗口的大小都是固定的,
记得是256KB。这些窗口用VACB来管理。

看上去Private Cache Map只是为了将同一个File Stream的多个File
Object串起来, 因为Cache管理器为了Unmap一个VACB,必须通知每个
File Object,所以这样一个 链表是有必要的。

Cache Manager的Fast IO看上去是NT的开发小组在最后时刻发现系统
性能问题后草草设计出来的,虽然现在这个接口已经发展的很成熟了,
但总的来说还是有些不好理解。

每个Handle都必须调用Cache管理器初始化缓存。

文件系统提供给Cache管理器的回调接口倒是很简单:

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

初始化CacheMap:
void CcInitializeCacheMap (
    IN PFILE_OBJECT PtrFileObject,
    IN PCC_FILE_SIZES FileSizes,
    IN BOOLEAN PinAccess,
    IN PCACHE_MANAGER_CALLBACKS CallBacks,
    IN PVOID LazyWriterContext
);
LazyWrite和ReadAhead是Cache管理器完成的,但是Cache管理器在
执行这些任务时,必须获得文件系统的支持,上面的Callback参数
就是文件系统提供给Cache管理器的许可证,Cache管理器会在
LazyWrite和ReadAhead的时候调用这些回调函数。这些回调的主要
任务通常是同步。

LazyWriterContext这个名字取的不好,实际上这个参数同时用作
LazyWriter和ReadAhead的上下文参数,通常这个参数就是Context
Control Block,一个和File Object并列的对象,不过后者是Io
管理器创建的而CCB则是文件系统创建的,另外,通常这个指针会
保存在FileObject对象的Context2字段中,这应该算是一种文化
了吧我想。

注意这个函数没有返回值,调用者必须作好准备接受任何可能产生
的例外。

Copy接口
文件系统在处理Read请求时,可以调用Cache管理器的Copy接口,
如果要copy的数据不在物理内存中,则会产生页面错误,将由
Memory Manager调用文件系统将数据从外设(磁盘或者网络服
务器)拷贝到物理内存中,也就是说,文件系统将被递归的调用。

写数据的时候也可能会需要等待,如果写的内容不是硬件扇区
对齐的,则可能需要执行读操作,这样就有可能造成Blocking;
另外写数据需要空闲物理内存页面,如果没有的话则需要执行
LazyWrite并分配新的内存,这也会造成阻塞。

CcCanIWrite
这个函数很有意思,由于在Write接口中的函数有可能会由于空闲
页面不足(即Dirty Page过多)造成等待,所以可以用这个函数
测试一下当前能否执行不阻塞的写操作,这个测试是建议性的,
如果测试完了立刻有个别的家伙又写了一堆东西,则随后的写操作
可能还是会阻塞,不过由于这个竞争情况不会影响程序的正确性,
只是会导致阻塞,所以通常不需要关心。

CcDeferWrite
请求Cache管理器调度一个事件,在能够进行写操作的时候执行
一个回调,通常就是文件系统的写入口函数。

Pin接口
和Copy接口不同,Copy接口始终是使用FileObject和Offset来
寻址Cache管理器管理的内存,而不是直接用指针,而Pin接口
则提供了Pin功能,这样文件系统就可以把Cache管理器管理的
内存’钉’在内存里,随后就可以用指针来寻址了。

Pin需要经过两个步骤:Map、Pin。
CcMapData//
CcPinMappedData//
CcPinRead//
CcSetDirtyPinnedData//
CcPreparePinWrite//
提供这个函数可以认为是一种优化措施,和CcPinRead相比,如果
参数中指定的区域刚好在Page边界上,则Cache管理器不会从二级
存储器中读入该页,因此这个函数返回的缓冲区不能用来读取数据。

CcUnpinData/CcUnPinDataForThread。

注意在Pin期间,在调用者线程上下文中不会有IO发生,所有的IO
都是异步的。
/section{MDL接口}
CcMdlRead//
CcMdlReadComplete//
CcPrepareMdlWrite//
CcMdlWriteComplete//
CcMdlRead和CcPrepareMdlWrite会返回一个MDL链表,这个链表在
对应的Complete函数中释放,所以这两对函数必须成对调用,以
避免内存泄漏。
/section{Cache管理器和VMM的交互}
初始化CacheMap时调用MmCreateSection,扩展文件流时调用
MmExtendSection。

这里有很多在DDK中都没有公开的函数,倒是作为系统服务导出给
ntdll了,例如MmCreateSection大致等于ZwCreateSection。

从工作集管理上看,Cache管理器对VMM来说是一个普通的客户,
即它和普通进程一样,其Working Set要经过VMM的修剪。
预读取模块
后写模块
文件子系统
文件系统不能区分两种请求,一种是应用程序调用产生的读写
请求,一种是由于Cache管理器进行Copy时的页面故障导致VMM向
文件系统发出的读写请求。

几种重要的同步对象

FSD锁(MainResource)
PagingIoResource
和文件流相关的字节范围锁
机会锁(oplock)

[返回顶部]

——————————————————————————–

2004-04-22 20:21:59

主题: 内核对象和执行体对象

内核和执行体具有一些同名对象,例如Event、Mutant、Process、Thread等,
通常的说法是执行体对象是对零个或者多个内核对象的封装,这个说法不妨
可以再直白一些:
/begin{verbatim}
class EPROCESS :
    public KPROCESS,
    public OBJECT_HEADER
{
};
class KPROCESS :
    public DISPATCHER_HEADER
{
};
/end{verbatim}
写成这样,为什么说Windows的进程对象(以及线程对象,道理类似)都是
可以等待的,应该就很清楚了吧:因为它继承了DISPATCH/_HEADER对象,而
后者正是实现可等待对象(用Windows自己的话,大概应该叫`可调度对象’吧)
的关键。

也就是说,在一个执行体进程对象中,包含了一个用来给内核管理用的
KPROCESS对象,一个用来使进程对象能够参与同步的DISPATCHER/_HEADER
头部对象,和一个给对象管理器用来实施管理的OBJECT/_HEADER结构。
其它执行体对象,例如Event、FileObject等都可以进行类似分析。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值