型号: PXF0256
wince注册表结构
与桌面Windows一样,Windows CE也使用注册表(Registry)来保存应用程序、驱动程序和用户的设定以及其他一些配置信息。Windows CE注册表也采用树形结构来管理配置信息,由于Windows CE注册表的结构和功能与桌面Windows几乎一样,在这里就不详细介绍了,读者可以参考其它关于注册表的资料。
Windows CE支持四个根键,描述如下:
键名 描述
HKEY_LOCAL_MACHINE 硬件和驱动配置数据
HKEY_CURRENT_USER 用户配置数据
HKEY_CLASSES_ROOT OLE和文件类型匹配配置数据
HKEY_USERS 适用于所有用户的数据
由于嵌入式系统的特点,一些嵌入式设备是没有外存的。因此Windows CE的注册表提供了两种实现方式:基于RAM的注册表(RAM-Based Registry)和基于Hive的注册表(Hive-Based Registry)。我们可以选择在Windows CE中使用任何一种注册表,注册表类型对于用户和应用程序来说是透明的。
 基于RAM的注册表
正如其名,基于RAM的注册表把整个注册表作为一个对象存储堆存放在系统的内存中。这意味着如果对系统进行冷启动或者系统断电,对注册表的所有改动都会丢失。
如果使用基于RAM的注册表,对注册表的读写访问操作会变得非常高效。因此基于RAM的注册表比较适用于没有外部存储,而且有电池保存内存数据(battery-backed RAM)的设备。如果有外存且经常冷启动的设备采用基于RAM的注册表,则需要在系统断电的时候对注册表进行保存,等系统再次启动时对保存的注册表进行还原。
Windows CE提供了两种方法用来断电保存基于RAM的注册表:
1. Windows CE提供了两个系统API用来保存和还原整个注册表,它们的原形如下:
BOOL RegCopyFile(
LPCWSTR lpszFile // 保存注册表信息的文件的名字
);
BOOL RegRestoreFile(
LPCWSTR lpszFile // 保存注册表信息的文件的名字
);
如果要保存和恢复注册表,我们只需要在系统断电的时候调用RegCopyFile函数将整个注册表保存为外存上的一个文件。当系统重新启动时,我们再调用RegRestoreFile函数将文件全部读出RAM中,然后再热启动系统,我们保存得注册表就可以生效了。值得注意的是这次热启动是必须的,因为只有在系统启动的时候才会去检测RegRestoreFile放在RAM里的注册表信息。这种方法的优点是完全可以使用应用程序来实现基于RAM的注册表的保存,而且这种方法相对简单。但是此方法的缺点是需要两次启动。因此效率相对比较低。
2. 第二种方法需要OEM的参与,OEM可以在BSP的OAL层中实现WriteRegistryToOEM和ReadRegistryFromOEM两个函数,它们的声明为:
DWORD ReadRegistryFromOEM(
DWORD dwFlags, // 参数, REG_READ_BYTES_START表示读新的注册表
LPBYTE lpData, // 指向注册表数据的缓冲区,由OS分配
DWORD cbData // 缓冲区的大小
);
BOOL WriteRegistryToOEM(
DWORD dwFlags, // 参数,REG_WRITE_BYTES_START表示写新的注册表
LPBYTE lpData, // 指向注册表数据的缓冲区,由OS分配
DWORD cbData // 缓冲区的大小,0表示到达注册表尾部
);
Windows CE会在系统启动和关闭的时候调用这两个函数来保存和恢复注册表。此种方法虽然可以避免两次启动,但是困难的地方是ReadRegistryFromOEM函数的实现比较困难,因为在系统启动的时候,块设备驱动和文件系统的驱动都还没有加载,因此不能使用CreateFile,ReadFile这样的文件系统API来实现ReadRegistryFromOEM函数,只能使用一些更底层的操作来实现。
 基于Hive的注册表
