嵌入式Linux基础教程(第2版)(txt+pdf+epub+mobi电子书下载)


发布时间:2020-08-20 22:05:40

点击下载

作者:(美)克里斯托弗·哈利南(Christopher Hallinan)

出版社:人民邮电出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

嵌入式Linux基础教程(第2版)

嵌入式Linux基础教程(第2版)试读:

前言

虽然讲Linux的好书很多,但是本书汇集了专门针对嵌入式Linux开发人员的各方面信息和建议。实际上,有关Linux内核、Linux系统管理等方面的优秀书籍已经有很多了。本书也参考了我认为在同类书籍中最好的几本。

本书的很多内容来自实际的问题,有些问题是我作为嵌入式Linux顾问时一些开发工程师提出的,还有些问题是我直接参与商业嵌入式Linux开发时遇到的。

嵌入式Linux会给经验丰富的软件工程师带来一些独特的挑战。首先,那些有多年老式实时操作系统(Real Time Operating System,RTOS)开发经验的工程师会发现,他们的思维习惯很难从旧的环境转换到Linux;其次,经验丰富的应用程序开发人员常常难以理解多种开发环境的相对复杂性。

虽然这是一本面向嵌入式Linux开发初学者的基础教程,但我相信即使是经验丰富的嵌入式Linux开发人员也能从中获益,书中包含了我多年积累的实用建议和技巧。给实际的嵌入式开发人员的实用建议

本书介绍了嵌入式工程师怎样才能迅速掌握嵌入式Linux环境的新知识。书中没有重点讲解Linux内核原理,而是在讲解内核的章节侧重从项目角度介绍内核。你可以阅读专门介绍内核原理的优秀图书来了解相关知识。你可以从本书学到内核源码树的组织和布局,了解组成内核镜像的二进制组件和加载它们的方法,以及它们在嵌入式系统中的作用。

在本书中,你会学到Linux内核构建系统的工作原理,以及怎样将满足项目需求的具体变化融合到系统中。你会了解到Linux系统初始化的细节,包括内核空间初始化和用户空间初始化。你还能看到很多对嵌入式项目有益的建议和技巧,涵盖引导加载程序、系统初始化、文件系统和闪存,以及高级的内核与应用程序调试技术。第2版中新增了很多内容,很多章节都有更新,比如讲开源构建系统、USB和udev的几章都是新的,而且有相当篇幅探讨如何在嵌入式Linux项目中配置和使用这些复杂的系统。目标读者

本书需要读者具有一定的C语言编程基础,对局域网和因特网有基本的了解,理解IP地址的概念以及IP地址在简单局域网中的用法,还需要理解十六进制和八进制编码方式以及它们常见的用法。

本书也涉及一些C语言编译和链接中较为深入的概念,因此你要是能粗略复习一下C语言链接器的概念就更好了。同时,了解GNU make操作和语法对于阅读本书也很有帮助。本书不是什么

本书不是一本详细介绍硬件的指南。硬件设备种类繁多是嵌入式开发者所面临的一大困难。集成了一些外围设备的现代32位处理器,其用户手册动辄就有3000页。没有捷径可走。作为程序员,要想理解硬件设备就必须花费大量时间研读硬件数据手册和参考指南,同时要花费更多的时间针对这些硬件设备编写和测试代码。

这也不是一本讲述Linux内核或内部原理的书。本书不会深入讨论用来实现Linux虚拟内存管理策略和过程的内存管理单元(MMU)。已经有许多关于这个主题的优秀图书了,建议你读一读每章后面的“补充阅读建议”。排版约定

命令和代码语句使用Courier New字体。用户输入的命令使用加粗的Courier New字体。新名词和重要概念使用楷体加以强调。

路径名前面有3个点表示这是大家熟知但未明确指定的顶层目录。上下文不同,顶层目录也会不同,但一般都是指顶层的Linux源码目录。例如,…/arch/powerpc/kernel/setup_32.c指位于Linux源码树体系结构分支中的文件setup_32.c。实际的文件路径有可能是~/sandbox/linux.2.6.33/arch/power/kernel/setup_32.c。本书结构

第1章简要介绍了促使Linux迅速应用于嵌入式环境的因素,同时也介绍了与嵌入式Linux相关的几个重要标准和组织。

第2章介绍了很多嵌入式Linux相关的概念,这些概念是后续几章的基础。

第3章概述了几种较流行的用于搭建嵌入式Linux系统的处理器和平台,介绍了几款主要处理器厂商的重要产品,涉及所有主要的处理器体系结构。

第4章从另一角度探讨了Linux内核。这里没有重点讲解内核理论或其内部原理,只是介绍了内核的结构、布局和构建结构,目的是使读者从一开始就能以自己的方式学习这个庞大的软件工程项目。更重要的是,要知道哪些内容是必须重点关注的。这一章还详细讲解了内核构建系统。

第5章详细说明了Linux内核的初始化过程:把与体系结构和引导加载程序相关的镜像组件拼接成适合下载到闪存的内核镜像,最终通过嵌入式系统的引导加载程序启动。这一章的知识将帮助你定制Linux内核,使之满足你自己的嵌入式应用的需求。

第6章继续详细介绍初始化过程。当Linux内核完成自身的初始化后,应用程序将根据预先确定的方式继续初始化过程。读完这一章,你就具备了定制用户空间应用程序启动顺序的知识。

第7章主要介绍引导加载程序及其在嵌入式Linux系统中的作用。这一章以现在流行的开源引导加载程序U-Boot为例说明了移植的概念;还简要介绍了其他几种现在使用的引导加载程序,以便用户有特殊需求时可以有多种选择。

