Chinaunix首页 | 论坛 | 博客
  • 博客访问: 10964
  • 博文数量: 5
  • 博客积分: 1410
  • 博客等级: 上尉
  • 技术积分: 70
  • 用 户 组: 普通用户
  • 注册时间: 2010-01-06 11:32
文章分类
文章存档

2010年(5)

我的朋友
最近访客

分类: LINUX

2010-04-07 14:19:38



摘   要
    在以计算机技术、通讯技术相结合的信息时代的快速发展和互联网的广泛应用的形势下,3C (Computer, Communication, Consumer)合一的趋势已经形成。其结果必然就是将计算机工业的中心从计算产品转移到信息产品,从而出现信息电器的概念。在信息电器的应用开发领域,由于需要的功能不断的增加,嵌入式系统也就将成为软件业的新宠儿。同时在计算机本身的领域里面,微型化和专业化成为了发展的新趋势,同样也需要嵌入式系统的支持。因此,研究与嵌入式系统有关的关键技术— 嵌入式操作系统有着相当重要的实际意义。
本文先介绍对比几种常用的Bootloader,而后分析u-boot的架构及其在AT91RM9200移植要点和起动流程。针对嵌入式操作系统,先对比几种常用的嵌入式操作系统,接着分析了linux的结构及其在AT91RM9200的移植要点和linux 起动过程。具体内容如下:

第一章 :嵌入式系统的概念
第二章 :Bootloader 的概念、作用,对比几种比较常见的bootloader,重点介绍u-boot
第三章 :嵌入式系统的机制和结构,对比几种嵌入式系统,重点介绍嵌入式linux系统
第四章 :基于AT91RM9200的u-boot移植要点,及起动流程
第五章 :基于AT91RM9200的嵌入式linux 操作系统的移植要点,及起动流程

关键词: 嵌入式操作系统 ;AT91RM9200;bootloader ;u-boot; linux;移植

Abstract

     With the development of Computer technology and Communication technologyin Information times and the board application of Internet, it is clear that 3C(Computer, Communication and Consumer) will converge in the near future whichwill lead the focus of Computer Industry from the Compute product to Informationproduct. It is the concept of Information Appliance. In the application and development field of Information Appliance, Embedded Operating System will be the most favorite thing in Software Development field. At the same time, in the field of computer science itself, the micromation and specialization is the new direction of the computer world. It also want the support of Embedded System. So, the research of the key technique of embedded system- Embedded Operating System is the most important thing。
This text  recommends and  compares with several kinds of commonly used Bootloader firstly, and then analyses the framework of u-boot and transplant in AT91RM9200 。this text analyes  procedure of starting bootloader also。To the embedded operating system, this text compares with several kinds of commonly used embedded operating systems firstly , then has analyzed the structure of linux and transplantation in AT91RM9200。As bootloader,it also analyses starting course of linux。This thesis is organized as blow:
Chapter 1  the basic conception and application of embedded
Chapter 2  the mechanism and structure of bootloader, compared with the several common bootloader
Chapter 3  the mechanism and structure of embedded operating systems, compared with the embedded operating systems
Chapter 4  the transplantation main point of u-boot, and the starting procedure in AT91RM9200
Chapter 5  the transplantation main point of linux, and the starting procedure in AT91RM9200

Keywords :Embeded Operating System ;AT91RM9200;Bootloader ;u-boot ;   linux;Porting
课题简介
1 课题任务
   嵌入式系统从某种程度上来说,就是一个小的计算机系统,从硬件上包括CPU及相应的外设,从软件上来说有bootloader、操作系统,当然有些时候也可以不用bootloder。本课题的主要任务如下:
1.研究嵌入式bootloader 、Linux 的原理和机制。
2.提出系统的设计方案。
3.设计软件系统,配置裁减Linux内核。
4.调式U-boot 及内核。
5.U-boot及Linux 的移植。
2 课题意义
   从产品角度上来讲,开发一种产品bootloader、操作系统是最重要和最复杂的地方,开发移植一种稳定可靠的bootlaoder、操作系统对产品的后续开发十分重要。
   从学习角度上来讲,也算是对所学知识的一个系统总结,当完成课题时,基本能对嵌入式系统的特点、要点有了个较清楚的认识。
3 本文组织结构
第一章 :嵌入式系统的概念
第二章 :Bootloader 的概念、作用,对比几种比较常见的bootloader,重点介绍u-boot
第三章 :嵌入式系统的机制和结构,对比几种嵌入式系统,重点介绍嵌入式linux系统
第四章 :基于AT91RM9200的u-boot移植要点,及起动流程
第五章 :基于AT91RM9200的嵌入式linux 操作系统的移植要点,及起动流程

第一章概述
  随着现代计算机技术的飞速发展和互联网技术的广泛应用,从PC时代过渡到了以个人数字助理、手持个人电脑和信息家电为代表的3C(计算机、通信、消费电子)一体的后PC时代。后PC时代里,嵌入式系统扮演了越来越重要的角色,被广泛应用于信息电器、移动计算机设备、网络设备和工控仿真等领域。嵌入式系统的开发也成为近年IT行业的技术热点。  嵌入式系统目前得到广泛的应用,而ATMEL9200在工业控制中用的较多,在本章中对其进行介绍。
1.1  嵌入式系统
1.1.1 嵌入式系统(Embedded Systems)的定义
在信息科学技术爆炸式增长的今天,嵌入式系统早已经融入了我们生活的方方面面。美国汽车大王福特公司的高级经理曾宣称,“福特出售的‘计算能力’已超过了 IBM”。这并不是一个哗众取宠或者夸张的说法,在真正感受这句话的震撼力之前,让我们先了解一下嵌入式系统(Embedded Systems)的定义:以应用为中心、以计算机技术为基础、软件硬件可裁剪、适应应用系统对功能、可靠性、成本、体积、功耗严格要求的专用计算机系统。举例来说,大到油田的集散控制系统和工厂流水线,小到家用VCD机或手机,甚至组成普通PC终端设备的键盘、鼠标、软驱、硬盘、显示卡、显示器、 Modem、网卡、声卡等均是由嵌入式处理器控制的,嵌入式系统市场的深度和广度,由此可见一斑,尽管如此,它的市场价值也许仍然超过了您的想象:今天,嵌入式系统带来的工业年产值已超过了1万亿美元。
1.1.2 嵌入式系统分类
根据不同的分类标准嵌入式系统有不同的分类方法,这里根据嵌入式系统的复杂程度,可以将嵌入式系统分为以下四类:

1.单个微处理器

这类系统可以在小型设备中(如温度传感器、烟雾和气体探测器及断路器)找到。这类设备是供应商根据设备的用途来设计的。这类设备受Y2K影响的可能性不大。

2.不带计时功能的微处理器装置

这类系统可在过程控制、信号放大器、位置传感器及阀门传动器等中找到。这类设备也不太可能受到Y2K的影响。但是,如果它依赖于一个内部操作时钟,那么这个时钟可能受Y2K问题的影响。

3.带计时功能的组件

这类系统可见于开关装置、控制器、电话交换机、电梯、数据采集系统、医药监视系统、诊断及实时控制系统等。它们是一个大系统的局部组件,由它们的传感器收集数据并传递给该系统。这种组体可同PC机一起操作,并可包括某种数据库(如事件数据库)。

4.在制造或过程控制中使用的计算机系统

对于这类系统,计算机与仪器、机械及设备相连来控制这些装置的工作。这类系统包括自动仓储系统和自动发货系统。在这些系统中,计算机用于总体控制和监视,而不是对单个设备直接控制。过程控制系统可与业务系统连接(如根据销售额和库存量来决定定单或产品量)。
1.1.3 嵌入式系统结构
一般而言,整个嵌入式系统的体系结构可以分成四个部分:嵌入式处理器、嵌入式外围设备、嵌入式操作系统和嵌入式应用软件,如图:



1.嵌入式处理器

嵌入式系统的核心是各种类型的嵌入式处理器,嵌入式处理器与通用处理器最大的不同点在于,嵌入式 CPU 大多工作在为特定用户群所专门设计的系统中,它将通用 CPU 中许多由板卡完成的任务集成到芯片内部,从而有利于嵌入式系统在设计时趋于小型化,同时还具有很高的效率和可靠性。
嵌入式处理器的体系结构经历了从 CISC(复杂指令集)至 RISC(精简指令集)和 Compact RISC 的转变,位数则由 4位、8位、16位、32位逐步发展到64位。目前常用的嵌入式处理器可分为低端的嵌入式微控制器(Micro Controller Unit,MCU)、中高端的嵌入式微处理器(Embedded Micro Processor Unit,EMPU)、用于计算机通信领域的嵌入式DSP处理器(Embedded Digital Signal Processor,EDSP)和高度集成的嵌入式片上系统(System On Chip,SOC)。
目前几乎每个半导体制造商都生产嵌入式处理器,并且越来越多的公司开始拥有自主的处理器设计部门,据不完全统计,全世界嵌入式处理器已经超过1000多种,流行的体系结构有30多个系列,其中以 ARM、PowerPC、MC 68000、MIPS 等使用得最为广泛。

2.嵌入式外围设备

