嵌入式Linux编程(txt+pdf+epub+mobi电子书下载)


发布时间:2020-09-14 19:12:49

点击下载

作者:(英)克里斯·西蒙兹

出版社:机械工业出版社

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

嵌入式Linux编程

嵌入式Linux编程试读:

前言

嵌入式系统是一种设备,它的里面有一台微控制器。洗衣机、电视机、打印机、汽车、飞机和机器人都是由一个或多个微控制器控制的。随着这些设备变得越来越复杂,以及我们对于这些设备所拥有功能期望的提高,对于一个强大的操作系统来控制它们的需求不断增长。Linux逐渐成为首选的操作系统。

Linux的优势来源于它的开源模型,它鼓励代码共享。这意味着,具有众多背景并且经常由不同竞争公司雇用的软件工程师们,可以合作创建最新的操作系统内核并且跟踪硬件开发。这样一个代码库,可以对上至最大的超级计算机下至手表提供支持。Linux只是操作系统的一个组件。要创建一个工作系统,还需要许多其他组件,从基本的工具,如命令外壳,到具有Web内容并且与云服务通信的图形用户界面。Linux内核与众多其他的开源组件一起,可以构建一个在广泛的领域中发挥作用的系统。

然而,灵活性是一把双刃剑。尽管它可以针对一个特定问题给系统设计师提供广泛的备选解决方案,但是它也提出了这样的问题,即需要知道哪个方案是最好的选择。本书的目的是详细描述如何使用免费的开源项目构建一个嵌入式Linux系统,以生成一个健壮、可靠、高效的系统。基于作者作为一名咨询顾问和培训师的多年经验,本书将使用实例来说明最佳实践。本书内容

本书是按典型的嵌入式Linux项目的生命周期线组织的。前6章介绍如何建立项目,组织、Linux系统,以及选择合适的Linux构建系统。下一步,到达需要对系统架构和设计选择做出某些关键决策的阶段,包括闪存、设备驱动程序和init系统。接着,是利用已构建的嵌入式平台编写应用程序的阶段,其中有两章是关于进程、线程和内存管理的。最后,来到调试和优化平台的阶段,这是在第12章和第13章讨论的。最后一章描述如何为实时应用程序配置Linux。

第1章 通过描述系统设计师在项目开始时的可行选择来设置场景。

第2章 描述工具链的组件,特别是交叉编译。本章描述从何处获取一个工具链,并且提供从源代码构建工具链的细节。

第3章 以U-Boot和Bareboot为例,解释引导加载程序在初始化设备硬件中的作用。本章还描述设备树,这是一种在许多嵌入式系统中使用的硬件配置编码方式。

第4章 提供关于如何针对一个嵌入式系统选择Linux内核以及为设备内部硬件配置Linux内核的信息。本章还包括如何将Linux移植到新的硬件。

第5章 通过一个关于如何配置根文件系统的分步指南,介绍关于嵌入式Linux实现的用户空间部分所隐含的思想。

第6章 包括两个嵌入式Linux构建系统,通过构建系统可以自动化前面4章描述的步骤,并且总结本书第一部分。

第7章 讨论闪存管理所引起的挑战,包括raw flash芯片和嵌入式MMC或eMMC封装。本章描述适用于每种技术类型的文件系统。本章还包括如何现场更新设备固件的技术。

第8章 通过一个简单的驱动程序实例描述内核设备驱动程序如何与硬件交互。本章还描述从用户空间调用设备驱动程序的各种方法。

第9章 说明第一个用户空间程序init如何启动系统的其余部分。本章描述init程序的三个版本,每个版本适用于一组不同的嵌入式系统,从BusyBox init到systemd复杂性递增。

第10章 从应用程序员的角度描述嵌入式系统。本章讨论进程和线程、进程间通信和调度策略。

第11章 介绍虚拟内存背后的思想,以及如何将地址空间划分为内存映射。本章还介绍如何检测正在使用的内存和内存泄漏。

第12章 介绍如何使用GNU调试器GDB,以交互方式调试用户空间和内核代码。本章还描述内核调试器kdb。

第13章 涵盖可用于测量系统性能的技术,从全系统分析开始,然后聚焦特定的区域,该区域通常是造成性能不佳的瓶颈。本章还描述Valgrind工具,用于检查应用程序是否正确使用线程同步和内存分配。

第14章 提供关于Linux实时编程的详细指南,包括内核配置和实时内核补丁,还提供关于测量实时延迟的工具描述。本章还包括关于如何通过锁定内存来减少页面故障数量的信息。本书所需配套环境

本书中使用的软件完全是开源的。在大多数情况下,使用的版本是在本书写作时可用的最新的稳定版本。尽管我试图以一种不针对特定版本的方式描述主要特性,但是不可避免地,在命令实例中包含的一些细节将无法在后来的版本中工作。我希望与它们相关的描述能够提供足够的信息,从而可以将同样的原则应用到软件包的后期版本中。

