800 likes | 926 Views
文件服务器和流. 文件服务器和流. 本讲介绍 文件服务器 流和流集 (Streams and stores ). 文件服务器和流. 文件系统服务器 简称为文件服务器 处理所有文件和目录管理的所有方面 提供访问 ROM 、 RAM 和 Flash 存储以及可移动存储设备的一致性接口 文件服务器运行时进程为 EFILE.EXE 客户端一侧实现的类由 EFSRV.DLL 提供. 文件服务器和流. 因为文件服务器包含了加载器( loader ) 它从数据保护( data-caged) 目录 sysbin 中加载可执行文件
E N D
文件服务器和流 • 本讲介绍 • 文件服务器 • 流和流集(Streams and stores)
文件服务器和流 • 文件系统服务器 • 简称为文件服务器 • 处理所有文件和目录管理的所有方面 • 提供访问ROM、RAM和Flash存储以及可移动存储设备的一致性接口 • 文件服务器运行时进程为EFILE.EXE • 客户端一侧实现的类由EFSRV.DLL提供
文件服务器和流 • 因为文件服务器包含了加载器(loader) • 它从数据保护(data-caged)目录\sys\bin中加载可执行文件 • 文件服务器是可信任计算机基础(trusted computer base ,TCB)的一部分 • 更多平台安全内容的在后面讲义中介绍
Symbian OS 文件系统 • 理解系统中文件服务器的角色 • 了解RFs 类提供的基本功能 • 了解正确打开文件服务器会话 (RFs) 和一个文件子会话 (RFile),以及读写文件的代码 • 知道RFile API四个打开文件的方法 • 理解如何利用TParse来操作和查询文件名称
文件服务器会话类 • 文件服务器提供 • 允许调用代码操作磁盘、目录及文件的基本服务 • 为了使用文件服务器 • 调用者必须首先创建一个文件服务器会话 • 由一个RFs类实例表示 • 通常的模式是 • 使用RFS会话连接文件服务器 • 创建和使用一个RFile子会话 • 然后释放会话和子会话 • 如下面例子所示:
文件服务器会话类 RFs fs; User::LeaveIfError(fs.Connect()); CleanupClosePushL(fs); ... _LIT(KASDExampleIni, "c:\\ASDExample.ini"); RFile file; User::LeaveIfError(file.Create(fs, KASDExampleIni, EFileRead|EFileWrite|EFileShareExclusive)); CleanupClosePushL(file); TBuf8<32> buf; ... User::LeaveIfError(file.Read(buf)); ... CleanupStack::PopAndDestroy(2, &fs); 连接会话 如果发生异常错误,关闭fs 创建文件 代表一个文件的子会话,如下所示 如果发生异常错误,关闭文件 使用子会话提交读文件请求 清楚RFile子会话和RFs会话 在文件上调用RFile::Close() 在fs上调用RFs::Close
文件服务器会话类 • 代码实例使用了清除栈 • 以确保关联到打开的文件服务器会话和文件子会话是异常退出安全的 • 注意:如果会话对象是一个类的成员变量 • 不需要使用清除栈保护它们,因为类的析构函数会保证会话和子会话被关闭 • 如果一个文件没有被显式关闭 • 通过调用RFile::Close() • 在与它关联的服务器会话关闭时,也会关闭它 • 但是在一个文件句柄不在需要时清除它是一个很好的习惯
文件服务器会话类 • 一个已经连接的RFs会话 • 能被用于打开任意数目的文件和目录(以子会话的形式) • 或者执行其他文件相关的操作 • 一个文件服务器会话 • 能够在应用程序的整个生命期中保持打开 • 类RFs • 提供许多游泳的文件系统相关操作 • 包括如下部分...
类 RFs • Delete()和 Rename() • 用于删除和重命名指定的文件 • Replace() • 用于将文件移动到另一个位置 • MkDir(), MkDirAll(), RmDir()和 Rename() • 用于创建、移除和重命名指定的目录 • Att(), SetAtt(), Modified()和 SetModified() • 用于读和改目录和文件的属性 • 例如隐藏、系统或只读标志
类 RFs • NotifyChange() • 一个异步请求,用以接收文件、目录和目录项修改的通知 • NotifyChangeCancel() • 用于取消已激活的请求 • Drive(), SetDriveName(), Volume()和SetVolumeLabel() • 用于操作磁盘和卷名 • ReadFileSection() • 用于不打开文件的情况下查看文件数据
类 RFs • AddFileSystem(), MountFileSystem(), DismountFileSystem()和 RemoveFileSystem() • 用于动态添加和删除文件系统插件 • 它扩展了Symbian OS所支持的文件服务器类型 • 潜在的文件系统插件的例子包括: • 对网络上的远程文件系统的支持 • 在存储文件数据之前对其进行加密 • 插件文件系统模块 • 实现为targettypefsy的多态DLL
文件句柄类 • RFile类 • 是连接到文件服务器的RFs客户端会话的子会话 • 一个RFile对象 • 代表了对一单个命名文件的访问 • 提供的功能包括: • 打开、创建和替换文件 • 或者打开一个临时文件 • 以及文件数据的读写
类 RFile • RFile::Open() • 用于打开一个现有的文件;如果文件不存在,则返回一个错误 • RFile::Create() • 用于创建和打开一个新文件 • 如果文件已经存在,则返回一个错误KErrAlreadyExists • RFile::Replace() • 创建一个不存在的文件 • 如果文件存在的话,将它删除并创建一个空文件 • RFile::Temp() • 打开一个临时文件,并为其赋予一个唯一的名称
文件句柄类 • 一个通用模式 • 调用Open()去试图打开一个已经存在的文件 • 如果它还不存在的话,调用Create() • 例如 • 当使用日志文件时,一个已经存在文件不能被替换 • 而是简单的将数据追加在它后面: RFile logFile; TInt err=logFile.Open(fsSession,fileName,shareMode); if (err==KErrNotFound) err=logFile.Create(fsSession,fileName,shareMode);
文件句柄类 • 当打开一个文件时 • 传入一个TFileMode的位模板值 • 用以指定文件使用的模式 • 例如,写文件还是读文件 • 共享模式 • 指示其他RFile对象是否能够访问打开的文件 • 以及访问是否是只读的 • 例如,文件可以以独占或者共享的方式打开 • 对于共享文件 • 可以用RFile::Lock()对文件某个区域进行加锁,以声明对文件的该部分进行暂时的独占访问 • 用RFile::Unlock()解锁
文件句柄类 • 当一个文件已经是共享方式打开时 • 它以能够被其他程序以同样共享的模式打开,就像第一个打开它一样 • 例如 • 为用其他客户端以可写和共享方式打开一个文件: • 如果另一个RFile对象视图以EFileShareExclusive或EFileShareReadersOnly方式打开ASDExample.ini,访问被拒绝 • 它只能以EFileShareAny模式访问 • 或者通过RFs::ReadFileSection()方法访问... RFile file; _LIT(KFileName,"ASDExample.ini"); file.Open(fsSession,KFileName,EFileWrite|EFileShareAny);
文件句柄类 • RFs::ReadFileSection()方法 • 能够在不打开文件的情况下读文件 • 这样文件的内容可以不用锁定 • (锁定)通过使用EFileShareExclusive标记调用RFile::Open()方法 • 或者通过调用RFile::Lock() • RFs::ReadFileSection() • 而Apparc和识别器框架使用,以能够快速检查文件内容以确定文件类型
文件句柄类 • RFile::Write()方法 • 从一个非修改8位描述符对象const TDesC8&写数据 • RFile::Read()方法 • 将数据读入到一个8位描述符TDes8& • Read()和 Write()方法 • 都有同步和异步方式 • 但是 • 异步 Read()和异步 Write()方法都不能取消
文件句柄类 打开 ASDExample.ini 写入文件 从文件读 readBuf包含 "Hello" _LIT(KASDExample,"c:\\ASDExample.ini"); RFile file; User::LeaveIfError(file.Open(fs, KASDExample, EFileShareExclusive|EFileWrite)); _LIT8(KWriteData,"Hello ASD"); file.Write(KWriteData); TBuf8<5> readBuf; file.Read(readBuf); file.Close();
文件句柄类 • 有多个 • RFile::Read()和 RFile::Write()函数的变体 • 它们是重载函数,允许 • 接收描述的长度可以被重载(考虑或者不考虑) • 指定开始查找的位置 • 异步完成 • 或上述情况的组合 • 在所有情况 • 都使用8位描述符...
使用8位描述符 • 使用8位描述符的结果 • RFile并不是特别的合适is not particularly well suited • 去读取或写入Symbian OS应用程序中使用的丰富多样的数据类型 • 这不是一个意外! • 而是一个深思熟虑的设计以便鼓励流的使用 • 流提供了必要的功能和额外的优化
操作文件名称 • Symbian OS 中的文件 • 由文件名称规格(file name specification)标识,名称最大长度有256个字符 • 一个文件规格(file specification)包含 • 一个设备,或磁盘,如c: • 一个路径,如\Document\Unfiled\,其中目录名称有反斜线(\)分隔 • 一个文件名称 • 一个可选的文件扩展名,与文件名用句点(.)分隔
操作文件名称 • Symbian OS 应用程序 • 通常不依赖扩展名来确定文件的类型 • 而使用存在文件中的一个或多个UID • 以确保文件类型匹配应用程序 • 服从总共256个字符的限制 • 目录名、文件名或者扩展名可以是任意长度 • RFs::IsValidName()方法 • 返回一个布尔型值以指示一个路径名是否合法
操作文件名称 • Symbian OS 文件系统 • 支持最多26 磁盘, 从a: 到 z: • 在Symbian OS 的手机上 • z: 盘总是被保留来作为系统ROM • c: 盘总是一个内部读写盘 • 在一些手机上,可能有一些功能受限 • 从d: 以后的磁盘 • 可能是内部磁盘,也可能是可移动介质 • 写所有磁盘是不太可能 • 除了z:盘,很多手机还有一个或多个只读磁盘 • 它们只能由系统使用
操作文件名称 • 文件系统 • 保持文件和目录的大小写 • 所有对于名字的操作都是大小写无关的 • 这意味着在同一个目录中不能有两个或多个文件 • 它们的名字只是字母大小写不同 • 文件名称 • 有类TParse及其成员函数创建和操作 • 例如, 设置一个TParse实例包含如下文件规范c:\Documents\Oandx\Oandx.dat: _LIT(KFileSpec, "c:\\Documents\\Oandx\\Oandx.dat"); TParse fileSpec; fileSpec.Set(KFileSpec,NULL,NULL);
类 TParse • 紧接上述代码 • TParse getter 函数可用于取得文件规范中不个不同部分 • 例如: filespec.Drive(); // 返回字符串“c:” fileSpec.Path(); // 返回字符串 "\Documents\Oandx\"
类 TParse • TParse::Set() • 接收三个参数 • 第一个参数 • 是需要解析的文件规范 • 第二个和第三个参数是是指另两个TDesC描述符的指针,一个或两个可以是NULL • 第二个参数 • 被用于提供第一个参数遗漏的部分
类 TParse • 第三个参数 • 应当指向一个缺省的文件规范 • 其中的任何没有由第一个和第二个参数提供的部分都会被接收 • 任何路径、文件名或扩展名 • 可以包含通配符 ?或 * • 表示任何单个或多个字符序列
类 TParse • 一个 TParse对象拥有一个TFileName实例 • 它是一个TBuf16<256> • 每个字符是2个字节大小 • 其数据缓冲区占用512个字节 • 它是个很大的对象! • 尽可能避免在栈上使用它
常见错误和低效 • 一个常见的编译时错误 • 初学者在使用Symbian OS 文件时经常碰见 • 当尝试使用16位描述符对一个文件进行读写时 • 通过 RFile句柄 • RFile::Read()和 RFile::Write()方法 • 只接收8位描述符 • 意味着宽字符串必须首先进行转换 • 另一个常见错误 • 是不能使基于栈的RFs或 RFile对象成为异常退出安全的 • 通过使用清除栈
常见错误和低效 • 对文件服务器的连接 • 需要很长的时间来建立 • RFs会话应该尽可能的在函数间传递 • 或者保存起来再利用 • 也可能通向 RFile句柄 • 在单个进程中 • 或者两个进程之间 • 允许一个打开的文件句柄 • 从一个进程传递给另一个进程 • 这是Symbian OS安全版本的一个必要特性
常见错误和低效 • 文件系统访问代码 • 也可以被做得更有效 • 通过牢记客户端-服务器交互的隐含影响 • 效率可以通过最小化客户端-服务器调用的数目 • 传递更多的数据从而对文件服务器发送更少的请求 • 例如 • 这样更有效:一次读取文件到一个大的缓冲区 • 然后访问和操作它们在客户端一侧 • 而不是多次请求读文件的小部分
常见错误和低效 • 绝大多数的文件服务器数据传输客户端 • 使用流存储(stream store )或关系数据库 • 它们自动的使用缓存 • 这些组件已经优化了它们对文件服务器的使用 • 调用者使用这些API而不是直接访问文件服务器 • 自动的获得了效率
流和流集 • 知道使用流APIs优于使用RFile的原因 • 理解如何最有效的使用流(stream)和流集(store)类来管理大的文档 • 能认识Sybmian OS存储和流类,知道每个类的基本特点 (例如 基类,内存存储,持久化、修改等) • 理解如何使用ExternalizeL()和操作符<<及RWriteStream来写一个对象到流, 以及InternalizeL()和操作符>>及RReadStream读回对象 • 知道操作符>>和 <<会异常退出
流 • 一个Symbian OS 的流 • 是一个或多个对象的外在表现 • 外化(Externalization ) • 是将对象写入一个流的过程 • 内化(Internalization) • 相反的过程–从一个流中读入一个对象的数据 • 流 • 可以在各种介质中 • 包括流集,文件或内存 • 流在最终的持久存储介质上提供了一个抽象层.
流 • 一个对象的外在表示 • 需要与对象的内在存储无关 • 例如字节顺利和数据对其 • 外化指针是没有意义的 • 在外在表示时,它必须由它所指向的内容代替
流 • 每项数据的表示 • 必须具有一个明白定义的长度 • 需要特别注意当外化TInt等数据类型时 • 它的内化表示可能在不同的处理器和/或C++编译器中存在大小的不同
流 • 存储多个数据项 • 它们可能来自多个对象 • 在但一个流中意味着它们被安排成一定的顺序 • 内化代码 • 它通过读入流中数据恢复对象 • 因此必须按遵循化它们时使用的顺序
流 • 流的概念 • 有两个基类实现 • RReadStream和 RWriteStream • 和从它们派生的类支持位于特定介质的流 • 例如: • RFileWriteStream和 RFileReadStream • 实现了位于文件中的流 • RDesWriteStream和 RDesReadStream • 实现了位于内存中的流,其存储有一个描述符标识
流 • RReadStream和RWriteStream基类 • 提供各种WriteXxxL()和 ReadXxxL()函数 • 它们处理特定的数据类型 • 从8位整数, 如WriteInt8L() • 到 64位实数,如WriteReal64L() • 这些函数被调用 • 当<< 和>> 操作符用于内建类型时 • 为了处理原始数据(raw data) • 流的基类也提供 • 一组WriteL()和 ReadL()函数 • 包括重载读和写16位unicode字符 • 而不是字节
流 • 原始数据函数 • 使用时应注意: • 1. 原始数据写入流中时应该与出现中内存中时完全相同 • 在调用WriteL()前,它必须是无实现无关的格式 • 2. 调用ReadL() • 必须读入与调用相应的WriteL()时所写入的相同数量的数据 • 这可以通过在数据前写入数据长度来保证 • 或者在数据的结尾有一个唯一性可识别的分隔符 • 3. 必须有一个方法可以获得希望数据的最大长度 • 4. 16位WriteL()和 ReadL()函数 • 并不提供标准的unicode压缩和解压缩
流- 外化实例 • 以下实例 • 外化一个TInt16值到名称为aFileName的文件中 • 假定文件在调用WriteToStreamFileL()之前是不存在的 • void WriteToStreamFileL(RFs& aFs, TDesC& aFileName, TInt16* aInt) • { • RFileWriteStream writer; • writer.PushL(); // put writer on cleanup stack • User::LeaveIfError(writer.Create(aFs, aFileName, EFileWrite)); • writer << *aInt; • writer.CommitL(); • writer.Pop(); • writer.Release(); • }
流- 外化实例 • 唯一引用流的地方是 • 将其置于栈中,以及后面紧接会异常退出的代码 • 有必要将流推入清除栈中 • 使用流的(而不是清除栈的) PushL()函数
流- 外化实例 • 一旦创建了文件 • 就使用操作符<< 将数据予以外化 • 然后调用写入流的CommitL()函数 • 以确保所有缓存的数据被写入到流中 • 将流从清除栈中移除 • 使用流的Pop()函数 • 最后,调用Release()关闭流 • 该函数会释放流所使用的资源
流 • 通用模式: • 操作符 << 被用于外化数据 • 操作符 >> 用于内化数据 • 可用于所有内建类型 • 除了那些像TInt这样大小没有指定而与编译器有关的 • 在Symbian OS 上 • 一个TInt至少有32位 • 它可能更长! • 于是使用操作符 << 外化它将产生一个未定义大小的外化表示 • 所以 ... • 数值的最大长度 • 被用于选择一个合适的内化或者外化方法
流 • 例如 • 如果存储在Tint中的数值不会超过16位 • 可以使用RWriteStream::WriteInt16L()外化它 • 使用 RReadStream::ReadInt16L()内化它: TInt i = 1234; writer.WriteInt16L(i); ... TInt j = reader.ReadInt16L(); ... // Cleanup etc
流 • 操作符 << 和 >> • 能用于任何类,如果它们实现了 • ExternalizeL()和 InternalizeL() • 它们的原型如下: • class TAsdExample • { • public: • ... • void ExternalizeL(RWriteStream& aStream) const; • void InternalizeL(RReadStream& aStream); • ... • };
流 • 对这样一个类 • 外化可使用: • 或者 • 它们在功能上等价 • 同样的,对于内化 • 可以使用操作符 >> 或者 InternalizeL() TAsdExample asd; ... writer << asd; // writer is RFileWriteStream // initialized and leave-safe TAsdExample asd; ... asd.ExternalizeL(writer); // writer is RFileWriteStream // initialized and leave-safe
流 • 注意: 操作符 << 和 >> 会异常退出 • 作为分配资源的结果操作 • 因此,如果没有足够可用的内存话,就会失败 • 操作符必须在陷阱套(TRAP harness)中使用 • 如果它们被一个不异常退出的函数调用的话