21世纪的文件系统:概述WindowsNT 5.0文件系统(NTFS)(一)

原文地址: http://www.microsoft.com/msj/1198/ntfs/ntfs.aspx

文章参考代码下载

正是因为NTFS系统存储文件属性的方式,单个文件的所有属性(包括它的数据属性)都被存储着。这一点在访问小型文件的时候提高了性能。在文件的目录条目中,NTFS系统同样存放着一个文件最常用的属性。
很多人的编程任务在利用了Windows NT 5.0文件系统(NTFS)新变革的优势之后变得简化了。让我们继续进行这些新特征的旋风之旅。记得,我们在讨论测试版的软件,那么每一样东西都可能发生变化。所以在基于这些信息写代码之前要查阅Microsoft的最新文档。
我们先来一个NTFS文件系统在硬盘布局上的总揽。然而这些信息对应用开发人员来说是程序禁地,但是这些高层次的解释会让你更容易的明白这些NTFS新特征。NTFS文件系统的核心是一个特殊的文件——主文件表(MFT)。在格式化NTFS卷的时候这个文件就被创建了。MFT包括一系列的1KB大小条目单位(数组);每个条目单位标识了硬盘卷中的一个单个文件。当你创建了一个文件,NTFS首先必须要在MFT数组中开辟一个空的条目单位,然后把这个新文件的信息填充到这个1KB空条目单位中。图1 展示了单文件(或者是目录)关联的标准属性清单。
Figure 1:
属性: 描述
标准信息 像只读、隐藏、系统文件等标志。还包括了创建、访问、修改的时间戳。一个文件的硬链接计数也在这里维护保存。
名字 Unicode格式的文件(目录)名。*如果一个文件还有一个短名或者有硬链接,那么它可以有多重名字的属性。
安全描述符 文件的安全数据结构支配着用户对文件的访问。
数据 这个是文件的内容。更具体些,这个属性标识文件未命名流中的数据。NTFs系统把一个文件的数据简单看做是该文件的另一个属性,这样就允许一个文件存在多重流。记住,和文件不同,目录没有相关的数据属性。
命名数据 这个可选的属性标识与文件相关的附加命名数据流。注意,单个文件或者一个目录可以有0到多个命名数据属性。
索引根、索引分配,位图 这三个属性用来实现大型目录的文件名索引。这些属性只和目录相关联,文件没有。
重分析 存储在这个属性中的数据标识了当访问这个文件或目录时执行的文件系统过滤器。


当文件被创建时,系统会为它的MFT文件条目单元创建属性集合,然后填充到1KB条目中。但有两个问题:很多属性都是变量长度,同时很多属性像名字、数据、命名数据的长度都会远远超过1KB。所以NTFS不能简单的把属性扔进MFT条目当中去,取而代之,NTFS要进行测试,如果一个属性长度值很小,那么把它扔进条目中,这称作常驻属性;如果属性长度值很大,系统会把属性值放在硬盘中的其他一个位置,(这个是非常驻属性)给条目单位中放一个指针指向属性值。
现在,每个人都会在硬盘里放很多很多的小文件。我们都有大量的.Lnk文件(快捷方式),可能还有很多的Desktop.ini文件,到处都是。由于NTFS在一个MFT条目单位中存放属性这种方式,它有可能为单个文件的所有属性,包括它的数据属性,来驻留。这个在访问小文件是大大提高了性能。进一步说,*NTFS还存储一个文件最常用属性,位于代表该文件的目录条目中。这意味着当系统做了FindFirstFile/FindNextFile操作来拿到一个文件的名字还有其基本属性,这些属性的数据是位于其目录条目中的,所以不需要其他硬盘访问了。
在Windows NT 4.0之前,NTFS的一个MFT条目大小是4KB,这个,当然是允许有略多数据的文件有它们自己的数据驻留。到了NT 4.0时,微软把大小削减到1Kb,因为已经意识到在很多NTFs系统上文件的数目及其大小导致了MFT条目浪费大量空间,如果把条目缩减到1KB,会更有效率的。现在让我们来复习一下软件开发人员可以利用的NTFS新功能。
流(Streams)
很少有人知道NTFS允许单个文件有多个数据流。这功能在NT 3.1时就产生了,但一直被淡化着。很不幸啊,因为流在很多情况下都是很有奇效的。举个例子,如果你正在开发一个位图编辑应用程序,当用户保存自己的数据时,你在其硬盘上创建一个BMP文件。并且你会保存一个图片的缩略版。缩略图通常存储在文件的末尾,在主图像文件之后。为了显示缩略图,必须要打开其文件,解析文件的头信息,寻至主图像后的字节数据,读取缩略图的数据,然后将其显示。其实我们可以把缩略图数据存在另外一个文件当中,不过这个主意不适合,这样很容易就让主图像文件和缩略图文件分离了。
NTFS命名流提供了两全其美的办法。当你的应用创建了文件,你可以把主图像数据写在默认流(未命名流)中,同时为缩略图数据创建另一个数据流(命名流),这两个流位于同一个文件中。只有一个文件,但它有两个数据流。为了搞懂到底是怎么运行的,我们做一个试验,在一台NT系统的机子上打开cmd(命令Shell),切换目录到一个NTFS分区下,然后输入:
C:\>ECHO "Hi Reader">XX.TXT:MyStream
当执行该命令时,系统会创建一个叫XX.TXT的文件。这个文件有两个流:一个0字节的未命名流和一个包含Hi Reader字符文本的命名流(MyStream)。如果至此你还没有猜到,你刚才做的是:在文件名称的后面放一个冒号紧跟着流的名字,以此访问该文件的命名流。正如文件名字一样,WIn32函数将流的名称作为保留大小写,同时搜索不区分大小写。
不幸的是,系统提供的这项功能至多把流视作二等公民,举个例子,执行下面命令:
C:\>DIR XX.TXT
Volume in drive C is wizard
Volume Serial Number is 40E5-92D4


