深入理解BootLoader.html.pdf

上传人:紫竹语嫣 文档编号:5518211 上传时间:2020-05-28 格式:PDF 页数:53 大小:7.31MB
返回 下载 相关 举报
深入理解BootLoader.html.pdf_第1页
第1页 / 共53页
深入理解BootLoader.html.pdf_第2页
第2页 / 共53页
深入理解BootLoader.html.pdf_第3页
第3页 / 共53页
深入理解BootLoader.html.pdf_第4页
第4页 / 共53页
深入理解BootLoader.html.pdf_第5页
第5页 / 共53页
点击查看更多>>
资源描述

《深入理解BootLoader.html.pdf》由会员分享,可在线阅读,更多相关《深入理解BootLoader.html.pdf(53页珍藏版)》请在三一文库上搜索。

1、前言 BootLoader就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射,从而将系统的软硬件环境设置 成一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。 在嵌入式系统中,通常没有像PC中的BIOS那样的固件程序,因此整个系统的加载启动任务就完全由BootLoader来完成。BootLoader是CPU上电后运行的第一段程 序,它的作用就是对嵌入式系统中的硬件进行初始化,创建内核需要的一些信息并将这些信息通过相关机制传递给内核,从而将系统的软硬件环境带到一个合适的状 态,最终调用操作系统内核,真正起到引导和加载内核的作用。实际上

2、,一个功能比较强大的BootLoader已经相当于一个微型的操作系统了。 不同的CPU体系结构有不同的BootLoader。有些BootLoader支持多种体系结构的CPU,比如U-Boot就同时支持ARM体系结构和MIPS体系结构。除了依赖于CPU的体 系结构外,BootLoader实际上也依赖于具体的嵌入式板级设备的配置。也就是说,对于两块不同的嵌入式板而言,即使它们是基于同一种CPU而构建的,要想让运行 在一块板子上的BootLoader程序也能运行在另一块板子上,通常需要修改BootLoader的源程序。因此每款嵌入式产品的BootLoader都是独一无二的,但我们可以总结出 开发或者

3、维护特定BootLoader需要哪些背景知识,掌握了这些背景知识,我们就可以做到以不变应万变。 为了引导操作系统,BootLoader与CPU体系结构和操作系统有着非常紧密的联系。在本书中,我们以ARM体系结构和嵌入式Linux操作系统为原型讲述BootLoader 的原理。通过理论联系实践的方法论,让读者理解BootLoader的概念,掌握开发BootLoader的方法。本书适合从单片机向ARM过渡并希望了解嵌入式开发的在校学生 以及想要从事BootLoader开发移植工作的工程师参考使用。 下面来梳理一下需要哪些背景知识: 1)因为我们引导的操作系统是Linux,所以需要熟悉Linux开发

4、环境。BootLoader也是一串代码,我们必须了解Linux下编辑器和编译器的用法;作为Linux下的开发 者,对shell脚本和Make工程管理工具也要有必要的了解。第1章对BootLoader做了基本介绍,第2章则详细介绍了Linux开发环境。 2)因为我们仅仅以ARM体系结构作为原型,所以会在第35章讲述ARM体系结构、ARM指令集和ARM独特的寻址模式。 3)很多嵌入式开发人员都是通信、电子和自动化等专业的,对于计算机的编译和链接掌握得不够深入,因此第6章介绍编译和链接。控制链接行为的链接脚本是 特有的脚本语言,第7章比较详细地介绍了该脚本的细节。 4)第8章采用流水灯的实验融会贯通

5、前面各章节的理论,接着第9章对U-Boot代码展开分析,最后在第10章一步一步地实现了BootLoader。在此过程中,我们更注 重ARM体系结构、编译链接等知识的融会贯通,不会花太多篇幅讲解DRAM、MMC等模块的驱动。 本书能够成书,要感谢LinkSprite团队的资源支持和左宝柱在pcDuino v3平台上的帮助。最后感谢家人和朋友对我的支持,鼓励我完成这项值得用心去做的工作。 因作者水平有限且时间仓促,书中难免存在疏漏,还望广大读者不吝赐教。 胡尔佳 2014年11月 第1章 BootLoader的概念 本章导读 本书讲述的主题是BootLoader,那么BootLoader是什么呢?