第8章介绍了Linux设备驱动程序模型,提供了很多开发设备驱动程序的背景资料,这些资料都在每章结尾的“补充阅读建议”中列出了。

第9章列举了目前嵌入式系统中使用的一些流行的文件系统,包括闪存设备上最常用的JFFS2文件系统。这一章还简要介绍了如何创建你自己的文件系统镜像,这也是嵌入式Linux开发人员所面临的一项艰巨任务。

第10章介绍了MTD(Memory Technology Device,内存技术设备)子系统。MTD是Linux文件系统和硬件内存设备(尤其是闪存)之间一种非常有效的抽象层。

第11章介绍了BusyBox,它是构建小型嵌入式系统最常用的工具。这一章讲述如何根据特殊需求来配置和构建BusyBox,随后介绍了仅使用BusyBox环境完成系统初始化的全过程。附录A列举了最新版本BusyBox提供的命令。

第12章详细介绍了典型交叉开发环境的特殊需求。这一章所介绍的一些技术能有效地提高嵌入式开发人员的工作效率,例如强大的NFS根目录挂载开发配置。

第13章介绍了一些有用的开发工具。这一章介绍了使用gdb进行调试,包括核心转储分析;并通过示例介绍了strace、ltrace、top和ps,以及内存性能评测工具mtrace和dmalloc。这一章最后介绍了几个更重要的二进制实用工具,如强大的readelf程序。

第14章深入探讨了很多Linux内核的调试技术,介绍了内核调试器KGDB的用法,提出了许多gdb和KGDB组合使用的调试技巧。这一章涉及的内容还包括硬件JTAG调试器的用法,以及当内核无法启动时的一些故障分析技巧。

第15章把调试环境从内核转移至应用程序。这一章继续完善前两章用到的gdb示例,讲述了多线程和多进程的调试技巧。

第16章取代了第1版的第16章(移植Linux)。那一章的内容已经过时了,如果要在现代内核中恰当地讨论其主题,则需要专门写一本书。我觉得你会对新版的第16章有兴趣的,这一章涵盖了常用的构建完整嵌入式Linux发行版的构建系统。我们会介绍OpenEmbedded,它已经在商业和其他开源项目中获得了极大的关注。

第17章介绍了嵌入式Linux中一个令人激动的发展:通过PREEMPT_RT选项来配置系统的实时性。这里介绍的特性通过RT选项得以实现,同时还介绍了如何在设计中使用这些特性。这一章也介绍了衡量应用程序配置延时的技巧。

第18章以简单易懂的语言描述了USB子系统。我们介绍了一些概念和USB拓扑结构,接着给出几个USB配置的例子。我们会详细分析sysfs和USB的作用,以帮助你理解这个功能强大的系统。我们还会介绍几个有助于理解USB和解决USB故障的工具。

第19章解密了udev这个强大的系统配置工具。我们分析udev的默认行为,并以此为基础来理解如何对它进行定制。我们会给出几个实际的例子。对于BusyBox的使用者,我们会考察BusyBox自带的mdev工具。

本书附录包含U-Boot配置命令、BusyBox命令、SDRAM接口注意事项、开源开发者的资源、BDI-2000调试器的配置文件范例。BDI-2000是目前很流行的硬件JTAG调试器。边看边做

如果你能边看书边在你喜欢的Linux工作站上动手实验,将会从书中得到最大的收获。你可以找一台较旧的x86计算机完成嵌入式系统实验。如果有条件能连接其他体系结构的单板计算机进行实验就更好了。BeagleBorad开发板是一个可以进行实验的物美价廉的平台,书中的好几个例子都基于这个平台。通过学习这个大型代码库(Linux内核)的布局和组织结构,你将获益良多,并且能够在研究内核和边学边做的过程中获得大量的知识和经验。

看一下本书使用的代码并试着理解书中的示例,要使用不同的设置方案、配置选项和不同的硬件设备进行实验。除可获得丰富的知识,还充满了乐趣!如果你也这么想,请登录本书网站www.embeddedlinuxprimer.com免费注册一个账号,添加些内容和评论,在这个逐步壮大的Linux社区中分享你自己的成功故事和解决方案。你分享的内容会帮助其他人学习。这是一项不断完善的工作,你的参与会使其成为一个有价值的社区资源。GPL版权声明

本书使用的部分开源代码的版权归很多个人或公司所有。复制代码遵循了GPL,即GNU公共许可。第2版致谢

首先,我必须感谢高级策划编辑Debra Williams Cauley,他指导有方,经验丰富,而且很有耐心。没有他,本书就不会出版。

非常感谢我的评审团队:Robert P.J.Day、Sandy Terrace、Kurt Lloyd、Jon Masters和丛书编辑Arnold Robbins。他们对本书质量提升所做的贡献,让我难以言表。

还要感谢Mark A.Yoder教授和他的嵌入式Linux课程,他在课堂上使用了本书的手稿,全面检验了这些内容。

特别感谢飞思卡尔半导体公司提供了硬件平台,书中的很多例子都基于这个平台。如果没有Kalpesh Gala帮忙安排,我就享受不到这样的支持。

还要感谢Embedded Planet和Tim Van de Walle,他们也提供了硬件平台供本书使用。

本书撰写过程中,有几个人给予了我很大帮助,他们提出建议并解答我的问题:Cedric Hommbourger、Klaas van Gend、George Davis、Sven-Thorsten Dietrich、Jason Wessels和Dave Anders(排名没有先后次序)。

我还想感谢这本书的制作团队,我的日程安排不够准确给他们添了麻烦。他们是Alexandra Maurer、Michael Thurston、Jovana San Nicolas Shirley、Gayle Johnson、Heather McNeill、Tricia Bronkella和Sarah Kearns。