Directory of C:\
03/18/98 08:36a 0 XX.TXT
                   1 File(s) 0 bytes
                   0 Dir(s) 3,399,192,576 bytes free


正如你看到的,DIR报告文件大小是0字节,这肯定不对。DIR命令仅仅把文件未命名流报告给用户;命名流的大小是不给用户看的。顺便说一句,资源管理器同样会报告一个0字节大小的文件。这使得一些古怪的派对游戏里你可以在朋友的硬盘上分配一个大数据流。所有硬盘空间耗尽了朋友也不会发现的,因为所有的工具会报告文件仅占据了0字节!在处理数据流时,记住,仅仅是这种工具不给予数据流以应得的对待;NTFS全面支持各种数据流(他甚至会计入你的存储指标)。现在,为了看流的内容,执行下面命令:
C:\>MORE <XX.TXT:MyStream
"Hi Reader"
这是使用流的另一个办法。假如说你正在写一个文字处理应用程序。当用户打开了一个现有文档,你可能会创建一个临时文件来保存住用户所有的操作。然后呢,当用户决定保存这些操作时,你把所有的更新信息写到临时文件中,原文件删掉,最后把临时文件给予其重命名移至原文件处。
这听起来相当的简单明了。但是你可能忘了点什么,最终文件应该具有和原文件一样的创建时间戳,你就不得不把这个修改过来。同样原文件的文件属性、安全信息都要考虑。这个文件保存的操作过程中很容易会错过正确更新某些属性。
如果你使用数据流,这些隐患都不用担心了。单个文件里的所有流都会共享这个文件的属性(像时间戳、安全信息等等),你应该修改你的程序,让用户的临时信息写入到文件里的命名数据流。然后,当用户保存了数据,重命名临时命名数据流到未命名数据流,NTFs系统会删掉旧有的未命名数据流,同时重命名操作是以一个全有或全无的方式。你一点也不用对文件属性做什么,它们肯定会准确的保留。
在我们离开数据流之前,我们再列举出一些事宜。首先,如果你从一个支持数据流的文件系统里拷贝一个文件到另一个不支持数据流的文件系统(像使用在软盘上的FAT系统),只有未命名流的数据被拷贝了;任何命名流里的数据没有得到拷贝。
第二,命名数据流可以和目录进行关联。目录从不会有未命名数据流来关联,但它们可以有命名数据流。可能你熟悉资源管理器下的DESKTOP.ini。如果资源管理器在某个目录里发现了这个文件,它知道要加载一个Shell命名空间扩展,并允许Shell命名空间扩展来解析目录的内容。系统把这技术用在了诸如My Document、Fonts、Internet Channels等等文件夹。既然DESKTOP.ini这个文件描述了资源管理器该怎么显示目录的内容,那微软要是把DESKTOP.ini的数据放进目录的命名数据流中岂不是更有意义?
微软不这么做的原因是向后兼容。数据流只在NTFS驱动器下实现;FAT文件系统或者CD-Rom驱动盘下是不存在数据流的,同样是这个原因,数据流可能并不适合你的应用程序。但如果你的程序需要NTFS,你当然要利用好这个新功能的优势。
FIgure 2 的代码展示了一个应用程序怎样与数据流配合工作。代码注释好了,那么这里就不再描述了,你编译这段代码后,编译器里按步调试,到达每一个TEST行,执行命令行的命令之后看显示的结果。
Figure 2:
FileStreams.cpp

	/***
	//Module name:FileStreams.cpp
	***/
	#define STRICT
	#include <windows.h>
	
	int WINAPI WinMain(HINSTANCE hinstExe,HINSTANCE hinstPrev,LPSTR pszCmdLine,int nCmdShow)
	{
		LPCTSTR pszFile = _TEXT("D:\\StreamTest.tst");
		LPCTSTR pszFirstStream = _TEXT("D:\\StreamTest.tst:FirstStream");
		LPCTSTR pszCopyStream = _TEXT("D:\\StreamTest.tst:CopyStream");
		LPCTSTR pszRenameStream = _TEXT("D:\\StreamTest.tst:RenameStream");
		LPCTSTR pszMoveStream = _TEXT("D:\\StreamTest.tst:MoveStream");
		
		char szDataToWrite[] = "THis is some data";
		char szDataToRead[100] = {0};
		HANDLE hfile;
		DWORD cb;
		//note:in a real application,you do not hace to open and close each stream's handle repeatedly as I've done below
		//create a file with no data in its unnamed stream and no named streams
		hfile = CreateFile(pszFile,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
		CloseHandle(hfile);
		//TEST:DIR (FILE should exist)
		
		//add a named stream to the file
		//note:step above does not have to execute first
		hfile = CreateFile(pszFirstStream,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
		CloseHandle(hfile);
		//TEST:more< c:\streamtest.txt (nothing should be displayed)
		//TEST:more < c:\streamtest.txt:FirstStream (nothing should be displayed)
		
		//put some data in the named stream
		hfile = CreateFIle(pszFirstStream,GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);
		WriteFile(hfile,(PVOID)szDataToWrite,strlen(szDataToWrite),&cb,null);
		CloseHandle(hfile);
		//TEST:more < c:\streamtest.txt(nothing should be displayed)
		//TEST:more <c:\streamtest.txt:firststream(text should be displayed)
		
		//get the size of the named stream
		hfile = Createfile(pszFirstStream,0,0,null,OPEN_EXISTING,0,null);
		DWORD dwSize = GetFIleSize(hfile,null);
		CloseHandle(dfile);
		//TEST:dwsize should be the correct number of bytes
		
		//Read the contents of the named stream
		hfile = CreateFile(pszFirstStream,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_FLAG_SWQUENTIAL_SCAN,NULL);
		ReadFile(hfile,(PVOID)szDataToRead,sizeof(szReadToread),&cb,null);
		CloseHandle(hfile);
		//TEST:szDataToRead should contain "this is some data"
		
		//make a copy of the named stream to another named stream
		CopyFile(pszFirstStream,pszCopyStream,false);
		//TEST:more < c:\StreamTest.txt:CopyStream(text should be dispalyed)
		
		//note:CopyFile does not always behave as expected;see below
		//1st param		2nd param		Result
		//Unnamedstream	Unnamedstream	complete file copy with all streams
		//unnamedstream namedStream		unnamedStream copied to NamedStream
		//NamedStream		UnnamedStream	File deleted:NameStream copied to UnnamedStream
		//NamedStream		NamedStream		nameStream copied to NamedStream
		
		//delete all the data in a stream
		hfile = CreateFile(pszCopyStream,GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);
		SetFilePointer(hfile,0,null,FILE_BEGIN);
		SetEndOfFile(hfile);
		CloseHandle(hfile);
		//TEST:more <c:\StreamTest.txt:CopyStream (nothing displayed)
		
		//delete the first named stream
		DeleteFile(pszFirstStream);
		//TEST: more <c:\streamtest.txt:FIrstStream(error should occur)
		//TEST:DIR(file should exist)
		
		delete the contents of the unnamed stream
		hfile = CreateFile(pszFile,GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);
		SetFilePointer(hfile,0,null,FILE_BEGIN);
		SetEndOfFile(hfile);
		CloseHandle(hfile);
		//TEST:MORE <c:\StreamTest.txt (nothing should display)
		//TEST:DIR (file should exist)
		
		//delete the file and all of its streams
		DeleteFile(pszFile);
		//TEST:more <c:\Streamtest.txt(error should occur)
		//TEST:DIR (file should not exist)
		
		//unfortunately,the win32 function moveFile does not support the moving/renaming of streams.this function only works on complete files.there is no documented way to move/rename a stream.
		
		//the win32 Backup functions can be used to enumerate the streams within a file.but they are very hard to work with and their performance is poor because the function also reads the stream's data.
		
		return 0;
}


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
理解Microsoft专家认证程序 理解Microsoft认证的不同等级和类型 选择成为MCP(Microsft认证专家)的考试科目 选择成为MCSD的考试科目 选择成为MCT的考试科目 MCSE认证考试的科目 选择合适的MCSE课程组合 核心课程考试 选修课程考试 考试编号的识别 课程内容和考试内容的对照 理解微软的MCSE长远考虑 理解微软出题的方式 使用本书帮助备考 在Internet上寻找对考试有帮助的信息 寻求微软认可的课程指导 寻找高质量的和三方帮助 寻找可利用的评估软件拷贝 报名参加考试 考试的费用问题 考前的自我调整 使用考试中心提供的考试工具 参加模拟测试 熟悉使用计算机进行考试 充分利用考试时间 考题的形式 理解多重选择题型 理解对错题题型 理解多重选择多重答案题型 理解基于解决方案型的问题 理解“建议方法”类型的考题 分析考试结果 准备重新考试 合理安排考试课程的顺序 熟悉Windows系列产品 比较Windows NT Server和NT Workstation 比较Windows NT Workstaton和Windows 95 在Windowx 95和Windows NT Workstation之间作出选择 关于Microsoft Windows NT的70-069号考试:实现和支持Microsoft Windows NT Server 4.0 70-069号考试(实现和支持Microsoft Windows NT Server 4.0)覆盖的内容 Windows NT 4.0界面简介 Windows NT 4.0任务栏(taskbar)的使用 Windows NT回收站简介 Windows NT帐号简介 理解单域模型支持和帐号数量 安全认证号简介 使用管理向导(Administrative Wizards)创建帐号 使用Server Manager(服务器管理器)程序创建计算机帐号 Userver Manager for Domains(域的用户管理器)简介 使用User Mnager for Domains创建用户帐号 刷新用户帐号列表 用户帐号列表的排序 事件查看器(Event View)程序简介 筛选Event Viewer中的事件 授予用户在本地登录的权利 使用Windows NT诊断程序查看系统配置 激活“Windows NT Security(Windows NT安全)”对话框 理解登录验证过程 理解访问令牌(Access Token) Windows NT目录服务简介 理解Windows NT如何构造用户帐号数据库 使用Windows NT中的Ctrl+Alt+Del组合键 把Windows NT计算机设置成自动登录 改变Windows NT口令 用拨号网络登录 复制用户帐号 为简化多个帐号的创建工作而建立用户帐号模板 删除和重新命名用户帐号 理解保护缺省的Administrator帐号的重要性 重新命名管理员帐号 理解缺省的Guest帐户 Windows NT在哪里创建帐号 设置口令限制条件 设置用户登录地点 创建宿主文件夹 设置用户登录时间 创建临时用户帐号 重新设置用户帐号口令 修改多个用户帐号 自动注销有时间限制的用户 要求用户在下次登录时改变口令 设置帐号规则 设置用户口令永不过期 停用用户帐号 解开登录失败后的用户帐号 Windows NT组简介 理解用户权限和组的访问权限 理解用户和组的权利 分清权限(permission)和权利(right) 设置组成成员关系 理解全局帐号 理解本地帐号 定义Everyone组 Network组的详细说明 Inteactive组的详细说明 Administrators组的详细说明 Guest组的详细说明 Users组的详细说明 Print Operators组的详细说明 Backup Operators(帐户操作员)组的详细说明 Replicator(复制员)组的详细说明 Domain Guests(域客户)组的详细说明 Domain Users(域用户)组的详细说明 Domain Admins(域管理员)组的详细说明 赋予拨号进入权限 理解用户配置文件(User Profile) 为Windows用户创建并使用登录脚本文件(Logon Script) 创建漫游式用户配置文件(Roaming User Profile) 创建强制性用户配置文件(Mandatory User Profile) 为用户帐号分配一个配置文件 创建帐户时变量的使用 创建随机初始化口令 理解内建组(Built-in Group) 理解组和策略 设置主组(Primary Group) 理解删除一个组的影响 域控制器(Domain Controller)简介 成员服
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值