6、首先从字面上来看,BootLoader是一个英文单词,那它是什么意思呢?如果使用词典查询一下,会得到 这样的结果:“对不起,词库中没有您查询的单词!您要查找的是不是:Boot Loader Boot-Loader”Boot Loader计引导装载程序。 可以看到BootLoader应该是Boot和Loader这两个单词的组合。Boot的意思是“计算机科学引导”,而Loader是“加载器”的意思。因此可以推断 BootLoader这个词是随着计算机科学的发展而衍生出现的。 1.1节介绍BootLoader在嵌入式系统或者计算机系统中所扮演的角色。 1.2节结合半导体芯片技术、计算机技术以及软件工

7、程技术的发展历史来探究BootLoader的出现过程,从而说明BootLoader的来历和它在整个计算机系统中的 定位、概念以及作用。 1.3节阐述BootLoader的概念,并以MCU下的BootLoader、嵌入式ARM和Linux下的BootLoader和PC下的引导流程为例进行说明。 1.1 BootLoader的角色 当一个嵌入式开发板上电时,哪怕执行最简单的程序,都要初始化非常多的硬件。每种体系结构、处理器都有一组预定义的动作和配置,它们包含从单板的存储 设备获取初始化代码的功能。最初的初始化代码是BootLoader的一部分,它负责启动处理器和相关硬件设备。 在上电复位时,大多数

8、处理器都有一个获取第一条执行指令的默认地址。硬件设计人员利用该信息来进行存储空间的布局。这样一来,上电的时候可从一个通用 的已知地址获取代码,然后建立软件的控制。 BootLoader提供最初的初始化代码,并初始化单板,这样就可以执行其他的程序。最初的初始化代码都是由该处理器体系结构下的汇编语言写成。当然,在 BootLoader已经执行完基本的处理器和平台的初始化之后,它的主要工作就是引导完整的操作系统。它将定位、加载操作系统,并将控制权移交给操作系统。另 外,BootLoader可能含有一些高级特性,比如校验OS镜像、升级OS镜像、从多个OS镜像中选择性引导。与传统的PC-BIOS不同,当

9、操作系统获取控制权后,嵌入 式下的BootLoader就不复存在了。 1.2 BootLoader的来历 我们从4个角度来阐述BootLoader的来历,第一个是从半导体芯片技术(特别是处理器的发展)来看Boot的出现: 在集成电路只读存储器出现之前,早期的计算机ENIAC在存储中并没有程序,而是通过连接电缆的配置来解决问题。ENIAC中并没有自举电路或引导程序,因为 它只要上电,其硬件配置就开始解决问题。在早期的计算机中,根本就没有BootLoader的概念,连Boot这个名称都没有。 稍晚时间出现的IBM 701计算机(19521956年)有一个“Load”按键,按下它,可以从外部储存中加

10、载最初的36位的字到主存中。有一个加载选择开关来决 定外部储存是穿孔卡片、磁带,还是磁鼓(看到这里是不是觉得它和现代的嵌入式系统有些类似,在有些嵌入式电路板中,同样用一些拨码开关来控制电路板从SD 卡、Flash等多种存储设备中选择某一种存储设备进行启动)。接下来的18位半字作为一条指令执行,它通常会读取更多的字到主存中。这时开始执行Boot程序,它 将依次从外部存储媒质加载更大的程序到主存中。在1958年,“Boot”这个计算机名词便开始使用了。请读者注意,这里的“Load”按键和Loader是有本质区别 的:“Load”是通过外部操作来进行加载的,而Loader是软件实现的加载器,它可以自

11、行完成加载的动作。 Boot这个词的原意是靴子,那靴子怎么会与计算机系统中的引导发生关联呢?原来,这里的Boot是Bootstrap(鞋带)的缩写,它来自于一句谚语:“Pull oneself up by ones own bootstraps”,直译的意思是“拽着鞋带把自己拉起来”,这当然是不可能的事情。最早的时候,工程师用它来做比喻,比如自举电路 振荡器(Bootstrap Generator)就是指,不外加激励信号而自行产生恒稳和持续的振荡。对于早期计算机的启动,也存在这样一个问题:必须先运行程序,然后计 算机才能启动,但是计算机不启动就无法运行程序。于是,当时的人们想尽各种办法,他们把

12、一小段程序装进内存,之后计算机就能正常运行了。因此,工程师们将 这个过程称为“拉鞋带”,久而久之就简称为Boot了。而且随着处理器的发展,Boot处理的事务越来越多,比如对CPU运行模式的设置,比如对CPU内部时钟的配 置,还包括对Cache和MMU的配置。 第二个是从存储技术的发展来看Loader的出现: 首先介绍两类半导体存储器:ROM和RAM。ROM是Read Only Memory的缩写,RAM是Random Access Memory的缩写。ROM在系统停止供电的时候仍然 可以保持数据,而RAM通常在掉电之后就丢失数据,但是其存储单元的内容可按需随意取出或存入,且存取的速度与存储单元

