相信看到我以前写的文章你已经会利用定制平台向导来定制简单的内核了。这次我仍然用这个向导,只不过采用自定义配置来选择平台的组件。自定义配置的优点是能够更细致的选择组件,这样定制的平台只包含我们需要的功能,而不要的全部抛弃。首先打开"New Platform",在"Step 2"中选择"EMULATOR: X86"。在这里强调一点:能够运行PB创建的CE平台的环境有三种。第一种是真实环境,包括某一种CPU和支持的主板还有其它配置;第二种是模拟环境,PB包含了模拟器,能够在PC上模拟运行CE平台;第三种是PC环境,PB可以创建能够在PC机运行的CE平台。并且提供了一组引导文件,通过这些引导文件可以加载CE内核文件(nk.bin)并启动Windows CE。三种环境相比较,如果具备真实的环境那是最好了,模拟环境只能模拟简单的功能,PC环境和模拟环境相似,只适合用来学习PB。PC环境需要的引导文件可以通过如下办法得到:在PB的安装所在位置(比如C盘)查找文件Websetup.exe,运行这个文件,这个程序会解压出一个名为WEBIMGNT.EXE的文件。再查找名为cepcboot.144的文件,将cepcboot.144文件复制到同WEBIMGNT.EXE同一个目录下。插入软盘到A驱,然后在控制台程序(cmd.exe)下输入命令"WEBIMGNT.EXE cepcboot.144"。在弹出的对话框中单击"A drive",程序把解压出来的启动文件都复制到软盘上。编辑启动文件中的"Autoexec.bat"批处理文件,如果PC配置的网卡是被支持的(微软推荐的网卡类型有NE2000、SMC9000兼容网卡、RealTek RTL8139等),并且是PCI接口的,按如下修改:
set NET_IRQ=9 set NET_IOBASE=0
修改之后还要输入一个静态的IP地址。这个IP地址要和运行PB的电脑的IP地址处于一个子网内。实际上PC环境还是无法实现CE平台的大多数功能,所以对于要学习PB而又不具备真实环境的人来说,模拟器是比较合适的。
回到定制平台向导,在"Setp 3"中单选"Custom configuration",输入平台名称和路径。在"Step 4"中必须单选"Custom Device with Shell and Graphical User Interfac"。表示平台将加入外壳程序和GUI。有了资源管理器这样的外壳程序我们操作就方便多了。在"Step 5"中列出的是"Application & Services Development"。这些都是用于软件开发的库。大多数支持库我们在PC上开发时早已熟悉了,有几个是CE独有的。比如"Simple Network Management Protocol"(简单网络管理协议),这是用在网络设备上的协议;"Pocket Outlook Object Model API",用于读取Pocket PC中"Inbox"软件中的数据;"Beta .NET Compact Framework"是.NET的支持框架。这个在PB v4.1中不要选取,它在PB v4.2中才是正式版。其余的选项说明参见CE帮助文档(位置:operating system development/windows ce.net overview/catalog features/applications and services development)。"Step 6"中列出了Windows CE自带的应用程序。"Step 7"中列出了操作系统内核支持的服务。包括串口支持、并口支持、USB口支持、调试工具、电源管理,还有一些其它特征。这里的调试工具不仅仅指能够用于调试的应用程序,还包括用于调试的API函数。Toolhelp就是专门用于查看当前操作系统的进程及进程包含的线程、DLL的信息。"Kernel Features"(内核特征)中的Fiber API是用于支持纤程的API。"Keyboard & Touch Driver Debugging Sample Applications"包括四个调试工具,用于调试触摸屏和键盘的驱动程序。"LMemDebug memory debugging hooks"用于查看当前操作系统正运行的程序的内存信息。在"Step 8"中列出的是网络特征(我有时称特征为组件,实质一样)。包含CE支持的所有网络协议。"Networking Features"中大多数的子项都要加入的,其它项按需加入。比如要支持红外线一定要加入PAN中的IrDA。要拨号上网的一定要加入WAN所有项。"Step 9"中列出了存储特征。包含和存储设备相关的支持。具体包括存储管理器(支持CDFS、FAT文件系统)、数据库支持、ROM和RAM文件系统、注册表存储支持。关于ROM和RAM、FAT我在以前的文章中提到过,这里就不再详细讲解了。"Step 10"中列出了CE包含的所有英文字体。要了解这些字体中每种字体所占空间大小,可以将鼠标放到字体名字的上面,系统就会显示这种字体的大小。一般来说一个定制的CE平台其中字体尤其是中文字体占用空间是很大的,所以选择字体要慎重。除非必要,否则不要添加太多的字体。"Step 11"列出了国际化选项。包括各种语言支持包。在这里选择"National Language Support[NLS]",再选择一种本地区语言,比如"Chinese {Simplified}"。"Agfa AC3 Font Compression"建议选择,这是一种字体压缩技术,适合中文字体。在"Chinese {Simplified}"第一子项中列出了字体。这一项选择至关重要,因为中文字体占用的空间太大了,直接影响CE平台的总体大小。具体选择哪些字体请参见CE帮助文档(位置:operating system development/windows ce.net overview/catalog features/ International)。从帮助文档中才能了解具体哪个子集包含哪些中文字体和字符集。"Chinese {Simplified}"其它子项包括中文输入法、输入法编辑器建议全部选择。"Step 12"列出了CE包含的Internet客户端程序、支持的组件和脚本。第一项"Browser Application"中建议选择第一子项"Internet Explorer 5.5 for Windows CE Components",而第二子项"Pocket Internet Exlporer"是用在PDA上的功能较少的Internet浏览器。第二项"Internet Explorer 5.5 for Windows CE Components"中建议选择全部子项。这些子项都是开发IE的API函数、Active X控件。第四项"Sample IE 5.5 Internet Options Control Panel"是"控制面板"中的用于修改IE选项的程序。和Windows 桌面操作系统下"控制面板"中的"Internet 选项"一样。"Step 13"列出了CE支持的多媒体服务。可以任意选取要播放的各种媒体和具体媒体的格式。Direct X也可以分拆选取。"Step 14"列出了CE支持的安全服务。"Step 15"列出了各种外壳和用户接口。外壳程序一般常常选择"Graphical Shell"中的标准外壳程序"Standard Shell",也就是资源管理器。"Step 17"是最后一步。单击"Done"按钮,PB将用户选择的所有选项保存到脚本文件(*.wce)中。当编译CE平台时,sysgen.bat批处理文件会调用cesysgen.bat批处理文件,cesysgen.bat读取*.wce文件的内容,根据此文件的内容来设置全部的环境变量。cesysgen.bat处理之后sysgen.bat调用nmake.exe 程序根据环境变量来编译模块(针对源码文件,如C语言文件)、复制模块(针对非源码文件,如DLL等)。具体编译操作以及编译时PB所做的工作我将在下一篇文章中详细讲述。
上一章所说,当用PB的"定制平台向导"选取了一个平台的所有特征(feature)后,接下来的工作就是编译了。即使你从来未曾编译过,你也能想象得到编译的时间一定很长。因为PB是在编译一个操作系统,而非一个应用程序。当然,越快的硬件环境编译的速度就越快。一般我编译一个内核需要5分钟到10分钟之间。
在编译之前你可能还要对这个CE平台的某些特征进行修改。为此,你必须熟悉PB的操作环境。下面图1是PB打开一个平台工程文件的界面。
图一 PB主界面
图一中数字所指示的内容描述如下:
要向当前CE平台添加特征,首先在右边"Catalog"窗口中找到要添加的特征,找到后移动鼠标光标到此特征上,然后单击右键,在弹出的菜单中单击"Add to Platform",PB就将此特征添加到左边当前CE平台中。如果无反应说明此特征已经被添加进去了。要删除当前CE平台中某一个特征,移动鼠标光标到此特征上,单击右键,在弹出的菜单中选择"Delete"。并非所有的特征都可以任意删除,因为有些特征是彼此关联的。这部分将在以后的文章中讲述。
对平台进行一些基本的设置,打开菜单"platform"-"Settings",检查"Locale"选项卡中地区和语言设置。这一点很重要,它决定着编译的平台采用的语言种类。再在"Environment"选项卡中添加"IMGRAM64"环境变量,值设置为1。单击"OK",PB开始将此环境变量加入到平台中。单击工具栏上"Build Platform"按钮开始编译。
在讲述编译过程前必须先了解环境变量以及如何读取和设置环境变量。一个环境变量包含了一个CE操作系统某一方面的信息。例如一个驱动程序、一个路径、一个配置文件、一个特征等。当PB编译CE平台时,先做的工作就是收集所有的环境变量供编译器使用。读取和设置环境变量的最好方法是单击PB菜单"Build"-"Open Build Release Directory",PB会弹出一个控制台窗口,也就是命令行外壳。键入"set"命令,当前平台所有的环境变量就显示出来了,不过要多屏显示。为了看清楚每个变量的值,可以键入"set |more",这样就可以分屏查看了。还可以将所有环境变量信息保存到硬盘上,比如键入"set >C:/envi.txt"。要查看单个环境变量值,键入"set 环境变量名"。要修改原环境变量的值键入"set 环境变量名=值"。有些环境变量无需键入值就可以达到修改目的。比如前缀为"BSP_"和"SYSGEN_"的变量,键入"set 环境变量名="就取消了这个环境变量。在IDE中也可以修改环境变量,如上面所说的设置"IMGRAM64"的值,就是在IDE中修改的。利用环境变量也可以添加和删除特征,如BSP变量。BSP变量分两种,一种以BSP_NO为为前缀,一种以BSP为前缀。以BSP_NO为前缀表示当前平台不支持某一特征,以BSP为前缀表示支持这一特征。例如BSP_SERIAL2表示此CE平台支持串口2;BSP_NOSERIAL表示此CE平台不支持串口。如果在PB的"catalog"中找不到要添加的特征,可以通过设置BSP变量来实现。具体BSP环境变量参见CE帮助文档。
下面讲述整个编译过程中PB所做的工作:
整个编译过程被调用的批处理文件和EXE文件主要包括:cebuild.bat、sysgen.bat、cesysgen.bat、nmake.exe、txt2ucde.exe、makeimg.exe、fmerge.exe、regcomp.exe、res2exe.exe、romimage.exe、build.exe。在这里声明一点,我不保证所讲述的PB的编译过程一定是准确无误的。从总体上讲PB所做的工作就是这样。
这篇文章主要讲解PB的配置文件。从用途方面分析,PB包含两种配置文件。分别是源码配置文件和镜像配置文件。下面分别讲解这两种配置文件。
一、源码配置文件
源码配置文件用于编译源码时使用。这里的源码是指Windows CE公开的源码,如驱动程序、系统应用程序等。PB在编译平台时将这些公开的源码即时编译并将编译链接后的文件复制到平台工程子目录里。记得前面讲过PB在开始编译时调用cebuild.bat批处理文件,cebuild.bat执行的一个步骤是针对_DEPTREES变量指定的所有目录执行build.exe和sysgen.bat。build.exe在编译源码文件时会寻找当前目录下存放的源码配置文件,根据配置文件的信息来编译和链接,产生EXE、DLL、LIB文件。CE的源码文件所在的目录中都包含了相应的配置文件,这些配置文件只对当前目录或者子目录的源码有效,具体分为三种:
二、镜像配置文件:
镜像配置文件用于在制作CE镜像文件时使用。CE的镜像文件扩展名为.bin。制作镜像工具romimage.exe除了能够产生.bin文件外,还能够产生.abx和.sre文件。整个镜像的制作过程由makeimg.exe控制,它调用cenlscmp.exe、fmerge.exe、res2.exe、txt2ucde.exe、regcomp.exe、romimage.exe等。这些工具大部分在前面已经介绍过了。镜像配置文件类型有.bib、.reg、.db、.dat、.str。如果主文件名为Common,表示是通用的配置文件。如果主文件名为Platform,表示是某一个BSP的配置文件。如果主文件名是Project,表示是定制的一个平台的配置文件。在PB中修改配置文件前如果没有把握最好先做好备份。
.bib(Binary image builder)
定义包含在内核镜像中的文件和模块的名称、加载位置。主要的bib文件有Common.bib,Config.bib, Project.bib, Platform.bib等。.bib文件内部分为几个部分:
【MEMORY】用于定义有效的物理内存块,在此将整个RAM分为几个部分。 格式: 名称 首地址 大小 内存类型 名称: 内存区域的唯一名称(RESERVE是预定义名称,可以用多次,表示此区域保留) 首地址: 内存区域的首地址(十六进制表示) 大小: 内存区域的大小(十六进制表示) 内存类型:分为三种。 RAM: 运行所有进程的内存区域(整个区域必须是连续的,且不能含空洞) RAMIMAGE:专用于保存镜像的内存区域。(每个.bin中只能指定一个RAMIMAGE) RESERVED:保留内存区域(这样的区域一般用于驱动程序使用,如显卡缓冲区、DMA缓冲区)
举例: ;名称 首地址 大小 内存类型 IF IMGRAM64 NK 80220000 009E0000 RAMIMAGE RAM 80C00000 03000000 RAM UMABUF 83C00000 00400000 RESERVED ENDIF
注:整个内核的地址都是从0x8000 0000开始的。如果是x86系列的CPU,那么物理内存地址与虚拟地址映射关系在oeminit.asm中指定。
【CONFIG】类似环境变量,PB预设置了一些配置变量。常用的配置及说明如下: AUTOSIZE: 格式:AUTOSIZE = OFF | ON
默认值为OFF。在config.bib中的MEMORY部分定义了有效的内存区域,其中两部分RAM、RAMIMAGE分别用于进程使用区域和保存镜像区域。如果为ON,romimage.exe在创建nk.bin时将RAM和RAMIMAGE两部分合并成一个部分,然后从最低地址开始保留RAMIMAGE大小的内存,其余都作为RAM使用。
BOOTJUMP: 格式:BOOTJUMP = address | NONE
默认值为NONE。每次重新启动CE内核,默认执行的代码从RAMIMAGE的首地址开始。如果在BOOTJUMP指定一个地址(必须在RAMIMAGE范围内),那么将从指定的地址开始执行。
COMPRESSION: 格式:COMPRESSION = OFF | ON
默认值为ON。romimage.exe在创建内核时默认压缩所有可写部分。对于文件,默认全部压缩。对于模块(.exe、.dll),默认压缩可写部分。模块的可写部分包括数据段,也就是在模块运行时一定加载到内存中的部分。如果模块在.bib中定义时具有C属性(表明压缩模块所有部分),那么当前这个选项就忽略了。
FSRAMPERCENT: 格式:FSRAMPERCENT = number
默认值为0x80808080。指定为文件系统分配的内存的百分比。number分为四个字节,由十六进制表示。
byte0的值(单位为4KB)表示在第一个2MB中,其中每1MB包含的4KB的整数倍。 byte1的值(单位为4KB)表示在第二个2MB中,其中每1MB包含的4KB的整数倍。 byte2的值(单位为4KB)表示在第三个2MB中,其中每1MB包含的4KB的整数倍。 byte3的值(单位为4KB)表示在剩下的内存中,每1MB包含的4KB的整数倍。
计算一下默认值0x80808080表示的百分比:0x80*4K/1M = 0.5,因为每个字节都等于0.5,所以整个占用的百分比是50%。
KERNELFIXUPS: 格式:KERNELFIXUPS = OFF | ON
默认值为ON。如果为ON,romimage.exe创建内核前重定位内核到RAM的开始位置。
OUTPUT: 格式:OUTPUT = path
指定romimaeg.exe将创建完成的内核文件nk.bin放置到的路径。一般放置到%_FLATRELEASEDIR%下。
ROMFLAGS 格式:ROMFLAGS = Flags
设置内核选项的位掩码,多个位掩码可以组合使用。
ROMSIZE 格式:ROMSIZE = size
指定内核镜像的大小
ROMSTART 格式:ROMSTART = address
指定内核镜像的首地址
ROMWIDTH 格式:ROMWIDTH = width
指定数据宽度,一般为32位
ROMOFFSET 格式:ROMOFFSET = address
指定偏移地址。
SRE 格式:SRE = OFF | ON
指定romimage.exe是否产生.src文件,一般烧录ROM的程序能够识别此文件。
注:config中绝大多数【CONFIG】选项不需要修改。凡是配置文件都可以使用IF/ENDIF 条件语句。
【MODULES】定义镜像要包含的模块并指定模块(DLL、EXE)如何被加载到内存表中。 格式:模块名称 路径 内存块 类型
模块名称一般为模块的真实名称;路径为当前文件所处的位置(路径中指定的文件名和前面模块名称最好一致);内存块是指这个模块将被存放到哪个内存块中,内存块的定义见前面MEMORY部分;类型指定这个模块将被存放的属性,具体类型如下:
举例: MODULES init.exe %_WINCEROOT%/RELEASE/INIT.EXE NK SH nk.exe $(_FLATRELEASEDIR)/kitlnokd.exe NK SHD nk.exe $(_FLATRELEASEDIR)/kitlnokd.exe NK SHN
【FILES】定义镜像要包含的文件并指定文件如何被加载到内存表中。 格式:模块名称 路径 内存块 类型
具体类型如下:
举例: FILES initobj.dat %_WINCEROOT%/RELEASE/INITOBJ.DAT NK SH
【.dat File System File】定义目录和指定文件位置。当冷启动CE平台时,filesys.exe用这些数据 创建目录、快捷方式、文件(在RAM文件系统)。
举例: ;;创建根目录下子目录Program Files root:-Directory("Program Files") ;;创建目录Program Files下一个子目录My Projects Directory("/Program Files"):-Directory("My Projects") ;;复制文件从/Windows/Myproj.exe到/Program Files/My Projects/My Project.exe Directory("/Program Files/My Projects"):-File("My Project.exe", "/Windows/Myproj.exe") ;;复制文件从/Windows/control.lnk到/control.lnk root:-File("/control.lnk", "/Windows/control.lnk")
快捷方式的运用:如果要在CE平台的桌面上显示一个程序的快捷方式,实现步骤为:
【.reg Registry file】设置注册表项。关于注册表见我的文章《开发实例二:保存信息》。
数据类型 格式 REG_SZ "my string" REG_DWORD DWORD: NNNNN (十六进制) REG_MULTI_SZ multi_sz: "my string" REG_BINARY hex: xx,xx,xx,xx ... HEX hex(xxxxxxxx): xx,xx,xx,xx
例如: [HKEY_LOCAL_MACHINE/init] "Launch60"="myproc.exe" ///REG_SZ类型 "Depend60"=hex:14,00, 1e,00 ///REG_BINARY类型 [HKEY_LOCAL_MACHINE/System/StorageManager/Profiles/TRUEFFS_UNAND/FATFS] "MountFlags"=dword:2 ///REG_DWORD类型
【.db The database files】数据库文件保存在对象存储中。实际应用的不多,在这里不再过多讲解。
【.str string files】类似EVC中的字符串资源。
用于指定ID与字符串的关联。CE支持很多国家语言,所以内核使用的字符串可能采用不同国家的语言。为此,CE将字符串用ID来定义,在.str文件中指定ID对应的字符串。包含.str文件的目录名采用国家码来设置,例如"C:/....../0410/cepc.str"。
本篇文章是对前面讲过的关于PB的系列文章做一些补充,因为PB包含的知识面太广也太杂,所以针对一些杂项归纳在一起写成这一篇文章。当然一篇文章绝不可能包罗万象,有时间我会陆续写出来。
/B:指定串口的波特率。例如 /B:19200 /C:指定串口的端口。1指"COM1:",2指"COM2:"。 例如 /C:1 /D:指定显示分辨率。0指320 x 200,1指480 x 240,等等。 /E:指定网卡IO地址和IRQ。例如/e:300:5 /L:指定显示分辨率和色深。它需要指定具体的分辨率,所以能够指定不标准的分辨率。例如/l:768x576x8, 表示分辨率为768 x 576,颜色位数为8位。 /P:指定使用并口传递数据。 /Q:指定使用串口传递数据。 /V:指定当loadcepc加载时添加状态信息。
随着CE的发展,对象存储(Object Store)的作用越来越小,而大容量的永久存储设备被越来越多地采用,这一章将针对CE的文件系统阐述相关的知识,让PB开发者除了能够加入对永久存储设备的支持,还能做一些优化。记得在以前讲过的文章中提到了如何在PB中向定制的内核加入对硬盘、光驱的支持(包括ATA设备驱动和各种文件系统),在这里就不再重复了。
CE提供了三种文件系统,基于ROM的文件系统、基于RAM的文件系统、FAT文件系统。FAT文件系统使用的范围最广,能够应用在ATA设备、Flash存储设备、SRAM存储设备上,另外CE还允许开发者自己编写并注册一套文件系统,只要接口符合Win32文件系统API即可。
CD/UDFS 文件系统
这两种文件系统被用于读取CD、DVD等。除了通过在PB的“catalog”中加入这个文件系统外,还可以在PB中添加SYSGEN_UDFS环境变量来实现。CDFS和UDFS在注册表中的注册信息如下:
; Default values for udfs. These can be overridden per profile. [HKEY_LOCAL_MACHINE/System/StorageManager/UDFS] "FriendlyName"="CDFS/UDFS FileSystem" "Dll"="udfs.dll" "Paging"=dword:1 [HKEY_LOCAL_MACHINE/System/StorageManager/Profiles/CDProfile] "Name"="IDE CDROM/DVD Drive" "Folder"="CDROM Drive" "DefaultFileSystem"="UDFS" "PartitionDriver"=""
上面注册表信息在文件common.reg中。注册表数据是从上至下有效,也就是说下面的数据可以覆盖上面的数据。从注册表数据可以看出udfs.dll包含了UDFS文件系统的驱动程序,CDROM的驱动器名为”CDROM Drive”,采用的文件系统为UDFS,没有分区驱动程序。如果我们要访问CDROM的目录或者文件就要在名字前加”/CDROM Driver/”。注意,可能你的common.reg文件中的数据在”Flolder”处不同于上面,比如为"Folder"=LOC_STORE_CD_FOLDER,那么你可以直接按照上面数据更改,或者在*.str文件中查找LOC_STORE_CD_FOLDER,找到这个ID对应的字符串再更改(查找到的文件可能很多,应查找以本国家码为目录名的目录)。
FAT文件系统
除了安全性外,FAT文件系统是一个很优秀的文件系统,很适合在嵌入式设备中使用。CE也把FAT作为外部存储设备的通用文件系统。添加FAT文件系统的环境变量为SYSGEN_FATFS。随便列出CE提供的操作FAT文件系统的函数:
DefragVolume 碎块整理,在碎块整理前先进行磁盘扫描。 DefragVolumeUI 同上,但是包含一个选项对话框。 FormatVolume 按要求格式化分区。 FormatVolumeUI 同上,但是包含一个操作对话框。 ScanVolume 扫描一个分区的FAT和目录。 ScanVolumeUI 同上,但是包含一个操作对话框。
FAT文件系统在注册表中的注册信息如下:
; Default values for fatfs. These can be overridden per profile [HKEY_LOCAL_MACHINE/System/StorageManager/FATFS] "FriendlyName"="FAT FileSystem" "Dll"="fatfsd.dll" "Flags"=dword:00000024 "Paging"=dword:1 "CacheSize"=dword:0
从注册表数据可以看出fatfsd.dll包含了FAT文件系统的驱动程序。对”Flags”值的描述如下:
标志 | 值 | 描述 |
FATFS_UPDATE_ACCESS | 0x00000001 | 更新访问时间 |
FATFS_DISABLE_AUTOSCAN | 0x00000004 | 不能自动调用ScanVolume |
FATFS_VERIFY_WRITES | 0x00000008 | 检验所有写操作 |
FATFS_ENABLE_BACKUP_FAT | 0x00000010 | 备份FAT表 |
FATFS_FORCE_WRITETHROUGH | 0x00000020 | 让系统可以直接将任何缓冲区中的数据写到磁盘上,这样系统将加快写数据到磁盘的速度 |
FATFS_DISABLE_AUTOFORMAT | 0x00000040 | 禁止自动格式化未格式化的分区 |
FATFS_DISABLE_COMPCHECK | 0x00000080 | 禁止自动检测压缩分区 |
“Paging”用于指定是否能够分页,值为1可以分页,0不能分页。“CacheSize”用于指定FAT文件系统缓冲区大小。具体的值是用16进制数表示的扇区数量。假如”CacheSize”的值为400,那么用于FAT缓冲的字节数为1024 * 512 = 512KB。
存储管理器默认的文件系统是FAT文件系统,所以硬盘、USB、PCMCIA等存储设备的注册表信息默认都没有指定文件系统。
文件系统过滤器
文件系统过滤器是一个DLL。存储管理器在调用文件系统API之前先调用文件系统过滤器的过滤函数,通过过滤函数能够实现对文件数据的加密、解密、压缩甚至扫描文件是否存在病毒。实现文件系统过滤器的步骤是先编写DLL,CE提供了例子程序,位于%_WINCEROOT%/Public/Common/OAK/Drivers/FSD下。添加代码之后再修改注册表数据。注册表位置如下:
注册表键及其键值例子如下:
"Dll" := "fsdspy.dll" ////DLL名称 Order = 0 ////顺序
这里ProfileName是指Profile的名称,比如HDProfile。FileSystem是指具体的文件系统,如FATFS, UDFS, RELFSD。数字1指出的文件系统过滤器对指定存储硬件的文件系统有效;数字2指出的文件系统过滤器对指定的文件系统有效;数字3为所有文件系统多有效。
存储管理器
存储管理器 (Storage Manager) 是 Windows CE .NET 的新功能,主要管理存储设备驱动程序、文件系统驱动程序、分区驱动程序、文件系统过滤器。存储管理器根据注册表数据来加载需要的模块。具体注册表数据如下:
[HKEY_LOCAL_MACHINE/System/StorageManager] "Dll"="fsdmgr.dll" "PNPUnloadDelay"=dword:1000
fsdmgr.dll包含存储管理器的代码。”PNPUnloadDelay”是指存储管理器在接收到即插即用设备的卸载通知后的延时时间。具体存储管理器需要管理的存储设备的注册表信息在HKEY_LOCAL_MACHINE/System/StorageManager/Profiles下。例如硬盘的注册表信息如下:
[HKEY_LOCAL_MACHINE/System/StorageManager/Profiles/HDProfile] "Name"="IDE Hard Disk Drive" "Folder"="Hard Disk" "AutoMount"=dword:1 "AutoPart"=dword:0 "AutoFormat"=dword:0 "MountFlags"=dword:0 "FileSystem"="fatfsd.dll" "PartitionDriver"="mspart.dll"
“Name”指名称;”Folder”指目录名,可以在此更改目录名称;”AutoMount”指如果检测到分区后就自动安装文件系统;”AutoPart”指如果没有分区则自动将最大可用空间划分成一个分区;”AutoFormat”指如果没有格式化则自动格式化;”FileSystem”指定这个存储设备采用的文件系统,如果不指定就采用默认的文件系统;”PartitionDriver”指定分区驱动程序;”MountFlags”指文件系统如何被安装。值为1指定一个隐藏文件系统,如果文件系统被隐藏,那么这个文件系统将不会被查找文件的API发现,但是如果指定文件的绝对路径,还是可以访问的。值为2指定当前文件系统能够包含系统注册表。一个存储设备第一个分区将包含基于HIVE的系统注册表(关于基于HIVE的注册表见以前讲过的文章)。值为4指定这个文件系统作为整个文件系统的根(/),这里要说明一下,CE默认对象存储作为整个文件系统的根(/),当在根下放置一个文件时,例如/a.dat,这个文件实际存放在对象存储中。如果指定其它文件系统作为根,那么文件将存放在这个文件系统所在的存储设备中。值为8指定当指定值为4时隐藏ROM。
增加对大容量物理内存的支持和永久存储注册表是在定制内核工作中常遇到的问题。本篇文章将对这两个方面阐述相关的知识并指导读者如何在PB中实现。
对大容量物理内存的支持
在PC上增加物理内存是很方便的,插上内存条后只要自检程序识别,那么桌面操作系统就能够支持。而在基于CE的产品上就没那么简单了。如果物理内存大于64MB,就要在定制内核时做一些工作。
一旦内存管理单元(MMU)开始工作,CPU就不再直接访问物理内存了,对于运行在x86和ARM系列CPU上的CE内核来说,必须先确立物理内存地址同虚拟内存地址的映射关系。这种关系实际是在一个名为OEMAddressTable的表中定义的。这个表在前面的文章中已经提到过。CE提供了两种虚拟地址映射方法,分别为静态映射和动态映射,这个表属于静态映射方法。静态映射的虚拟地址空间只能由内核访问,而动态映射的地址空间可以由用户模式的应用程序访问。OEMAddressTable在文件%_WINCEROOT%/Public/Common/Oak/Csp/i486/Oal/OEMInit.asm中。在这个文件的最后有一段代码:
; RAM 0x80000000 -> 0x00000000, size 64M dd 80000000h, 0, 04000000h dd 0, 0, 0
这段代码表示将虚拟地址80000000映射到物理地址0,大小为64MB。将04000000h改成实际的物理内存大小,然后保存。接着单击PB菜单"Build"-"Open Build Release Directory",在命令行中先用cd命令进入上述目录,如:
cd %_WINCEROOT%/Public/Common/Oak/Csp/i486/Oal
然后键入下列命令:
build -c sysgen i486oal
build命令根据配置文件内容编译整个目录,sysgen批处理将build 编译的文件i486oal.lib文件复制到CE的安装目录和内核工程目录下。我安装的BSP是基于x86的,所以相应目录为
%_WINCEROOT%/PUBLIC/COMMON/OAK/LIB/X86/RETAIL 和 %_PROJECTROOT%/cesysgen/oak/lib/x86/retail两个目录。上一步做完之后,接着开始修改config.bib文件。在以前的文章中讲过在config.bib文件中定义内存区域。在config.bib中预设的配置没有超过64MB的,所以要自己手工添加。可根据原有的IMGRAM64配置更改,原有的IMGRAM64如下:
; 64 MB of RAM (note: AUTOSIZE will adjust boundary) IF IMGRAM64 NK 80220000 009E0000 RAMIMAGE RAM 80C00000 03000000 RAM UMABUF 83C00000 00400000 RESERVED ENDIF
假如要支持128MB,更改如下:
IF IMGRAM16 ! IF IMGRAM32 ! IF IMGRAM64 ! NK 80220000 009E0000 RAMIMAGE RAM 80C00000 07000000 RAM UMABUF 87C00000 00400000 RESERVED ENDIF ENDIF ENDIF
在这里没有更改NK的大小,只是修改了RAM的大小。在config.bib定义之后,还可以在OAL层通过变量或者函数更改物理内存的大小,适合设备可能出现增加或减小内存的情况。CE的帮助文档介绍了几种方法,这里只提一下CreateStaticMapping函数。CreateStaticMapping函数作为config.bib文件的补充,适合在用户模式的应用程序或驱动程序中调用,调用这个函数能够将指定首地址的物理内存块映射到虚拟地址空间,函数返回虚拟地址。虚拟地址范围在C400 0000 到 E000 0000之间,这是内核的地址空间,只能由内核访问。相比较VirtualCopy函数用于动态地将指定首地址的物理内存块映射到虚拟地址空间,这个虚拟地址空间一般为用户进程的地址空间,因为VirtualCopy函数被设计专用于驱动程序调用,它常被用于将一个物理内存块映射到不同的虚拟地址空间。
实现永久保存注册表数据
关于注册表在前面的文章中已经介绍过了,这里只讲述如何实现永久保存注册表数据。
注册表类型分为基于对象存储的注册表和基于HIVE的注册表,在定制内核的时候只能选择其中一种。从理论上讲这两种注册表都能够实现永久保存注册表数据,但是采用不同的类型会影响CE的启动顺序和启动速度,还会影响内存的使用量。我还是趋向于采用基于HIVE的注册表来实现永久保存注册表数据,这也是一个发展趋势。在讲解之前先简单描述如果CE采用基于HIVE的注册表,那么在启动时如何加载已经保存的注册表数据:
因为引导HIVE和系统HIVE肯定有重复的地方,所以可能出现重复加载了驱动程序或者重复启动了应用程序。为此,CE允许在描述驱动程序的注册表信息中加入防止重复的标志,而应用程序可以采用事件对象来防止重复启动,如device.exe。
下面讲述如何设置基于HIVE的注册表(假如保存系统HIVE的是FAT文件系统):
; HIVE BOOT SECTION [HKEY_LOCAL_MACHINE/init/BootVars] "SYSTEMHIVE"="Documents and Settings//system.hv" "PROFILEDIR"="Documents and Settings" "Start DevMgr"=dword:0 IF BSP_ENABLE_FSREGHIVE "Start DevMgr"=dword:1 ENDIF ; END HIVE BOOT SECTION"SYSTEMHIVE"的值为系统HIVE文件的路径。"Start DevMgr"是一个布尔值,指示是否开始就执行设备管理器device.exe,按照CE帮助文档的说法,只有想把系统HIVE存储在对象存储中才在此设置为0,所以一般都要设置为1。
"Flags"=dword:1000这个标志是一个位掩码,它可以和其它已经存在的"Flags"或运算。值1000表示此驱动程序只加载一次,这样device.exe就不会把当前驱动程序加载两次了。
[HKEY_LOCAL_MACHINE/System/StorageManager/Profiles/HDProfile] "MountFlags"=dword:2这个标志表示这个存储设备包含系统HIVE文件。
按照如上所述设置后的内核就能实现永久存储注册表数据了。对于保存注册表数据的执行动作在此必须阐述清楚:
正常情况下,CE能够保证重要的注册表数据能够从内存刷到(Flush)永久存储器上。但是这并不能完全保证所有数据都能完整地保存而不丢失,所以要保证万无一失,应该主动地调用RegFlushKey函数强制将内存中的数据刷到永久存储器上。这个函数的参数只有一个,就是注册表分支。CE还增加一个注册表项(如下所示),它的作用是每当函数RegCloseKey被调用时都自动调用RegFlushKey函数。
[HKEY_LOCAL_MACHINE/init/BootVars] "RegistryFlags"=dword:1
如果CE在启动过程中发现系统HIVE出现错误,它会自动删除文件并创建一个默认的系统HIVE文件,如果出现下面的注册表项,说明发生了这种事情。
[HKEY_LOCAL_MACHINE] "RegPersisted"=dword:1