在嵌入系统硬件系统中,除了中心控制部件(MCU、DSP、EMPU、SOC)以外,用于完成存储、通信、调试、显示等辅助功能的其他部件,事实上都可以算作嵌入式外围设备。目前常用的嵌入式外围设备按功能可以分为存储设备、通信设备和显示设备三类。
存储设备主要用于各类数据的存储,常用的有静态易失型存储器(RAM、SRAM)、动态存储器(DRAM)和非易失型存储器(ROM、EPROM、 EEPROM、FLASH)三种,其中FLASH凭借其可擦写次数多、存储速度快、存储容量大、价格便宜等优点,在嵌入式领域内得到了广泛应用。
目前存在的绝大多数通信设备都可以直接在嵌入式系统中应用,包括 RS-232 接口(串行通信接口)、SPI(串行外围设备接口)、IrDA(红外线接口)、I2C(现场总线)、USB(通用串行总线接口)、Ethernet(以太网接口)等。
由于嵌入式应用场合的特殊性,通常使用的是阴极射线管(CRT)、液晶显示器(LCD)和触摸板(Touch Panel)等外围显示设备。

3.嵌入式操作系统

为了使嵌入式系统的开发更加方便和快捷,需要有专门负责管理存储器分配、中断处理、任务调度等功能的软件模块,这就是嵌入式操作系统。嵌入式操作系统是用来支持嵌入式应用的系统软件,是嵌入式系统极为重要的组成部分,通常包括与硬件相关的底层驱动程序、系统内核、设备驱动接口、通信协议、图形用户界面(GUI)等。嵌入式操作系统具有通用操作系统的基本特点,如能够有效管理复杂的系统资源,能够对硬件进行抽象,能够提供库函数、驱动程序、开发工具集等。但与通用操作系统相比较,嵌入式操作系统在系统实时性、硬件依赖性、软件固化性以及应用专用性等方面,具有更加鲜明的特点。
嵌入式操作系统根据应用场合可以分为两大类:一类是面向消费电子产品的非实时系统,这类设备包括个人数字助理(PDA)、移动电话、机顶盒(STB)等;另一类则是面向控制、通信、医疗等领域的实时操作系统,如WindRiver公司的VxWorks、QNX系统软件公司的QNX等。实时系统(Real Time System)是一种能够在指定或者确定时间内完成系统功能,并且对外部和内部事件在同步或者异步时间内能做出及时响应的系统。在实时系统中,操作的正确性不仅依赖于逻辑设计的正确程度,而且与这些操作进行的时间有关,也就是说,实时系统对逻辑和时序的要求非常严格,如果逻辑和时序控制出现偏差将会产生严重后果。
实时系统主要通过三个性能指标来衡量系统的实时性,即响应时间(Response Time)、生存时间(Survival Time)和吞吐量(Throughput)
实时系统根据响应时间可以分为弱实时系统、一般实时系统和强实时系统三种。弱实时系统在设计时的宗旨是使各个任务运行得越快越好,但没有严格限定某一任务必须在多长时间内完成,弱实时系统更多关注的是程序运行结果的正确与否,以及系统安全性能等其他方面,对任务执行时间的要求相对来讲较为宽松,一般响应时间可以是数十秒或者更长。一般实时系统是弱实时系统和强实时系统的一种折衷,它的响应时间可以在秒的数量级上,广泛应用于消费电子设备中。强实时系统则要求各个任务不仅要保证执行过程和结果的正确性,同时还要保证在限定的时间内完成任务,响应时间通常要求在毫秒甚至微秒的数量级上,这对涉及到医疗、安全、军事的软硬件系统来说是至关重要的。
时限(deadline)是实时系统中的一个重要概念,指的是对任务截止时间的要求,根据时限对系统性能的影响程度,实时系统又可以分为软实时系统(soft real-time-system)和硬实时系统(hard real-time-system)。软实时指的是虽然对系统响应时间有所限定,但如果系统响应时间不能满足要求,并不会导致系统产生致命的错误或者崩溃;硬实时则指的是对系统响应时间有严格的限定,如果系统响应时间不能满足要求,就会引起系统产生致命的错误或者崩溃。如果一个任务在时限到达之时尚未完成,对软实时系统来说还是可以容忍的,最多只会降低系统性能,但对硬实时系统来说则是无法接受的,因为这样带来的后果根本无法预测,甚至可能是灾难性的。在目前实际运用的实时系统中,通常允许软硬两种实时性同时存在,其中一些事件没有时限要求,另外一些事件的时限要求是软实时的,而对系统产生关键影响的那些事件的时限要求则是硬实时的。

4.嵌入式应用软件

嵌入式应用软件是针对特定应用领域,基于某一固定的硬件平台,用来达到用户预期目标的计算机软件,由于用户任务可能有时间和精度上的要求,因此有些嵌入式应用软件需要特定嵌入式操作系统的支持。嵌入式应用软件和普通应用软件有一定的区别,它不仅要求其准确性、安全性和稳定性等方面能够满足实际应用的需要,而且还要尽可能地进行优化,以减少对系统资源的消耗,降低硬件成本。
1.2 ATMEL9200开发平台
1.2.1 常见嵌入式系统平台
嵌入式系统的硬件平台通常以CPU种类划分,常见的有以下几种架构:

1、x86 架构

这里所说的x86系列CPU是针对嵌入式系统为主,并不是日常所见的Intel Pentium 系列或 AMD公司的Athlon CPU,而是美国国家半导体(NS)公司的Geode 系列CPU。相对于一般PC而言,嵌入式的 x86 架构CPU并不需要那么强大的运算功能,嵌入式系统的硬件需求一般较低,目前这类CPU通常用于Set Top Box。

2、ARM 架构

ARM 系列CPU中最常见的是Intel公司的StrongArm CPU,目前被广泛应用于Handheld PC(功能强大的PDA),例如:COMPAQ的iPAQ、CASIO的CASIO Cassiopeia E-115、联想的天玑2000等等。

3、MIPS 架构

MIPS 系列CPU中以NEC公司出产的NEC VR系列最为著名,采用NEC VR系列CPU的有CASIO的CASIO Cassiopeia E-115等。

4、PowerPC 架构

PowerPC系列CPU中首推Palm所采用的Motorola公司的DragonBall系列CPU,Palm系列在业界是耳熟能详的机种,例如:Palm V系列、m系列,Handspring的Visor系列,令人惊异的Sony CLIEPEG系列PDA等等。

5、其他

其他还有一些功能比较简单的芯片,例如:m68k、8051..等等,这类芯片常用于工业工程,例如:机械手臂控制。
1.2.2 ATMEL9200平台
Atmel公司的AT91RM9200是基于ARM Thumb的ARM920T微控制器,时钟频率为180MHz,运算速度可以达到200MIPS。AT91RM9200内部分别有16KB的数据缓存和指令缓存,具有存储器管理单元(MMU)。此外,AT91RM9200内部还包括16KB的SRAM和128KB的ROM,具有外部总线接口(EBI),支持SDRAM、静态存储器、Burst Flash、CompactFals、SmartMedia以及NAND Flash。
AT91RM9200微控制器提供的系统外设包括:增强的时钟发生器和电源管理控制器;2个具有双PLL的片上晶振,低时钟操作模式以及通过软件实现的电源优化功能;具有4个可编程的外部时钟信号;系统定时器包括定时中断、看门狗和第二计数器;具有报警中断的实时时钟;具有调试单元、两线UART,并且支持调试通信通道;具有8个优先级的高级中断控制器,可独立屏蔽的向量中断源,具有伪中断保护功能;拥有7个外部中断源和1个快速中断源;4个32位的 PIO控制器,多达122条可编程的I/O线,每条I/O线具有输入变化中断和漏极开路电容;具有20个通道的外围数据控制器(PDC)。

第二章    BootLoader简介

2.1  概述
引导加载程序是系统加电后运行的第一段软件代码。回忆一下 PC 的体系结构我们可以知道,PC 机中的引导加载程序由 BIOS(其本质就是一段固件程序)和位于硬盘 MBR 中的 OSBoot Loader(比如,LILO 和 GRUB 等)一起组成。BIOS 在完成硬件检测和资源分配后,将硬盘 MBR 中的 Boot Loader 读到系统的 RAM 中,然后将控制权交给 OS Boot Load 核的入口点去运行,也即开始启动操作系统。而在嵌入式系统中,通常并没有像 BIOS 那样的固件程序(注,有的嵌入式 CPU 也会内嵌一段短小的启动程序),因此整个系统的加载启动任务就完全由 Boot Loader 来完成。比如在一个基于 ARM7TDMI core 的嵌入式系统中,系统在上电或复位时通常都从地址 0x00000000 处开始执行,而在这个地址处安排的通常就是系统的 Boot Loader 程序。   
2.1.1 Boot Loader概念
简单地说,Boot Loader 就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。通常,Boot Loader 是严重地依赖于硬件而实现的,特别是在嵌入式世界。因此,在嵌入式世界里建立一个通用的 Boot Loader 几乎是不可能的。尽管如此,我们仍然可以对 Boot Loader 归纳出一些通用的概念来,以指导用户特定的 Boot Loader 设计与实现。
2.1.2 Boot Loader位置
   系统加电或复位后,所有的 CPU 通常都从某个由 CPU 制造商预先安排的地址上取指令。比如,基于 ARM7TDMI core 的 CPU 在复位时通常都从地址 0x00000000 取它的第一条指令。而基于 CPU 构建的嵌入式系统通常都有某种类型的固态存储设备(比如:ROM、EEPROM 或 FLASH 等)被映射到这个预先安排的地址上。因此在系统加电后,CPU 将首先执行 Boot Loader 程序。