出版本书是个很大的项目,有无数人提供了帮助,他们及时回答问题,跟我在交谈中提出想法。这些人太多了,无法一一列举。但他们对我有意或无意的帮助都是我真心感激的。

在本书第1版中,我特别感谢了Cary Dillman,因为每当写完一章,她就会不辞劳苦地审读。她现在是我心爱的妻子了。在第2版的写作过程中,Cary继续支持我,给了我必要的灵感、耐心,作出了不少牺牲。第1版致谢

我由衷地敬佩开源软件工程师的崇高精神,深深地折服于我们社区中远远超过我的天才们。在本书的撰写过程中,我向Linux和开源社区的很多人提出了大量问题,大多数问题都能很快得到回答,而且还经常获得鼓励。我要向Linux和开源社区中帮我解答问题的朋友致以真挚的谢意(排名不分先后):

Dan Malek为第2章的部分内容提供了创作灵感。

Dan Kegel和Daniel Jacobowitz耐心地帮我解答了关于工具链的问题。

Scott Anderson 提供了第14章中gdb宏的最初思想。

Brad Dixon不断用他所掌握的知识挑战和扩展我的技术洞察力。

George Davis帮我解答了ARM的问题。

Jim Lewis为我提供了关于MTD的意见和建议。

Cal Erickson帮我解答了关于gdb用法的问题。

John Twomey就第3章内容给出了建议。

Lee Revell、Sven-Thorsten Dietrich和Daniel Walker就实时Linux的内容提供了建议。

非常感谢AMCC、Embedded Planet、Ultimate Solutions和United Electronic Industries公司,它们提供了示例硬件。感谢我的公司Monta Vista Software,因为写这本书偶尔会从工作中分点儿心,而且公司还为某些示例提供了软件支持。在创作过程中,还有很多人贡献了他们的想法,并给予我鼓励和支持,我也非常感激!

我要诚挚地感谢最初审阅本书的团队,他们迅速阅读了每一章,提供了极好的反馈、意见和想法。谢谢Arnold Robbins、Sandy Terrace、Kurt Lloyd和Rob Farber。还要感谢Arnold教给我这个写作新手撰写技术图书的规则。虽然我已经努力减少错误,但错误肯定还会存在,这都得归咎于我。

感谢Mark L.Taub使本书得以完成,感谢他的鼓励和无限的耐心。还要感谢制作团队,包括Kristy Hart、Jennifer Cramer、Krista Hansing和Cheryl Lenser。

最后,把最特别、最衷心的感谢献给Cary Dillman,在我撰写本书时她阅读了每一章,整个创作过程中都有她的不断鼓励和重要的贡献。第1章入门本章内容

为什么选择Linux

嵌入式Linux现状

开源和GPL

标准及相关组织

小结

很多老牌嵌入式操作系统公司纷纷抛弃专有嵌入式操作系统,而这一举动一般都会在公司内部引发不少争论。出于各方面的考虑,许多产品都采用Linux作为其操作系统,这些产品的种类繁多,超出了Linux占据传统优势的服务器领域。手机、DVD播放器、电子游戏机、数码相机、网络交换机和无线网络设备都在使用嵌入式系统。在你家里或汽车里多半也会有Linux的身影。Linux已经成为很多设备的嵌入式操作系统,包括机顶盒、高清电视、蓝光DVD播放器、汽车的信息娱乐中心和很多其他日常使用的电器。1.1 为什么选择Linux

凭借经济和技术方面的诸多优势,Linux正被越来越多的嵌入式设备所使用。几乎在所有的市场和技术领域都能发现这种趋势。Linux已经被很多重要的嵌入式产品所采用,包括遍布世界的公共电话交换网、全球数据网络、手机、无线基站控制器,以及管理这些无线蜂窝网络的通信基础设施。Linux在众多领域都取得了成功,包括汽车车载设备、消费电子产品(比如游戏机和PDA)、打印机、企业级交换机和路由器以及其他很多产品。全世界内置Linux操作系统的手机数以亿计。Linux在嵌入式系统市场的占有率越来越高,目前来看这一趋势还将继续。

以下是嵌入式Linux增长的几个原因。

Linux支持的硬件设备种类繁多,可能超过其他任何一种操作系统。

Linux支持非常多的应用程序和网络协议。

Linux的扩展性很好,从小型的消费电子产品到大型、笨重的电信级交换机和路由器都可以采用Linux。

和传统的专有嵌入式操作系统不同,部署Linux不需要缴纳专利费。

Linux吸引了为数众多的活跃的开发者,能很快支持新的硬件架构、平台和设备。

越来越多的硬件和软件厂商,包括几乎所有的顶级芯片制造商和独立软件开发商,现在都支持Linux。

出于这些原因,我们看到Linux正加速渗透到众多的日常用品之中,范围涵盖了从高清电视到手机等多种产品。1.2 嵌入式Linux现状

Linux在嵌入式领域已经取得了长足的进步,这一点并不让人感到惊讶。实际上,阅读本书就已表明Linux已经影响了你的生活。嵌入式Linux的市场规模难以估量,因为很多公司仍然在继续打造它们自己的嵌入式Linux发行版。

LinuxDevice.com是一个广受欢迎的新闻和资讯门户网站(它由Rick Lehrbaum创建,现在属于Ziff Davis),这个网站每年会开展一次嵌入式Linux的市场调查。其最近的调查报告显示,Linux已经成为占据主导地位的嵌入式操作系统,每年都有数千种新产品使用Linux。实际上,有超过半数的调查对象表示他们在嵌入式产品设计中使用了Linux。报告同时显示,仅有大约八分之一的调查对象使用排名第二的操作系统,而那些曾经统治嵌入式市场的商业操作系统的使用率还不到十分之一。即使你有理由怀疑这些调查结果,但没有人能够忽视现今嵌入式Linux市场的蓬勃生机。1.3 开源和GPL

