wince槛外人窥探--存储布局

来源:互联网 发布:linux改权限命令 编辑:程序博客网 时间:2024/05/02 03:02

    首先我想声明的是我没有开发过wince应用系统,没有任何实践经验,所以说我是槛外人。下面是我在wince存储布局方面的一些学习心得,由于一些结论我无法证实,所以我将他们归结为猜想,仅供参考,写出来希望众高手指正。

Bootloader

    wince使用的bootloader最常见的当属eboot,这是微软提供的bootloader,其功能强大,支持分区,binfs等功能。但是正因为其功能强大,其处理流程也比较复杂,有一些行为让初学者很难理解。与eboot相对而言,我见过比较简单而且使用又比较广的bootloader有“优龙”的bootloader,但它不支持文件系统分区和binfs。

    现在很多应用方案,为了节约成本,都是直接从nandflash启动的。这就需要一个大小小于4K的bootloader做一级引导程序,一般使用nboot,再由nboot加载eboot,eboot再加载内核。

nk.bin和nk.nb0

    wince在build之后会生成 nk.bin和nk.nb0,他们的内容相同,包含的都是wince系统的程序和数据。nk.nb0是一个特殊的可执行镜像,只要将其拷贝到系统内存中的指定位置,然后跳转到指定地址便可启动wince系统。之所以说他是一个特殊的映象,是因为它还有文件系统镜像的特性,这一点在后面将会详细描述。

    再说nk.bin,nk.bin是一个记录型文件,他将nk.nb0中的数据分段保存,每条记录包括一段数据和一些附加信息,这些信息有下载到内存的地址,数据长度和校验和。当然nk.nb0中也可能包含一些特殊的记录,这些记录保存和镜像相关的其他一些信息如:镜像起始地址、镜像长度、镜像入口地址。我们可以把nk.bin看成一个PC机和bootloader间的一个协议,PC机将wince镜像以nk.bin的形式发送到bootloader,并在nk.bin中记录了booloader应该怎么处理,应该怎么把它解析成一个nk.nb0镜像,应该跳到哪里开始执行。其实bootloader本身也会生成一个bin文件和nb0文件,可用于升级自身,但第一份bootloader肯定是用其他方式如jtag等工具下进系统的。

    选择下载nk.bin而不是直接下载nk.nb0的另一目的,我认为是为了节约通信带宽,提高传输效率。

    可能是受名字的误导吧,网上很多人将nk.bin和binfs联系起来,我可以肯定是没有道理的,nk.bin在下载到内存后就已经不复存在了,也就是说在bootloader将wince保存到flash时,根本无法找到和nk.bin相关的任何信息。

binfs

    前面说了nk.nb0是一个特殊的镜像,它是一个可以在内存中直接运行的可执行镜像,特殊的地方就在于它也可以算是一个文件系统的镜像。当我们在flash上分区并创建号一个binfs,然后发送命令给eboot将系统保存到binfs后,eboot其实就是将内存中nk.nb0的镜像原模原样地复制到flash上。所以与其说binfs和nk.bin存在联系,不如说binfs和nk.nb0的联系更紧密。

    我们甚至可以将binfs挂载到wince系统,然后通过路径去访问它,那说明nk.nb0这个镜像中肯定包含了一些结构,这些结构组织着里面的文件,所以我们才可以通过这些结构查找和访问这些文件。romimage在将编译出来的各个文件保存到nk.nb0时,也会将这些文件的管理信息写入到一个头里面,将这些头存放在一起构成一个结构体,这个结构体就是ROMHDR结构,严格来说,这些文件是一个列表的形式保存在ROMHDR结构的最后。binfs的管理信息就是一个线性表,我不敢肯定,但我猜测binfs被挂在到系统后只有一级目录,至少目前我见到的是这样子的。

    那么,ROMHDR到底在nk.nb0中的哪个位置呢?当然后一个固定的偏移,而且还有一个标识“CECE”,表示后面紧接着就是ROMHDR结构体,这个标识一般出现nk.bn0文件头开始偏移64字节的位置。为什么会在偏移为64字节的位置,而不是在文件最开头,我认为微软是有考虑的,他们使用最开始的空间保存一些更重要的信息,比如可以在最开始存放一条跳转指令,跳转到nk.exe入口处,这样镜像的开始就变成镜像的入口。

    其实ROMHDR结构体最初位置不是在偏移为64字节的位置,而是保存在ROMIMAGE动态计算出的一个位置上,只有在使用eboot为bootloader时,eboot会拷贝ROMIMAGE到偏移为64字节的位置。至于微软为什么要绕这么个圈子,我想大概是想给予eboot修改ROMHDR的机会,因为有的系统的bootloader会把内存重新映射到另一个地址,这就使得内核下载时的内存地址和运行时的内存地址不同。

    wince镜像的运行其实也是依赖于ROMHDR结构的,比如当nk.exe需要加载某个dll时,他就是通过查找romhdr后的文件列表实现的。这也是为什么nk.nb0可以作为一个镜像执行的原因,这个镜像中包含多个分开编译的文件,而他们之所以能协调工作,是因为这个镜像中已经包含了各个文件间的组织信息以及其访问方法。像优龙的bootloader就只使用了wince的镜像特性,而不支持binfs分区和挂载。

