分享
 
 
 

探寻Windows NT/2000 Copy On Write机制

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

探寻Windows NT/2000 Copy On Write机制

WebCrazy(http://wecrazy.yeah.net)

Copy On Write机制是典型的Lazy evaluation实现,现代操作系统如Windows NT/2000,UNIX/Linux的内存管理部分大量使用这种机制。本文通过对Windows NT/2000中Copy On Write机制作一深入分析,旨在探寻Windows NT/2000内核态内存管理器的几个重要的数据结构,在继续以下的讨论之前,您务必要明白PDE/PTE、VAD等一些术语(参见我先前的《小议Windows NT/2000分页机制》与《分析Windows NT/2000堆内存与虚拟内存组织 》),另外我将述及另几个与内存子系统相关的术语,如Control Area,Subsection,Working Set List,Page Frame Database。下面列出我在本文中用于分析Copy On Write机制的代码,文中所有的叙述均基于这段代码。

// cow.c

// Writed by ChenChengQin(tsu00@263.net )

#include <stdio.h>

#include <string.h>

#define BUFSIZE 10

#pragma data_seg(".seg_cow")

char data[BUFSIZE]={'A','A','A','A','A','A','A','A','A','\0'};

#pragma data_seg()

#pragma comment(linker, "/SECTION:.seg_cow,RWC")

void main(int argc,char *argv[])

{

int i;

if (argc>1){

//memset(data,'B',BUFSIZE);

__asm int 3;

for(i=0;i<BUFSIZE;i++)

data[i]='B';

data[BUFSIZE-1]='\0';

}

printf("%s",data);

getchar();

}

非常简单的一段代码,使用如下指令编译:

cl /Zi /Fa cow.c

运行一个实例(别退出):

c:>cow

AAAAAAAAAA

使用SoftICE看看cow.exe映像在内存中的分布:

:addr cow

:map32 cow

Owner Obj Name Obj# Address Size Type

cow .text 0001 001B:00401000 00006BB8 CODE RO

cow .rdata 0002 0023:00408000 000005C8 IDATA RO

cow .data 0003 0023:00409000 00002EC4 IDATA RW

cow .idata 0004 0023:0040C000 00000633 IDATA RW

cow .seg_cow 0005 0023:0040D000 0000010C IDATA RW

cow .reloc 0006 0023:0040E000 000006B2 IDATA RO

//查看.seg_cow段(虚拟地址0x40d000)的首地址的PTE,详见《小议Windows NT/2000分页机制 》

:dd c0000000+1*1000+d*4 l 10

0023:C0001034 003F9225 00000000 00000000 00C3C067 %.?.........g...

--------

|

|_cow.exe进程.seg_cow段首地址的PTE

SoftICE的page命令可以dump出这个PTE的属性,如下所示:

:page 40d000

Linear Physical Attributes

0040D000 003F9000 P A U R

不过这里列出的Attributes并没有指出.seg_cow的Copy On Write属性,因为X86的PTE的低12位(0-11),即属性位并没有指出这个属性,但Microsoft(当然不仅仅Microsoft)使用了这12位中的保留的系统(OS)位,下面是这一DWORD值在Windows 2000的具体格式:

struct _HARDWARE_PTE_X86 (sizeof=4)

+0 bits0-0 Valid

+0 bits1-1 Write

+0 bits2-2 Owner

+0 bits3-3 WriteThrough

+0 bits4-4 CacheDisable

+0 bits5-5 Accessed

+0 bits6-6 Dirty

+0 bits7-7 LargePage

+0 bits8-8 Global

+0 bits9-9 CopyOnWrite

+0 bits10-10 Prototype

+0 bits11-11 reserved

+0 bits12-31 PageFrameNumber

从上的PTE为003F9225的值可以看出0x40d000的只读、CopyOnWrite属性。只读属性是实现CopyOnWrite的保证,这样X86才能在另一个cow实例试图写.seg_cow段时raise一个0eh(页故障)中断(陷阱),让Windows处理这个Copy On Write操作。基于这个原理,我即运行cow的另一个实例,跟踪Copy On Write机制:

c:>cow 1(设置一个参数,让cow试图更新.seg_cow段)

我在代码中设置了int 3指令让softice在i3here设置为on状态时让softice激活。然后使用bpint e指令让softice捕获0eh中断。i386在raise 0e中断时将发生页故障的虚拟地址保存于CR2寄存器中。Windows 2000页故障处理入口KiTrap0E就是根据这个地址制作cow进程.seg_cow的副本,并替代第二个cow实例工作集(Working List,这儿只指进程工作集,未牵涉系统工作集)的原始页面,实现CopyOnWrite的目的。我并不准备将KiTrap0E的汇编代码列于此,只是讲一讲Windows寻找CR2指定的地址的属性的步骤,Windows 2000中由MiQueryAddressState过程实现。KiTrap0E当然也会调用MiQueryAddressState。

1、首先检查0x40d000(由CR2指定)的PDE。由MiDoesPdeExistAndMakeValid实现。

2、检查PTE。

3、查页框数据库(PFN,Page Frame Database,由内核结构数组变量MmPfnDatabase指定)

4、根据PFN中工作集索引寻找Working Set List Entry(Working Set基址由内核变量_MmWsle指出),这一步骤由内核例程MiLocateWsle完成。

5、然后根据Wsle的属性(也是前12位)查找MmProtectToValue数组,以获得用户态可以理解的格式。即在winnt.h中定义的PAGE_WRITECOPY、PAGE_READWRITE等等。

步骤3至5其实也实现了物理地址至线性地址的转换,当然这个是在这个地址Present的前提下。这也是为什么Windows 2000使用如此复杂且繁琐的结构来管理内存子系统。确切的说我并没有谈到PFN的PteAddress成员(下面i386kd输出可以看到),这些都是使用分页文件(pagefile.sys,通过ProtoPTE,即原型PTE实现的),共享内存块等的基础。David Solomon的书《Inside Windows NT,2nd Edition》有四种PFN形态的具体结构等等其他很多详细的说明。

下面是i386kd的分析,其实根据上面我给定的一些内核变量SoftICE也可以看出些东西。不过远没有i386kd来的容易。

// cow.exe 进程ID(ClientID)=4ac

kd> !process 4ac

!process 4ac

Searching for Process with Cid == 4ac

PROCESS ff605d60 SessionId: 0 Cid: 04ac Peb: 7ffdf000 ParentCid: 04a0

DirBase: 017bc000 ObjectTable: ff610f88 TableSize: 12.

Image: cow.exe

VadRoot ff624168 Clone 0 Private 37. Modified 0. Locked 0.

.

.

.

//转换40d000至物理地址,取得PFN。

//SoftICE中使用Page命令

kd> !vtop 017bc000 40d000

--------

|_DirBase(见!process输出)

!vtop 017bc000 40d000

Pdi 1 Pti d //输出PDE与PTE

0040d000 00a6e000 pfn(00a6e) //输出PFN

//查PfnDatabase

//Softice中:

// dd @MmPfnDatabase+a6e*18 (每一PFN项占用0x18字节)

kd> !pfn a6e

!pfn a6e

PFN 00000A6E at address FFB8EA50

flink 00000097 blink / share count 00000001 pteaddress E151E1B4

reference count 0001 color 0

restore pte 056B04B0 containing page 002AF3 Active P

Shared

/*

查MmWsle

SoftICE中:

:dd @MmWsle+97*4 l 10

0023:C05028FC 0040DF29 000006A0 00510C09 000009B0 ).@.......Q.....

--------

|_0040D000是不是PFN为A6E的Virtual Address

*/

kd> !wsle 4ac

!wsle 4ac

// KPEB中的VmWorkingSetList成员(Windows 2000 Server Build 2195中偏移0f0处,值为C0502000)指出Working Set List

// 见《Windows 2000内核KPEB/KTEB详细结构 》

Working Set @ c0502000

Quota: 2f FirstFree: 22 FirstDynamic: 4

LastEntry ad NextSlot: 16 LastInitialized 257

NonDirect 1e HashTable: c06f3000 HashTableSize: 200

.

.

.

这些i386kd指令已经较清楚的指出以上5个步骤。但要理解Copy On Write还必须对Section(用户态的FileMapping)对象有一基本的理解。在使用NtCreateSection/NtOpenSection(CreateFileMapping/OpenFileMapping间接使用这些例程)等时,Windows 2000一般会在VAD这个自平衡的二叉树中插入一个节点。(我在《分析Windows NT/2000堆内存与虚拟内存组织 》中详细介绍过VAD)。SoftICE在使用query指令dump VAD树时有一成员MMCI(内存管理结构),她指向的是Control Area。如下所示:

// SoftICE输出

:query cow

Address Range Flags MMCI PTE Name

00010000-00010000 C4000001

.

.

.

00400000-0040E000 07100005 FF62FD48 E11EBF80 cow.exe

--------

|_Control Area

.

.

.

其实我们使用i386kd的!memusage可以dump出系统中所有的Control Area:

kd> !memusage

loading PFN database

loading (99% complete)

Zeroed: 15 ( 60 kb)

Free: 0 ( 0 kb)

Standby: 1274 ( 5096 kb)

Modified: 686 ( 2744 kb)

ModifiedNoWrite: 1 ( 4 kb)

Active/Valid: 14380 ( 57520 kb)

Transition: 11 ( 44 kb)

Unknown: 0 ( 0 kb)

TOTAL: 16367 ( 65468 kb)

Building kernel map

Finished building kernel map

Control Valid Standby Dirty Shared Locked PageTables name

.

.

.

ff62fd48 32 0 0 0 0 0 mapped_file( cow.exe )

--------

|_Control Area与上面SoftICE的MMCI一致

.

.

.

i386kd的ca命令可以看出一个Control Area结构:

kd> !ca ff62fd48

ControlArea @ff62fd48

Segment: e11ebf48 Flink 0 Blink: 0

Section Ref 1 Pfn Ref 8 Mapped Views: 1

User Ref 2 Subsections 7 Flush Count: 0

File Object ff684288 ModWriteCount 0 System Views: 0

WaitForDel 0 Paged Usage a0 NonPaged Usage 120

Flags (10000a0) Image File HadUserReference

File: \Desktop\hack\cow.exe

Segment @ e11ebf48:

Base address 0 Total Ptes f NonExtendPtes: f

Image commit 5 ControlArea ff62fd48 SizeOfSegment: f000

Image Base 0 Committed 0 PTE Template: 54f1c30

Based Addr 400000 ProtoPtes e11ebf80 Image Info: e11ebfc0

Subsection 1. @ ff62fd80

ControlArea: ff62fd48 Starting Sector 0 Number Of Sectors 8

Base Pte e11ebf80 Ptes In subsect 1 Unused Ptes 0

Flags 15 Sector Offset 0 Protection 1

ReadOnly CopyOnWrite

.

.

.

由ca命令的输出结果可以看出这个Control Area有7个子区域(Subsection),限于篇幅我删掉部分输出结果,你可以将所有结果与SoftICE的map32指令输出比较比较。Control Area中实际上所有Subsection结构均是使用线性结构组织,每个Subsection在Windows 2000 Server Build 2195中占用0x20字节。所以SoftICE可以很容易的分析所有的这些。

需要指出的是不仅仅Section对象使用Control Area,她在Windows 2000中也由SECTION_OBJECT_POINTERS结构使用:

typedef struct _SECTION_OBJECT_POINTERS {

PVOID DataSectionObject; //Control Area

PVOID SharedCacheMap;

PVOID ImageSectionObject; //Control Area

} SECTION_OBJECT_POINTERS;

而每一个FILE_OBJECT都有SECTION_OBJECT_POINTERS成员(见ntddk.h)。这个机制是Windows 2000装载可执行文件、文件IO操作(看到DataSectionObject与ImageSectionObject了吗?)的关键所在。只有熟悉这些结构,才可能真正初步明白Copy On Write机制。剩下的只有你自己多研究研究了。

关于cow.exe,我还有两点要说明的是:

1、cow.c使用#pragma comment(linker, "/SECTION:.seg_cow,RWC")显式的指出.cow_seg段的Copy On Write属性,实际上在Windows NT/2000中这种共享属性是默认存在的。可执行文件的映射、可读可写的数据都被默认设为Copy On Write属性,您可以使用Jeffrey Richter的VMMAP验证这种说法。

2、Windows NT/2000使用可执行文件名(ImageName)用于识别这种程序多个实例间使用Copy On Write共享内存,对于有不同文件名的可执行文件,即便其内容完全一致,这种机制也不起作用,而用分别映射Section对象替之。

所有以上讨论的都没有找到Microsoft的Full Documented,所有以上讨论的也只是在我初步分析Windows 2000后得到的,有什么技术问题需要交流的,欢迎赐教(tsu00@263.net)!

参考资料:

1.David Solomom《Inside Windows NT,2nd Edition》

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有