Linux是开源软件,这是促使Linux广泛使用的一个重要因素。如果你想了解更多开源运动的历史和文化,请看Eric S.Raymod的书(见本章末尾),该书引人入胜且富有见地。

Linux内核基于GNU GPL[1](General Public License,通用公共许可证)的条款进行授权,这导致了一个常见的误区:Linux是免费的。事实上,GNU GPL第3版[2]的第2段声明:“当我们谈论自由软件时,我们指的是自由,而不是指价格上的免费。”大多数的职业开发经理都同意:你可以免费下载Linux,但是在一个嵌入式平台上开发和部署任何操作系统都是有代价的(这个代价通常很大)。在这方面,Linux并不例外。

GPL非常简短且通俗易懂。这里列出了它的一些重要特点。

许可证是自我存续的。

许可证给予用户运行程序的自由。

许可证给予用户研究和修改源代码的权利。

许可证允许用户分发原来的代码以及他所做的修改。

许可证有病毒的特性。也就是说,如果你把GPL软件分发给某个人,GPL会给予他和你相同的权利。

如果软件是基于GPL条款发布的,它必须永远附带这个许可证[3]。即使代码被大幅改动(这是许可证允许甚至是鼓励的),GPL要求改动后的代码也必须以相同的许可证发布。这样做的目的是为了保证软件的自由使用,包括修改后的软件(或通常所说的派生软件)的自由使用。

不管软件是如何获取的,GPL允许无限制地分发该软件,而无须支付任何专利费或按件收取的许可费。这并不意味着软件厂商不能够对GPL软件收费——收费是合理和普遍的商业行为。这表明一旦拥有GPL软件,你可以修改和重新分发这个软件,不管这个软件是否被修改过。然而GPL规定,软件的修改者如果决定发布修改后的软件,则必须以GPL的条款发布。无论以什么形式发布派生软件,比如交付给客户,都必须遵守这个规定。免费和自由

在讨论开源软件的自由特性(free nature)时,常常会提及两个流行的短语:“free as in freedom”和“free as in beer”(本书作者非常喜欢后者)。[4]GPL的存在保证了软件的自由。它确保了你使用、学习和修改这个软件的自由。它也确保了当你分发修改后的代码给某个人时,他同样也获得这些自由。这个概念已经被广泛接受和理解。

很多人对Linux存在一个误解,那就是Linux是免费的。你可以免费获得Linux,你也可以花几分钟的时间下载一个Linux内核。然而,正如任何一个职业开发经理所理解的,在产品设计中使用任何软件都是有一定代价的。这些代价包括软件的获取、整合、修改、维护和支持。除此之外,你还需要花费其他费用,从而获得和维护一个配置正确的工具链、程序库、应用程序以及和你选择的硬件架构兼容的专用交叉开发工具。很快你会发现,为了开发和部署嵌入式Linux系统,配置其所需的软件工具和开发环境并不是件轻松的事情。1.4 标准及相关组织

在Linux不断获得桌面、企业和嵌入式等细分市场份额的同时,为推动用户使用和接受Linux,一些新标准和新组织也应运而生。本节介绍一些读者应该了解的标准。1.4.1 Linux标准基础

对于一个Linux发行版的维护者来说,也许关系最紧密的标准莫过于Linux标准基础(Linux Standard Base,LSB)。LSB的目标是建立一套设计良好的标准,以提升应用程序在不同Linux发行版之间的互操作性。目前,LSB的标准涵盖了好几种硬件架构,包括IA32/64、32位和64位Power架构,以及AMD64等。标准分为核心部分和单独的硬件架构部分。

LSB规定了Linux发行版的公共属性,包括目标文件的格式、标准库的接口、命令和实用工具的最小集合以及它们的行为、文件系统布局、系统初始化等。

通过本章末尾的网址可以了解有关LSB的详细信息。1.4.2 Linux基金会

据其网站所述,Linux基金会是“一个致力于促进Linux发展的非营利组织”。Linux基金会赞助Linux创始人Linus Torvalds的工作。Linux基金会还赞助了几个工作组,帮助他们制定标准和参与开发针对很多重要Linux平台的新功能。接下来的两节介绍一些由该组织发起的项目。1.4.3 电信级Linux

世界上很多大型的网络和通信设备制造商都在开发或销售采用Linux操作系统的电信级设备。电信级设备的重要特征包括高可靠性、高可用性和快速的可服务性。这些厂商设计的产品采用冗余可热交换的架构、具备容错和集群化特点,并且常常具有实时性能。

Linux基金会的电信级Linux工作组制定了一个规范,其中定义了电信级设备必须满足的一组需求。这个规范的当前版本涵盖了7个功能领域。

可用性——这类需求用于提供增强的可用性,包括在线维护操作、冗余备份和状态监测。

集群——这类需求用于提升设备的冗余服务性能,例如集群成员的管理和数据检查点的设置。

可服务性——这类需求适用于远程服务和维护,例如SNMP和对风扇及电源的诊断监测。

性能——这类需求定义了性能和可扩展性、对称多处理能力、延时等。

标准——这类需求定义了符合CGL规范的设备应当遵循的标准。

硬件——这类需求与高可用性硬件相关,例如刀片服务器和硬件管理接口。

安全——这类需求用于提升整个系统的安全性并且保护系统免受各种外部威胁。1.4.4 移动Linux计划:Moblin

