设备管理代码在private\winceos\coreos\device\目录.右边的WinCE6.0省略INC, IROM, NOPMIF, PMIF4个目录没有列出.这些目录是头文件, io资源管理, 电源管理接口.
看看设备管理器的入口点devmain.c. 在WinCE5.0时代, 设备管理器是作为一个进程来实现的:devece.exe. 所以里面就是一个入口函数WinMain()调用StartDeviceManager()函数. 再看WinCE6.0, devmain.c多出来了DevMainEntry(), DllEntry(), 啊哈~ 看来设备管理器也惨遭kernel的命运.变成了device.dll了.
oal.exe加载了kernel.dll, kernel.dll加载device.dll, device.dll加载devmgr.dll, 这个就是负责加载, 卸载, 管理流驱动的. 流程比较了半天,WinCE6.0的驱动加载和WinCE5.0的加载过程没有发现什么差别.也是分为静态加载和动态加载.静态加载就是启动时候加载.设备管理器找到HKLM\Drivers\BuiltIn下BusEnum.dll加载, 这是一个总线枚举驱动, 依照ORDER值指示的加载顺序,它来完成后续的加载工作, 也是使用ActiveDevice来加载. 至于动态加载,就是在系统起来后, 使用ActiveDevice加载一个驱动.加载后,也是在注册表的Active下面显示设备信息.
驱动在注册表需要提供的信息基本是一样的, 也是Prefix, Dll, Index, Order, Iclass这些基本的都一致,关键区别在Flags, UserProcGroup这2个键.
注册表里每个驱动可以包含一个键FLAGS, 这个配置决定了驱动的加载.下面是WinCE5.0的FLAGS的可选配置,(可以多项相与得到复合值)说明如下:
DEVFLAGS_NONE
注册表没有指定FLAGS
DEVFLAGS_UNLOAD
指示设备管理器执行完Init后卸掉驱动,并且返回成功.总线枚举驱动都这么干.
DEVFLAGS_LOADLIBRARY
通知设备管理器使用LoadLibrary代替LoadDriver.2者的区别:LoadLibrary加载的可以paged out.
DEVFLAGS_NOLOAD
指示设备管理器,驱动将不会被加载.
DEVFLAGS_NAKEDENTRIES
指示设备管理器前缀不要用.可以用前缀来active,但找函数入口点时候不要用前缀. 比如电池驱动指定这个标记后,设备管理器会用BAT这个前缀去实现驱动,但在调用接口时候不会默认的用BAT_Init,.BAT_***,而是自己去找入口点. 这样的目的是可以自由修改驱动接口函数名,可以不要和前缀相同了.
DEVFLAGS_BOOTPHASE_1
要求加载驱动时候,必须BootPhase大于1. BootPhase就是启动阶段的意思. 设备管理器启动是分阶段的.BootPhase1在找注册表.;BootPhase2 加载驱动;BootPhase3开始运行.(题外话,也可以只分2个阶段.)
DEVFLAGS_IRQ_EXCLUSIVE
在访问IRQ时候再加载.
WinCE6.0在此的基础上增加了几个
DEVFLAGS_LOAD_AS_USERPROC
这个是重头戏, 指示设备管理器,把驱动给加载到user mode. 设备管理器会创建一个Reflector.这个就是WinCE6.0主要的改进了.现在我也不懂, 后面再说说这个.
DEVFLAGS_NOUNLOAD
阻止驱动被卸载.
DEVFLAGS_TRUSTEDCALLRERONLY
指示设备管理器限制驱动只能被信任的应用程序open. 在WinCE5.0的文档里面也说有这个,但代码中没有发现,所以5.0应该是没有实现.(时空错乱?还是文档设计先行?还是ms藏私货了,反正我的版本没有.)
WinCE6.0的驱动模型难道没有变化?对比WinCE5.0,所有驱动都是被Device.exe或者GWES.exe加载. 在WinCE6.0, 这二者不存在了,变成了dll被nk.exe加载,自然的想法是, 所有驱动被加载到了nk.exe的进程空间? 仅此而已?
不管怎样,先启动模拟器观察证实一下, WinCE6.0的设备只有nk.exe, shell.exe, servicesd.exe, explorer.exe, 4个udevice.exe .嗯哼~, 不止device.exe没有了, 包括filesys.exe, gwes.exe……全体阵亡了,全变成nk.exe加载的dll. udevice.exe出现4个! 直觉这是驱动. 这些现象反映到驱动模型到底有什么变化?
前面的驱动加载时候提到注册表的Flags差异和新增UserProcGroup.Flags增加了一个DEVFLAGS_LOAD_AS_USERPROC,这个选项指示设备管理器将驱动加载成为user mode driver.再看看下面在devcore.c中StartDeviceManager()的伪代码:
StartDeviceManager()
{
初始化设备数据结构, 启动io资源管理,pnp设备管理,电源管理
DevloadInit();// 这儿加载设备驱动.
InitUserProMgr();//CE6新增,这儿初始化 user mode driver handling
While(1)
{
ProcessAutoDeregisterDevs();
ProcessDyingDevs();
ProcessDyingOpens();
}
}
嗯哼~ 看来还支持user mode 的驱动.既然叫user mode, 那肯定就不是加载在内核空间,不是在nk.exe空间了. 找到下面这张图, 简单明了.
设备管理器会依据DEVFLAGS_LOAD_AS_USERPROC的指示创建一个新的进程加载一个驱动, 这个进程就是udevice.exe了, 难怪模拟器里面有4个udevice.exe.在看左上边那个udevice.exe, 里面包含一组user-mode driver dll. 嗯哼~ 一个进程的确是可以加载多个dll的. 但哪些user mode驱动会被加载到一个udevice.exe? 呵呵, 一下就能猜到UserProcGroup这个键的作用了:给用户模式驱动分组啊, 相同组的加载到一个进程. 嗯, 顺便想起手册里面user mode driver host是什么了,用户模式驱动的寄主啊即udevice.exe.
具体怎么分组?UserProcGroup要等于什么值? 看看注册表:
[HKLM\Drivers\ProcGroup_0002]
ProcName=”servicesd.exe”
ProcVolPrefix=”$services”
ProcTimeout=20000
[HKLM\Driver\ProcGroup_0003]
ProcName=”udevice.exe”
ProcVolPrefix=”$udevice”
嗯哈~ 逻辑很简单, 在这建立一个组, 比如0003, 如果要加入这个组, 设置UserProcGroup等于这个3即可.0003只是表示代号,不是表示3个驱动哦.ProcName代表加载驱动的host进程名字. 原来udevice.exe和servicesd.exe进程是这么来的.
赶紧总结一下. 如果没有Flags指定DEVFLAGS_LOAD_AS_USERPROC, 那么驱动将被加载到nk.exe,即kernel mode driver;如果指定了,驱动将被加载到一个用户进程udevice.exe,成为user mode driver. 一个udevice.exe可以加载多个user mode driver或者只加载一个.