允许拥有比有效RAM多的数据和代码内容,是CE的一个特性。RAM中按页分配方式,给固定的或者ROM中的资源使用。当系统有效RAM太少时候,可以释放页面。系统中不能直接在ROM上执行代码的地方,是替代RAM使用的唯一存储地方,例如Nandflash。
系统在RAM中的部分代码和数据,是被读的或者被lock住。这表示当它们被加载后,就不能再分配出去。这些代码和数据必须是有效的,当它们的存储介质被释放后,就不再有效了。例如,最好的节电方式是进入低电模式或者深度休眠模式,最好也把nandflash的供电切断。
应用程序是典型的可重分配页面,由于操作系统进入低电模式前,会把应用程序线程都停止。这时,应用程序代码不会再被执行,数据也不会再被访问到。那么这些代码和数据所在页面,就可以按需重新被分配出去。设备驱动就不大一样,大部分CE的驱动是被设备管理器,通过无分页的方式加载起来的。这就是说不管驱动体积多大,当驱动加载起来就会占满它需要的体积,这部分内容是不可再分配出去的。用户态的驱动,是通过udevice.exe加载起来的,而不是设备管理器。但它也是按照同样的方式,确定页面是否可重分配。
随着驱动的功能和复杂度增加,体积也不断的增大。一个支持多种格式和功能的camera驱动,体积是非常大的。然而,如果camera驱动不再使用了,它所占据的RAM也不会被释放掉。看来,驱动可以重分配页面是有必要的。
要让驱动支持页面重分配功能,有以下步骤
1、 告诉设备管理器,驱动需要页面重分配功能
2、 告诉内核,页面重分配功能被打开
3、 标记那些不能页面重分配的代码
最后一步比前2步更复杂,即使你有一个很大的驱动,你也不知道那些应该不可重分配。最重要的是,当文件系统没有运行时,驱动的页面是不能被重分配出去的。
在2个情况下,决定驱动有无重分配功能。
1、驱动注册表设置时候,注册表有个键值Flags,其中DEVFLAGS_LOADLIBRARY被设置后,驱动才有此功能。如下
[HKEY_LOCAL_MACHINE/Drivers/BuiltIn/GPIO]
"Dll"="gpio.dll"
"Flags"=dword:10002 ;Trusted caller only & pageable
...
2、在决定生成二进制镜像文件的bib文件中,没有加入M的标记,驱动才能具有重分配功能。M标记是告诉内核,不要给驱动分配页面,把驱动全部加载到RAM中。
下面是一个驱动的bib例子
msm7x00_gpio.dll $(_FLATRELEASEDIR)/gpio.dll NK SH
留意这段句子最后的标记中,没有M标记。如果用户希望不使用重分配功能,那么标记应该是MSH。
使用注册表或者bib文件,都可以达到目的。但注意的是,注册表内容可能会被用户修改,那么驱动加载是否使用分页功能,是不确定的。而bib文件确定后,镜像生成时候已经决定了,是不能再被改变。注册表提供了一个动态改变驱动模式的途径,而bib则提供一个固定的途径。
然后,有许多步骤找出和隔离出不能重分配的代码。如上所述,这部分代码运行在单线程模式下,文件系统不能释放出代码和数据所占据的页面。典型的例子,如下
- XXX_PowerUp
- XXX_PowerDown
- Interrupt Service Threads和Interrupt Service Routines (ISTs、ISRs) ,当文件系统无效时候,它们可能正在执行中。
- 这部分函数用到的只读的定值
- 这部分函数调用的子函数
- 和文件系统的路径相关操作的代码
当找到这部分代码后,可以用链接器提供的#pragma描述包含起来。如下
pragma comment(linker, "/section:.no_page,ER!P")
#pragma code_seg(push, ".no_page")
XXX_PowerDown()
{
//Perform single-threaded power off logic
}
XXX_PowerUp()
{
//Perform single-threaded power on logic
}
UtilityFuncOne()
{
// Non-Paged utility function that can be called by
// both page and non-paged code
}
#pragma code_seg(pop)
UtilityFuncTwo()
{
// Paged utility function that can only be called by other
// paged code.
}
实例代码说明了,如何把powerup和powerdown函数,标记成可重分配的。这样就允许操作系统,在文件系统没有运作时,在RAM中执行这些代码。UtilityFuncOne函数也是不可重分配的函数,所以把它放到描述体中。UtilityFuncTwo在描述体外面,所以它能被重分配出去。同时如果处理器进入低电模式,有不再生效的风险。
为了测试重分配的功能,注册表键值PageOutAllModules可以让内核把所有代码页面释放出去。可以用来检查,文件系统不生效时,powerup和powerdown的函数。通过页面错误,来检查驱动是否有问题。键值设置如下
[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Power]
"PageOutAllModules"=dword:1
设置好键值后,让系统强行进入节电模式,系统就会把所有标记为可重分配的代码页面,释放出去。如果驱动不正确的使用了重分配的属性,那么会产生缺页错误,设备会死机。这个技术,可以在发布版本前,检查驱动的重分配功能是否正常。
驱动设置重分配的属性后,能够减少系统不再使用的资源占用。驱动释放了大量页面,还要保持运行时候,以上内容必须留意。
http://blogs.msdn.com/ce_base/archive/2008/02/28/making-sections-of-windows-ce-device-driver-code-non-pageable.aspx