13、的位置无关。 ROM分为ROM、PROM、EPROM和EEPROM等。只读存储器(ROM)是只能读取资料的内存。其资料内容在写入后就不能更改。此种内存的制造成本极低, 常用于电脑中的开机启动。可编程只读存储器(PROM)一般可编程一次。PROM存储器在出厂时各个存储单元皆为1,或皆为0,用户在使用时,再采用编程的方法 使PROM存储所需要的数据。可擦除可编程存储器(EPROM)可多次编程,这是一种便于用户根据需要来写入,并能把已写入的内容擦去后再改写,即为一种多次 改写的ROM。由于能够改写,因此能对写入的信息进行校正,修改错误后再重新写入。电子可擦除可编程只读存储器(EEPROM)的运作原理

14、类似于EPROM,但是 擦除的方式更加方便。 RAM按照存储单元的工作原理分为静态随机存储器(SRAM)和动态随机存储器(DRAM)。SRAM的存取速度要比DRAM快,同时价格也更高。在计算机中 SRAM常用来作为Cache,而DRAM常作为内存来使用。 闪存(Flash Memory)是一种高密度、非易失性的读/写半导体存储器。它既有EEPROM的特点,又有RAM的特点,是一种全新的存储结构。Nor Flash和 Nand Flash是市场上两种主要的闪存技术。Intel公司于1988年首先开发出Nor Flash技术,彻底改变了原先由EPROM和EEPROM一统天下的局面。紧接着,1989

15、 年,东芝公司发表了Nand Flash结构,强调降低每比特的成本,提供更高的性能,并且Nand Flash像磁盘一样可以通过接口轻松升级。Nor Flash和Nand Flash有 几个重要的区别:Nor Flash带有SRAM接口,有足够的地址引脚来寻址,可以很容易地存取内部的每一个字节,可以做到芯片内执行(XIP),应用程序可以直接在 Nor Flash内运行。Nand Flash使用复杂的I/O口来串行地存取数据,并且读写操作都是块操作,因此它无法做到XIP。Nor Flash的读取速度比Nand Flash稍快,但 是写入速度却要慢很多。Nand Flash的存储密度更高,成本更低。

16、因此,很多嵌入式单板都使用小容量的Nor Flash来运行启动代码,而使用Nand Flash存储其他 信息。 假如说当时已经有一种既可以运行程序,而且价格低廉、存储空间大的非易失性存储的设备,那么我们的所有程序都可以存放在此类存储设备中,这样就不存在 加载的问题了。然而实际上,一般的计算机系统都使用多级存储器层次结构,既有存储空间大、价格低廉、运行速度慢的非易失性存储设备(磁盘),也有空间小、 价格高、运行速度略快的非易失性存储设备(PROM等),还有价格高,运行速度非常快的易失性存储设备(RAM)。这样的层次结构会保证硬件最佳的性价比, 然而从软件上,我们必须引入Loader。它将自动完成

17、从非易失性存储设备向易失性存储设备加载程序的功能。 在现实的发展过程中,随着集成电路只读存储器时代的到来,由于集成电路技术的发展和ROM的出现(包括可掩码编程的ROM、可编程的ROM(PROM)、 可擦除可编程的ROM和Flash存储器),引导流程发生了极大的改变。它们使得固件引导程序开始安装到计算机中。 通常情况下,在上电和复位的条件下,每个微型处理器会执行下面两种可能的启动流程:开始执行从某一特定地址开始的代码;在特定地址处查询代码,然 后跳转到指定地址,开始执行代码。运用这种微处理器的系统使用ROM来保存这些特定的位置,如此,系统就不需要操作人员的干预就能开始运转。举个例 子,Intel

18、 x86处理器一直从位于FFFF:0000的指令处开始运行,而MOS 6502处理器,读取位于$FFFD(MS byte)和$FFFC(LS byte)的两个字节长度的向量地 址,然后跳转到该地址开始运行引导程序。 Apple的第一代计算机(1976年推出的Apple1)使用了PROM芯片而不再需要前面板的配置。因此当时Apple的宣传广告是“No More Switches,No More Lightshttp:/ firmware in PROMS enables you to enter,display and debug programs(all in hex)from the key