在创建一个嵌入式系统时涉及两个系统:用于交叉编译软件的主机系统和用于运行软件的目标系统。对于主机系统,我使用的是Ubuntu 14.04,但是大多数Linux发行版只需要很小的修改即可工作。同样地,需要选择一个目标系统来表示嵌入式系统。我选择了两个:BeagelBone Black和QEMU CPU仿真器,用于仿真ARM目标。后者意味着你可以试验这些实例,而不必为一个实际的目标设备来投资硬件。同时,通过对特定细节的修改,例如设备名称和内存布局,应该有可能将实例应用到范围更广泛的目标。

目标系统的主软件包版本是U-Boot 2015.07、Linux 4.1、Yocto Project 1.8"Fido"和Buildroot 2015.08。读者对象

本书非常适合那些已经熟悉嵌入式系统以及想知道如何创建一流设备的Linux开发人员和系统程序员阅读。基本理解C语言编程和具有系统编程经验是必要的。本书约定

在这本书中,你会发现用于区别不同种类信息的文本样式。下面是这些样式的一些例子以及它们含义的说明。

代码块设置如下:

当我们希望提醒你注意代码块的特定部分时,相关行或项被设置为粗体:

任意命令行输入或输出书写如下:

表示警告或重要说明。

表示提示和技巧。样例源码下载

你可以从http://www.packtpub.com通过个人账号下载你所购买书籍的样例源码。如果你是从其他途径购买的,可以访问http://www.packtpub.com/support,完成账号注册,就可以直接通过邮件方式获得相关文件。

你也可以访问华章图书官网:http//www.hzbook.com,通过注册并登录个人账号,下载本书的源代码。第1章 概述

如果你即将开始从事你的下一个项目,而这次它将运行在Linux之上。那么在你将手指放在键盘上之前应该思考些什么?让我们首先从高层观察嵌入式Linux并且分析它为何如此流行,开源许可证的含义是什么,以及你需要何种类型的硬件以运行Linux。

Linux最早大约在1999年成为嵌入式设备的一个可行选择。当时,Axis(www.axis.com)发布了它们第一款基于Linux的网络相机,TiVo(www.tivo.com)发布了它们第一款数字摄像机(DVR)。自1999年以来,Linux变得越来越流行,直到今天它已成为许多类型产品所选择的操作系统。就在写作本书的2015年,大约有20亿台设备运行Linux。这包括大量的运行安卓系统(使用Linux内核)的智能手机,以及数以亿计的机顶盒、智能电视和Wi-Fi路由器,更别提各种各样装在更小体积中的设备,如车辆诊断、称重秤、工业设备和医疗监测单元。

那么,为什么你的电视机会运行Linux?乍看起来,电视机的功能很简单:它只是在屏幕上显示视频流。为什么需要一个像Linux这样的复杂的UNIX类操作系统呢?

简单的答案是摩尔定律:戈登摩尔,英特尔公司的联合创始人,他在1965年发现芯片的器件密度大约每两年翻一番。与其适用于台式机、笔记本电脑和服务器一样,这一定律也适用于我们设计的和日常生活中使用的各类设备。大多数嵌入式设备的核心是一个高度集成的芯片,它包含一个或多个处理器内核以及主存、大容量存储器和众多类型外设的接口。通常将其称为片上系统(System on Chip,SoC),它们的复杂性按照摩尔定律增加。一个典型SoC的技术参考手册通常洋洋洒洒上千页。你的电视不同于旧的模拟电视,它并不只是简单地显示视频流。

视频流是数字编码的并且可能被加密,它需要进行处理以创建图像。你的电视已经(或者即将)连接到互联网。它可以从智能手机、平板电脑和家庭媒体服务器接收内容,可以用它来玩游戏,不一而足。你需要一个完整的操作系统来管理这种程度的复杂性。

以下几点促使了Linux的流行:

·Linux具有必要的功能。它具有一个很好的调度程序,一个很好的网络协议栈,支持USB、Wi-Fi、蓝牙以及多种存储介质,很好地支持多媒体设备等等功能。它为所有功能的选项框都打上了钩。

·Linux已经被移植到各种处理器架构,包括一些在SoC设计中很常见的架构,如ARM、MIPS、x86和PowerPC。

·Linux是开源的,你可以自由地获取源代码并修改它以满足需要。你可以为特定SoC板或设备创建板级支持包。你可以增加主源代码中缺失的协议、功能和技术。你可以删除不需要的功能以减少内存和存储需求。Linux是十分灵活的。

·Linux具有活跃的社区,尤其是对于Linux内核来说。每10至12周就有一个新版本的内核,每个版本包含大约来自1000名开发人员提交的代码。活跃的社区意味着Linux是与时俱进的,能够支持当前的硬件、协议和标准。

·开源许可证保证你可以访问源代码。没有与厂商捆绑在一起。

基于这些原因,Linux是复杂设备的理想选择。但是我应该在这里提出一些忠告。复杂性使得Linux更难以理解。再加上快速变化的开发过程和开放源码的分散结构,你必须投入一些精力来学习如何使用它,并在它变化时重新学习。我希望这本书能在该过程中对你有所帮助。1.1 选择合适的操作系统

Linux适合你的项目吗?Linux能够很好地工作,而目前正在解决的问题证明了Linux的复杂性。它尤其擅长需要连接性、健壮性以及复杂用户界面的场合。但是,它并不能解决所有问题,因此在你投入学习之前需要考虑一些事情:

·你的硬件能胜任工作吗?相比传统的实时操作系统(RTOS)如VxWorks,Linux需要更多的资源。它至少需要一个32位处理器以及更多的内存。在1.5节,我将更详细地介绍相关内容。

·你有正确的技能组合吗?在项目的早期,bring-up板需要关于Linux以及它如何与你的硬件相关联的详细知识。同样,在调试和调优应用程序时,你需要能够解释结果。如果你自身不具备这些技能,你可能需要将某些工作外包出去。当然,阅读本书将对你有所帮助!

·你的系统是实时的吗?只要你留意某些特定细节,Linux可以处理许多实时活动。在第14章中,我将详细介绍相关内容。

仔细考虑这些要点。或许,成功的最佳指导就是寻找运行Linux的类似产品,并且观察它们是如何运行的。然后遵循最佳的实践方式。1.2 参与者

开放源码软件是从哪里来的?是谁编写的?特别是,它与嵌入式开发的关键组件,如工具链、引导加载程序、内核以及根文件系统中的基本工具等有何联系?

主要的参与者是:

·开源社区。毕竟,这是生成你将要使用软件的引擎。社区是一个开发人员的松散联盟,其中许多人是通过某些方式得到资助的——可能是非盈利组织、学术机构或者商业公司。他们共同工作以推动各种项目的目标。有许多这样的项目,有些项目比较小,而有些项目则比较大。我们在本书中将要使用的一些项目包括:Linux自身、U-Boot、BusyBox、Buildroot、Yocto项目,以及GNU下的许多项目。

·CPU体系结构。有多个组织设计我们所使用的CPU。这里重要的组织包括ARM/Linaro(基于ARM的SoC)、英特尔(x86和x86_64)、Imagination Technologies(MIPS),以及Freescale/IBM(PowerPC),它们实现基本CPU体系结构,或者至少对其支持具有重要影响。

·SoC厂商(Atmel、Broadcom、Freescale、Intel、Qualcomm、TI以及许多其他厂商)。他们从CPU体系结构中获得并修改内核和工具链以支持他们的芯片。他们还创建参考板:下一级别厂商可以使用该设计创建开发板和工作产品。

·板供应商和OEM。这些人从SoC厂商获得参考板并且将它们构造成特定产品,例如机顶盒、相机或者创建更通用的开发板,例如Avantech和Kontron所提供的产品。一个重要的种类是廉价开发板,例如BeagleBoard/BeagleBone和Raspberry Pi,它们已经创建了自己的包括软件和硬件插件的生态系统。

这些形成一个链条,而你的项目通常是在链条的尾部,这意味着你不能自由地选择组件。你不能简单地从kernel.org下载最新的内核,除了一些极少数的情况,因为它并不支持你正在使用的芯片或板。

这是嵌入式开发领域长期存在的一个问题。理想情况下,在链条中每个环节的开发人员都会向上游推送他们改变的内容,但是他们并不这样做。因此,发现一个内核有众多的补丁没有合并到上游也并非不寻常。而且SoC厂商往往只是为它们的最新芯片积极地开发开源组件,这意味着对于任何超过几年的旧芯片的支持将被冻结,得不到任何更新。

现实是,大多数嵌入式设计都是基于软件的旧版本。它们没有获得安全修复、性能增强或者较新版本中的特性。诸如Heartbleed(OpenSSL库中的缺陷)和Shellshock(bash shell中的缺陷)等问题继续存在而没有得到修复。在本章后面的部分将更多地讨论安全主题。

对于这个问题,你能做些什么呢?首先,询问你的供应商:他们的更新策略是什么?他们多长时间更新一次内核版本?当前的内核版本是什么?在这之前的版本是什么?他们向上游合并变化的策略是什么?一些厂商正在以这种方式取得巨大进步,你应该更喜欢他们的芯片。

其次,你可以采取措施使自己更加自给自足。本书的目的是以更详细的方式解释依赖关系,同时说明在哪些方面可以帮助自己。不要只是拿着SoC或者板厂商提供给你的包并且盲目地使用它,而不考虑其他替代方案。1.3 项目生命周期

本书分为四个部分,分别反映一个项目的不同阶段。这些阶段不一定是按次序的。它们通常是重叠的,并且你可能需要跳转回去以重新审视以前做过的事情。然而,它们代表的是项目进展过程中开发人员的关注点。

·嵌入式Linux基本要素(第1~6章)将有助于你建立开发环境,为以后的阶段创建一个工作平台。它通常被称为“板bring-up板”阶段。

·系统架构和设计选择(第7~9章)将有助于你考查一些你不得不做出的设计选择,如关于程序和数据存储,如何在内核设备驱动程序和应用程序之间划分工作,以及如何对系统进行初始化。

·编写嵌入式应用程序(第10章和第11章)说明如何有效利用Linux进程和线程模型,以及如何在资源受限的设备中管理内存。

·调试和优化性能(第12章和第13章)描述如何在应用程序和内核中跟踪、剖析以及调试你的代码。

关于第五部分实时性(第14章),在某种程度上是一个单独的部分,因为它是一个很小但是很重要的嵌入式系统类型,实时行为的设计对于四个主要阶段的每个阶段都有影响。嵌入式操作系统的四个要素