2.1.3 Boot Loader启动过程
系统上电后,先执行第一阶段代码,进行相应的初始化后,将 Blob 第二阶段代码复制到 RAM 地址 bloc_abs_base,然后跳转到第二阶段开始执行。在第二阶段中,从汇编跳转到 C 的 Main() 函数,继续进行如下工作:
外围的硬件初始化(串口,USB 等);
将 Flash 中的 kernel 加载到 DRAM 的 kernel 区域;
将 Flash 中的 ramdisk 加载到 DRAM 的 ramdisk 区域;
根据用户选择,进入命令行模块或启动 kernel。
2.1.4总结
由以上介绍可以看出,最简单的 bootloader 是引导系统,这也是 bootloader 的本质作用。还可以根据需要添加相关协议实现更复杂的功能,如 TFTP,当然要有相关硬件的支持。
2.2  常用的Bootloader
2.2.1 BLOB
Blob是Boot Loader Object的缩写,是一款功能强大的Bootloader。它遵循GPL,源代码完全开放。Blob既可以用来简单的调试,也可以启动Linux kernel。Blob最初是Jan-Derk Bakker和Erik Mouw为一块名为LART(Linux Advanced Radio Terminal)的板子写的,该板使用的处理器是StrongARM SA-1100。现在Blob已经被移植到了很多CPU上,包括S3C44B0。

Blob编译后的代码定义最大为64KB,并且这64KB又分成两个阶段来执行。第一阶段的代码在start.s中定义,大小为1KB,它包括从系统上电后在0x00000000地址开始执行的部分。这部分代码运行在Flash中,它包括对S3C44B0的一些寄存器的初始化和将Blob第二阶段代码从 Flash拷贝到SDRAM中。除去第一阶段的1KB代码,剩下的部分都是第二阶段的代码。第二阶段的起始文件为trampoline.s,被复制到 SDRAM后,就从第一阶段跳转到这个文件开始执行剩余部分代码。第二阶段最大为63KB,单词trampoline词义为“蹦床”,所以在这个程序中进行一些BSS段设置,堆栈的初始化等工作后,最后跳转到main.c进入C函数(图1为Blob程序启动流程)。
2.2.2  armboot
Armboot是一个bootloader,是为基于ARM或者StrongARM CPU的嵌入式系统所设计的.它支持多种类型的Flash;允许映像文件经由bootp . dhcp . tftp从网络传输;支持从串口线下载S-record或者binary文件;允许内存的显示及修改;支持jffs2文件系统等.Armboot源码公开,可以在下载。
2.2.3  u-boot
U-Boot,全称Universal Boot Loader,是遵循GPL条款的开放源码项目。从FADSROM、8xxROM、PPCBOOT逐步发展演化而来。其源码目录、编译形式与Linux内核很相似,事实上,不少U-Boot源码就是相应的Linux内核源程序的简化,尤其是一些设备的驱动程序,这从U-Boot源码的注释中能体现这一点。但是U-Boot不仅仅支持嵌入式Linux系统的引导,当前,它还支持NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS嵌入式操作系统。其目前要支持的目标操作系统是OpenBSD, NetBSD, FreeBSD,4.4BSD, Linux, SVR4, Esix, Solaris, Irix, SCO, Dell, NCR, VxWorks, LynxOS, pSOS, QNX, RTEMS, ARTOS。这是U-Boot中Universal的一层含义,另外一层含义则是U-Boot除了支持PowerPC系列的处理器外,还能支持MIPS、 x86、ARM、NIOS、XScale等诸多常用系列的处理器。这两个特点正是U-Boot项目的开发目标,即支持尽可能多的嵌入式处理器和嵌入式操作系统。就目前来看,U-Boot对PowerPC系列处理器支持最为丰富,对Linux的支持最完善。其它系列的处理器和操作系统基本是在2002年11 月PPCBOOT改名为U-Boot后逐步扩充的。从PPCBOOT向U-Boot的顺利过渡,很大程度上归功于U-Boot的维护人德国DENX软件工程中心Wolfgang Denk[以下简称W.D]本人精湛专业水平和持着不懈的努力。当前,U-Boot项目正在他的领军之下,众多有志于开放源码BOOT LOADER移植工作的嵌入式开发人员正如火如荼地将各个不同系列嵌入式处理器的移植工作不断展开和深入,以支持更多的嵌入式操作系统的装载与引导。

特点:

1.支持多协议:SCC/FEC以太网、OOTP/TFTP引导、IP和MAC的预置功能,这一点和其它BootLoader(如BLOB和RedBoot等)类似。
2.在线读写Flash、DOC、IDE、IIC、EEROM、RTC,其它的BootLoader根本不支持IDE和DOC的在线读写。
3.支持串行口kermit和S-record下载代码,U-BOOT本身的工具可以把ELF32格式的可执行文件转换成为 S-record格式,直接从串口下载并执行。
4.识别二进制、ELF32、uImage格式的Image,对Linux引导有特别的支持。U-BOOT对Linux 内核进一步封装为uImage。

选择U-Boot的理由:

① 开放源码
② 支持多种嵌入式操作系统内核,如Linux、NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS
③ 支持多个处理器系列,如PowerPC、ARM、x86、MIPS、XScale
④ 较高的可靠性和稳定性
⑤ 高度灵活的功能设置,适合U-Boot调试、操作系统不同引导要求、产品发布等
⑥ 丰富的设备驱动源码,如串口、以太网、SDRAM、FLASH、LCD、NVRAM、EEPROM、RTC、键盘等
⑦ 较为丰富的开发调试文档与强大的网络技术支持
2.3  总结
bootloader 的最本质作用是引导系统,在此基础上可以实现多种协议如Tftp等,以利于开发调试。从PPCBOOT发展而来的U-boot可谓bootloader的集大成者,实现了众多的协议,具备一些其它bootloader所没有的功能,所以在开发中决定选用u-boot作为bootloader。

第三章    嵌入式linux操作系统

完成简单功能的嵌入式系统一般不需要操作系统,如以前许多M CS51系列单片机组成的小系统就只是利用软件实现简单的控制环路。但是随着所谓后PC时代的来临,嵌入式系统设计日趋复杂,嵌入式操作系统就必不可少了。   一般而言,嵌入式操作系统不同于一般意义的计算机操作系统,它有占用空间小、执行效率高、方便进行个性化定制和软件要求固化存储等特点。  从八十年代起,国际上就有一些IT组织、公司,开始进行商用嵌入式系统和专用操作系统的研发。这其中涌现了一些著名的嵌入式系统,如Microsoft公司的 WinCE和WindRiverSystem公司的VxWorks就分别是非实时和实时嵌入式操作系统的代表。但是商用产品的造价都十分昂贵,用于一般用途会提高产品成本从而失去竞争力。
嵌入式操作系统是嵌入式系统软硬件资源的控制中心,它以尽量合理的有效方法组织多个用户共享嵌入式系统的各种资源。其中用户指的是系统程序之上的所有软件。所谓合理有效的方法,指的就是操作系统如何协调并充分利用硬件资源来实现多任务。复杂的操作系统都支持文件系统,方便组织文件并易于对其规范化操作。嵌入式操作系统还有一个特点就是针对不同的平台,系统不是直接可用的,一般需要经过针对专门平台的移植操作系统才能正常工作。  
3.1 常用的嵌入式操作系统
3.1.1  uc/os
uC/OS适合小型控制系统,具有执行效率高、占用空间小、实时性能优良和可扩展性强等特点,最小内核可编译至2k。并且具有免费公开源代码、结构小巧、具有可剥夺实时内核的特点。其内核提供任务调度与管理、时间管理、任务间同步与通信、内存管理和中断服务等功能。  
3.1.2  vxworks
VxWorks操作系统是美国WindRiver公司于1983年设计开发的一种嵌入式实时操作系统(RTOS),是Tornado嵌入式开发环境的关键组成部分。良好的持续发展能力、高性能的内核以及友好的用户开发环境,在嵌入式实时操作系统领域逐渐占据一席之地。首先,它十分灵活,具有多达1800个功能强大的应用程序接口(API);其次,它适用方面广,可以适用于从最简单到最复杂的产品设计;再次,它可靠性高,可以用于从防抱死刹车系统到星际探索的关键任务;最后,适用性强,可以用于所有的流行的CPU平台。
它采用微内核的结构,具有支持处理器多,网络协议丰富,兼容性和裁减性好等特点,同时具有程序动态连接和下载的功能