19、board”。 由于当时只读存储器的造价很高,Apple II系列采用多种存储器来构建存储器系统,以保证性能和价格的最优化。它使用一串小步骤来引导磁盘上的操作系统, 每一个小步骤都将控制权递交到愈加复杂的引导流程中。我们可以预见,每个步骤都必须能控制下一个步骤所用的存储器的读取操作,从而才能将其内容加载到特定 的位置以执行,这样就有了Loader(加载器)的概念。结合Boot和Loader的概念,BootLoader是既能“自举”又能“举人”的一段程序,由此,BootLoader在整 个计算机系统中可以说起着举足轻重的作用,任何一个计算机系统都需要一个稳定强大的BootLoader。 第三个是

20、从软件语言的发展(特别是C语言的出现)来看BootLoader的发展: 最早的计算机用于读取纸带的数据,上面记录着二进制格式的机器指令,计算机每读一条机器指令就执行一条机器指令。机器指令繁多,而且不适合人来阅读和 使用,所以后来出现了汇编语言,汇编语言实质上是机器语言的助记符。汇编语言中出现了函数的概念,那么在编写汇编代码时,就需要对栈进行小心的设计,并注 意保护好上下文现场。再往后,出现了C语言,C语言是一种更抽象的语言,它是针对于程序员的语言,隔离了许多底层上的概念,比方说隔离函数调用栈的设计。 并且C语言无法完成很多底层操作,因为只有汇编语言才能使用芯片架构上的一些特殊的指令。因此对于B

21、ootLoader来说,它是由汇编和C语言混合编码的,Boot中 的汇编代码完成只能用汇编指令完成的操作,并提供C语言的运行环境,还需要对栈指针和堆空间进行初始化和分配。C语言的出现无疑加快了BootLoader的设计和 开发,从而促进了BootLoader的发展。 第四个是从软件工程的角度来看BootLoader的发展: 最初的计算机软件统称为Firmware,而随着软件的复杂度提高,操作系统的出现,我们相应地将BootLoader独立出来,并作为专门的一个固件。BootLoader 和操作系统分开设计,使得各自的扩展性、跨平台、可移植性都大大提高。另外从软件工程的模块化设计、分层设计来讲,

22、像UBoot这样的BootLoader的设计越来 越像Linux了,其代码布局和Linux十分类似,使用了按照模块划分的结构,并且充分考虑了体系结构和跨平台问题。 1.3 BootLoader的概念 这一节我们全面地介绍BootLoader的基本概念。 简单地说,BootLoader就是在操作系统内核运行前执行的一段小程序。通过这段小程序,可以对硬件设备,如CPU、SDRAM、Flash、串口等进行初始化,也 可以下载文件到系统板、对Flash进行擦除和编程,真正起到加载和引导内核镜像的作用。但是随着嵌入式系统的发展,BootLoader已经逐渐在上述基本功能的基础 上进行了扩展,BootLo

23、ader可以增加更多的对具体系统的板级支持,即增加一些硬件模块功能上的使用支持,以方便开发人员进行开发和调试。 从这个层面上看,功能扩展后BootLoader可以虚拟地看成一个微小的系统级的代码包。BootLoader是依赖于硬件实现的,特别是在嵌入式系统中。不同的体系 结构下,对BootLoader的需求是不同的;除了体系结构,BootLoader还依赖于具体的嵌入式板级设备的配置。也就是说,对于两块不同的嵌入式电路板而言,即使 它们基于相同的CPU构建,运行在其中一块电路板上的BootLoader,都未必能够运行在另一块电路板上。 当现代的计算机关机时,它的软件,包括操作系统、应用程序和数

24、据,都会存放在非易失性存储设备上,比如硬盘、CD、DVD、Flash存储卡(SD卡)、USB 盘和软盘。当计算机上电时,在它的RAM中是没有操作系统和加载器的。计算机会首先执行存放在ROM中的相对较小的程序,读取小容量的必需数据,这样才能访 问非易失性存储设备中的操作系统和数据,并将其加载到RAM中。 这个过程中的小程序被称为“Bootstrap Loader”“Bootstrap”或“Boot Loader”。这段小程序的目的是加载其余的数据和程序到RAM中并开始执行。若使 用多阶段的Boot Loader,它们会一个接一个的加载。 有一些计算机系统,当从操作人员或者外设接到一个引导信号后,

25、会加载少量的固定指令到内存中的特定地址,初始化至少一个CPU,然后让CPU开始执行这些 指令。这些指令通常会对一些外围设备做输入操作。有一些系统会向外围设备或者I/O控制器直接发送硬件命令来进行非常简单的输入操作,加载一小部分Boot Loader指令到内存中,I/O设备的一个信号会通知CPU开始执行指令。 小型的计算机经常使用不那么灵活但是更自动化的Boot Loader机制来保证计算机在预定的软件配置下能够快速启动。在很多桌面型计算机中,比如,在 ROM(IBM PC的BIOS)的预定义地址中有一软件(有一些CPU,包括Intel的x86系列,在复位后无需外部的辅助就可以执行这个软件),这