每个项目都是从获取、定制和部署这四个要素开始的:工具链、引导加载程序、内核和根文件系统。这是本书第一部分的主题:

·工具链:包括编译器和其他工具,为你的目标设备创建代码。其他一切都取决于工具链。

·引导加载程序:对于初始化板以及装载和引导Linux内核来说,引导加载程序是必需的。

·内核:这是系统的核心,用于管理系统资源以及与硬件交互。

·根文件系统:包含若干库和程序,一旦内核完成初始化将运行这些库和程序。

当然,还有第五个要素,这里没有提到。它是专门针对于你的嵌入式应用的程序集合,使得设备做任何它应该做的事情,包括货物称重、显示电影、控制机器人或者驾驶无人机。

通常,当你购买SoC或开发板时,将会以包的形式向你提供一些或所有这些要素。但是,由于前面章节提到的原因,对你来说它们可能不是最好的选择。我将在前6章提供背景知识让你能够做出正确的选择,并且介绍两个用于自动化整个过程的工具:Buildroot和Yocto项目。1.4 开放源码

嵌入式Linux的组件是开放源码的,因此现在是很好的时机来考虑这意味着什么,为什么开放源码的工作如此有效,以及它如何影响你将要基于开放源码创建的、通常是专有的嵌入式设备。许可证

当谈到开放源码时,“免费”(free)一词经常被使用。初学者通常以为这意味着不用支付,开源软件许可证确实能够保证你可以免费的使用软件开发和部署系统。然而,这里更重要的含义是“自由”(freedom),因为你可以任意获得源代码,以认为合适的方式修改它并且重新部署到其他系统中。这些许可证给予你这些权利。相比之下,共享软件许可证允许你免费复制二进制文件但是不提供源代码,或者其他许可证允许你在某些特定条件下免费使用软件,例如个人使用而不是商业用途。这些都不是开放源码。

我将提供以下建议帮助你理解与开源软件许可证一起工作的含义,但是我想指出的是,我是一名工程师而不是律师。以下是我对许可证的理解和解释它们的方式。

开放源码许可证大致可分为两类:自由软件基金会的GPL(General Public License)和来自BSD(Berkeley Software Distribution)的许可证,Apache基金会以及其他。

本质上,许可证表示你可以修改源代码并且可以在你自己选择的系统中使用它,只要你不要以任何方式修改许可证条款。换句话说,只要遵循这一条限制,你可以用它做任何你想要做的事情,包括将它构造成为可能的专有系统。

GPL许可证是类似的,但是它有条款强制你将获取和修改软件的权利传递给你的最终用户。换句话说,要共享你的源代码。一种选择是通过将它放在公共服务器上,使得它完全公开。另一种方式是,只有在需要时才通过书面提供方式向你的最终用户提供代码。GPL进一步要求你不能将GPL代码混合进专有程序中。对于任何这样做的企图将会使GPL适用于整个软件。换句话说,你不能在一个程序中结合GPL和专有代码。

那么,关于软件库怎么样呢?如果它们是通过GPL许可的,任何与之链接的程序也会是GPL的。然而,大多数库都是由LGPL(Lesser General Public License)许可的。如果是那样的话,你将可以从专有程序链接它们。

前面的所有描述具体涉及GPL v2和LGPL v2.1。下面我要说的是最新版本的GPL v3和LGPL v3。这版许可证是有争议的,并且我承认自己并不完全理解其含义。然而,我的意图是确保在任何系统中的GPL v3和LGPL v3组件可以被最终用户替换掉,对于每个人来说这是开源软件的精神。但是它确实会引发某些问题。一些Linux设备根据订阅级别或者其他限制获取信息,而替换软件的关键部分可能造成损害。机顶盒就属于这种类型。这里还有安全方面的问题。如果设备的拥有者可以访问代码,那么一个不受欢迎的入侵者也可能这么做。通常的防御措施是由厂商作为权威机构对内核映像签名,从而禁止未授权的更新。如果我对自己的设备进行修改,这是否侵权呢?对此看法不一。

TiVo机顶盒是这场争论的重要组成部分。它使用在GPL v2许可下的Linux内核。TiVo发布其内核版本的源代码,因此符合该许可证。TiVo还提供一个引导加载程序,该程序只能装载由他们签名的内核二进制文件。因此,你可以为TiVo机顶盒构造一个已修改的内核,但是不能将其装载到硬件上。自由软件基金会(FSF)认为这不符合开源软件的精神,并且将该过程称为“tivoization”。GPL v3和LGPL v3在书面中明确阻止这种情况发生。一些项目,特别是Linux内核一直不愿意采取第三版许可证,就是因为它对于设备制造商的限制。1.5 嵌入式Linux系统硬件

如果你正在为一个嵌入式Linux项目设计或选择硬件,那么应该注意些什么呢?

第一,需要一个由内核支持的CPU体系架构,除非你自己打算增加一个新的架构!查看Linux 4.1的源代码,那里有30个架构,每个架构由arch/目录中的一个子目录表示。它们都是32位或64位架构,大多数具有一个内存管理单元(MMU),但是一些没有。嵌入式设备中最常见的是ARM、MIPS、PowerPC和X86,每个都有32位和64位的变体,并且它们都有内存管理单元。