1.板级支持包(BSP)是运行的任何目标板都需要的。BSP对各种板子的硬件功能提供了统一的接口,它包括硬件初始化、中断的产生和处理、硬件时钟和计时器管理、局域和总线内存地址映射、内存分配等等。每个板级支持包包括一个ROM启动(Boot ROM)或其他启动机制。
2.高性能的操作系统核心wind支持所有的实时特性,其设计减少了系统开销,高效的任务管理保证了对外部事件快速、确定的反应。快速灵活的任务间和进程间通信允许独立的任务在实时系统中与其行动相协调。
3.网络系统提供了对其他网络和TCP/IP网络系统的"透明"访问。无论是松耦合的串行线路,标准的以太网连接还是紧耦合的利用共享内存的背板总线,所有的VxWorks网络机制都遵循标准的Internet协议。
3.1.3  QNX
位于加拿大首府渥太华的QNX软件系统有限公司成立于1980年,是创新主导型公司。在20多年的时间里,QNX公司与时俱进,不断推出代表时代技术水平的实时操作系统,包括QNX2、QNX4和当前QNX6,是当之无愧的行业领袖。
QNX实时操作系统还是一个开放的系统,其应用程序接口完全符合POSIX标准。使Linux/UNIX程序能够方便地移植到QNX系统上来,极大地扩展了QNX系统的可用资源。
QNX开放性还表现在网络联结性上。QNX不仅有QNX机器之间专用网络,还支持与异型机器之间网络通信的协议,如TCP/IP族的各种协议。
3.1.4  Hopen
Hopen是我国自主版权的操作系统。它的出现,不仅打破了电脑操作系统被国外企业一统天下的局面,而且使我国软件产业拥有了国际席位。Hopen是一种 16或32位的实时多任务操作系统,最大的特色是内核非常小,才10K左右,配置极为灵活。用户可以按需随意扩、减系统模块,从而实现新的应用。 Hopen可以支持中英文文字、语音、图像,甚至因特网。
3.1.5  Deltaos
Deltaos是由科银京成自主开发的操作系统。具有高可靠性和实时性的内核DeltaCORE、嵌入式TCP/IP DeltaNET、嵌入式文件系统DeltaFILE,以及嵌入式图形接口DeltaGUI。DeltaOS支持ARM7、StrongARM、 PPC8XX、PPC4XX、X86、MIPS等多种嵌入式微处理器。其中,DeltaCORE是一个高效的、时间确定性强的、可配置的、可靠的,且部分满足POSIX标准的嵌入式强实时操作系统内核。DeltaNET是一个支持多任务的嵌入式TCP/IP网络组件,适用于内存要求较小、可靠性要求较高的网络应用。DeltaFILE是一个管理DeltaCORE操作系统的输入/输出和文件操作的功能模块。
3.1.6WinCe
微软Windows CE 被设计成针对小型设备(它是典型的拥有有限内存的无磁盘系统)的通用操作系统。Windows CE 可以通过设计一层位于内核和硬件之间代码来用设定硬件平台,这即是众所周知的硬盘压缩层(HAL),(在以前解释时,这被称为 OEMC (原始设备制造)适应层,即 OAL; 内核压缩层,即 KAL。 以免与微软的 Windows NT 操作系统 HAL 混淆)

    不象其它的微软 Windows 操作系统,Windows CE 并不是代表一个标准的相同的对所有平台适用的软件。为了足够灵活以达到适应广泛产品需求, Windows CE 采用标准模式,这就意味着,它能够由一系列软件模式做出选择,从而使产品定制。另外,一些可利用模式也可作为其组成部分,这意味着这些模式能够通过从一套可利用的组份做出选择,从而成为标准模式,通过选择,能够达到系统要求的最小模式, OEM 能够减少存储脚本和操作系统的运行。
3.2 Linux
以上介绍了几种国内常见的嵌入式操作系统。在本节中主要介绍一下linux及其在嵌入式系统中的应用情况。
3.2.1 Linux 简介
有这样一群人,他们是一支由编程高手,计算机玩家,黑客组成的奇怪队伍,完全独立地开发出在功能上毫不逊色于微软的商业操作系统的一个全新的免费UNIX 操作系统--Linux(发音为Li-nucks)。Linux是如今的桌面系统领域中,唯一能做到与Windows相抗衡的操作系统。

1、Linux是什么?

按照Linux开发者的说法,Linux是一个遵循POSIX(Portable Operating System Interface)标准的免费操作系统,具有BSD(Berkely Software Distribution) 和SYSV(System V)的扩展特性(表明其在外表和性能上同常见的UNIX非常相象,但是所有系统核心代码已经全部被重新编写了)。
1991年8月一位来自芬兰赫尔辛基大学的年轻人inus Benedict Torvalds,对外发布了一套全新的操作系统。事情的来龙去脉是这样的:为了学习使用著名的计算机科学家 Andrew S. Tanenbaum开发的 Minix (一套功能简单,简单易懂的UNIX操作系统,可以在8086上运行,后来也支持80386,在一些PC机平台上非常流行),Linus购买了一台486 微机,但是他发现Minix的功能还很不完善,于是他决心自己写一个保护模式下的操作系统,这就是Linux的原型。最开始的Linux是用汇编语言编写的。主要工作是用来处理80386保护模式。短短四年之后,Linux在1994年的3月14日发布了它的第一个正式版本1.0版。

2、Linux源代码的结构

Linux用来支持各种体系结构的源代码包含大约4500个C语言程序,存放在270个左右的子目录下,总共大约包含200万行代码,大概占用58MB磁盘空间。Linux内核源代码位于/usr/src/linux目录,其框架结构如下:

/include子目录包含了建立内核代码时所需的大部分头文件,这个模块利用其他模块重建内核。
/init    子目录包含了内核的初始化代码,这是内核工作开始的起点。
/arch   子目录包含了所有硬件结构特定的内核代码。如:i386,arm…
/drivers 子目录包含了内核中所有的设备驱动程序,如块设备和SCSI设备。
/fs     子目录包含了所有的文件系统的代码。如:ext2,vfat等。
/net    子目录包含了内核的连网代码。
/mm   子目录包含了所有内存管理代码。
/ipc    子目录包含了进程间通信代码。
/kernel  子目录包含了主内核代码。

3、Linux的开发方式

Linux是基于GPL(GNU General Public License)的open-source软件[3]。open-source(开放源码)软件是一个新名词,这种软件的源代码可以被公众使用、修改和发布,开放源码软件通常是有copyright的,它的许可证可能包含这样一些限制: 必须保护开放状态,著者身份公告,或者开发的控制。"开放源码"正在被代表公众利益的软件组织注册为认证标记,这也是正式的开放源码定义的一部分。开放源码软件主要由散布在全世界的编程者队伍所开发,但是同时一些大学,政府机构承包商,协会和商业公司也开发它[4]。GPL是GNU通用公共许可证的缩写,Linux操作系统以及与它有关的大量软件是在GPL的推动下开发和发布的。GPL的核心思想体现在三个方面:一是有复制程序并且把它送给别人的自由;二是有获取代码后,按照获取者意愿修改的自由;三是有发布软件的改进版并且以此创建自由软件社团的自由。很多开放源码软件的拥护者认为开放源码的软件比它们的对手商业软件要好。一个公司即使是可以调集上万测试人员来进行排错的工作,这些测试者通常也不会把握说,"这些软件的错误被修补过了,而且有三个大家都需要的新功能。"自由软件的资深作家、<< 新黑客词典>>的编辑Eric S. Raymond认为,开放源码的开发方式天生就要比传统的自顶向下设计方法优越。脑力劳动者集市化的运作逻辑保证,只有最好的代码和整体结构 -- 由编程者的同行来判断 -- 才会最后保留下来。公司的软件通常是按照预先确定的计划编制出来的,很少有改进和革新的余地。

4、Linux成为优秀的嵌入式OS的必然性

Linux成为优秀的嵌入式操作系统,有其自身的必然性,这种必然性的根源在于自由软件的开发模式和开放性。自由软件的由一个标准组织控制,全世界范围软件人员并行开发的方式造就了Linux可剪裁的内核结构,而可剪裁性恰恰是将Linux与嵌入式系统紧密联系在一起的纽带,另一方面嵌入式系统对成本的严格限制也使得具备开放特征的Linux超越其它诸如Vxwork、pSOS、Neculeus和Windowss CE等强劲竞争对手成为市场占有率第一的嵌入式操作系统。
3.2.2 嵌入式Linux
1、什么是嵌入式Linux

嵌入式Linux(Embedded Linux)是指对Linux经过小型化裁剪后,能够固化在容量只有几十万字节或几十亿字节的存储器芯片或单片机中,应用于特定嵌入式场合的专用Linux操作系统。

2、嵌入式Linux的特点

嵌入式Linux操作系统产品在2000年开始涌现,短短一年之后,嵌入式Linux操作系统达到了整个嵌入式操作系统市场的1/4。人们惊呼,没想到Linux行业的赢利竟然是从嵌入式Linux系统开始的!Linux在嵌入式系统领域能取得成功有以下几点原因:

1)性能稳定,功能强大,占用资源较少。Linux是按照POSIX标准编写的,许多源代码借鉴了UNIX。
2)模块的动态加载,独特的内核结构和工作方式使Linux非常适合于工作在嵌入式系统中。
3)良好的平台可移植性,Linux-2.4.0已经支持除i386、i586、i686和arm之外的alpha、m68k、mips、mips64、ppc、sparc、sparc64、ia64的十多种体系结构。
4)系统可剪裁性,可以灵活的添加和删减各种驱动程序,具体的PDE(Portable Digital Equippment)跟实际应用联系十分密切,采用的器件多种多样,良好的平台可移植性和系统可剪裁性对灵活的系统设计有着极其重要的意义,也为开发调试提供了方便。
5)整个系统是免费的,只要遵守GPL规则,任何人都可以使用、修改甚至销售Linux,全世界有无数个人和组织在不断的完善Linux,有众多的计算机厂商提供Linux相关的产品和服务。开放源代码意味着对新设计、制造的硬件产品的快速支持。嵌入式系统的应用领域通常要求系统高度个性化、高度细分。因此,对新产品的快速支持是一个适合推广的嵌入式操作系统必须具备的能力。
6)对多种网络协议的完美支持。在即将到来的后PC时代,联网将成为数字产品必备的能力,没有联网能力的设备,在信息化时代会显得毫无意义。而Linux天生具备优良的网络连接能力,支持当前所有的网络协议。

3、从Linux到嵌入式Linux

