Chinaunix首页 | 论坛 | 博客
  • 博客访问: 28496
  • 博文数量: 14
  • 博客积分: 772
  • 博客等级: 军士长
  • 技术积分: 125
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-07 17:27
文章分类

全部博文(14)

文章存档

2010年(7)

2009年(6)

2008年(1)

我的朋友

分类:

2009-04-16 18:06:11

     uClinux是近几年兴起的一个嵌入式Linux的变种。它主要用于微控制领域的一些没有MMU(Memory  Management Unit)的系列CPU。近来在消费类电子产品中也得到了广泛的应用。
     目前uClinux可以稳定的运行在ARM,MIPS,Powrepc等系列CPU上。因为没有MMU单元的CPU相较而言更为便宜,因此国内也有大批的爱好者自己制作开发板来进行uClinux的开发。作为此类的嵌入式系统一般采用Flash作为存储设备。本文主要讨论在uClinux系统中启动,运行时使用Flash的理论和实践方法。
     本文首先要介绍一下常用的Flash以及uClinux系统在Flash中如何进行存储;接着会阐述一下Flash在内核中被如何驱动,同时介绍一下常用根文件系统的选择。
     本文在理论上分析,阐述了怎样使用,选择根文件系统以及它们在Flash上的实现。并且给出了一个例子。但是真正的嵌入式系统中如何使用Flash将和使用的硬件相关。欢迎大家和我讨论这个问题。                  
     1,Flash的简介
     在过去的20年里,嵌入式系统一直使用ROM(EPROM) 作为它们的存储设备。然而近年来Flash 全面代替了ROM(EPROM)在嵌入式系统中的地位。因为相较ROM而言,Flash有成本低,可写,容易改写等优点。