全球市场上已经有几款基于嵌入式Linux设计和生产的手机。据各方报道,市面上已有上亿部手机采用Linux作为操作系统平台。唯一可以肯定的是这个数量还在继续增加。原本由专有实时操作系统统治的阵地,有望成为Linux发展最为迅猛的细分市场。Linux已整装待发,吹响了进军商业嵌入式应用领域的号角。

Linux基金会赞助了一个原名为移动Linux计划(Mobile Linux Initiative)的工作组,这个工作组现在叫做Moblin。据Linux基金会的网站介绍,这个工作组的目标是推动Linux在移动设备中的使用,包括下一代手机和其他融合语音/数据的便携设备。这个工作组关注的领域包括开发工具、I/O和网络、内存管理、多媒体、性能、电源管理、安全和存储。Moblin的网址是http://moblin.org。你可以尝试某个Moblin版本,例如Fedora/Moblin(网址为http://fedoraproject.org/wiki/Features/FedoraMoblin)或者Ubuntu Moblin remix,作者的Dell Mini 10上网本上就安装了后面这个版本。

嵌入式Linux的版图还在不断扩张。在准备这一版的内容时,Moblin项目和Maemo项目已经合并成MeeGo。可以从http://meego.com/了解MeeGo的更多信息,甚至可以下载一个MeeGo镜像试验一下。1.4.5 服务可用性论坛

如果你正致力于打造需要具备高可靠性、可用性和可服务性(Reliability、Availability、Serviceability,RAS)的产品,你就应该知道服务可用性论坛(SA Forum)。这个组织定义了一组公共接口用于电信设备和其他商业设备的系统管理。这个组织的网址是www.saforum.org。1.5 小结

嵌入式Linux已经取得了胜利。实际上,你的汽车或家中很可能就有嵌入式Linux设备。这一章仔细考察了下列现象的产生原因。

采用Linux的嵌入式产品开发者和生产厂商不断增加。

Linux在嵌入式设备中的使用率继续以令人激动的速度增长。

很多因素推动了Linux在嵌入式市场的增长。

几个标准和相关的组织正影响着嵌入式Linux的发展。补充阅读建议

The Cathedral and the Bazaar(《大教堂与市集》),Eric S.Raymond,O’Reilly Media公司,2001。

Linux 标准基础项目

http://www.linuxfoundation.org/collaborate/workgroups/lsb

Linux 基金会

http://www.linuxfoundation.org/

[1].See http://www.gnu.org/licenses/gpl.html for complete text of the license.

[2].目前Linux内核源码仍采用GNU GPL第2版。——编者注

[3].如果所有的版权持有人能够达成一致的话,理论上,这个软件可以基于新的许可证发布。实际上,出现这种情况的可能性很小,特别是对于那些由数千人共同参与开发的大型软件。

[4].前一个短语中free的意思是自由,后一个短语中free的意思是免费。——译者注第2章综述本章内容

嵌入与非嵌入

剖析嵌入式系统

存储

嵌入式Linux发行版

小结

通常,理解特定任务的最佳途径是从全局角度认识它。很多基本概念都会给嵌入式系统开发的新手带来挑战。这一章将带领你观摩一个典型的嵌入式系统及其开发环境,重点介绍那些让开发这类系统变得独特和富有挑战的概念和组件。2.1 嵌入与非嵌入

嵌入式系统具有一些关键属性。你可能不会认为桌面PC是一个嵌入式系统。但在远程数据中心运行的桌面PC很可能就是嵌入式系统,这个平台完成至关重要的监控和报警任务。而且假设这个数据中心是无人值守的,那我们对这个硬件平台的需求就不一样了。例如,如果断电后接着电力恢复,你会希望该硬件平台在没有操作人员干预的情况下继续执行它的任务。

嵌入式系统的种类很多,形状大小各异,大到多机架数据存储中心或网络动力站,小到MP3播放器或手机。下面列出嵌入式系统的一些常见特性。

包含一个处理引擎,比如通用微处理器。

一般是针对某种具体的应用或目的而设计的。

包含一个简单的用户界面(或没有用户界面),比如汽车引擎的点火控制器。

通常资源有限。例如只有很少的内存,并且没有硬盘驱动器。

可能会受到供电的限制,比如需要使用电池。

一般不会用做通用的计算平台。

一般都内置了应用程序,用户不能选择。

出厂时软硬件已经预先集成好了。

常常是针对无人值守的应用环境。

与传统的桌面PC相比,嵌入式系统的资源很有限。嵌入式系统常常只有很少的内存,小容量的硬盘驱动器(或是没有硬盘驱动器),有时还没有外部的网络连接。常常可以看到,一个系统仅有的用户界面就是一个串行端口加上几个发光二极管。诸如此类的问题会给嵌入式开发者带来挑战。BIOS和引导加载程序的对比

桌面电脑刚加电时,一个叫做BIOS的软件程序立刻获得了处理器的控制权。(历史上,BIOS是Basic Input/Output Software的缩写,但现在这个单词已经有了自身的含义,因为其完成的功能比以前复杂多了。)BIOS可能实际存储在一块闪存中(稍后会介绍闪存),便于升级BIOS程序。

BIOS是一个复杂的系统配置软件,它拥有硬件架构的底层信息。大多数人都不清楚BIOS涉及的硬件范围和功能,但它是桌面电脑的重要组成部分。当电脑加电时,BIOS首先获得处理器的控制权。它的主要任务是初始化硬件,特别是内存子系统,并且从PC的硬盘驱动器中加载操作系统。

在典型的嵌入式系统中(假设这个系统不是工业标准的x86 PC硬件平台),引导加载程序(bootloader)完成与BIOS相同的功能。对于定制嵌入式系统,你必须在开发计划中预留出时间,开发针对具体硬件板卡的引导加载程序。幸运的是,有几个很好的开源引导加载程序可供选择,你可以按照项目需求进行定制。这些内容将在第7章介绍。