由于Linux开始是为台式机开发的,与台式机相比,嵌入式系统有自己的一些特点,如内存容量有限等,所以把Linux应用于嵌入式系统,还需要做大量的工作。这些工作包括:
1)嵌入式系统中的挥发性/非挥发性存储器容量受到严格的限制,为适应系统对尺寸的要求,需要剪裁嵌入式Linux的内核。
2)嵌入式系统的主频和总线带宽也经常由于成本或应用的原因,不能或不必提供更高的性能,作为操作系统,嵌入式Linux也需要做出相应的改变,改写某些核心模块和驱动程序。
3)嵌入式Linux直接面向特定的应用,为了适应一些特殊的应用需求,有时必须改变嵌入式Linux的工作方式。例如Linux是一个多线程、多任务的操作系统,并不适用于某些实时性要求较高的环境中,目前已经出现了经过改造的,专门用于实时环境的嵌入式实时Linux。
4)嵌入式Linux的图形环境与桌面系统相比简陋的多,对中文的支持也不够完善,对于某些手持数字设备,这方面还有许多工作要做。
5)在嵌入式系统领域,没有Wintel这样的垄断组织,相比于桌面系统Linux,嵌入式Linux有更大的发展空间。

第四章    基于AT91RM9200的u-boot 移植

   
4.1  u-boot 结构
u-boot 是一个层次式结构。做移植工作的软件人员应当提供串口驱动(UART Driver),以太网驱动(Ethernet Driver),Flash 驱动(Flash 驱动),USB 驱动(USB Driver)。目前,通过USB 口下载程序显得不是十分必要,所以暂时没有移植USB 驱动。驱动层之上是u-boot 的应用,command 通过串口提供人机界面。我们可以使用一些命令做一些常用的工作,比如内存查看命令md。
Kermit 应用主要用来支持使用串口通过超级终端下载应用程序。TFTP 则是通过网络方式来下载应用程序,例如uclinux 操作系统。

U-Boot主要目录结构

board      目标板相关文件,主要包含SDRAM、FLASH驱动
common    独立于处理器体系结构的通用代码,如内存大小探测与故障检测
cpu        与处理器相关的文件。如mpc8xx子目录下含串口、网口、LCD驱动及中断初始化等文件
driver      通用设备驱动,如CFI FLASH驱动(目前对INTEL FLASH支持较好)
doc        U-Boot的说明文档
examples    可在U-Boot下运行的示例程序;如hello_world.c,timer.c
include     U-Boot头文件;尤其configs子目录下与目标板相关的配置头文件是移植过程中经常要修改的文件
lib_xxx    处理器体系相关的文件,如lib_ppc, lib_arm目录分别包含与PowerPC、ARM体系结构相关的文件
net         与网络功能相关的文件目录,如bootp,nfs,tftp
post        上电自检文件目录。尚有待于进一步完善
rtc         RTC驱动程序
tools       用于创建U-Boot S-RECORD和BIN镜像文件的工具
4.2  u-boot 移植相关文件
由于u-boot 已经支持at91rm9200系列的CPU,所以所做的移植也只是针对不同的板子进行移植,即所谓的板级移植。跟板子相关的文件有common.h、flash.c、flash.h、”./board/at91rm9200dk/config.mk”

1.CPU 目录

U-boot 是从 cpu/at91rm9200/start.s 开始的。这个文件的主要任务是设置CPU状态、初始化中断、内存时序等,并确定是否需要对整个代码进行重定位。

2.board 目录

CPU 目录主要是针对处理器的初始化,而board 目录主要对开发板进行初始化。
u-boot.lds    内核链接脚本
at91rm9200dk.c 开发板初始化
flash.c          对flash_info_t{}数据结构初始化

3.lib_arm 目录

此目录是体系结构相关的代码,里面包括start_armboot函数要根据开发板的配置,进行一系列的初始化工作。  

4.3u-boot 起动分析
1.u-boot.lds
u-boot.lds在AT91RM9200-U-Boot\AT91RM9200-U-Boot\board\at91rm9200dk目录下,其内容如下:
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
        . = 0x00000000;

        . = ALIGN(4);
.text      :
{
  cpu/at91rm9200/start.o(.text);此处决定从..cpu/at91rm9200/start.o处开始执行启动
  *(.text)
}

        . = ALIGN(4);
        .rodata : { *(.rodata) }

        . = ALIGN(4);
        .data : { *(.data) }

        . = ALIGN(4);
        .got : { *(.got) }

armboot_end_data = .;

        . = ALIGN(4);
        .bss : { *(.bss) }

armboot_end = .;
}
u-boot.ld中决定u-boot的开始的代码是cpu/at91rm9200/start.s。
2.start.s
start.s在 .. cpu/at91rm9200目录下
其开始代码如下:
.globl _start
_start:b       reset
ldrpc, _undefined_instruction
ldrpc, _software_interrupt
ldrpc, _prefetch_abort
ldrpc, _data_abort
ldrpc, _not_used
ldrpc, _irq
ldrpc, _fiq
根据ARM核的特点,在0x000000000地址处存放的是中断处理向量,当发生中断或异常时跳到“_start:b       reset”下面相应的处理程序。
/*
* CFG_MEM_END is in the board dependent config-file (configs/config_BOARD.h)
*/
_TEXT_BASE:
.wordTEXT_BASE ;这个值在/board/ at91rm9200/config.mk 定义

.globl _armboot_star   
_armboot_start:
.word _start    ;代码段的起始地址值也是TEXTBASE,和具体板子有关
.word armboot_end_data  ;以下各段由链接脚本
开机执行0x00000000处的 b reset 跳到如下代码:
reset:
/*
         * set the cpu to SVC32 mode
         */
        mrs     r0,cpsr
        bic     r0,r0,#0x1f
        orr     r0,r0,#0x13
        msr     cpsr,r0
reset这段代码将CPU设置为SVC32模式。
接下来执行如下:
/*
* relocate exeception table
*/
ldrr0, =_start    //当前代码位置
ldrr1, =0x0   //设置,为下面拷贝作准备
movr2, #16
此段是对代码进行重定向。
接下来做如下操作:
copyex:
subsr2, r2, #1
ldrr3, [r0], #4   //源地址
strr3, [r1], #4  //目的地址
bnecopyex
这段代码是将源地址[r0]复制到目的地址[r1]
/*
         * we do sys-critical inits only at reboot,
         * not when booting from ram!
         */
#ifdef CONFIG_INIT_CRITICAL
        bl      cpu_init_crit
#endif

        /* set up the stack */
        ldr     r0, _armboot_end
        add     r0, r0, #CONFIG_STACKSIZE
        sub     sp, r0, #12             /* leave 3 words for abort-stack */
        ldr pc,_start_armboot
_start_armboot: .word start_armboot
本段代码设置堆栈,而后跳到 start_armboot。到此汇编结束,开始执行C代码。
3.start_armboot
Start_armboot()在lib_arm/board.c 中定义,它类似于Linux的start_kernel,它是系统初始化的接口函数。在其中要完成内存、flash、网络、dataflash等资源的初始化工作。这个函数的如下:
void start_armboot (void)
{
DECLARE_GLOBAL_DATA_PTR;

ulong size;
gd_t gd_data;
bd_t bd_data;
init_fnc_t **init_fnc_ptr;
char *s;
:
:
;

}
第一条语句DECLARE_GLOBAL_DATA_PTR,这个宏在/include/asm-arm/global_data.h文件中。
其内容是:
#define DECLARE_GLOBAL_DATA_PTR     register gd_t *gd asm ("r8")
接下来start_armboot执行如下:
gd = &gd_data;
memset (gd, 0, sizeof (gd_t));
gd->bd = &bd_data;
memset (gd->bd, 0, sizeof (bd_t));

monitor_flash_len = _armboot_end_data - _armboot_start;