目前Flash主要有两种 NOR Flash 和 NADN Flash 它们在应用上有所不同因此也用于不同的场合。
     读取NOR Flash和读取我们常见的SDRAM是一样的。它的所有地址都是可见的,你可以读取它任意随机地址的值。同时它和SDRAM一样你可以直接运 行装载在NOR FLASH里面的代码,这就是作谓的XIP(Execute-In-Place)技术。因为NOR Flash有这种特性,所以它非常适用于小型嵌入式系统。你可以把你的代码装载到Flash中,在系统启动的时候直接运行它,而减少SRAM的容量从而节约了成本。
     从这种意义上来说,NOR FLASH已经可以代替原先我们一直使用的标准的ROM。并且还具有ROM所没有的特性。目前市面上的FLASH 主要来自Intel,AMD,Fujitsu,和Toshiba。常用的容量一般在128K到64M之间。
     NAND Flash 没有采取内存的随机读取技术。它的读取是以一次读取一快的形式来进行的,通常是一次读取512个字节。采用这种技术的Flash比较廉价。但是和所有块设备一样,NAND Flash 比较容易出现怀位。这需要我们采用软件来避免使用这些位。这样以来就增加了软件的复杂度。你不能直接运行NAND Flash上的代码。因此好多使用NAND Flash的开发板除了使用NAND Flah以外,还作上了一块小的NOR Flash来运行启动代码。这样作会增加系统的复杂度。不过最近这种现象有所改观。三星最近生产的一批采用ARM Core的CPU,采用了一个内部的缓冲来存放NAND Flash里读取的东西。以此来直接运行NAND FLASH里面启动代码。比如基于Arm920T和新的S3c2410芯片。
    另外,我们最常见的NAND FLASH的应用是嵌入式系统采用的DOC(Disk On Chip)和我们通常用的“闪盘”。
    目前生产NAND Flash的主要厂家有Samsung 和Toshiba。最大容量已经突破了1G位。写Flash和写SRAM截然不同。它是通过一系列指令才能完成一个写操作的。而我们同用的RAM直接写入 即可。无论是NOR Flash 还是NAND Flash都有一个“扇区”的概念。这个“扇区”从8K到256K不等。在写操 作中它将作为一个整体来操作。 要向某个地址里面写如一个值得先看一下这个地址原先的值是不是全为“1“。如果全为“1”,那么通过一系列指令可以将这个 值写如。反之,则先要进行擦除使其全部变为“1”。擦除操作是不能用一个地址来操作的。擦除必须一次擦除一个“扇区“。把这个“扇区”所有的值都变为 “1”,然后才能进行写操作。
   不同型号的Flash的操作指令不同。具体操作的时候需要自习阅读你所使用产品的产品说明书。关于Flash的话题还有很多,但是本文着重谈论Flash在uClinux系统中的应用。对Flash本身感兴趣的读者可以自行阅读其他相关资.

  2,基于Flash的uClinux系统
     传统意义上嵌入式系统的存储系统通常由Flash和RAM组成。Flash的成本通常又高于RAM的成本。因此系统的存储系统由多大容量的RAM多大容量的FLASH组成,通常根据产品的需求和成本的考虑来决定。
     在基于uClinux的嵌入式系统中,Flash是整个系统代码和数据的储存器件。通常的做法是将uClinux核心的起始代码放在处理器加电所运行的地址 处。这个地址必定是在FLASH中的。因为NOR Flash里面的代码可以直接执行。在很多的应用中采用了不使用RAM,直接让uClinux在 Flash里面运行。而出于速度和成本的考虑更多的做法是将uClinux系统存放在Flash中根据系统运行的情况把要执行的部分拷贝到RAM中执行。
    为了实现上述思想我们通常把Flash分区。当然这里分区的概念和我们给硬盘分区的含义大不一样。这里的分区十分简单,就是把Flash按照地址分成不同的区间在特定的区间中放入不同的代码或者数据。使得整个系统比较有序和容易调试开发。
    一个典型的例子,比如:

           块号 地址 用途
           0 0x0—0x? 启动代码
           1 
           2 0x….. uClinux 核心
           3 
           4 0x… 根文件系统
           5  
     在这个例子中第0个分区,也就是Flash的起始位置我们放置了系统启动代码。第1个分区我们可以放置一些配置数据之类的数据。接下来的一个分区我们存放着uClinux的核心。并且我们用另外的一些分区来放置系统的根文件系统。
     以此可见,我们完全可以根据自己的需要把Flash分成不同的区来使用。在以后的讨论中我们可以直到uClinux的块设备驱动支持我们这样的操作。因此我们的焦点将集中在如何划分不同的区域来达到我们的应用。
     通过上面的介绍我们知道Flash的擦写需要根据Flash的扇区来进行。一次擦写至少要擦除一个扇区内所有的内容。因此我们给Flash分区的时候最先要确定的是每个分区需要多少个Flash的扇区。从第几个到第几个,这样以后的操作才可*简单。
     从上面的例子来看,uClinux的内核和根文件系统并不在一个分区。内核放在一个特定的地址。要访问内核必须从某个地址开始整个的访问。而不同于我们同用的桌面版或者服务器版的Linux可以把内核作为一个文件来存放在文件系统中。
     那么这两种方法有什么不同呢?
     在我们的例子,把内核单独放在一个分区中。这需要启动的时候启动程序把整个内核拷贝到RAM里面然后运行它。或者采用我们前面提到的XIP技术在Flash里面运行。而传统的Linux则需要启动程序来确定内核的位置和需要把内核的那部分加载到RAM里面来运行(类似于台式机Linux系统上的LILO或者GRIUB)。
     那么如何选择Flash的分区,以及各个分区存放的内容呢?我想这个要根据各自产品的特点和开发的周期来考虑。接下来我们列出在uClinux系统中常用的几种分区方法并且讨论它们各自的优劣。
                  a) 内核和根文件系统都在固定的分区固定的地址
                  b) 内核在根文件系统之后或者之前
                  c) 压缩的内核作为根文件系统下的一个文件
     a的优势在与系统主要的组成部分都有各自固定的地址。启动程序可以直到内核所在的地址,而内核直到根文件系统的地址。这样启动程序加载内核或者内核挂装根文 件系统的时候所进行的操作比较简单。并且我们可以很方便的升级这些组成部分。缺点就是将不可避免的造成内核和根文件系统之间Flash的浪费。
     b的做法节约了一部分Flash的空间。但是它把内核同根文件系统一快编译成了一个二进制文件。这样你必须同时升级内核和根文件系统。不过这样作的好处是编译选项比较简单。容易维护。初次开发建议使用这种方法。
     c的做法由于使用了压缩的核心所有节约了大量的空间。但是必须需要一个启动程序来把内核解压缩到RAM中。这样就需要一个比较充裕的RAM空间。启动程序增 加了复杂度。不过一旦你写好这个驱动程序,你就可以不再修改它而将所有精力放在uClinux核心和根文件系统的开发上。
     当然,根据产品需要我们不排除使用多个文件系统的选择。原因很简单,比如你需要对你的某个分区进行读/写操作而对其他一个区只要进行只读操作。由于Flash的读/写特性您就要作比较复杂的设计。对于这种情况,下文有所提及。

     3,uClinux系统的bootloader
      作为系统的启动程序,最先要考虑的是CPU在加电的 时候运行那个地址的代码。有些CPU比如X86,ARM在加电的时候运行固定地址的代码;也有些CPU比如m68k, ColdFire,在加电的时候读 取一个固定的地址,然后用这个地址的值作为最先执行代码的的地址。在现有的系统中这个地址是在Falsh里面的。
      那么我们要考虑的就是在这个地址里面放入我们的代码,以便CPU加电后就执行这行代码。我 们允许CPU加电后直接运行uClinux的内核代码。这时候uClinux的代码需要作一系列的事情。比如初始化硬件,比如初始化RAM;把uClinux内核中的数据段拷贝到RAM中去;清空BSS段等。不过最重要的还是将它的首行代码放到合适的地方。
    使用BootLoader我们就可以做根多的事情。比如我们可以初始化硬件,比如RAM和系统的I/O设备等。同时它还可以装载写在Flash上的不同内核,或者通过外部设备传输过一个内核并且装载运行它。
    在现阶段的开发板上,很多都采用了这种方式通过串口或者网口加载Pc机上编译好的uClinux内核或者根文件系统。
    除此以外,一个好的bootloader还能够保证内核影像的正确执行,防止新传输来的内核影像不完整等等。一般情况下bootloader都是固定的烧 写在Flash上面并且一般采用锁定的方法防止被擦除。目前有很多成数的bootloader可以在互联网上自由下载。它们支持各种开发板上的uClinux的开发。比较著名的有CoLilo, My Right Boot (MRB), PPCboot and Motorola dBUG。它们功能强大并且可以很方面的移植到你自己的开发板上。

      4,uClinux内核的块设备驱动
        对于uClinux 的根文件系统,目前有三种块设备的驱动可以选择它们分别是:
                  a) Blkmem 驱动
                  b) MTD 驱动
                  c)  RAM disk 驱动 
       Blkmem 驱动是专门为uClinux开发的一种块设备驱动。是uClinux系统中最为古老和通用块设备驱动。它原理相对简单但是配置比较复杂,需要根据你即的Flash的分区使用情况来修 改代码。当然修改的结果是它可以对一些NOR型的Flash进行读写操作。不过目前支持的Flash类型不够多。如果新加入对一种Flash的支持需要作的工作量比较大。
      Linux的MTD驱动是标准Linux的Flash驱动。它支持大量的设备,有足够的功能来定义Flash的分区,进行地址映射等等。使用MTD你可以 在一个系统中使用不同类型的Flash。它可以将不同的Flash组合成一个线性的地址让你来使用。在标准的Linux 2.4内核中MTD有一系列的选相,你可以根据个人系统的需要来选择定制。
     另外一种选择就是RAM disk  驱动。在PC上它经常用于没有硬盘的Linux的启动过程。它和Flash没有直接的关系。不过当Flash上启动的是经过压缩的内核时。 RAM disk 可以作为根文件系统。MTD 驱动提供了对Flash强大的支持,你通过它甚至可以在Flash上运行一个可以读写的真正的文件系统,比如JFFS2。而 Blkmem驱动则望尘莫及。

   5,uClinux的文件系统 
     在uClinux下根文件系统有集中选择。ROMfs是最常用的一种。它的特点是紧凑,只读。它把所有的文件按照一个文件的次序组合成一定的次序。并且它可以让它的应用程序直接在FLAH里面运行(XIP)。这样以来就减少了运行时对RAM尺寸的要求。在目前基于AMR和ColdFire等CPU的 uClinux开发中绝大多数使用这种文件系统作为根文件系统。
     Cramfs是在Linux内核2.4版本以后出现的新的文件系统,它的特点是把只读的文件系统进行压缩。从而可以在Flash上存储更多的应用程序。不过因为压缩它不能本地执行应用程序,而必须解压到RAM中运行。要求比较多的RAM。
     在一些系统中需要可以读写的根文件系统。在uClinux系统中利用MTD驱动,我们可以实现一些基于Flash的日志文件系统比如JFFS 或 者 JFFS2。这个文件系统的优点在于它可以避免突然断电对系统存储的影响(在标准Linux中ETX2文件系统掉电会造成数据丢失)。同时因为它们是 和为Flash设计它可以保证在Flash中实现读写。如果你使用RAM disk 那么ETX2 就成为首选的文件系统。因为它是标准Linux的文件系统。所以很多操作非常方面。不过缺点是ETX2文件系统不是为嵌入式操作系统所作, 所以并没有考虑存储空间的问题。由于RAM disk的特性你在该文件系统上所作的改动下次启动后将不会再有。
    当然Linux支持很多文件系统,你可以根据你的喜好随意选择。不过以上说说的是uClinux系统中最常用的。
    那么我们怎么在Flash上建立一个根文件系统呢? 
    通常的做法是先在开发机上(通常是PC)作好这个文件系统的镜像。然后通过烧写Flash的工具直接烧写到Flash中去。也可以通过阅读一些文件系统自带的工具来进行构建。这里不一一赘述。

   6,uClinux 的Flash工具
     在uClinux的源码包中带有一些对Flash操作的应用程序。当你采用MTD的时候这些工具变得非常有用,它们分别是:
                     erase      -- 擦除Flash的某些扇区
                     eraseall   -- 擦除整个Flash
                     lock       -- 锁住Flash(写不进去)
                     unlock     -- 解锁
                     mkfs.jffs  -- 建立一个目录结构的JFFS2文件系统
                     mkfs.jffs2 – 建立一个目录结构的JFFS2文件系统的镜像

      除此之外,还有一些更为复杂的程序是和其他应用相关的。比如和JTAG相关,Net相关等等。可以根据这个工具的说明来加以应用。

     7,应用实例
        现在我们看一个实例。首先说一下我们的硬件平台。这个平台是基于ARM940T核心的CPU+2M的AMD Flash + 4M  SDRAM。在这个平台上我们运行uClinux 2.4 的内核,采用MTD驱动。因为我们不需要对Flash进行读写操作,所以选择了ROMfs。这样使得开发和维护想对比较简单。AMD的Flash,我所选用的是可启动型的。因为ARM的CPU在加电的时候从零地址开始运行。因此Flash的地址分配为从0X0到0X1FFFFF。根据这个Flash的扇区大小,我们把整个Flash的分区如下:
                  2M Flash  ---MTD 6

                  启动代码 (16K)MTD1
                  启动所需参数 (8K)MTD2
                  设备配置信息(8K)MTD3
                  备用空间(32K)MTD4
                  系统运行参数表(64K)MTD5
                  内核和根文件系统(1984K)MTD6

      在内核的MTD驱动的选项中,我们根据所需要的分区信息对代码进行配置。运行的时候,MTD驱动会找到这个设备,并且按照以上我们的分区方案对Flash的地址进行分配。一些详细的信息和细节,在MTD驱动的代码部分有详细的描述。
     完成了这个分区,我们就可以利用我们手头的工具,把我们事先做好的系统启动程序和内核,根文件系统写入Flash。
      常用的方法有:
           1)建立整个Flash的影像,使用烧录工具烧到Flash中
           2)书写一个Bootloader,使用系统所带的外部接口将内核还有根文件系统传送到系统RAM中,然后写入Flash。常用的有通过串口和网口的。此类的程序有很多比如netflash等等,具体的用法请参照各命令的手册。
       运行完这些,uClinux系统已经在我们的开发板上安装好了。