本书的大部分内容是针对这类处理器编写的。还有另外一组处理器没有MMU,它们运行Linux的一个子集,称为微控制器Linux或者uClinux。这些处理器架构包括ARC、Blackfin、Microblaze和Nios。我将会不时地提到uClinux,但是我不会详述细节,因为这是一个相当专业的话题。

第二,你将需要合理的RAM空间。16 MB是一个比较合适的最小大小,尽管运行Linux很可能就会使用其一半的空间。如果你不怕麻烦,准备优化系统的每个部分,那么利用4 MB运行Linux也是可能的。RAM的大小甚至有可能降得更低,但是达到某一个点它就不再是Linux了。

第三,非易失性存储,通常是闪存。对于简单的设备如网络摄像头或者简单路由器,8 MB就足够了。和内存一样,如果你希望的话,可以用更少的存储创建一个可工作的Linux系统,但是你降得越低,就会变得越困难。Linux广泛支持各种闪存存储设备,包括raw NOR和NAND闪存芯片,以及各种形式的托管闪存,如SD卡、eMMC芯片、USB闪存等等。

第四,调试端口是非常有用的,最常用的是RS-232串行端口。它不需要安装在生产板上,但是能够使bring-up板、调试和开发更加容易。

第五,从头开始时需要一些加载软件的方法。几年前,板上已经安装了JTAG接口用于该目的,但是现在SoC具有直接从可移动介质特别是SD、Micro SD卡或者诸如RS-232或USB串行接口加载启动代码的能力。

除了这些基础事项之外,还有一些特定的硬件接口可以帮助你的设备完成工作。主流Linux为成千上万个不同设备提供开源驱动程序,而在设计中可能会有包括来自SoC厂商的(不同质量的)驱动程序以及OEM提供的第三方芯片的驱动程序,但是请记住我在前文中做出的评论以及一些厂商的能力。作为一名嵌入式设备的开发人员,你将发现在评价和适配第三方代码上会花费很多时间,或者联系厂商让他们来处理。不论如何,你将需要为那些对于设备来说是唯一的接口编写设备支持代码,或者找人为你做这项工作。1.6 本书使用的硬件

本书中的工作实例会尽量通用,但是为了使得它们相互关联并且易于学习,不得不选择一个特定设备作为例子。这里使用两个样例设备:BeagleBone Black和QEMU。前者是一个广泛使用的廉价开发板,可用于正式的嵌入式硬件。后者是一个机器模拟器,可用来创建嵌入式系统特有的范围广泛的系统。完全使用QEMU是很有吸引力的,但是正如所有的模拟器,它和真实的东西并不完全相同。使用BeagleBone,你可以在和真实的硬件交互中获得满足并且能够看到真实的LED闪光。选择一个比BeagleBone Black更时新的板也是很有吸引力的,因为BeagleBone Black毕竟已经有好几年了,但是我相信它的流行给它提供了一定的生命力,这意味着它还将继续使用若干年。

在任何情况下,我鼓励你使用这两个平台尝试尽可能多的实例,或者你可能获得的任何嵌入式硬件。1.6.1 BeagleBone Black

BeagleBone和后来的BeagleBone Black是Circuitco LLC公司生产的具有开放硬件设计的开发板,它的尺寸非常小,只有信用卡大小。主要信息库见网站:www.beagleboard.org。其规格的要点是:

·TI AM335x 1GHz ARM Cortex-A8 Sitara SoC

·512 MB DDR3 RAM

·2或4 GB 8位eMMC板载闪存

·调试和开发用串行端口

·MicroSD连接器,可用作引导设备

·Mini USB OTG客户/主机端口,也可用于开发板供电

·全尺寸的USB 2.0主机端口

·10/100以太网端口

·视频和音频输出用HDMI

此外,板上还有两个46引脚的扩展头,因为有各种各样的子板,称为配套板(cape),它们能让你调整开发板来完成许多不同的事情。然而,在本书的实例中,你不需要安装任何配套板。

除了板本身,你还需要:

·用于供电的Mini USB至全尺寸USB的电缆(由板提供),除非你具有该列表的最后一项。

·可以与板提供的6针3.3伏TTL电平信号接口对接的RS-232电缆。Beagleboard网站提供兼容电缆的链接。

·MicroSD卡以及从你的开发PC或笔记本电脑写至该卡的手段,需要用它将软件加载至板上。

·以太网电缆,因为有些例子需要网络连接。

·可选的,但是推荐提供一个5V电源,能够传送1A或更大的电流。1.6.2 QEMU

QEMU是一个机器模拟器。它会有许多不同的风格,其中每个都可以模拟一个处理器体系架构,以及若干使用该架构的板。以下是一些实例:

·qemu-system-arm:ARM

·qemu-system-mips:MIPS

·qemu-system-ppc:PowerPC

·qemu-system-x86:x86和x86_64

对于每个体系架构,QEMU模拟一系列硬件,可以使用-machine help选项查看。每个机器模拟通常在板上找到的大多数硬件。存在将硬件链接至本地资源的选项,例如使用本地文件来模拟硬盘驱动器。下面是一个具体实例:

在上面的命令行中,使用的选项是:

·-machine vexpress-a9:创建一个具有Cortex A-9处理器的ARM Versatile Express开发板仿真

·-m 256M:组装256 MB内存

·-drive file=rootfs.ext4,sd:连接sd接口至本地文件rootfs.ext4(包含一个文件系统映像)

·-kernel zImage:从名为zImage的本地文件加载Linux内核

·-dtb vexpress-v2p-ca9.dtb:从本地文件vexpress-v2p-ca9.dtb加载设备树

·-append"...":提供该字符串作为内核命令行

·-serial stdio:连接串口至已启动QEMU的终端,这样你就可以通过串行控制台登录到模拟的机器上

·-net nic,model=lan9118:创建一个网络接口

·-net tap,ifname=tap0:连接网络接口至虚拟网络接口tap0

为了配置网络的主机端,你需要来自用户模式Linux(UML)项目的tunctl命令,在Debian和Ubuntu中,该软件包命名为uml-utilities。通过使用以下命令,你可以使用它创建一个虚拟网络:

该命令创建一个名为tap0的网络接口,它连接至模拟的QEMU机器中的网络控制器。配置tap0的方式与任何其他接口完全相同。

所有这些选项将在下面的章节中进行详细描述。在大多数例子中,我将使用Versatile Express,但是使用不同的机器或体系架构应该也很容易。1.7 本书使用的软件

对于开发工具以及目标操作系统和应用程序,我只使用开源软件。我假设你在开发的系统中使用Linux。我使用Ubuntu 14.04测试了所有主机命令,因此稍微偏向该特定版本,但是任何现代Linux发行版本都应该可以正常工作。1.8 总结

遵循摩尔定律设定的轨迹,嵌入式硬件将继续变得更为复杂。Linux具有高效使用硬件的能力和灵活性。

在创建一个工作产品时,Linux只是你所需要的众多开源软件中的一个组件。代码免费自由可用的事实,意味着位于许多层次的人员和组织都可以做出贡献。然而,由于嵌入式平台的种类繁杂以及快速发展,导致隔离的软件池并没有实现应有的充分共享。在许多情况下,你会变得依赖于该软件,特别是你的SoC或板厂商提供的Linux内核,其次是工具链。一些SoC制造商在将它们的变化推送至上游方面做得越来越好,并且维护这些变化也变得越来越容易。

幸运的是,有一些功能强大的工具可以帮助创建和维护设备软件。例如,Buildroot对于小型系统来说十分理想,而Yocto项目主要用于较大的系统。

在描述这些构建工具之前,我将描述嵌入式Linux的四个要素,你可以将其用于所有已创建的嵌入式Linux项目中。下一章是关于这些要素中的第一个,即工具链,你需要它为目标平台编译代码。第2章 学习工具链

工具链是嵌入式Linux的第一个要素,也是你的项目起点。你在该早期阶段所做的选择将对最终成果产生深远的影响。通过使用处理器的优化指令集、浮点单元(如果有的话)等,你的工具链应该能够高效使用硬件。它应该支持你所需要的语言,并且具有POSIX和其他系统接口的可靠实现。不仅如此,它还应该在发现安全缺陷或错误时及时更新。最后,它应该在整个项目中保持不变。换句话说,一旦你选择了工具链,坚持使用它就显得非常重要。在项目期间,以不一致的方式改变编译器和开发库将会导致细微的错误。

获取工具链就像下载安装包一样简单。但是工具链本身十分复杂,在本章中我会将这一点细致展示给你。2.1 工具链是什么

工具链是一个工具集,用于将源代码编译成可以在你的目标设备中运行的可执行文件,主要包括编译器、链接器和运行时库。最初,你需要一个工具链构造嵌入式系统的其他三个要素:引导程序、内核和根文件系统。它能够编译以汇编语言、C或C++语言编写的代码,因为这些都是在基础的开源软件包中使用的语言。