26、个软件有查找booting 过程中用到的设备的基本功能,然后从设备的特定部分(通常是boot sector)加载一个小程序。 Boot Loader会有一些特殊的约束,特别是大小。比如,在IBM PC和其兼容机上,引导扇区只能是系统存储中的32KB(后来扩充到64KB),而且不能使用 8088/8086处理器不支持的指令。 BootLoader的启动过程可以是单阶段的,也可以是多阶段的。通常多阶段的BootLoader能提供更为复杂的功能,以及更好的可移植性。从固态存储设备上启动 的BootLoader大多数是二阶段的启动过程,即启动过程可以分为stage1和stage2两部分。 第二阶段的B

27、ootLoader,比如GNU GRUB、BOOTMGR、Syslinux、NTLDR或者BootX,能够正确加载操作系统并移交控制权;接着操作系统会进行一系列初 始化并可能加载额外的设备驱动。第二阶段的Boot Loader在自己的操作过程中不需要驱动,但是需要由系统固件(比如BIOS或者Open Firmware)提供通用存储 访问方法,然而这样会限制硬件的功能,降低其性能。 很多Boot Loader(比如GNU GRUB、Window的BOOTMGR和Windows NT/2000/XP的NTLDR)能够为用户提供多个引导选项。这些选项可以是多个不同的 操作系统(不同分区或者不同驱动器

28、上的多个引导),也可以是同一操作系统的不同版本,还可以是同一操作系统的不同加载选项(比如说进入恢复模式或者安全模 式),甚至可以是一些不是操作系统的独立程序,比如内存测试程序(memtest86+)。有一些BootLoader会加载其他的BootLoader,比如GRUB不会直接加载 Windows,而是加载BOOTMGR。通常有一个倒计时选择可以让用户通过按键来进行选择。倒计时结束后,默认的选择是自动运行正常的引导程序。 当计算机能够与用户交互时,或者操作系统能够运行系统程序和应用程序时,引导流程可以看成结束了。通常现代的个人计算机在1min以内完成引导,其中15s 用于上电自检和初步引导,

29、剩下的时间用于加载操作系统和其他软件。操作系统加载完成后花费的时间可以缩短至3s,仅仅只是启动核心的服务。而大型服务器可能 需要数分钟来引导和启动服务。 很多嵌入式系统必须非常迅速地完成引导。比如说,花一分钟来等待数字电视或者GPS设备的启动是不被用户接受的。因此这类设备在ROM或者Flash中有特殊 的软件系统,使得设备更快速地启动,几乎不需要加载,因为在生产设备的时候加载的内容已预先存在ROM中了。 其他类型的引导顺序如下。 一些现代的CPU和微处理器(例如TI OMAP),甚至一些DSP在它们的芯片中含有已经烧录好引导代码的引导ROM,那么这样的处理器能够执行相当复杂的引 导流程,比如它

30、能够从Nand Flash、SD或MMC卡等多种存储设备引导。由于很难用电路逻辑来处理这些设备,那么这种情况下就使用集成的引导ROM。引导ROM 能够提供比硬件逻辑更灵活的引导顺序。比如,引导ROM能够试着从多个引导源设备执行引导过程。另外,引导ROM也能够从串口,比如UART、SPI或者USB等接 口来加载BootLoader或者诊断程序。当存放在非易失性存储中的引导程序被擦除时,这个特性可以用来恢复系统,而且当系统中的非易失性存储中没有程序可用 时,也可以对非易失性存储进行编程。 有些嵌入式系统可能包含中间的引导流程步骤,由内建的引导ROM加载到系统RAM中。由于平台的限制,比如说RAM大

31、小的限制,需要加一个中间的引导流程 步骤,比如Das U-Boot,它就有SPL的架构:由SPL引导U-Boot,接着U-Boot引导系统。 我们有时会使用硬件的调试接口,比如JTAG(JTAG是一个标准的流行接口,许多CPU和微处理器都带有JTAG接口)来控制系统。这类调试接口可以用于向可引 导的非易失性存储设备中烧写Boot Loader代码。另外,这类调试接口可以用于加载一些诊断或者引导代码到RAM中,并且使得处理器开始执行代码。当可引导的存 储设备没有程序时,它可以用于恢复嵌入式系统,也可以用于不含有内建引导ROM的处理器。 有些微处理器包含不能获取系统控制权或者直接运行代码的特殊硬件