系统设计

   Flash在每MB的存储开销上较RAM要昂贵,但对于uClinux系统来说,选择Flash作为存储器具有一定的优势。UClinux系统在上电后,需要运行的程序代码和数据都可以存储在Flash中,甚至放在CPU起始地址中的uClinux启动内核都可以写入Flash中。从一定意义上 讲,嵌入式系统只用Flash就可以完成所需的存储功能。
Flash存储器的分区较硬盘的分区更为简单,分区后的Flash使用起来更加方便。典型的Flash分区如下。
SEGMENT PURPOSE
0 Bootloader
1 factory configuration
2
┆ kernel
X
┆ root filesystem
Y
分区0放置Bootloader,分区1放置factory configuration,分区2到分区X放置系统内核,分区X到分区Y放置根文件系统。Flash的分区可以根据需要划分,uClinux中支持Flash存储器的块设备驱动负责定义上述的分区。和PC机下的Linux不同,Flash的分区把系统内核文件和根文件系统单独划分到两个分区中,而PC机的硬盘是把内核文件和根文件系统放在 一个分区内。PC机下Linux的Bootloader是LILO或GRUB。它们在系统启动时能智能地在分区中找到内核文件块,并把它加载到RAM中运行。对于Flash而言,把内核的镜像文件写进一个单独的分区对嵌入式系统有两大优点:①系统可以直接在Flash上运行;②LILO或GRUB更易找到内核代码并加载,甚至可以不用LILO或GRUB引导而直接运行。
     内核文件和根文件系统在Flash中的放置,可以根据系统设计需要适当选择。