fat文件系统

     binfs是一个只读文件系统,对binfs中的任何一个改动都会导致需要重新下载整个文件系统。而fat文件系统不同,他是可写的,一般的应用中,跟在binfs后会有一个fat32文件系统,用于保存一些需要在运行时改动的文件。一般情况先fat文件系统中的文件不是在系统下载过程中做成一个镜像下载,而是在系统启动后使用其他方式下载,比如作为一个U盘挂入系统,或者通过网络拷贝。我很迷惑的是微软为什么不做一个fat文件系统的镜像制作工具,这样子对wince应用系统的量产速度要提高很多。

    fat文件系统的格式和PC机上的差不多,这里重点说一下他的读写机制,因为是在flash上,必须要考虑flash的磨损平衡,一般使用wince提供的fal来实现磨损平衡。下面将重点讲述fal的实现。

fal

    fal:flash抽象层的简称,他与fmd一起实现wince对flash的访问。fmd是flash的设备驱动,实现对flash硬件的直接访问,它向fal提供统一的接口,使得fal不需要关心硬件的具体型号。fal与硬件无关,他的主要功能是向上层提供一个实现了磨损平衡后的访问接口。

    首先,在系统启动时,fal会扫描芯片各个扇区的oob区,根据oob区中的逻辑扇区号建立一张逻辑扇区到物理扇区的映射表,以后的访问都会经过这一级映射表。此外还会有一张空闲扇区表和一张脏块表,空闲扇区表用于空间分配,脏块表用于垃圾回收。fal的实现机制是每次写数据时不会写到原有块中,而是会被写到一个空块中,同时将原有块标记为脏块,这样保证flash上各个块擦写次数的基本相同。当空块不足时,垃圾回收机制会被调用,它将脏块中的有效数据页转移合并,再将脏块擦除,这样完成空间的循环利用。熟悉yaffs文件系统的人可能会发现,fal的实现和yaffs文件系统很相似,我个人认为yaffs做得跟好些。

    fal会在启动时全片扫描flash的oob区,可想而知,这是一个漫长的过程,很多系统都遇到这一问题。我认为有几种改善办法:1.修改fal源码和fmd驱动,在flash的某一位置保存一张映射表,每次启动时只需读出这一映射表。fal的源码在wince5下没有开放,在wince6下已经开放了。2.虽然还没有读fal的源码,但是我认为fal对flash中的保留块,只读块,空块,和可写块的处理策略是不同的。fal会跳过系统前面的引导区保留块,前提这些块必须被标记为坏块,要不然在系统运行的过程中你会发现bootloader不见了。而对于只读块,因为它是只读的,它的内容在下载后就不会发生改变,所以fal其实只需要读出第一个页的oob,就可以计算出后面的页的逻辑块好,因为他们是连续的,当然这只是我的猜测,还没有明显的证据证明。对于空块,fal自然就不用进行什么处理,直接块中的所有页列入空闲页列表。真正要整块扫描的只有可写块,所以一个系统中如果可写块越少(fat文件系统越小),他的启动速度就越快。所以我建议,将大部分的程序代码及只读数据存入只读的binfs中,而将需要修改数据存放在后面的较小的fat文件系统中。3.如果是wince6的话,抛弃fal+fmd方案,采用pdd+mdd方案,我对这一方案的实现方式也不是很了解,但是可以肯定的是他是采用在将映射表保存到flash中的方式实现的。

    fal的另一不足是当采用2k页flash时不能直接支持fat文件系统,因为fat文件系统只支持512byte的扇区规格,不过网上有人说wince5.0的fal已经支持了2k页flash。其实我们可以在fmd中实现对2k页flash的支持,在这一层将2k页划分为4个512byte页,不管是在fmd中实现还是在fal中实现,这个划分是必须的。

    fal的另一个限制是不支持MLC类型的flash,因为fal需要单独寻址和修改oob区,而MLC类型的flash不支持这种操作。pdd+mdd方案没有这一限制。

multi-xip

    multi-xip是将wince镜像中的文件打包到多个包中,启动时只加载必须的文件,其他文件等到需要时再加载。这样不但可以节约内存空间,还可以加快启动速度。

    使用multi-xip的系统,build后会生成多个bin文件,xipkernel.bin保存启动时加载的内核必须文件,nk.bin保存其他文件,还包括一个chain.bin,这个bin文件很关键,他是连接各个包之间的纽带。最后下载的是由多个包组合而成的一个XIP.bin文件,xip.bin中会记录每个包应该下载到哪个位置。下载时eboot系统会通过查看改bin文件的romhdr结构体中有没有包含nk.exe来判断这个bin文件是不是xipkernel.bin,同样以差不多的方式判断出chain.bin,记录下这两个文件解压后镜像在flash中的位置。当系统启动时,会先加载xipkernel.nb0,然后加载chain.nb0。启动后,原来nk.nb0镜像的文件查找方式需要做一些扩展,比如,当kn.exe需要加载dll库时,系统会先在xipkernel.nb0的romhdr结构体中查找,如果找不到,则会通过chain.nb0查找。找到文件后,如发现该文件存放在flash中,则会将它从flash中读取出来。

总结

    纵观wince系统的存储布局,我发现在这方面wince的可选择性和复杂度都比linux大。linux系统一般都是bootloader+kernel+rootfs结构,而对于wince系统。首先要看你使用什么bootloader,如果使用想优龙那样的bootloader,则可以把系统分成很多个镜像,然后按flash地址分段存储和寻址。如果使用eboot,又有几种选择,可以将应用程序和系统程序全部放在nk.nb0中,这时就不再需要fatfs;如果使用fatfs,可以将应用程序存放在fatfs中,这样更便于升级;此外,还可以使用multi-xip机制,将应用程序和系统程序都放在binfs中,但不在同一时间加载。另,wince系统是否存在TOC块也是可选的,完全可以把TOC固化在代码里。所以说wince的存储方案比linux更加多样化。

   

原创粉丝点击