通常,Linux工具链是基于GNU项目的组件(http://www.gnu.org),在写作本书的时候大多数情况下仍然如此。然而,在过去的几年中,Clang编译器以及相关的LLVM项目(http://llvm.org)已经取得了重要进展,并且它现在成为GNU工具链的一个可行的替代方案。LLVM和基于GNU的工具链之间的一个主要区别在于授权许可,LLVM具有BSD许可证,而GNU则是GPL许可证。Clang也有一些技术上的优势,例如更快的编译和更好的诊断,但是GNU GCC在已有代码库的兼容性以及体系架构和操作系统的广泛支持方面具有优势。事实上,还有一些Clang无法取代GNU C编译器的领域,特别是当它用来编译一个主流Linux内核时。很有可能的情况是,在未来一年左右的时间里,Clang将能够编译嵌入式Linux需要的所有组件,从而成为GNU的一个替代选择。关于如何使用Clang进行交叉编译的详细描述,请参见http://clang.llvm.org/docs/CrossCompilation.html。如果你希望将它作为嵌入式Linux构造系统的一部分,那么EmbToolkit是一个很好的选择,EmbToolkit(https://www.embtoolkit.org)完全支持GNU和LLVM/Clang工具链,并且许多人正在通过Buildroot和Yocto Project使用Clang。第6章将讨论嵌入式构造系统。同时,本章重点介绍GNU工具链,因为它是目前来说唯一完整的选项。

一个标准的GNU工具链包括以下三个组件:

·Binutils:二进制工具集合,包括汇编器和链接器ld。可通过以下链接获取:http://www.gnu.org/software/binutils/。

·GNU编译器集合(GCC):主要是C和其他语言的编译器,取决于GCC的版本,包括C++、Objective-C、Objective-C++、Java、Fortran、Ada和Go。它们都使用一个公共的后端生成汇编代码,并且提交给GNU汇编器。可通过以下链接获取:http://gcc.gnu.org/。

·C库:基于POSIX规范的标准化API,这是从应用程序到操作系统内核的主要接口。除此之外,还有几个库需要考虑,这部分内容后面会讲到。

除了这些,你还需要一份Linux内核头文件的副本,其中包含了直接访问内核时所需的定义和常量。现在,你不仅需要它们才能够编译C库,而且以后在编写与特定Linux设备交互的程序或编译相关的库时还需要它们,例如通过Linux帧缓冲驱动程序显示图形。这不仅仅是简单地在内核源代码的include目录中创建一个头文件副本。这些头文件仅适用于内核,其包括的定义如果以原始状态用于编译正常的Linux应用程序将会导致冲突。

你将需要生成一个已净化的内核头文件集合,我将在第5章中详细阐述。

通常,内核头文件是否从你将要使用的Linux确切版本中生成并不重要。因为内核接口总是向后兼容的,唯一必要的是,内核头文件的版本与你在目标中使用的版本相同或者更老。

大多数人认为GNU调试器GDB也是工具链的一部分,通常它是在这个时候构建的。我将在第12章中讨论GDB。2.2 工具链类型:本地工具链和交叉工具链

对于我们而言,有两种类型的工具链:

·本地:该工具链运行在相同类型的系统上,有时就和它生成的程序运行在同一个系统上。这对于台式机和服务器来说是常见的情况,而在特定类型的嵌入式设备上这种情况也变得越来越普遍。例如,运行ARM Debian的Raspberry Pi具有自托管的本地编译器。

·交叉:该工具链运行在与目标不同类型的系统上,允许在快速的桌面PC上完成开发,然后加载到嵌入式目标上用于测试。

几乎所有的嵌入式Linux开发都使用交叉开发工具链,部分原因是大多数嵌入式设备缺乏计算能力、内存和存储,并不适合于程序开发,更多的是因为它能保持主机和目标环境分离。当主机和目标使用相同的体系架构如X86_64时,后面一点尤为重要。在这种情况下,在主机上本地编译并且简单地复制二进制文件到目标是很有吸引力的。这在一定程度上是有效果的,但是主机发行版接收更新比目标更为频繁,导致不同的工程师在为目标构造代码时,将会具有稍微不同的主机开发库版本,从而你会违反工具链应该在整个项目生命周期中保持不变的原则。如果能够确保主机和目标构建环境相互保持一致,该方法也能够正常工作,但是更好的方法是保持主机和目标分离,而交叉工具链就提供了这样一种方式。

然而,也有相反的观点支持本地开发。交叉开发需要为目标交叉编译所有的库和工具,因而造成了负担。稍后在本章我们将看到,交叉编译并不总是简单的,因为大多数开源包都不是设计成这种构建方式。集成构建工具,包括Buildroot和Yocto Project,通过封装规则来交叉编译在典型嵌入式系统中需要的一系列包,从而提供帮助,但是如果你希望编译大量额外的包,那么最好还是本地编译它们。例如,要想为Debian发行版提供Raspberry Pi或者BeagleBone,使用交叉编译器是不可能的,它们必须通过本地编译。从零开始创建一个本地编译环境并不容易,包括首先创建一个交叉编译器,从而在目标上引导一个本地构建环境,然后使用它构建包。你将需要为配置良好的目标板提供构建环境,或者可以使用QEMU模拟目标。如果你想进一步探讨该问题,你可能希望查看Scratchbox项目,现在它发展至第二版Scratchbox2(https://maemo.gitorious.org/scratchbox2)。它是由诺基亚开发的,用于构建其Maemo Linux操作系统,如今在Mer项目、Tizen项目以及其他项目中使用。

与此同时,在本章中我将重点放在更为主流的交叉编译器环境,它相对容易安装和管理。CPU体系架构

工具链必须根据目标CPU的功能进行构建,主要包括:

·CPU体系架构:arm、mips、x86_64等。

·高或低字节序操作:有些CPU可以在两种模式下运行,但是每种模式下的机器代码是不同的。

·浮点支持:并非所有版本的嵌入式处理器都实现硬件浮点单元,在这种情况下,工具链可以通过调用软件浮点库来代替硬件浮点单元。

·应用二进制接口(ABI):用于在函数调用之间传递参数的调用约定。

对于许多体系架构来说,ABI在整个处理器家族中是保持不变的。一个显著的例外是ARM。在2000年后期,ARM体系架构转换到扩展应用二进制接口(EABI),导致以前的ABI被命名为旧的应用二进制接口(OABI)。虽然OABI已经过时,但你可以继续查看EABI的参考资料。从那时起,根据浮点参数传递的方式,EABI已经分裂为两种。最初的EABI使用通用(整数)寄存器,而新的EABIHF使用浮点寄存器。在浮点操作方面,EABIHF明显更快,因为它无需在整数和浮点寄存器之间复制数据,但是它不兼容那些没有浮点单元的CPU。那么,你只能在两个互不兼容的ABI之间选择:既然不能混合和匹配两者,你不得不在这个阶段做出决定。

GNU使用一个工具前缀来识别可能产生的不同组合,包括由三个或四个组件构成的多元组,组件名之间通过折线分开,描述如下:

·CPU:CPU体系架构,如arm、mips或x86_64。如果CPU具有两种字节序模式,它们可以通过增加el(代表低字节序)或者eb(代表高字节序)来加以区别。比较好的例子是,低字节序MIPS表示为mipsel,而高字节序ARM表示为armeb。

·厂商:标识工具链的提供商。例子包括buildroot、poky或是未知。有时它完全被忽略。

·内核:对于我们来说,它永远是“Linux”。

·操作系统:用户空间组件的名称,可能是gnu或者uclibcgnu。ABI也可能附加在这里,因此,对于ARM工具链,你可能看到gnueabi、gnueabihf、uclibcgnueabi或uclibcgnueabihf。

通过使用gcc的-dumpmachine选项,你可以找到在构建工具链时使用的多元组。例如,你可以在主机计算机上看到以下信息:

当本地编译器安装在机器上时,对于创建的到工具链中每个工具的链接,没有前缀是很正常的。因此,你可以通过gcc命令调用编译器。

下面是使用交叉编译器的一个例子:2.3 选择C库

UNIX操作系统的编程接口是用C语言定义的,现在它是通过POSIX标准定义的。该接口是用C库实现的,对于Linux程序来说,它是到内核的入口,如图2-1所示。即使你用其他语言编写程序,可能是Java或者Python,相应的时支持库最终将会调用C库:

每当C库需要内核的服务时,它就会使用内核system call接口在用户空间和内核空间切换。直接通过内核系统调用,是有可能绕过C库的,但这非常麻烦并且几乎从不需要这样做。图2-1 C库是应用程序到内核的入口

有几个C库可供选择。主要的选项如下:

·glibc:可通过以下网站获取:http://www.gnu.org/software/libc。这是标准GNU C库。它体积较大,并且直到现在仍然不容易配置,但它是POSIX API的最完整实现。

·eglibc:可通过以下网站获取:http://www.eglibc.org/home。这是嵌入式的GLIBC。它是glibc的一系列补丁,增加配置选项以及glibc没有覆盖的体系架构支持(特别是PowerPC e500)。eglibc和glibc之间的划分总是人为的,幸运的是,自从2.20版本,eglibc的代码库已经合并回glibc中,留给我们一个改进的库。eglibc已经不再维护。

·uClibc:可通过以下网站获取:http://www.uclibc.org。字母‘u’实际上是希腊语‘mu’字符,说明这是微控制器C库。它首先被开发用来与uClinux(针对没有内存管理单元的CPU提供的Linux)一起工作,但是后来已被调整为与完整的Linux一起使用。通过一个配置实用程序,可以微调它的特性以满足需要。即使完整配置的uClibc也小于glibc,但它并不作为POSIX标准的一个完整实现。

·musl libc:可通过以下网站获取:http://www.musl-libc.org。这是一个用于嵌入式系统C库的新设计。

那么,应该选择哪一个呢?我的建议是,只有在使用uClinux或者存储或内存空间非常有限的情况下才使用uClibc,这种情况下小尺寸将会是一种优势。否则,我更愿意使用最新的glibc或eglibc。我并没有musl libc的使用经验,但是如果你发现glibc/eglibc并不合适的话,无论如何要去试一试它。该过程概括为如图2-2所示:图2-2 选择一个C库2.4 寻找工具链

对于交叉开发工具链,你有三个选择:寻找一个已构建好的能够满足需求的工具链;使用一个由嵌入式构建工具生成的工具链,在第6章中将介绍这一点;或者按照本章后面的描述创建属于你自己的工具链。

预先构建的交叉工具链是一个有吸引力的选项,你只需要下载和安装它,但是你被局限于特定的工具链配置,并且依赖于向你提供该工具链的人员或者组织。最有可能是以下一种情况:

·SoC或开发板供应商。大多数厂商提供一个Linux工具链。

·专门为指定体系架构提供系统级支持的联盟。例如,Linaro(https://www.linaro.org)为ARM体系架构提供预先构建的工具链。

·第三方Linux工具厂商,例如Mentor Graphics、TimeSys或者MontaVista。

·用于Linux桌面发行版的交叉工具包。例如,基于Debian的发行版提供针对ARM、MIPS和PowerPC目标的交叉编译包。

·由某个集成的嵌入式构建工具生成的二进制SDK。Yocto Project在http://autobuilder.yoctoproject.org/pub/releases/CURRENT/toolchain提供了一些实例,另外还有位于ftp://ftp.denx.de/pub/eldk/的Denx嵌入

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

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载