for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
这段代码为动态内存作准备,并初始化_armboot_end_data-_armboot_startx空间。
/* configure available FLASH banks */
size = flash_init ();
display_flash_config (size);
代码中size = flash_init () 初始化Flash,函数代码
ulong flash_init (void)
{
int i, j, k;
unsigned int flash_nb_blocks, sector;
unsigned int start_address;
OrgDef *pOrgDef;

ulong size = 0;

for (i = 0; i < CFG_MAX_FLASH_BANKS; i++) {
ulong flashbase = 0;
:
:
}

#ifdef CONFIG_VFD
#  ifndef PAGE_SIZE
#   define PAGE_SIZE 4096
#  endif
为VFD显示预留内存
/* armboot_real_end is defined in the board-specific linker script */
addr = (_armboot_real_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
size = vfd_setmem (addr);
gd->fb_base = addr;
/* round to the next page boundary */
addr += size;
addr = (addr + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
mem_malloc_init (addr);
#else
/* armboot_real_end is defined in the board-specific linker script */
mem_malloc_init (_armboot_real_end);
#endif /* CONFIG_VFD */

#if (CONFIG_COMMANDS & CFG_CMD_NAND)
nand_init();/* go init the NAND */
#endif
armboot_real_end在board-specific连接脚本中定义。

#ifdef CONFIG_HAS_DATAFLASH
AT91F_DataflashInit();
dataflash_print_info();
#endif
初始化AT91F_DataflashInit()、dataflash_print_info()函数。
AT91F_DataflashInit()代码
int AT91F_DataflashInit (void)
{
int i, j;
int dfcode;

AT91F_SpiInit ();

for (i = 0; i < CFG_MAX_DATAFLASH_BANKS; i++) {

dataflash_info.id = 0;
dataflash_info.Device.pages_number = 0;
dfcode = AT91F_DataflashProbe (cs[1], &dataflash_info.Desc);

switch (dfcode) {
case AT45DB161:
dataflash_info.Device.pages_number = 4096;
dataflash_info.Device.pages_size = 528;
dataflash_info.Device.page_offset = 10;
dataflash_info.Device.byte_mask = 0x300;
dataflash_info.Device.cs = cs[1];
dataflash_info.Desc.DataFlash_state = IDLE;
dataflash_info.logical_address = cs[0];
dataflash_info.id = dfcode;
break;

case AT45DB321:
dataflash_info.Device.pages_number = 8192;
dataflash_info.Device.pages_size = 528;
dataflash_info.Device.page_offset = 10;
dataflash_info.Device.byte_mask = 0x300;
dataflash_info.Device.cs = cs[1];
dataflash_info.Desc.DataFlash_state = IDLE;
dataflash_info.logical_address = cs[0];
dataflash_info.id = dfcode;
break;

case AT45DB642:
dataflash_info.Device.pages_number = 8192;
dataflash_info.Device.pages_size = 1056;
dataflash_info.Device.page_offset = 11;
dataflash_info.Device.byte_mask = 0x700;
dataflash_info.Device.cs = cs[1];
dataflash_info.Desc.DataFlash_state = IDLE;
dataflash_info.logical_address = cs[0];
dataflash_info.id = dfcode;
break;
default:
break;
}

for (j = 0; j < dataflash_info.Device.pages_number; j++)
dataflash_info.protect[j] = FLAG_PROTECT_SET;

}
return (1);
}
把flash的相关信息存储在相关结构里。

接着执行:
for (;;) {
main_loop ();
}

main_loop()在/inlucde/common.h定义 在common/main.c 中实现其代码如下:
void main_loop (void)
{
#ifndef CFG_HUSH_PARSER
static char lastcommand[CFG_CBSIZE] = { 0, };
int len;
int rc = 1;
int flag;
#endif

#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
char *s;
int bootdelay;
#endif


/*
* 处理从外部输入的命令
*/
#ifdef CFG_HUSH_PARSER
parse_file_outer();
/* This point is never reached */
for (;;);
#else
}
在此外提供了监控功能,根据用户的输入执行不同的操作;在经过一些操作后调用:
void do_bootm_linux(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
ulong addr, ulong *len_ptr, int   verify)
{
DECLARE_GLOBAL_DATA_PTR;

    ulong len = 0, checksum;
    ulong initrd_start, initrd_end;
    ulong data;
    void (*theKernel)(int zero, int arch);
    image_header_t *hdr = &header;
    bd_t *bd = gd->bd;
#ifdef CONFIG_CMDLINE_TAG
    char *commandline = getenv("bootargs");
#endif

theKernel = (void (*)(int, int))ntohl(hdr->ih_ep);
;
;
theKernel(0, bd->bi_arch_number);

}
在这个函数中设置内核起动所要的参数,而后搬运内核到相应的位置,最后跳到相应的地址处起动内核。
static void setup_start_tag(bd_t *bd)
{
    params = (struct tag *)bd->bi_boot_params;

    params->hdr.tag = ATAG_CORE;
    params->hdr.size = tag_size(tag_core);

    params->u.core.flags = 0;
    params->u.core.pagesize = 0;
    params->u.core.rootdev = 0;

    params = tag_next(params);
}

执行env_relocate ()初始化环境,重定位环境变量参数区。获取IP地址和MAC地址,并赋给gd结构:
/* initialize environment */
env_relocate ();

#ifdef CONFIG_VFD
/* must do this after the framebuffer is allocated */
drv_vfd_init();
#endif

/* IP Address */
bd_data.bi_ip_addr = getenv_IPaddr ("ipaddr");

/* MAC Address */
{
int i;
ulong reg;
char *s, *e;
uchar tmp[64];

i = getenv_r ("ethaddr", tmp, sizeof (tmp));
s = (i > 0) ? tmp : NULL;

for (reg = 0; reg < 6; ++reg) {
bd_data.bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
if (s)
s = (*e) ? e + 1 : e;
}
}
这些参数可以传递给内核中。
#if defined(CONFIG_MISC_INIT_R)
/* miscellaneous platform dependent initialisations */
misc_init_r ();
#endif

/* enable exceptions */
enable_interrupts ();

#ifdef CONFIG_DRIVER_CS8900
cs8900_get_enetaddr (gd->bd->bi_enetaddr);
#endif

#ifdef CONFIG_DRIVER_LAN91C96
if (getenv ("ethaddr")) {
smc_set_mac_addr(gd->bd->bi_enetaddr);
}
/* eth_hw_init(); */
#endif /* CONFIG_DRIVER_LAN91C96 */

/* Initialize from environment */
if ((s = getenv ("loadaddr")) != NULL) {
load_addr = simple_strtoul (s, NULL, 16);
}
#if (CONFIG_COMMANDS & CFG_CMD_NET)
if ((s = getenv ("bootfile")) != NULL) {
copy_filename (BootFile, s, sizeof (BootFile));
}
#endif/* CFG_CMD_NET */
初始化主板上的多功能设备,网络设备驱动。
#ifdef BOARD_POST_INIT
board_post_init ();
#endif
初始化board_post_init ()
接着执行:
for (;;) {
main_loop ();
}
4.main_loop
main_loop()在/inlucde/common.h定义,在common/main.c 中实现其代码如下:
void main_loop (void)
{
#ifndef CFG_HUSH_PARSER
static char lastcommand[CFG_CBSIZE] = { 0, };
int len;
int rc = 1;
int flag;
#endif

#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
char *s;
int bootdelay;
#endif


/*
* 处理从外部输入的命令
*/
#ifdef CFG_HUSH_PARSER
parse_file_outer();
/* This point is never reached */
for (;;);
#else
}
在此外提供了监控功能,根据用户的输入执行不同的操作
4.do_bootm_linux
do_bootm_linux在AT91RM9200-U-Boot\lib_arm\armlinux.c中定义
void do_bootm_linux(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
ulong addr, ulong *len_ptr, int   verify)
{
DECLARE_GLOBAL_DATA_PTR;

    ulong len = 0, checksum;
    ulong initrd_start, initrd_end;
    ulong data;
    void (*theKernel)(int zero, int arch);
    image_header_t *hdr = &header;
    bd_t *bd = gd->bd;
#ifdef CONFIG_CMDLINE_TAG
    char *commandline = getenv("bootargs");
#endif

theKernel = (void (*)(int, int))ntohl(hdr->ih_ep);
;
;
theKernel(0, bd->bi_arch_number);

}
在这个函数中设置内核起动所要的参数,而后搬运内核到相应的位置,最后跳到相应的地址处起动内核。
static void setup_start_tag(bd_t *bd)
{
    params = (struct tag *)bd->bi_boot_params;
    params->hdr.tag = ATAG_CORE;
    params->hdr.size = tag_size(tag_core);

    params->u.core.flags = 0;
    params->u.core.pagesize = 0;
    params->u.core.rootdev = 0;
    params = tag_next(params);
}

初始处参数区,内核将在此处读取相应的参数。
theKernel(0, bd->bi_arch_number);
启动内核,至此u-boot 完成的内核搬运和启动任务。
4.4  u-boot 常用命令
命令                   功能
go           启动在地址"addr"处的应用程序
bootm        引导应用程序或操作系统内核(压缩格式)
tftp           通过以太网(tftp协议)加载程序
loadb         通过串口下载程序
md           显示内存内容
mm           修改内存内容
mw           写内存
cp            复制内存(同时也是烧写flash)
prinenv        打印环境变量
setenv         设置环境变量
saveenv        保存环境变量
erase          擦除flash内容
flinfo         显示flash信息
reset          复位CPU
version        显示版本信息
4.5  总结
本章从u-boot 架构分析入手,接着分析了移植的要点,最后详细分析了u-boot 的整个启动过程。

第五章    基于AT91RM9200的嵌入式linux系统移植

linux本身是一个宏内核结构,和其它微内核结构的操作系统比起来在移植性上要差一些,但由于其本身已经支持许多不同体系结构的CPU,所以移植 linux到一种已支持的系统结构下的CPU并不是一件难事。况且,如今的CPU生产商大部分都对linux加入其生产CPU的支持。将LINUX移植到新的微处理器体系非常快捷,一般是将其移植到一种新型的目标板,其中包含有独特的外设。大部分的内核代码都是相同的,因为它们与微处理器无关,所以,移植的工作多集中在一些存储器管理及中断处理程序上。一旦完成,它们将非常稳定。本章主要对下面的内容进行介绍:
1.移植概念
2.Linux与移植相关内核结构
3.如何进行移植
5.1 移植概念
在同一个硬件平台上可以运行不同的操作系统,比如在PC机上可以运行windows、linux。同样把操作系统和硬件相关的部分做相应的修改就可以运行在不同的硬件平台上,这就叫移植,即把运行在一个平台上的软件,经过修改运行在其它平台。
Linux 本身是个宏内核,这给移植带来了困难,但由于其有清晰的结构,所以移植也相对容易。Linux和CPU相关的目录是 arch/和arch/asm-/目录中,是具体CPU体系的名字如arm。与之相关的结构是arch/arm和arch/asm-arm。嵌入式系统是“硬件可裁剪的”,因此工程师设计的硬件电路有所不同,从而要根据具体的硬件电路进行相应的内核电码移植。

5.2  Linux与移植相关内核结构
在移植Linux以前,先认识一下Linux 的内核结构。总的来说Linux 内核主要有5个子系统:进程调度、内存管理、虚拟文件系统、网络接口、进程间通信。

1.内存管理:

内存管理的代码主要在/mm,但是特定结构的代码在arch/*/mm。缺页中断处理的代码在/mm/memory.c ,而内存映射和页高速缓存器的代码在/mm/filemap.c 。缓冲器高速缓存是在/mm/buffer.c 中实现,而交换高速缓存是在mm/swap_state.c和mm/swapfile.c。

2.进程间通信:

所有的SystemVIPC对象权限都包含在ipc_perm数据结构中,这可以在include/linux/ipc.h中找到。SystemV消息是在ipc/msg.c中实现。共享内存在ipc/shm.c中实现。信号量在ipc/sem.c中,管道在/ipc/pipe.c中实现。

其它子系统从略另外在linux 中,中断处理内核的中断处理代码几乎所有的微处理器特有的。中断处理代码在arch/i386/kernel/irq.c中,其定义在include/asm-i386/irq.h中。   
5.2.1 结构分析
/include  子目录包含了建立内核代码时所需的大部分包含文件,这个模块利用其他模块重建内核。
/init     子目录包含了内核的初始化代码,这是内核工作的开始的起点。
/arch    子目录包含了所有硬件结构特定的内核代码。如:i386,alpha。
/drivers  子目录包含了内核中所有的设备驱动程序,如块设备和SCSI设备。
/fs      子目录包含了所有的文件系统的代码。如:ext2,vfat等。
/net     子目录包含了内核的连网代码。
/mm    子目录包含了所有内存管理代码。
/ipc     子目录包含了进程间通信代码。
/kernel  子目录包含了主内核代码。

(1)arch 目录

Linux系统能支持如此多的平台部分原因是其清晰的结构。Arch目录包含了体系结构相关部分的内核代码。其中的每一个目录都代表一种硬件平台,比如我们使用的ARM平台和PC的i386.对于任何平台,都必须包括以下几个目录。

Boot: 包括启动内核所使用的部分或全部平台的特有的代码。
Kernel:存放支持系统结构特有的有函数实现。
Lib:存放高速的体系结构特有的函数实现。
Mm:存放体系结构特有的内存管理实现。
Math-emu:模拟FPU。

(2)  drivers 目录

在些目录下有的驱动平台与有些平台无关。

(3)  fs 目录

此目录下是Linux支持的文件系统。一般来说,有些目录与硬件无关。

(4)init 目录
此目录下包含核心的初始化代码,有main.c 和version.c 两个文件。
(5)mm 目录
此目录包含了所有的内存管理代码。与硬件体系相关的内存管理代码位于arch/*/mm 目录下。
(6)其他目录
其它目录大数和平台无关,在此就不详细介绍了。
5.2.2 Linux内核的配置
Linux内核的配置系统由三个部分组成,分别是:

Makefile:分布在 Linux 内核源代码中的 Makefile,定义 Linux 内核的编译规则;

配置文件(config.in):给用户提供配置选择的功能;

配置工具:包括配置命令解释器(对配置脚本中使用的配置命令进行解释)和配置用户界面(提供基于字符界面、基于 Ncurses 图形界面以及基于 Xwindows 图形界面的用户配置界面,各自对应于 Make config、Make menuconfig 和 make xconfig)。
这些配置工具都是使用脚本语言,如 Tcl/TK、Perl 编写的(也包含一些用 C 编写的代码)。本文并不是对配置系统本身进行分析,而是介绍如何使用配置系统。所以,除非是配置系统的维护者,一般的内核开发者无须了解它们的原理,只需要知道如何编写 Makefile 和配置文件就可以。所以,在本文中,我们只对 Makefile 和配置文件进行讨论。另外,凡是涉及到与具体 CPU 体系结构相关的内容,我们都以 ARM 为例,这样不仅可以将讨论的问题明确化,而且对内容本身不产生影响。
5.3  嵌入式Linux 操作系统移植
了解Linux内核以后,就可以开始移植工作了。
5.3.1 根目录
根目录只要修改Makefile文件。这个Makefile文件的任务有两个:产生 vmlinux 内核文件和生成模块。根据linux 的内核结构,此Makefile将递归调用其子目录的Makefile。
 内核根据Makefile来编译源程序,并用其来组织内核的各模块,记录各模块间的相互关系。仔细阅读各子目录中的Makefile 可以看出各个文件之间的依赖关系。现摘录部分内容如下:
AS    = $(CROSS_COMPILE)as
LD    = $(CROSS_COMPILE)ld
CC    = $(CROSS_COMPILE)gcc
CPP    = $(CC) -E
AR    = $(CROSS_COMPILE)ar
NM    = $(CROSS_COMPILE)nm
STRIP= $(CROSS_COMPILE)strip
OBJCOPY= $(CROSS_COMPILE)objcopy
OBJDUMP= $(CROSS_COMPILE)objdump
MAKEFILES= $(TOPDIR)/.config
GENKSYMS= /sbin/genksyms
DEPMOD= /sbin/depmod
MODFLAGS= -DMODULE
在移植的过程中需要修改的内容是
(1)指定目标平台:ARCH :=arm
(2)指定交叉编译器:CROSS_COMPILE=arm-linux-
5.3.2 arch
arch 目录存放着和体系结构相关的部分的代码。
1.MakeFile 文件:
我们平台是9200,属于ARM平台。因为2.4.19 没有包含AT91RM9200,所以要在arch/arm中的Makefile增加下面的内容:
ifeq(($CONFIG_ARCH_AT91RM9200),y)
TEXTADDR=0xC0008000
MACHINE=AT91RM9200
Endif
TEXTADDR 决定内核起始运行地址,即image.ram 应下载的位置。根据目前的电路设计,此地址是0xC0008000.
此移植要点是:要根据自己的电路设置TEXTADDR变量。
2.config.in
config.in 是配置文件,运行make menuconfig 命令时出现的菜单就是config配置的。因为内核中没有AT91RM9200 处理器的信息,所以需要config文件加入以下相关信息:
if[“CONFIG_ARCH_AT91RM9200”=”y”]; then
comment ‘AT91RM9200 Implememntation’
5.3.3 arch/arm/boot 目录
编译出来的内核是存放在这个目录中。这是指定内核解压到目标板的地址,当内核无法启动时,很有可能就是这里的地址指定错误。
1.Makefile
     移植后:
 ifeq(($CONFIG_ARCH__ AT91RM9200),y)
   ZTEXTADDR=0x30008000
ZREALADDR=0x30008000
Endif
ZREALADDR 决定内核解压后数据输出的地址。
ZTEXTADDR 为没有解压时的开始运行地址。
移植要点:要根据自己的电路设置ZTEXTADDR 和ZREALADDR
2.compressed /Makefile
    通过这个文件将从vmlinux创建一个压缩的镜像。此文件中用到的ZTEXTADDR、ZREALADDR 等变量是从arch/arm/boot/Makefile传来的。
   移植后:
ifeq(($CONFIG_ARCH__ AT91RM9200),y)
OBJS+=head- AT91RM9200.o
Endif
移植要点:加入相关头文件。
5.3.4 entry-armv.S目录
此目录主要是和硬件中断相关的。

5.3.5 setup.c 目录
这个文件中有一个比较重要的函数 setup_arch。这个函数用来完成和体系相关的初始化工作,比如对物理内存的meminfo的初始化。其中 nr_banks 指定的内存块的数量。Bank指定了每块的范围。PAGE_OFFSET是内存的开始地址。这个函数和板子具体情况关系比较大。就根据具体的情况进行设置。后面的初始化函数将根据meminfo进行内存结构的初始化。
5.3.6 外设及设备驱动移植
把linux 移植到一种新的CPU下,需要修改大量的文件,限于篇幅,本文从原理进行了分析,并给出了一些比较重要文件的修改方法。在移植的过程中,定时器,中断,CACHE管理、MMU等和硬件紧密相关的地方都是要相关平台的底层代码支持的,要根据自己的硬件电路进行相应的修改,也是在移植进程中要特别注意的。
5.4 Linux启动分析
首先Linux内核可以压缩也可以不压缩,当压缩时,应该先执行开始的解压程序,再跳到解压后的地址处执行linux的初始化程序。压缩执行相对来说比较复杂,现在就分析带有压缩的linux内核的启动。当进入内核时:
        r0      - should be 0
        r1      - unique architecture number
        MMU     - off
        I-cache - on or off
D-cache – off
首先执行:linux-2.4.19-rmk7\arch\arm\boot\compressed\head.s
start:
.typestart,#function
.rept8
movr0, r0
.endr

b1f
.word0x016f2818@ Magic numbers to help the loader
.wordstart@ absolute load/run zImage address
.word_edata@ zImage end address
1:movr7, r1@ save architecture ID
movr8, #0@ save r0
上面这些代码主要是保存机器号,把zImage 地址写入寄存器等。
/*
* Check to see if we will overwrite ourselves.
*   r4 = final kernel address
*   r5 = start of this image
*   r2 = end of malloc space (and therefore this image)
* We basically want:
*   r4 >= r2 -> OK
*   r4 + image length <= r5 -> OK
*/
cmpr4, r2
bhswont_overwrite
addr0, r4, #4096*1024@ 4MB largest kernel size
cmpr0, r5
blswont_overwrite

movr5, r2@ decompress after malloc space
movr0, r5
movr3, r7
bldecompress_kernel

addr0, r0, #127
bicr0, r0, #127@ align the kernel length
/*
* r0     = decompressed kernel length
* r1-r3  = unused
* r4     = kernel execution address
* r5     = decompressed kernel start
* r6     = processor ID
* r7     = architecture ID
* r8-r14 = unused
*/
addr1, r5, r0@ end of decompressed kernel
adrr2, reloc_start
ldrr3, LC1
addr3, r2, r3
1:ldmiar2!, {r8 - r13}@ copy relocation code
stmiar1!, {r8 - r13}
ldmiar2!, {r8 - r13}
stmiar1!, {r8 - r13}
cmpr2, r3
blo1b

blcache_clean_flush
addpc, r5, r0@ call relocation code
下面的代码主要是解压内核到相应地址而后跳到该处执行内核程序:
reloc_start:addr8, r5, r0
debug_reloc_start
movr1, r4
1:
.rept4
ldmiar5!, {r0, r2, r3, r9 - r13}@ relocate kernel
stmiar1!, {r0, r2, r3, r9 - r13}
.endr

cmpr5, r8
blo1b
debug_reloc_end

call_kernel:blcache_clean_flush
blcache_off
movr0, #0
movr1, r7@ restore architecture number
movpc, r4@ call kernel
内核解压完后执行:\linux-2.4.19-rmk7\linux-2.4.19-rmk7\arch\arm\kernel\head-armv.S
#if (TEXTADDR & 0xffff) != 0x8000
#error TEXTADDR must start at 0xXXXX8000
#endif

.globlSYMBOL_NAME(swapper_pg_dir)
.equSYMBOL_NAME(swapper_pg_dir), TEXTADDR - 0x4000

.macropgtbl, reg, rambase
adr\reg, stext
sub\reg, \reg, #0x4000
.endm

/*
* Since the page table is closely related to the kernel start address, we
* can convert the page table base address to the base address of the section
* containing both.
*/
.macrokrnladr, rd, pgtable, rambase
bic\rd, \pgtable, #0x000ff000
.endm

.section ".text.init",#alloc,#execinstr
.typestext, #function
ENTRY(stext)
movr12, r0
.rept8
movr0, r0
.endr

adrr2, 1f
ldmdbr2, {r7, r8}
andr3, r2, #0xc000
teqr3, #0x8000
beq__entry
bicr3, r2, #0xc000
orrr3, r3, #0x8000
movr0, r3
movr4, #64
subr5, r8, r7
b1f

.word_stext
.word__bss_start

1:
.rept4
ldmiar2!, {r6, r7, r8, r9}
stmiar3!, {r6, r7, r8, r9}
.endr
subsr4, r4, #64
bcs1b
movsr4, r5
movr5, #0
movnepc, r0

movr1, #MACH_TYPE_NETWINDER@ (will go in 2.5)
movr12, #2 << 24@ scheduled for removal in 2.5.xx
orrr12, r12, #5 << 12
__entry:
__lookup_processor_type:
adrr5, 2f
ldmiar5, {r7, r9, r10}
subr5, r5, r10@ convert addresses
addr7, r7, r5@ to our address space
addr10, r9, r5
mrcp15, 0, r9, c0, c0@ get processor id
1:ldmiar10, {r5, r6, r8}@ value, mask, mmuflags
andr6, r6, r9@ mask wanted bits
teqr5, r6
moveqpc, lr
addr10, r10, #36@ sizeof(proc_info_list)
cmpr10, r7
blt1b
movr10, #0@ unknown processor
movpc, lr

该函数主要是判断体系结构类型、CPU类型,如果不对则出错返回。最后执行
adrr3, __switch_data + 4
ldmiar3, {r4, r5, r6, r7, r8, sp}@ r2 = compat
@ sp = stack pointer

movfp, #0@ Clear BSS (and zero fp)
1:cmpr4, r5
strccfp, [r4],#4
bcc1b

strr9, [r6]@ Save processor ID
strr1, [r7]@ Save machine type
#ifdef CONFIG_ALIGNMENT_TRAP
orrr0, r0, #2@ ...........A.
#endif
bicr2, r0, #2@ Clear 'A' bit
stmiar8, {r0, r2}@ Save control register values
bSYMBOL_NAME(start_kernel)//跳转到start_kernel
初始化BSS段,全部清零,BSS是全局变量区域,重新设置堆栈指针,指向init_task的堆栈。init_task是系统的第一个任务,init_task的堆栈在task structure的后8K,我们后面会看到跳到start_kernel(),此函数主要是C语言,做一些初始化外设建立MMU页表等功能。
start_kernel在 /linux/init/main.c中定义:

asmlinkage void __init start_kernel(void)
{
char * command_line;
unsigned long mempages;
extern char saved_command_line[];
lock_kernel();
printk(linux_banner);
setup_arch(&command_line); //arm/kernel/setup.c
printk("Kernel command line: %s ", saved_command_line);
parse_options(command_line);

trap_init(); //中断的初始化
init_IRQ();//中断处理函数的初始化
sched_init();
softirq_init();//软中断初始化
time_init();

start_kernel中的函数个个都是重量级的,首先用printk(linux_banner);打出系统版本号,系统才刚开始,printk和 printf不同,他首先输出到系统的一个缓冲区内,大约4k,如果登记了console,则调用console->wirte函数输出,否则就一直在buffer里呆着。所以,用printk输出的信息,如果超出了4k,会冲掉前面的。在系统引导起来后,用dmesg看的也就是这个buffer中的东东。
下面就是一个重量级的函数:
setup_arch(&command_line); //arm/kernel/setup.c
完成内存映像的初始化,其中command_line是从bootloader中传下来的。

void __init setup_arch(char **cmdline_p)
{
struct tag *tags = (struct tag *)&init_tags;
struct machine_desc *mdesc;
char *from = default_command_line;

ROOT_DEV = MKDEV(0, 255);

setup_processor();
mdesc = setup_machine(machine_arch_type);
machine_name = mdesc->name;
;
}
ROOT_DEV是宏,指明启动的设备。 
下面是init_mm的初始化,init_mm定义在/arch/arm/kernel/init_task.c:
struct mm_struct init_mm = INIT_MM(init_mm);
当建立起页表以后,余下只是做一些网张的初始化,外设的初始化等。
5.5编译Linux 内核
内核的编译包括以下几部分:建立内核源码的依存关系,建立内核映像,以及建立内核模块。
5.5.1建立依存关系
内核源码树中大多数文件都会于一些头文件有依存关系。要想顺利建立内核,内核源码树里各个makefile必须知道这些依存关系。依存关系建立期间会在内核源码树中每个子目录里产生一个隐藏的.depend文件。此文件内含子目录里各文件所依存的头文件清单。如同其他靠make建立的软件,自从上一次完成建立以来,如果要重新建立内核,只有在于头文件有依存关系的文件被改动后才需要经过重新编译的程序。

从内核源码树的根目录,以如下的命令来建立内核源码的依存关系:

$ make ARCH=arm CROSS_COMPLIE=arm-linux- clean dep
5.5.2建立内核
建立依存关系后,接着编译内核映像:

$ make ARCH=arm CROSS_COMPLIE=arm-linux- zImage

zImage这个建立目标用来指示makefile建立经gzip算法压缩过的内核映像。
5.5.3建立模块
内核映像正确建立后,接着建立内核模块:

$ make ARCH=arm CROSS_COMPLIE=arm-linux- modules

这个阶段的时间长短与“你选择不要-链接成主内核映像的一部分而要建立成模块的内核选项的数量“有很大的关系。这个阶段的时间很少会长过建立内核映像的时间。如同内核映像一样,倘若配置不符合目标版的需要或是内核选项有问题,这个阶段也可能会失败。

内核映像和内核模块都正确建立后,表示我们已经准备好可以为目标版进行安装工作了。有一件事很重要

,在进行之前要特别注意,如果需要清理内核的源码,让他恢复到配置设定和依存关系建立或编译之前的

初始状态,可以使用如下的命令:

$ make ARCH=arm CROSS_COMPLIE=arm-linux- distclean

但务必在执行次命令之前将内核的配置文件备份起来,因为make distclean会清除前面这几个阶段产成的

文件,包括.config文件和所有目标文件以及内核映像。
5.5.4安装内核
最后,我们将产生的内核映像以及它的模块复制到目标板上。
5.6总结
本章主要分析了Linux移植的相关文件,并对linux 的起动流程进行了比较详细的介绍。


    结束语

   本文从嵌入式系统的概念入手,详细介绍了Bootloader 的特点、作用、分析了u-boot的结构,及移

植要点。接着对linux 的结构、移植的步骤作了详细的说明。最后介绍了linux驱动编写要点,以一个简

单字符型驱动实例做了说明。 
嵌入式Linux是近几年才发展起来的新事物,它以其诸多优点为整个IT行业带来新的活力,成为后PC时代

的重要标志之一,促进了整个嵌入式领域的竞争与发展,为我国的IT行业带来了新的机遇。但嵌入式

Linux技术还不是特别成熟,现正处于快速发展阶段,其广泛的应用还尚需时日,这有待于我们进一步的

深入分析研究。

 

 

参考文献:

[1]. 毛德操 胡希明, 《Linux 内核情景分析》上下册,?   浙江大学出版社
[2]. 毛德操 胡希明, 《嵌入式系统采用公开源码和StrongARM/XScale 处理器》,        浙江大

学出版社
[3]. ALESSANDRO RUBINI等著 魏永明等译,《Linux 设备驱动程序》(第二版)                     

                                 中国电力出版社
[4]  陈莉君,《Linux操作系统内核分析》,人民邮电出版社
[5]   KARIM YAGHMOUR 著, 《构建嵌入式Linux系统》,中国电力出版社
[6]  周魏松等, 《Linux系统分析与高级编程》,机械工业出版社
[7]  李善平 郑扣根,《Linux内核分析和实验教程》,机械工业出版社

阅读(907) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~