32、接口,但是它们通过一些简单的协议可以将引导代码注入非易失性存储设备中。那么在生产 过程中,使用这类接口向非易失性存储中注入引导代码。当系统复位后,微处理器开始执行非易失性存储中的代码,就像使用ROM引导一样。Atmel AVR微处理器 就使用了该项技术。很多情况下这类接口都是由硬件逻辑设计的。另外的一些情况下,这类接口可以通过配置GPIO运行内建的引导ROM中的代码来创建。 大多数DSP有一个串行模式的引导和一个并行模式的引导。在DSP中,系统设计中通常有一个微处理器,它负责所有的系统行为、中断处理、外部事件和用户接 口等。而DSP只专注信号处理。在这类系统中,DSP被其他的处理器引导。这个处

33、理器被称为主处理器(Host Processor),有时候也被称为主机,因为它首先从自 己的存储中引导,接着控制所有的系统行为,包括引导DSP,最后控制DSP的运行。DSP的引导内存通常很小,而且依赖于主处理器。用于手机、调制解调器、音视 频播放器等设备的系统都是这么设计:DSP和CPU一体化。另外,很多FPGA芯片在上电时从外部的EEPROM中加载配置(配置ROM)。 我们较为全面地介绍了BootLoader的概念和发展。随着计算机技术的发展,BootLoader这个新名词开始被使用,并且它的定位越来越清晰,功能也越来越多, 越来越强。细心的读者可能还发现了,我们对存储技术的讲解比对CPU技

34、术的讲解要多,这是因为系统的存储架构对BootLoader设计的影响比CPU的影响要大许 多。现在我们将发展过程缩略如图1-1所示,其中反映了几种不同体系下的BootLoader。虽然复杂程度相差许多,但是本质是一致的:都是先引导再进行加载。下面 一小节介绍BootLoader的概念,并对MCU下的BootLoader、嵌入式ARM和Linux下的BootLoader和PC下的引导流程的异同做了一个概述。 图1-1 不同体系下的BootLoader 1.4 本章小结 本章首先从4个不同的角度来叙述BootLoader的由来和发展过程,进而阐述BootLoader的概念。在对BootLoader

35、有了初步认识以后,抛出一些掌握 BootLoader概念所必须理解的技术细节,而这些技术细节将在后续几章中慢慢铺开讲述。 第2章 Linux开发环境 本章导读 “工欲善其事,必先利其器。”既然是开发嵌入式Linux下的BootLoader,那么必须选择合适的编辑器和编译器加以熟练使用。 我们选用的编辑器是Linux下流行的编辑器Vim,编译器是Linux下最流行的GCC编译器和arm-linux-gcc交叉编译器。开发嵌入式Linux下的BootLoader使用的 语言是汇编语言和C语言,汇编语言和C语言都是最早开发出来的编译型语言,基于这两种语言编写的程序在执行之前都需要编译器将代码编译为机

36、器码的二进制文 件才能运行。 最后,对于在Linux下阅读和开发BootLoader这样的工程,也必须对Linux下的shell脚本以及工程管理工具Make做一些学习。 2.1节介绍嵌入式Linux下最流行的常用编辑器Vim的基本使用。 2.2节介绍GNU编译器套件GCC和适用于ARM体系结构的交叉编译器。 2.3节介绍常用shell命令以及简单shell脚本的写法。 2.4节介绍Linux下强大的工程管理工具Make和Makefile。 2.1 编辑器Vim 圈内有一种说法:世界上有三种程序员,一种用Emacs,一种用Vi,剩下的使用其他编辑器。Emacs号称是“神的编辑器”,而Vim是“编

37、辑器之神”;这么说 是有群众基础和道理的,谁用谁知道。其实,萝卜青菜,各种所爱,习惯就好。Vi的设计采用了UNIX传统的设计哲学“do one thing and do it well”,而且 通过Vim脚本无限地扩展功能,使其成为了编辑器中的“神器”。 2.2 编译器GCC和交叉编译器 2.2.1 GCC的编译流程 GCC(GNU Compiler Collection,GNU编译器套装)是一套由GNU开发的编程语言编译器,它是一套基于GPL及LGPL许可证所发布的自由软件,也是GNU 计划的关键部分,亦是类UNIX操作系统及苹果Mac OS X操作系统的标准编译器。GCC(特别是其中的C语

38、言编译器)常被认为是跨平台编译器的事实标准。 最初学习编程时都使用IDE,写好代码后单击Build按钮然后就开始编译链接最终的可执行程序了,非常的方便快捷,但是这些IDE屏蔽了许多编译链接的过程细 节。而当我们在嵌入式Linux平台下做开发时,经常会遇到编译和链接出现问题,所以需要我们关注编译和链接的细节。深入理解了编译链接的原理和机制,就能够 游刃有余地解决这些问题了。甚至可以说,命令选项是深入理解编译链接的第一步。 在Linux下,不理解编译链接,就没办法真正学习掌握BootLoader。而本章了解编译器GCC的使用步骤。当我们使用GCC编译程序时,只需使用最简单的命 令“gcc test