下面列出一些引导加载程序在系统加电时完成的重要任务。

初始化关键的硬件,比如SDRAM控制器、I/O控制器和图形控制器。

初始化系统内存,并准备将控制权移交给操作系统。

为外设控制器分配必要的系统资源,比如内存和中断电路。

提供一个定位和加载操作系统镜像的机制。

加载操作系统,并将控制权移交给它,同时传递必要的启动信息。这些信息可能包括内

存总容量、时钟频率、串行端口速率和其他与底层硬件相关的配置数据。

这里只是简单概括了在通常的嵌入式系统中引导加载程序所完成的任务。需要记住的是:如果你的嵌入式系统基于定制的硬件平台,这些引导加载程序的功能必须由你,也就是系统的设计者来提供。如果你的嵌入式系统基于商用现货(Commercial Off-The-Shelf,COTS),比如ATCA机架[1],那么其引导加载程序(通常还有Linux内核)一般就包含在硬件板卡中了。第7章将会更深入地讨论引导加载程序。2.2 剖析嵌入式系统

图2-1是一个典型嵌入式系统的框图。这个例子很简单,描述了一个系统的高层硬件架构,无线接入点设备可能就是采用这种硬件构架。这个系统架构以一个32位的RISC处理器为中心,系统中的闪存用于存储非易失性程序和数据,主存储器是SDRAM(同步动态随机存储器),其容量可以从几兆至几百兆字节,视应用而定。一个通常由电池供电的实时时钟模块记录着当前时间(包括日期)。这个例子里面包含以太网和USB接口,也包含串行端口,利用串行端口可基于RS-232标准访问控制台。802.11芯片组或模块实现了无线调制解调器的功能。图2-1 嵌入式系统

通常,嵌入式系统的处理器完成很多功能,不仅仅是处理传统的核心指令流。图2-1中的假想处理器包含集成的串行接口UART、集成的USB和以太网控制器。很多处理器都包含集成在处理器中的外设,有时这些处理器被称为片上系统(SOC,System On Chip)。第3章会考察几个集成处理器的例子。2.2.1 典型的嵌入式Linux开发环境

嵌入式Linux开发新手经常提出的一个问题,就是开发之前需要准备些什么。为了回答这个问题,图2-2展示了一个典型的嵌入式Linux开发环境。

图中展示了一个主机开发系统,其中运行你最喜欢的桌面Linux发行版,比如Red Hat、SUSE或Ubuntu Linux。嵌入式Linux目标板通过一根RS-232串行端口线与开发主机相连。目标板的以太网接口插接到本地以太网集线器或交换机上,开发主机也通过以太网连接到上面。开发主机包含开发工具和程序以及目标文件,通常这些都可从一个嵌入式Linux发行版中获得。图2-2 典型的嵌入式Linux开发环境

在这个例子中,主机和嵌入式Linux目标板主要通过一个遵循RS-232标准的串行端口连接。主机上运行的串行端口终端程序用于和目标板通信。minicom是最常用的串行端口通信应用程序之一,几乎所有的桌面Linux发行版中都有这个应用程序[2]。本书使用screen作为串行端口通信程序,这个程序可以取代minicom的功能,而且更灵活,特别是在trace捕捉方面。对于系统启动或解决故障时串行端口线上的垃圾信息,screen也更加宽容。为了在USB转串行端口线上使用screen,可以在主机终端调用它并指定速率:2.2.2 启动目标板

第一次加电时,目标板上的引导加载程序立即获得处理器的控制权。该程序执行一些非常底层的硬件初始化,包括处理器和内存的设置,初始化UART用于控制串行端口,以及初始化以太网控制器。代码清单2-1显示了目标板加电后从串行端口接收到的字符。在这个例子中,我们选择了飞思卡尔半导体公司的目标板PowerQUICC III MPC8548 可配置开发系统(Configurable Development System,CDS)。这个开发系统包含了PowerQUICC III MPC8548处理器。这个目标板从飞思卡尔出厂时就预装了U-Boot引导加载程序。

代码清单2-1 引导加载程序从串行端口输出的初始信息

当MPC8548CDS目标板加电时,U-Boot执行一些底层的硬件初始化,包括配置串行端口,然后打印标题行,见代码清单2-1中显示的第一行。接着显示了CPU和核心(Core)的型号及版本,接下来是描述时钟配置和缓存配置的数据,再后面是一串文字描述了这个目标板。

初始的硬件配置完成后,U-Boot根据其静态设置来配置其他硬件子系统。这里,我们看到U-Boot配置了I2C、DRAM、闪存(FLASH)、2级缓存(L2 cache)、PCI和网络子系统。最后U-Boot等待来自串行端口控制台的输入,显示为命令行提示符“=>”。2.2.3 引导内核

现在U-Boot已经初始化了硬件、串行端口和以太网接口,在其短暂但有益的生命中还剩一件工作:加载并引导Linux内核。所有的引导加载程序都提供了命令用于加载和执行操作系统镜像。代码清单2-2显示了使用U-Boot手动加载并引导Linux内核的一种常用方法。

代码清单2-2 加载Linux内核

代码清单2-2开头的tftp命令指示U-Boot使用TFTP[3]协议将内核镜像uImage通过网络加载到内存。在这个例子中,内核镜像存放于开发工作站(通常,这个开发工作站就是通过串行端口与目标板相连的那台主机)。执行tftp命令时,需要传入一个地址参数,这个地址用于指定内核镜像将要被加载到的目标板内存的物理地址。读者现在不用担心这些细节,第7章将会详细介绍U-Boot。

