对已发表的文章中错误的总结
对已发表的文章中错误的总结
作者:付林林
从写完第一篇文章到写这篇文章已经过去好几个月了。因为写文章不仅能够锻炼自己,促进自己更加认真地研究CE,而且还能和其它正从事CE下内核和应用软件开发的网友交流心得,取长补短。所以虽然这段时间我很忙碌,但是我仍然愿意抽出时间来写一些文章。现在看来写这些文章对我的确有很大的帮助,得到了很多网友的支持,也学到了很多知识。当然,有几篇文章中的内容也受到了网友的批评,我绝对欢迎网友的批评,也会虚心接受网友提供的建议,但是更希望我们的交流能理性些。因为我们的目的只有一个,那就是学到知识。
没人会为自己出现错误而自豪,但是大多数人会为发现了自己的错误而高兴。由于很多原因,在我发表的文章中的确存在着一些错误,但是我不是网站管理员,不能随意地更改文章的内容,所以在这篇文章中我总结了错误和网友提出的建议。如果还有错误希望网友能够发电子邮件给我,我将非常感激。
《EVC开发实例三:浏览器窗口》:有网友提出"包含的头文件不要那么复杂,只要包含Exdisp.h就可以了",我写的代码包含的头文件信息源于MSDN,是否只需包含exdisp.h我没有做实验。另外这种方法不能用于实现网络浏览器。如果要编写类似IE的浏览器,先安装Windows
CE.NET,在安装目录下能够找到IESample子目录,这个目录就是CE自带的IE浏览器的源码(同样利用了IWebBrowser组件)。
《模拟器和远程调试工具》:实际上常用的工具没有几个,所以这两篇文章我都介绍的很简略。我还自己编写了一个类似Remote Process
Viewer的工具CEInfo,主要用于研究CE的内存管理。一般性的软件开发可能用不到CE的内存管理方面的知识,但是了解CE的内存管理的确会对开发特殊用途的软件有很大的帮助。
《Windows CE 进程、线程和内存管理(一)》:这一篇文章没有对函数TerminateThread详细说明。在此说明:
TerminateThread函数的危险在于,此函数一旦成功执行,指定的线程可能会立刻终止执行,那么有很多的结束处理工作还没来得及进行,但是此线程加载的DLL会被通知结束执行。要调用TerminateThread函数就必须先了解要终止的线程的具体情况。另外如果指定的线程正在执行系统API调用时,TerminateThread函数不能立刻终止此线程的执行,而是在API函数执行完毕后才退出。所以使用TerminateThread前必须对线程的具体情况要了解。
《Windows CE 进程、线程和内存管理(二)》:InterlockedExchangeAdd函数,此函数将参数2的值加到参数1指向的值中。函数返回参数1指向的变量的初始值。
《Windows CE 进程、线程和内存管理(三)》:这篇文章中介绍ROM和RAM的部分错误较大,为此我修改了这部分,修改后内容如下:
对于早期采用的存储设备一般为ROM + RAM
,在ROM中存放的所有文件可以是压缩的也可以是不压缩的,这取决于OEM(原始设备制造商)。OEM在定制CE内核时可以设置一个标志告诉ROM镜像制作工具(romimage.exe)是否压缩文件。对于ROM中存放的模块(DLL、EXE文件)来说,如果是压缩的,模块在运行前先解压并全部存放到RAM中。如果是不压缩的,并且ROM介质支持线性访问(line-accessed),就可以本地执行(executed
in place,缩写为XIP)。利用本地执行方式运行应用程序、DLL的优点是:采用这种技术在加载EXE或DLL时,其中的代码段数据不加载到物理内存中,内核只是分配虚拟地址空间给代码段,当执行代码时内核会到实际存放在ROM存储设备上的文件中寻找代码并执行。采用这样的技术既可以节省可用内存又可以减少加载的时间。但是这种技术有一定的局限性,如果要让CPU到ROM中去寻找代码执行,那么ROM介质必须支持线性访问,这就要求ROM介质支持线性访问,而不是块访问。XIP这种加载方式的缺点就是执行速度相对较慢,CPU访问ROM的速度肯定远慢于访问RAM的速度。
基于Windows CE的产品开始采用FLASH、IDE等永久存储设备时,内核镜像(.bin)和其它应用程序文件开始存放到永久存储设备中而不是ROM中,这不仅仅是因为硬盘或者FLASH的I/O速度比ROM快,更因为现在的内核包含的功能多并且文件数量增加,因而需要的存储空间很大,一般都在20MB左右。再加上其它开发商开发的应用程序文件,要求的空间就更大了。CE启动时内核镜像由加载程序解压并将系统文件加载到RAM的NK,NK是在config.bib中定义的一段RAM区域,专用于保存内核镜像解压出来的所有文件。Windows
CE将NK看作是ROM,当执行一个应用程序时,CE内核将这个应用程序需要的系统DLL(在NK中保存)加载到Slot 1(地址范围0x0200
0000-0x03FF FFFF,在Windows CE.NET中Slot 1专用于XIP DLL使用)。Slot
1是一段虚拟地址,当CPU执行DLL的代码时,CPU会根据地址映射关系到NK中寻找实际的代码执行,因为NK是一段实际的物理内存,I/O速度非常快,所以相对于在ROM中执行,DLL的运行效率得到很大提高。
非XIP DLL在加载时CE内核会在调用DLL的进程的地址空间中申请足够大的地址空间,并且执行代码时按需提交物理内存。
RAM和ROM文件系统是Windows
CE默认的文件系统。RAM文件系统的优点是支持文件压缩、支持事务机制(和数据库中的事务机制相似)、数据I/O较快。Windows CE.NET启动时把除了NK以外的RAM分为对象存储(object
store)区域和应用程序内存(program memory)区域,并且默认各使用一半RAM。在基于Windows
CE的设备没有采用永久存储器之前,对象存储的作用相当于永久存储器,对象存储区域采用RAM文件系统来保存文件,对象存储中可以存储的对象类型有文件、目录、数据库、记录、数据库卷。默认在对象存储中存储的对象全部是压缩的。当整个系统关闭时,设备的电源还继续提供电力给RAM,这样对象存储中保存的所有数据就不会丢失。应用程序内存区域留给所有应用程序运行时使用。基于Windows
CE的设备采用永久存储器后,对象存储的作用就被永久存储器替代了,所以采用永久存储器后,应该减小对象存储区域的大小。如果定制的Windows
CE的内核包含了资源管理器(explorer.exe),那么打开"控制面板",在"系统"-"内存"中,可以调节这两个存储区域的比例。滑块向左,则释放对象存储区域的一些可用内存并将这些内存划到应用程序内存区域中。滑块向右则相反。
《Windows CE下串口通信》:此篇文章中ReadThread的代码不适合所有需要,只适合读取终端设备返回来的少量数据。大家要编写串口读函数应该具体问题具体分析。另外如果想在任意时刻停止读串口线程,应该按如下形式调用函数:
SetCommMask(hPort,EV_RXCHAR|EV_TXEMPTY);
EV_TXEMPTY表示当输出字符都发送出去作为一个事件。那么我们可以发送任意一个字符,这个字符发送出去后WaitCommEvent就返回。
《在Windows CE下申请大容量物理内存》:我在写这篇文章前曾遇到过类似事情,一个软件需要加载几十MB的文件到物理内存,然而用new根本办不到。申请物理内存只有两种方法,一种是调用VirtualAlloc,先申请足够的虚拟地址空间,然后提交足够的物理内存。另一种方法是调用CreateFileMapping在0x4200
0000到0x8000 0000之间占用足够的虚拟地址空间,再调用MapViewOfFile提交足够的物理内存。我是在Virtual不能够实现的情况下采用内存映射方法的,事实证明我的方法是对的。CE的帮助文档中也显著地标明了使用内存映射技术可以用来申请虚拟地址空间。至于是否能够提交足够的物理内存,那取决于设备的空闲物理内存。如果大家有条件的话可以查阅National的Geode/MediaGX显卡驱动程序源码。源码中有一段代码(申请物理内存)清晰地标明了使用CreateFileMapping替代VirtualAlloc,因为要保证申请的地址空间在0x4200
0000到0x8000 0000之间。最后说一句:如果大家能够通过VirtualAlloc或者其它方法申请到了30MB以上的物理内存,希望你能告诉我,让我多掌握一种方法。
写作时间:2004-09-23
未经本文作者同意,不准擅自转载本篇文章。联系作者请邮至 windowsce@tom.com或fllsoft@sina.com