39、.c”生成可执行文件,默认的文件名是a.out。 然而,上述一条命令可以分为四个步骤,分别是预处理、编译、汇编和链接。 第一步预处理的过程相当于如下命令(-E表示只进行预编译): gcc E test.c o test.i 预处理的主要作用就是通过预处理的内建功能对一个资源进行等价替换。最常见的比如文件包含、条件编译和宏替换。 第二步编译过程就是把预处理的文件进行一系列词法分析、语法分析、语义分析及优化生成相应的汇编代码文件,这个过程是通常所说的整个程序构建的核心部 分,也是最复杂的部分之一。编译过程相当于如下命令: gcc S test.i o test.s 第三步汇编器将汇编代码转为机器码

40、,每一个汇编语句几乎都对应着一条机器指令。汇编过程可以用如下的命令: gcc c test.s o test.o 这个阶段GCC实际调用了另一个程序as完成汇编工作。 第四步链接是编译的最后一个阶段,它将各个目标文件链接起来,生成可执行文件。其命令如下所示: gcc test.o o test 在这个阶段GCC实际调用了另一个程序ld完成链接工作。 通常在实际的编译中并不用一步步来执行,一般都使用如下命令: gcc test.c o test 2.3 常用shell命令和脚本 作为Linux来源的UNIX系统最初根本就没有图形化界面,所有的任务都是通过命令行来完成。因此UNIX下的命令行系统得

41、到了很大的发展,并且成为一个功能 强大的系统。Linux系统继承了这一特点,许多强大的功能都可以从shell中轻松实现。shell非常强大,以至于有许多书专门介绍shell脚本。这里我们假定读者了解 shell中常用的基本命令和脚本文件的编写格式。 本节我们将介绍在阅读代码时经常用到的两个特别有用的命令行工具:find和grep;还将介绍体现Linux设计哲学的管道和重定向的概念。 2.4 工程管理Make和Makefile 一个软件工程通常会包含成百上千个文件,如果每次编译都通过命令行手动编译或是写一个批处理脚本对其进行编译,往往很麻烦而且效率较低。Windows下 有许多软件开发的IDE工

42、具,IDE工具中都有工程的概念。新建了工程以后,IDE工具会自动维护其中的各种文件,无论是添加、删除或是修改文件,都能根据需要对 工程文件进行处理并高效地完成编译工作。举个例子,比如跨平台的Qt,新建一个Qt的工程后都会生成一个以.pro为后缀的工程文件,这个工程文件定义了该工程使 用了哪些基本的库、包含的头文件和C+源文件等。Qt有内部的工具根据工程文件生成Makefile文件,最后根据生成的Makefile文件进行编译链接。如果不接触类 似Qt这样跨平台的工具,使用Windows下的IDE可能无需关心工程到底是如何组织维护的,只需要单击Build等几个按键即可。而跨平台的Qt使用了UNIX

43、/Linux下的 功能强大、使用方便的工程管理工具,这就是Make和Makefile。其实,Windows下大多数的IDE都有Make这个命令,比如Delphi的make、Visual C+的 nmake,只是它们都深深地被IDE隐藏了起来。 Linux程序员如果不会使用Make工具以及通过编写Makefile文件来构建和管理自己的工程,那么就不能算一个专业的Linux程序员。通过Make工具能够比较容 易地构建一个属于自己的工程,只需要一个命令就可以完成编译链接以及最后的执行。它极大地提高了实际项目的工作效率,几乎所有的Linux下的项目都会使用 它,尤其在做BootLoader和内核移植时

44、,在很多情况下就是阅读各层级的Makefile并修改配置。在阅读优秀开源代码的学习过程中,第一步就是阅读Makefile,从 全局把握整个代码结构。可以说Makefile是分析和构建大型工程的必备知识,是分析大型工程的入口点,是构建大型工程的利器。在实际项目开发中,能不能构建整 个项目的Makefile,从侧面反映了一个人对整个项目的理解程度和其个人是否具备完成大型工程的能力。 2.5 本章小结 本章介绍了Linux下的开发环境,包括编辑器Vim、编译器GCC、shell脚本以及工程管理工具Make,其中的每一项都值得用一本书来详尽地讲述。这里仅仅给 出它们的基本用法,请读者在学习过程中加强实