第2次执行tftp命令加载了一个目标板配置文件,称为设备树(device tree),这个文件还有其他的名字,包括扁平设备树(flat device tree)和设备树二进制文件(device tree binary)或dtb。你将在第7章了解到这个文件的更多信息。现在,你只要知道这个文件与具体目标板相关,包含了内核所需的用于引导目标板的信息就足够了。这些信息包括内存大小、时钟速率、板载设备、总线和闪存布局。

接着,执行bootm(从内存镜像引导)命令来让U-Boot引导刚才加载至内存的内核,起始地址就是在tftp命令中指定的地址。在这个使用bootm命令的例子中,我们让U-Boot加载放在地址0x600000处的内核,并将加载到地址0xc00000处的设备树二进制文件(dtb)传给内核。bootm命令会将控制权移交给Linux内核。假设内核配置正确,这个命令的结果是引导Linux内核直至在目标板上出现控制台命令行提示符,如同登录提示符所示。

注意bootm命令为U-Boot敲响了丧钟。这是一个重要的概念。与桌面PC的BIOS不同,大多数的嵌入式系统都采用这样一种架构:当Linux内核掌握控制权时,引导加载程序就不复存在了。Linux内核会要求收回那些之前被引导加载程序所占用的内存和系统资源。将控制权交回给引导加载程序的唯一方法就是重启目标板。

最后还需要注意一点。在代码清单2-2的串行端口输出中,下面这行之前的信息(包含这一行)都是由U-Boot引导加载程序产生的:

其余引导信息是由Linux内核产生的。对于这一点,我们在后续章节还要详细说明,但我们需要注意U-Boot是在哪儿离开的以及Linux内核是在哪儿取得控制权的。2.2.4 内核初始化:概述

当Linux内核开始执行时,它会在其相当复杂的引导过程中输出大量状态消息。在当前讨论的这个例子中,在显示登录提示符之前,Linux内核大约显示了200行printk[4]打印信息(代码清单中省略了这些打印行以便讨论的重点更加清晰)。代码清单2-3再现了登录提示符之前的最后几行输出。这个练习的目的不是要深入到内核初始化的细节中去(第5章会讲述这方面的内容),而是要对正在发生的事情,以及对嵌入式系统中引导Linux内核需要哪些组件有一个概览。

代码清单2-3 Linux内核加载的最后几行引导消息

Linux在串行端口终端上显示登录提示符之前,会挂载一个根文件系统。在代码清单2-3中,Linux通过一系列必要步骤,从一个NFS[5]服务器来远程(通过以太网)挂载其根文件系统,这个NFS服务器程序运行于IP地址为192.168.0.9的主机之上。通常这个主机就是你的开发工作站。根文件系统包含构成整个Linux系统的应用程序、系统库和工具软件。

重申一下这里讨论的重点:Linux必须有一个文件系统。很多老式的嵌入式操作系统不需要文件系统,因此那些从老式嵌入式操作系统迁移到嵌入式Linux系统的工程师往往会感到惊讶。一个文件系统由一组预定义的系统目录和文件组成,这些目录和文件按照特定的布局存储在硬盘或其他存储介质上,而Linux内核可以挂载这些介质作为其根文件系统。

注意Linux也可以从其他设备挂载根文件系统。最常见的情况当然是挂载一个硬盘分区作为根文件系统,就像笔记本或工作站中的Linux系统所做的那样。实际上,当你将嵌入式Linux小玩意带出房门或远离开发环境时,NFS就没什么用处了。然而,在读这本书的过程中,你会逐渐体会到在开发环境中挂载NFS根文件系统带来的威力和灵活性。2.2.5 第一个用户空间进程:init

继续探讨其他内容之前,还有一个重点需要强调一下。请注意代码清单2-3中的这一行:

直到这时,内核都是自己在执行代码,它在一个称为内核上下文(kernel context)的环境中完成大量的初始化工作。在这个运行状态下,内核拥有所有的系统内存并且全权控制所有的系统资源。内核能够访问所有的物理内存和所有的I/O子系统。它在内核虚拟地址空间中执行代码,使用一个由内核自己创建和支配的栈。

当内核完成其内部初始化并挂载了根文件系统后,默认会执行一个名为init的应用程序。内核一启动init,它随即进入用户空间(user space)或用户空间上下文运行。在这个运行状态下,用户空间进程对系统的访问是受限的,必须使用内核系统调用(system call)来请求内核服务,比如设备和文件I/O。这些用户空间进程或程序,运行在一个由内核随机[6]选择和管理的虚拟内存空间中。在处理器中专门的内存管理硬件的协助下,内核为用户空间进程完成虚拟地址到物理地址的转换。这种架构的最大好处是某个进程中的错误不会破坏其他进程的内存空间。这是老式嵌入式系统的一个普遍缺陷,会产生那些最难查找的故障。

对这些概念不熟悉也不用惊慌。本节的目标只是提纲挈领地作个介绍,在此基础上,你会在阅读本书的过程中逐步获得更加深入的理解。后续章节将详细解释这些概念。2.3 存储

嵌入式Linux开发的一大挑战性源自大多数嵌入式系统的物理资源非常有限。虽然你的台式电脑会拥有酷睿2双核处理器和500 GB大小的硬盘,但很难找到拥有如此巨大硬盘容量的嵌入式系统。多数情况下,硬盘通常被更小和更便宜的非易失性存储设备所取代。硬盘不仅笨重,包含旋转部件,对物理震动敏感,并且要求提供多种供电电压,因此并不适合用在许多嵌入式系统中。2.3.1 闪存