3 引导程序选择
    系统启动之前的引导过程是CPU初始化的过程。包括ARM和X86在内的许多CPU是从固定地址单元开始运行引导程序(Bootloader) 的。其它的部分CPU是从某个地址单元读入引导程序的入口地址,然后再运行引导程序,譬如M68K和Coldfire系列。所以这些都影响到Flash中系统启动代码的存放地址。
   系统首先要考虑的是在什么地址存放Bootloader,或者说系统从哪个地址单元开始加载运行系统内核代码。CPU启动后直接运行系统内核是可以实现的。对于uClinux来说,启动代码必须包括芯片的初始化和RAM的初始化等硬件配置;同时加载内核 的代码段到RAM中,并清除初始化的数据段内容。尽管这些实现起来很直观,但是要具体把启动代码存放在Flash中正确的地址偏移单元内,使CPU一启动便能执行就比较困难了。不过,现在技术比较先进的CPU都将默认的偏移地址设置为0,或者在偏移地址为0的附近存放起始地址。
Bootloader是一段单独的代码,用以负责基本硬件的初始化过程,并且加载和运行uClinux的内核代码。作为系统启动工具,Bootloader经过配置可以加载Flash中的多个内核,甚至可以通过串口和网口来加载内核和系统的镜像到RAM中运行。Bootloader 同时也提供对内核镜像文件的多级别保护,这一点对于以Flash作为存储设备的系统来说非常重要。譬如,当系统进行内核升级和重要数据备份时,系统突然掉电,正如PC机进行BIOS刷写过程中的旧电一样,都是灾难性的。但是利用Bootloader就可以实现保护性的恢复。
目前运行在uClinux上的免费Bootloader有COLILO、MRB、PPCBOOT和DBUG。也有为特殊需求设计的SNAPGEAR和ARCTURUS NETWORKS。
4 uClinux的块驱动器
    对于嵌入式系统的块设备,可选择存储文件系统的块驱动器(Block Driver)主要有三种选择。
    ①Blkmem driver。Blkmem driver仍是uClinux上使用最普通的Flash驱动器。它是为uClinux而设计的,但是它的结构相对比较简单,并且仅支持NOR Flash的操作,需要在RAM中建立根文件系统。同时它也很难配置,需要代码修改表来建立Flash分区。尽管如此,它还是提供了最基本的分区擦/写操作。
    ②MTD driver。MTD driver是Linux下标准的Flash驱动器。它支持大多数Flash存储设备,兼有功能强大的分区定义和映像工具。借用交叉存取技术 (interleaving),MTD driver甚至可支持同一系统中不同类型的Flash,Linux内核中关于MTD driver配置有较为详细的选择项。
    ③RAM disk driver。在无盘启动的标准Linux中,用得最多的就是RAM disk driver;但它不支持底层的Flash存储器,仅对根文件系统的建立有意义,即根文件系统压缩以后存放在Flash的什么地方。
    通过上面的比较可以看到,MTD driver提供对Flash最有力的支持同,同时它也支持在Flash上直接运行文件系统,譬如JFFS和JFFS2,而B1kmem driver则不支持。