45、践,加强对用法的扩展学习,并熟练地运用在BootLoader的开发学习中。 第3章 ARM体系结构 本章导读 ARM是一款RISC处理器,因此它集成了以下典型的RISC架构的特性: 数量很多的通用寄存器。 使用load/store的体系结构操作寄存器中的数据,而不直接操作内存中的数据。 简单的寻址模式,所有的load/store地址都由寄存器内容和指令格式决定。 采用统一固定长度的指令格式来简化指令的译码。 除此之外,ARM体系结构还提供一些独特的特性: 在绝大多数数据处理指令中包含算术逻辑和移位逻辑,最大化的高效利用ALU和移位器。 使用自增和自减的地址模式来优化程序循环处理。 load/s

46、tore指令可以批量传输数据,从而提高数据传输的效率。 几乎所有的指令都可以条件执行,从而提高指令执行效率。 这些在基本RISC体系上的增强属性使得ARM处理器在性能、小的代码体积、功耗和硅片面积几方面取得极佳的平衡。 3.1节介绍ARM的几种处理器模式。 3.2节介绍ARM处理器的异常模式。 3.3节介绍ARM下的寄存器构成。 3.4节紧接着详细介绍ARM下的通用寄存器。 3.5节介绍ARM下的程序状态寄存器。 3.1 处理器模式 ARM处理器支持表3-1中的7种处理器模式。 表3-1 ARM处理器模式 通过软件操作及外部的中断或异常处理可以切换模式。 大多数应用程序在用户模式下执行。当处理

47、器处于用户模式时,正在执行的程序不能访问保护的系统资源和切换模式,除非有异常产生。操作系统正是利用这个 特性来控制系统资源使用的。 除用户模式,其他的模式都是特权模式。它们可以完全访问系统资源和自由切换模式,其中的5种又称为异常模式: FIQ IRQ Supervisor Abort Undefined 当发生特定的异常时,处理器就会进入相对应的模式中。每一种模式都有额外的寄存器使其不影响原先的用户模式状态。 最后一种模式叫做系统模式,它不是由异常进入的,而且它与用户模式有完全一样的寄存器。但它还是一种特权模式,并没有用户模式的限制。这种模式在操作 系统任务需要访问系统资源,但是不希望使用其他

48、异常模式的额外寄存器时使用。 3.2 异常 ARM支持7种异常类型,每种类型都有其特权处理模式。这7种异常类型是: 重启异常 未定义指令异常 软中断异常 预取中止异常 数据中止异常 中断异常 快速中断异常 由内部源或外部源产生的异常引起处理器处理一个事件,比如外部产生的中断或者试图执行一条未定义指令。处理异常之前的处理器模式通常会保存,这样当异 常处理完成后就会恢复原先的程序。同一时间可以出现多个异常。 ARM体系结构支持7种异常,表3-2列出了异常类型和其对应的处理器模式。当异常发生时,程序强制跳转到异常对应的地址。这些固定的地址称为异常向量。 表3-2 异常处理模式 当异常发生时,一些寄存

49、器就由异常模式下的相应寄存器取代。所有异常模式都有替代的寄存器R13和R14。快速中断模式还有用于快速中断处理的额外的寄存 器。 当进入异常处理程序中时,R14保存异常处理的返回地址。 R13为每种异常处理保存私有的栈指针,快速中断模式还有自己的R8R12可用,所以中断处理程序无须备份这些寄存器。 第六种特权处理模式系统模式也使用用户模式的寄存器。它用于运行需要访问存储系统和协处理器权限的任务。 3.3 ARM寄存器 ARM处理器共有37个寄存器: 31个通用寄存器:包括一个程序计数器在内。这些寄存器是32位大小的。 6个状态寄存器:这些寄存器同样是32位大小的,但是只有32位中的部分会分配或者需要执行。 所有的寄存器编排为有部分重叠的分组,由当前的处理器模式决定使用哪一个分组。在任何时候,15个通用寄存器(R0R14),一个或两个状态寄存器和程序 计数器是可见的。图3-1所示的每一列显示在指定处理器模式下的那些通用寄存器和状态寄存器是可见的。 图3-1 寄存器组成结构 3.4 通用寄存器

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 建筑/环境 > 建筑资料


经营许可证编号:宁ICP备18001539号-1