型号: PXF0249
WinCE5.0/6.0知识总结
WinCE5.0简介
1.2 基于WinCE的嵌入式系统开发
1.3.1 WinCE自带的远程工具及简介
1. Viewbin
Viewbin是微软提供的一个命令行工具,在WinCE6.0中,可以在"WINCE600PUBLICCOMMONOAKBINI386"找到他。Viewbin工具可以用来查看NK.bin文件。它可以从NK.bin中获得这个NK image的大小,运行的起始地址等。还能查看到里面所包含的模块,应用程序及相关的信息。
Viewbin的使用格式如下:
viewbin [parameter] [filename]
举几个例子吧:
1. viewbin -nk.bin
ViewBin... nk.bin
Image Start = 0xC02C0000, length = 0x00D5E380
Start address = 0xC02C1006
Checking record #126 for potential TOC (ROMOFFSET = 0x40000000)
Found pTOC = 0x8101c8d0
ROMOFFSET = 0x40000000
Done.
这条命令可以查看NK image的起始地址,长度等信息,这些信息应该和config.bib文件中定义的一致。
2. viewbin -t nk.bin > output.txt
这条命令可以将NK image里面包含的模块及相关信息列成一个表并输出到output.txt里面,这样打开output.txt就可以看到里面包含了哪些模块,其中包括驱动,应用程序等。
3. viewbin -r nk.bin > output.txt
打印记录信息到output.txt中。
第1章 WinCE5.0的体系结构和功能
1. 注册表主要键的作用
[HKEY_LOCAL_MACHINE\Drivers\Active]:所有已经加载的驱动信息都放在该键下;
[HKEY_LOCAL_MACHINE\Drivers\Active]:所有要Device.exe加载的驱动都放在该键下面;
2. 驱动主要键的作用
1) DLL:指明了该驱动使用的动态库;
2) Prefix:表明驱动的前缀,前缀通常是3个大写的英文字母,通过前缀就知道导出的函数,例如XXX.Init();
3) Index:指定驱动程序的索引。如果为1,那么是CreateFile()函数时第一个参数必须为XXX1;
4) Order:指明驱动程序的加载顺序。值越小加载得越早,这有助于解决驱动程序之间的依赖关系,比如A驱动要用到B驱动,那么可以把A驱动的Order设置得比B驱动大,让B驱动先起来;
5) Friendly Name:是一个字符串,方便用户更好的理解;
6) Ioctl:是一个可选项,设备管理器在驱动加载完毕后会检查这个键,如果有这个键,则通过DeviceIoControl()函数给设备发送Ioctl指定的控制命令;
7) IClass:是个可选项,在处理即插即用提醒时用得到。如果设置了IClass,则在加载驱动程序时,会向注册的应用程序发出此IClass的提醒;
3. Hive注册表和Ram注册表
WinCE支持2种注册表类型:RAM-based注册表和Hive-based注册表。开发者可以决定设备采用何种类型的注册表,这对于应用开发者和使用者是透明的。
RAM-Based注册表:
RAM-Based注册表把注册表信息存放在内存的Object
1. 同步与异步
同步就是你叫我去吃饭,我听到了就和你去吃饭;如果没有听到,你就不停的叫,直到我告诉你听到了,才一起去吃饭。
异步就是你叫我,然后自己去吃饭,我得到消息后可能立即走,也可能等下才去吃饭。
在多数情况下,线程之间难免要相互通信、相互协调才能完成任务。比如,当有多个线程共同访问同一个资源时,就必须保证一个线程正读取这个资源数据的时候,其它线程不能够修改它。这就需要线程之间相互通信,了解对方的行为。 再有当一个线程要准备执行下一个任务之前,它必须等待另一个线程终止才能运行,这也需要彼此相互通信。 实际开发过程中,线程间需要同步的情况非常多。Windows CE.NET给我们提供了很多的同步机制,熟练的掌握这些机制并合理运用会使线程之间的同步更合理、更高效。进程间的通信机制在下一篇文章中讲解。
Windows CE.NET具有两种运行模式:用户模式和内核模式。并且允许一个运行于用户模式的应用程序随时切换为内核模式,或切换回来。线程同步的有些解决办法运行在用户模式,有些运行在内核模式。《Windows核心编程》上说从用户模式切换到内核模式再切换回来至少要1000个CPU周期。我查看过CE下API函数SetKMode的源码,这个函数用于在两种模式间切换,改变模式只需修改一些标志,至于需要多少个CPU周期很难确定。但至少可以肯定来回切换是需要一定时间的。所以在选择同步机制上应该优先考虑运行在用户模式的同步解决办法。
1、 互锁函数
互锁函数运行在用户模式。它能保证当一个线程访问一个变量时,其它线程无法访问此变量,以确保变量值的唯一性。这种访问方式被称为原子访问。互锁函数及其功能见如下列表:
函数 |
参数和功能 |
InterlockedIncrement |
参数为PLONG类型。此函数使一个LONG变量增1 |
InterlockedDecrement |
参数为PLONG类型。此函数使一个LONG变量减1 |
InterlockedEx改变Add |
参数1为PLONG类型,参数2为LONG类型。此函数将参数2加到参数1指向的变量中 |
InterlockedEx改变 |
参数1为PLONG类型,参数2为LONG类型。此函数将参数2的值赋给参数1指向的值 |
InterlockedEx改变Pointer |
参数为PVOID* 类型,参数2为PVOID类型。此函数功能同上。具体参见帮助 |
InterlockedCompareEx改变 |
参数1为PLONG类型,参数2为LONG类型,参数3为LONG类型。此函数将参数1指向的值与参数3比较,相同则把参数2的值赋给参数1指向的值。不相同则不变 |
InterlockedCompareEx改变Pointer |
参数1为PVOID* 类型,参数2为PVOID类型,参数3为PVOID。此函数功能同上。具体参见帮助 |
2、 临界区
临界区对象运行在用户模式。它能保证在临界区内所有被访问的资源不被其它线程访问,直到当前线程执行完临界区代码。除了API外,MFC也对临界区函数进行了封装。临界区相关函数:
void InitializeCriticalSection ( LPCRITICAL_SECTION ); |
举例如下:
void CriticalSectionExample (void) |
MFC类使用更简单: CCriticalSection cs;
cs.Lock(); |
使用临界区要注意的是避免死锁。当有两个线程,每个线程都有临界区,而且临界区保护的资源有相同的时候,这时就要在编写代码时多加考虑。
3、事件对象
事件对象运行在内核模式。与用户模式不同,内核模式下线程利用等待函数来等待所需要的事件、信号,这个等待过程由操作系统内核来完成,而线程处于睡眠状态,当接收到信号后,内核恢复线程的运行。内核模式的优点是线程在等待过程中并不浪费CPU时间,缺点是从用户模式切换到内核模式需要一定的时间,而且还要切换回来。在讲解事件对象前应该先谈谈等待函数。等待函数有四个。具体参数和功能见下表:
函数 |
参数和功能 |
WaitForSingleObject |
参数1为HANDLE类型,参数2为DWORD类型。此函数等待参数1标识的事件,等待时间为参数2的值,单位ms。如果不超时,当事件成为有信号状态时,线程唤醒继续运行。 |
WaitForMultipleObjects |
参数1为DWORD类型,参数2为HANDLE * 类型,参数3为BOOL类型,参数4为DWORD类型。此函数等待参数2指向的数组中包含的所有事件。如果不超时,当参数3为FALSE时,只要有一个事件处于有信号状态,函数就返回这个事件的索引。参数3为TRUE时,等待所有事件都处于有信号状态时才返回。 |
MsgWaitForMultipleObjects |
参数1为DWORD类型,参数2为LPHANDLE类型,参数3为BOOL类型,参数4为DWORD类型,参数5为DWORD类型。此函数功能上同WaitForMultipleObjects函数相似,只是多了一个唤醒掩码。唤醒掩码都是和消息有关的。此函数不但能够为事件等待,还能为特定的消息等待。其实这个函数就是专为等待消息而定义的。 |
MsgWaitForMultipleObjectsEx |
参数1为DWORD类型,参数2为LPHANDLE类型,参数3为DWORD类型,参数4为DWORD类型,参数5为DWORD类型。此函数是MsgWaitForMultipleObjects函数的扩展。将原来函数的参数3除掉,添加参数5为标志。标志有两个值:0或MWMO_INPUTAVAILABLE。 |
如果一个线程既要执行大量任务同时又要响应用户的按键消息,这两个专用于等待消息的函数将非常有用。
和事件有关的函数有:
HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes,BOOL bManualReset, BOOL bInitialState, LPTSTR lpName); |
事件对象是最常用的内核模式同步方法。它包含一个使用计数和两个BOOL变量。其中一个BOOL变量指定这个事件对象是自动重置还是手工重置。另一个BOOL变量指定当前事件对象处于有信号状态还是无信号状态。
函数CreateEvent创建一个事件对象,参数1必须为NULL,参数2指定是否手工重新设置事件对象的状态。如果为FALSE,当等待函数接到信号并返回后此事件对象被自动置为无信号状态。这时等待此事件对象的其它线程就不会被唤醒,因为事件对象已经被置为无信号状态。如果参数2设置为TRUE,当等待函数接到信号并返回后事件对象不会被自动置于无信号状态,其它等待此事件对象的线程都能够被唤醒。用ResetEvent函数可以手工将事件对象置为无信号状态。相反SetEvent函数将事件对象置为有信号状态。PulseEvent函数将事件对象置为有信号状态,然后立即置为无信号状态,在实际开发中这个函数很少使用。OpenEvent函数打开已经创建的事件对象,一般用于不同进程内的线程同步。在调用CreateEvent创建一个事件对象时,传递一个名字给参数4,这样在其它进程中的线程就可以调用OpenEvent函数并指定事件对象的名字,来访问这个事件对象。
3.2 Platform Builder5.0各部分的功能和作用
3.3 Platform Builder5.0的目录结构及包含的内容
第3章 WinCE5.0的Bootloader
第4章 WinCE5.0的OAL
第5章 WinCE5.0的驱动程序
6.1 WinCE5.0驱动程序的基本知识
6.1.1 WinCE驱动程序开发简介
操作硬件是驱动开发的首要任务。
在WinCE5.0下所有的驱动都以用户态的DLL文件形式存在。WinCE驱动程序用到的方法及工具与编写其他任何一个普通的DLL没有什么区别。与所有Win32 DLL一样,Windows CE驱动程序DLL中的代码要得以运行,必须被一些进程动态加载到地址空间。
优点:
1) 灵活性,运行时动态加载,这样可以轻松实现外设的即插即用。
2) 稳定性,因为放在内核态虽然是程序运行的更快,但在内核态的驱动如果出现崩溃会导致系统崩溃,但处于用户态的驱动程序即使崩溃也不会影响到内核。
6.1.2 WinCE5.0驱动程序的宿主
上文提到DLL文件本身是无法运行的,如果要运行DLL中的代码,必须先有一个exe进程,把 DLL加载到自己的地址空间内,然后才可执行DLL中的代码。
在WinCE5.0中,有3个系统的进程会加载和执行驱动程序的DLL。它们是Device.exe,GWES.exe和FileSys.exe。它们各自加载的驱动程序类型如下图。
1) Device.exe:设备管理器,负责加载WinCE下绝大多数的设备驱动程序;
2) GWES.exe:加载的驱动程序是一些和图形界面相关的I/O设备驱动程序;
3) FileSys.exe:负责加载所有的文件系统驱动程序。
1. 内建的驱动程序与可安装的驱动程序
内建驱动程序有时也称为本地驱动程序,它们被静态地链接到GWES;
可安装的驱动程序也称为流设备驱动程序,他们是由设备管理器动态加载的用户模式的DLL。
2. 分层的驱动程序和不分层的驱动程序
这是根据驱动的结构来分类的。
1. 简介
在WinCE中,系统进程Device.exe负责对几乎所有的外设驱动程序进行管理,它也被称为设备管理器。
严格来说,设备管理器不是WinCE操作系统内核的一部分,仅仅是一个用户态下的普通进程,而且没有设备管理器WinCE也一样可以启动、运行。但设备管理器在WinCE中的作用却非常重要。
在WinCE系统启动时,设备管理器会随着启动,并且在整个系统运行时都会持续运行。在Commom.reg中可以看到device.exe的启动:
……
[HKEY_LOCAL_MACHINE\init]
IF IMGCELOGENABLE
; At most one CeLog flush app (CeLogFlush, OSCapture) can be used
IF IMGAUTOFLUSH
"Launch05"="CeLogFlush.exe"
ENDIF
IF IMGOSCAPTURE
"Launch05"="OSCapture.exe"
ENDIF
ENDIF
; @CESYSGEN IF CE_MODULES_SHELL
IF IMGNOKITL !
"Launch10"="shell.exe"
ENDIF IMGNOKITL !
;
url:
http://www.51lm.cn/p/templates/cn/show.php?cid=905&aid=249