5 根文件系统
    uClinux中的文件系统可以有多种选择。通常情况下,ROMfs是使用最多的文件系统,它是一种简单、紧凑和只读的文件系统。ROMfs顺序存储文件数据,并可以在uClinux支持的存储设备上直接运行文件系统,这样可以在系统运行时节省许多RAM空间。
    Cramfs是针对Linux内核2.4之后的版本所设计的一种新型文件系统,也是压缩和只读格式的。它主要的优点是将文件数据以压缩形式存储,在需要运行的时候进行解压缩。由于它存储的文件形式是压缩的格式,所以文件系统不能直接在Flash上运行。虽然这样可以节约很多Flash存储空 间,但是文件系统运行需要将大量的数据拷贝进RAM中,消耗了RAM空间。
     考虑到多数系统需要能够读/写的文件系统,可以使用MTD driver的JFFS和JFFS2日志式文件在Flash头部建立根文件系统(Root Filesystem)。日志式文件系统可以免受系统突然掉电的危险,并且在下一次系统引导时不需要文件系统的检查。由于JFFS和JFFS2文件格式是特别为Flash存储器设计的,二者都具一种称为“损耗平衡”的特点,也就是说Flash的所有被擦写的单元都保持相同的擦写次数。利用这些特有保护措施,Flash的使用周期得到相当大的提升。JFFS2使用压缩的文件格式,为Flash节省了大量的存储空间,它更优于JFFS格式在系统中使用。值得注意的是,使用JFFS2格式可能带来少量的Flash空间的浪费,这主要是由于日志文件的过度开销和用于回收系统的无用存储单元,浪费的空间大小约是两个数据段。
    如果使用RAM disk,一般应选择EXT2文件格式,但EXT2并不是一块特别高效的文件存储空间。由于存在RAM disk上,所以任何改变在下一次启动后都会丢失。当然,也有许多人认为对嵌入式存储空间来讲,这是一种优势,因为每次系统启动都是从已知的文件系统状态开始的。
    虽然在Linux下有许多的文件格式可供选择,但是对于uClinux一般只选择上述的几种文件格式。另外一点就是如何在目标系统上建立根文件系统,步骤如下:首先在开发宿主机上建立一个目标机的根文件系统的目录树,然后利用嵌入式根文件系统生成工具在宿主机上生成目录树的二进制文件镜像,最后下载到目标机上就可以了。对于不同的文件格式有不同的二进制镜像生成工具,譬如JFFS的mkfs.jffs2、ISO9660的mkisofs。
阅读(547) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~