自从Windows CE 4.0之后,Windows CE提供了基于Hive的注册表。基于Hive的注册表把注册表数据存放在文件系统的文件上,这种文件被称作蜂箱Hive。这就意味着不再需要在系统断电和启动时进行保存恢复注册表操作。
Hive是注册表中的一组键,子键和值。Hive是文件系统上表现为单个文件。Windows CE中有三种Hive。
类型 文件 描述
Boot hive ROM中的Boot.hv HKEY_LOCAL_MACHINE, HKEY_CLASSES_ROOT, HKEY_USERS中的所有数据。只在启动时使用。
System hive 由OEM决定
(通常是System.hv) HKEY_LOCAL_MACHINE, HKEY_CLASSES_ROOT, HKEY_USERS中的所有数据。包含设备范围内不随着用户改变而改变的数据。
User hive User.hv HKEY_CURRENT_USER下的所有数据。 包含用户特有的设置,每个用户都有一个单独的User.hv。
基于Hive的注册表适用于对于有永久存储并且需要经常冷启动的设备。我们也可以看到,基于Hive的注册表把系统数据和用户数据分开存放,这就意味着基于Hive的注册表还提供多用户支持。对于每一个用户,可以提供不同的User.hv,当用户登录时加载相应的User.hv,从而达到多用户目的。
注册表类型分为基于对象存储的注册表和基于HIVE的注册表,在制定内核的时候只能选择其中一种。从理论上讲这两种注册表都能够实现永久保存注册表数据,不过采用不同的类型会影响CE的启动顺序和启动速度,还会影响内存的使用量。我还是趋向于采用基于HIVE的注册表来实现永久保存注册表数据,这也是个发展趋势。在讲解之前先简单描述如果CE采用基于HIVE的注册表,那么在启动时怎么加载已保存的注册表数据:
1、nk.exe执行,启动filesys.exe。
2、filesys.exe加载引导HIVE,此时引导HIVE位于nk.bin解压之后的文件中。
3、filesys.exe启动device.exe,之后处于等待状态,等待device.exe将包含系统HIVE的文件系统和存储设备的驱动程式加载完毕。而这个文件系统和存储设备的驱动程式存在于引导HIVE中。
4、device.exe加载上述所说的文件系统驱动程式和存储设备驱动程式,使之开始工作。之后device.exe处于等待状态。
5、filesys.exe被唤醒,加载并且安装系统HIVE。之后filesys.exe处于等待状态。
6、nk.exe按照系统HIVE的信息开始执行初始化工作。其中包括加载驱动程式和启动一些应用程式。其中加载驱动程式一般由device.exe执行,而启动应用程式由filesys.exe执行。这时device.exe和filesys.exe已被唤醒。
因为引导HIVE和系统HIVE肯定有重复的地方,所以可能出现重复加载了驱动程式或重复启动了应用程式。为此,CE允许在描述驱动程式的注册表信息中加入防止重复的标志,而应用程式能采用事件对象来防止重复启动,如device.exe。
下面讲述怎么设置基于HIVE的注册表(如果保存系统HIVE的是FAT文件系统):
1、在PB中加入"Hive-based Registry",如果是Geode平台,再加入BSP_ENABLE_FSREGHIVE环境变量。
2、打开platform.reg,找到如下信息:
; HIVE BOOT SECTION
[HKEY_LOCAL_MACHINEinitBootVars]
"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。
3、如果是多用户,能在上述的注册表位置下输入"DefaultUser"="<username>",指定默认的用户名。如果是单用户系统,能不设置。
4、确保将包含系统HIVE的文件系统驱动程式的注册表信息和存储设备的驱动程式的注册表信息被包含在“; HIVE BOOT SECTION”和“; END HIVE BOOT SECTION”之间,在这两个语句之间的注册表数据全部属于引导HIVE。如果我们将系统HIVE文件system.hv存放在硬盘上,并采用FAT文件系统。那么就要将[HKEY_LOCAL_MACHINESystemStorageManagerFATFS]和[HKEY_LOCAL_MACHINESystemStorageManagerProfilesHDProfile]移动到“; HIVE BOOT SECTION”下。
5、在“; HIVE BOOT SECTION”和“; END HIVE BOOT SECTION”之间的所有驱动程式的注册表信息中都加入下列一个标志:
Flags"=dword:1000
这个标志是个位掩码,他能和其他已存在的"Flags"或运算。值1000表示此驱动程式只加载一次,这样device.exe就不会把当前驱动程式加载两次了。
6、在包含系统HIVE的存储设备的驱动程式的注册表信息中,加入如下标志(假设是硬盘):
[HKEY_LOCAL_MACHINESystemStorageManagerProfilesHDProfile]
"MountFlags"=dword:2
这个标志表示这个存储设备包含系统HIVE文件。
按照如上所述设置后的内核就能实现永久存储注册表数据了。对于保存注册表数据的执行动作在此必须阐述清晰:
正常情况下,CE能够确保重要的注册表数据能够从内存刷到(Flush)永久存储器上。不过这并不能完全确保所有数据都能完整地保存而不丢失,所以要确保万无一失,应该主动地调用RegFlushKey函数强制将内存中的数据刷到永久存储器上。这个函数的参数只有一个,就是注册表分支。CE还增加一个注册表项(如下所示),他的作用是每当函数RegCloseKey被调用时都自动调用RegFlushKey函数。
[HKEY_LOCAL_MACHINEinitBootVars]
"RegistryFlags"=dword:1
如果CE在启动过程中发现系统HIVE出现错误,他会自动删除文件并创建一个默认的系统HIVE文件,如果出现下面的注册表项,说明发生了这种事情。
嵌入式设备与桌面PC的一个显著不同是它的应用程序中通常需要直接访问某一段物理内存,这在驱动程序中对物理内存的访问尤为重要,尤其是像ARM体系结构下,I/O端口也被映射成某一个物理内存地址。因此,与桌面版本Windows相比,Windows CE提供了相对简单的物理内存访问方式。无论是驱动程序还是应用程序都可以通过API访问某一段物理内存。
Windows CE的有些函数中需要用到物理内存结构体PHYSICAL_ADDRESS, Windows CE在ceddk.h中定义了PHYSICAL_ADDRESS,它其实是LARGE_INTEGER类型,其定义如下:
// in ceddk.h
typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS;
// in winnt.h
typedef union _LARGE_INTEGER{
struct{
DWORD LowPart;
LONG HighPart;
};
LONGLONG QuadPart;
} LARGE_INTEGER;
可见,Windows CE中用64个Bit来代表物理地址,对于大多数32位的CPU而言,只需要把它的HighPart设置为0就可以了。
如果要直接访问某一个地址的物理内存,Windows CE提供了VirtualAlloc()和VirtualCopy()函数,VirtualAlloc负责在虚拟内存空间内保留一段虚拟内存,而VirtualCopy负责把一段物理内存和虚拟内存绑定,这样,最终对物理内存的访问还是通过虚拟地址进行。它们的声明如下:
// 申请虚拟内存
LPVOID VirtualAlloc(
LPVOID lpAddress, // 希望的虚拟内存起始地址
DWORD dwSize, // 以字节为单位的大小
DWORD flAllocationType, // 申请类型,分为Reserve和Commit
DWORD flProtect // 访问权限
);
// 把物理内存绑定到虚拟地址空间
BOOL VirtualCopy(
LPVOID lpvDest, // 虚拟内存的目标地址
LPVOID lpvSrc, // 物理内存地址
DWORD cbSize, // 要绑定的大小
DWORD fdwProtect // 访问权限
);
VirtualAlloc对虚拟内存的申请分为两步,保留MEM_RESERVE和提交MEM_COMMIT。其中MEM_RESERVE只是在进程的虚拟地址空间内保留一段,并不分配实际的物理内存,因此保留的虚拟内存并不能被应用程序直接使用。MEM_COMMIT阶段才真正的为虚拟内存分配物理内存。
下面的代码显示了如何使用VirtualAlloc和VirtualCopy来访问物理内存。因为VirtualCopy负责把一段物理内存和虚拟内存绑定,所以VirtualAlloc的时候只需要对内存保留,没有必要提交。
FpDriverGlobals =
(PDRIVER_GLOBALS) VirtualAlloc(
0,
DRIVER_GLOBALS_PHYSICAL_MEMORY_SIZE,
MEM_RESERVE,
PAGE_NOACCESS);
if (FpDriverGlobals == NULL) {
ERRORMSG(DRIVER_ERROR_MSG, (TEXT(" VirtualAlloc failed!\r\n")));
return;
}
else {
if (!VirtualCopy(
(PVOID)FpDriverGlobals,
(PVOID)(DRIVER_GLOBALS_PHYSICAL_MEMORY_START),
DRIVER_GLOBALS_PHYSICAL_MEMORY_SIZE,
(PAGE_READWRITE | PAGE_NOCACHE))) {
ERRORMSG(DRIVER_ERROR_MSG, (TEXT("VirtualCopy failed!\r\n")));
return;
}
}
CEDDK还提供了函数MmMapIoSpace用来把一段物理内存直接映射到虚拟内存。此函数的原形如下:
PVOID MmMapIoSpace(
PHYSICAL_ADDRESS PhysicalAddress, // 起始物理地址
ULONG NumberOfBytes, // 要映射的字节数
BOOLEAN CacheEnable // 是否缓存
);
其实,MmMapIoSpace函数内部也是调用VirtualAlloc和VirtualCopy函数来实现物理地址到虚拟地址的映射的。MmMapIoSpace函数的原代码是公开的,我们可以从%_WINCEROOT%\PUBLIC\COMMON\OAK\DRIVERS\CEDDK\DDK_MAP\ddk_map.c得到。从MmMapIoSpace的实现我们也可以看出VirtualAlloc和VirtualCopy的用法:
PVOID MmMapIoSpace (
IN PHYSICAL_ADDRESS PhysicalAddress,
IN ULONG NumberOfBytes,
IN BOOLEAN CacheEnable
)
{
PVOID pVirtualAddress; ULONGLONG SourcePhys;
ULONG SourceSize; BOOL bSuccess;
SourcePhys = PhysicalAddress.QuadPart & ~(PAGE_SIZE - 1);
SourceSize = NumberOfBytes + (PhysicalAddress.LowPart & (PAGE_SIZE - 1));
pVirtualAddress = VirtualAlloc(0, SourceSize, MEM_RESERVE, PAGE_NOACCESS);
if (pVirtualAddress != NULL)
{
bSuccess = VirtualCopy(
pVirtualAddress, (PVOID)(SourcePhys >> 8), SourceSize,
PAGE_PHYSICAL | PAGE_READWRITE | (CacheEnable ? 0 : PAGE_NOCACHE));
if (bSuccess) {
(ULONG)pVirtualAddress += PhysicalAddress.LowPart & (PAGE_SIZE - 1);
}
else {
VirtualFree(pVirtualAddress, 0, MEM_RELEASE);
pVirtualAddress = NULL;
}
}
return pVirtualAddress;
}
此外,Windows CE还供了AllocPhysMem函数和FreePhysMem函数,用来申请和释放一段连续的物理内存。函数可以保证申请的物理内存是连续的,如果函数成功,会返回虚拟内存的句柄和物理内存的起始地址。这对于DMA设备尤为有用。在这里就不详细介绍了,读者可以参考Windows CE的联机文档
Windows CE 6.0有4个基本的注册表键值, HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS。其它的都是这4个注册表的子键。
我 们以一个内建的串口驱动为例,它在注册表文件Platform.reg中的描述如下: 其中Prefix 和 Dll项是必不可少的,Prefix代表设备文件名前缀, 与Index合用表示该设备的名称,该注册表子键的设备名称就是"COM1:",该名称可以用于CreateFile调用。Dll则是动态链接库名称。 Index为设备序号。Flags为1表示系统启动时不加载,需要应用程序自己加载,为0表示该驱动在系统启动时加载。
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\Serial]
"Prefix"="COM"
"Dll"="$(_TRL-GTPLAT_PFX)_serial.dll"
"Flags"=dword:0
"Index"=dword:1
在%WinCE Dir%Public%Common%OAK%INC%目录下,文件cregedit.h中,定义了一个类CRegistryEdit来封装了注册表的操作。许多的硬件驱动,比如串口类,也继承了CRegistryEdit类。
还有一种方法是利用windows CE提供的API进行注册表项的操作。
方法一: 利用系统提供的注册表类CRegistryEdit
 
url:
http://www.51lm.cn/p/templates/cn/show.php?cid=905&aid=256