几乎所有人都对消费电子设备,比如数码相机和PDA(这两者都是很好的嵌入式系统的例子)中广泛使用的Compact Flash卡和SD卡很熟悉。这些基于闪存技术的模块可以看做是固态硬盘,它们能够在很小的空间内存储许多兆甚至几吉字节的数据。它们内部没有活动的部件,相对坚固,只需一种供电电压。

生产闪存的厂家有好几家。闪存的类型多种多样,电气规格、物理封装形式以及容量各有不同。只拥有小到4MB或8MB非易失性存储容量的嵌入式系统并不罕见。嵌入式Linux系统对存储容量的典型需求是16MB~256MB。越来越多的嵌入式Linux系统拥有数吉字节的非易失性存储空间。

闪存可以在软件的控制下写入和擦除数据。采用旋转硬盘驱动器技术的普通硬盘仍然是最快的可写入存储媒介。虽然和普通硬盘相比,闪存的写入和擦除仍然相当慢,但与以前相比,其写入和擦除速度已经有了显著提高。了解硬盘驱动器和闪存技术的根本区别才能正确地使用相应技术。

闪存的存储空间被分割成相对较大的可擦除单元,称为擦除块(erase block)。闪存的一个显著特征就是闪存中的数据写入和擦除的方式。在典型的NOR型[7]闪存芯片中,数据可以在软件的控制下,使用直接向某个存储单元地址写入的简单方法将其从二进制1改为二进制0。然而,要将数据从0改回1,则要擦除整个擦除块,在擦除时需要向闪存芯片写入一串特别的控制指令序列。

典型的NOR型闪存包含多个擦除块。例如,一个4MB容量的闪存芯片可能包含64个擦除块,每块大小为64KB。市面上也有擦除块大小不一致的闪存,以便灵活存放数据。这种闪存常常被称为引导块(boot block)或引导扇区(boot sector)闪存。通常,引导加载程序存储在较小的块中,内核和其他必要的数据则存放在更大的块中。图2-3说明了一个典型的顶部引导(top boot)闪存芯片的块大小布局。图2-3 引导块闪存的架构

为了修改存储在闪存阵列中的数据,必须完全擦除待修改数据所在的块。即使只修改某个块中的一个字节,都必须擦除并重新写入整个块[8]。相比于传统硬盘的扇区,闪存的块大小相对较大。相对而言,一个典型的高性能硬盘的可写扇区大小为512 B或1024 B。结果显而易见:更新闪存中的数据所耗费的写入时间会是硬盘驱动器的很多倍,部分原因就是每次更新数据时都有相对大量的数据需要被擦除和写回。在最坏的情况下,一个写周期会耗费几秒钟的时间。

关于闪存,另一个需要考虑的限制是存储单元的写寿命(write lifetime)。NOR型闪存存储单元的可写入次数是有限制的,超出次数限制后写入就会失败。虽然这个次数的数值比较大(典型的写入次数限制是每块100 000次),但不难想象一个设计很差的闪存存储算法(甚至是一个软件故障)会迅速毁坏闪存设备。显然,应该避免配置你的系统日志输出到闪存。2.3.2 NAND型闪存

NAND型闪存使用一种相对较新的闪存技术。当NAND型闪存投放市场时,前一节介绍的传统闪存就被称为了NOR型闪存。它们之间的区别与闪存存储单元的内部架构有关。NAND型闪存设备通过提供更小的块尺寸改进了传统(NOR型)闪存的一些限制,它可以更快更有效地进行写操作,同时大大提高了闪存阵列的使用效率。

NOR型闪存为微处理器提供的接口方式与很多微处理器外围设备的做法类似。也就是说,它们有并行的数据和地址总线,直接[9]连接到微处理器的数据/地址总线上。闪存阵列的每个字节或字(word)可以随机寻址。相反,NAND型闪存设备是通过复杂的接口串行访问的,而且这些接口因厂商而异。NAND型闪存设备的操作模式更类似于传统的硬盘驱动器加上附带的控制器。数据是以串行突发(burst)方式访问的,每次突发访问的数据量远远小于NAND型闪存的块大小。相比于NOR型闪存,虽然NAND型闪存的写入时间要少很多,但其写寿命却比NOR型闪存高出一个数量级。

总的来说,NOR型闪存可以被微处理器直接访问,甚至于代码可以直接在NOR型闪存中执行。(然而,因为性能方面的原因,很少有人这样做,除非系统资源极其匮乏。)实际上,很多处理器都不能像对待DRAM一样缓存(cache)访问闪存的指令。这进一步降低了代码的执行速度。相反,NAND型闪存更适合于以文件系统的格式大容量存储数据,而不是撇开文件系统,直接存储二进制可执行代码和数据。2.3.3 闪存的用途

有多种闪存布局和使用方法可供嵌入式系统的设计者选择。在最简单的系统中,资源没有过度受限,可以将原始的二进制数据(可能是压缩过的)存储在闪存设备中。系统引导时,存储在闪存中的文件系统镜像被读入Linux内存磁盘(ramdisk)块设备中。这个块设备由Linux挂载为一个文件系统,并且只能从内存中访问。当闪存中的数据几乎不需要更新时,这种方式通常是很好的选择。相比于内存磁盘的容量,需要更新的数据量是很少的。但是,当系统重启或断电时,对内存磁盘中文件的修改会丢失,务必牢记这一点。

图2-4说明了一个简单嵌入式系统中的典型闪存组织结构。在这个系统中,动态数据对非易失性存储的需求很少且更新不频繁。图2-4 典型的闪存布局

引导加载程序通常存放在闪存阵列的顶部或底部。引导加载程序之后的存储空间被分配给Linux内核和内存磁盘文件系统镜像[10],这个镜像中包含了根文件系统。一般来说,Linux内核和ramdisk文件系统镜像都被压缩过,并由引导加载程序在系统引导时解压。

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载