我们先对嵌入式Linux做一个高层次的了解,看看它为什么受欢迎,开放源码许可证有什么意义,以及你需要什么样的硬件来运行Linux。
1999年左右,Linux首次成为嵌入式设备的可行的选择。那是在Axis (https://www.axis.com)发布他们的第一台Linux驱动的网络摄像机和TiVo (https://business.tivo.com)发布他们的第一台数字视频录像机(DVR)时。自1999年以来,Linux变得越来越流行,以至于今天它是许多类别产品的首选操作系统。2021年,有超过20亿台设备运行Linux。这包括大量运行Android的智能手机,它使用Linux内核,以及数以亿计的机顶盒、智能电视和Wi-Fi路由器,更不用说非常多样化的设备,如车辆诊断、地磅、工业设备和医疗监测装置等。
选择Linux
为什么Linux会如此普及?为什么像电视这样简单的东西需要运行像Linux这样复杂的东西才能在屏幕上显示流媒体视频?
简单的答案是摩尔定律: 英特尔公司的联合创始人戈登-摩尔(Gordon Moore)在1965年观察到,芯片上的元件密度大约每两年就会增加一倍。这适用于我们在日常生活中设计和使用的设备,正如它适用于台式机、笔记本电脑和服务器一样。大多数嵌入式设备的核心是一个高度集成的芯片,它包含一个或多个处理器内核以及与主存储器、大容量存储和多种类型的外设的接口。这被称为片上系统,或SoC(System on Chip),而且SoC的复杂程度正按照摩尔定律不断提高。典型的SoC有一本技术参考手册,长达数千页。你的电视不是简单地显示一个视频流,就像以前的模拟机那样。
该数据流是数字的,可能是加密的,它需要处理以创建一个图像。你的电视已经(或很快将)与互联网连接。它可以接收来自智能手机、平板电脑和家庭媒体服务器的内容。它可以(或很快就会)用来玩游戏等等。你需要完整的操作系统来管理这种程度的复杂性。
以下是推动采用Linux的一些要点:
- Linux有必要的功能。它有好的调度器个好的网络堆栈,支持USB、Wi-Fi、蓝牙、多种存储介质,对多媒体设备有很好的支持,等等。它满足了所有的要求。
- Linux已经被移植到广泛的处理器架构上,包括一些在SoC设计中非常常见的架构–Arm、MIPS、x86和PowerPC。
- Linux是开放源码的,所以你可以自由地获得源代码并修改它以满足你的需要。你,或代表你工作的人,可以为你的特定SoC板或设备创建板卡支持包。你可以添加协议、功能和技术,这些可能是主线源代码中所缺少的。你可以删除你不需要的功能,以减少内存和存储要求。Linux是灵活的。
- Linux有一个活跃的社区;就Linux内核而言,非常活跃。每隔8到10周就会个新的内核版本,每个版本都包含1000多个开发者的代码。活跃的社区意味着Linux是最新的,支持当前的硬件、协议和标准。
- 开放源码许可证保证你可以获得源代码。没有供应商的束缚。
由于这些原因,Linux是复杂设备的理想选择。但在这里我应该提到一些注意事项。复杂性使它更难理解。再加上快速发展的开发过程和开源的分散结构,你必须付出一些努力来学习如何使用它,并随着它的变化而不断地重新学习。我希望这本书能在这个过程中有所帮助。
什么时候不选择Linux
Linux适合你的项目吗?在所要解决的问题能够证明其复杂性的情况下,Linux的效果很好。在需要连接性、稳健性和复杂的用户界面的地方,它尤其好。然而,它不能解决所有的问题,所以在你加入之前有一些事情需要考虑:
- 你的硬件是否能胜任这项工作?与传统的实时操作系统(RTOS)如VxWorks或QNX相比,Linux需要更多的资源。它至少需要一个32位的处理器和大量的内存。我将在关于典型硬件的章节中更详细地介绍。
- 你有正确的技能组合吗?一个项目的早期部分,即电路板的搭建,需要详细了解Linux以及它与你的硬件之间的关系。同样地,在调试和调整你的应用程序时,你需要能够解释结果。如果你内部没有这些技能,你可能想把一些工作外包出去。当然,读这本书会有帮助
- 你的系统是实时的吗?Linux可以处理许多实时活动,只要你要注意某些细节,我将在第21章详细介绍这些细节:实时编程。
- 你的代码是否需要监管部门的批准(医疗、汽车、航空等等)?监管部门的核查和验证的负担可能使另一个操作系统成为更好的选择。即使你选择Linux用于这些环境,从一个已经为现有产品(如你正在建造的产品)提供Linux的公司购买商业上可用的发行版可能是有意义的。
仔细考虑这些问题。成功的最佳指标可能是四处寻找运行Linux的类似产品,看看他们是如何做到的;遵循最佳实践。
参与者
开源软件从哪里来?谁写的?特别是,这与嵌入式开发的关键组件–工具链、引导程序、内核和根文件系统中的基本实用程序–有何关系?
主要的参与者如下:
- 开源社区: 毕竟,这是产生你将要使用的软件的引擎。这个社区是一个松散的开发者联盟,其中许多人以某种方式得到资助,也许是由一个非营利组织、学术机构或一业公司资助。他们一起工作,以进一步实现各种项目的目标。有许多这样的项目–有些小,有些大。在本书的其余部分,我们将使用的一些项目是Linux本身、U-Boot、BusyBox、Buildroot、Yocto项目以及GNU旗下的许多项目。
- CPU架构师: 这些是设计我们使用的CPU的组织。这里重要的是Arm/Linaro(Arm Cortex-A)、Intel(x86和x86_64)、SiFive(RISC-V)和IBM(PowerPC)。他们实现或至少影响对基本CPU架构的支持。
- SoC供应商(Broadcom、Intel、Microchip、NXP、Qualcomm、TI和许多其他公司): 他们从CPU架构师那里获得内核和工具链,并修改它们以支持他们的芯片。他们还创建了参考板:下一级使用的设计,以创建开发板和工作产品。
- 板卡供应商和OEMs: 这些人从SoC供应商那里获得参考设计,并将其构建到特定的产品中,例如机顶盒或相机,或创建更多的通用开发板,如研华和控创的开发板。一个重要的类别是廉价的开发板,如BeagleBoard/BeagleBone和Raspberry Pi,它们创造了自己的软件和硬件附加组件的生态系统。
- 商业Linux供应商: 西门子(Mentor)、Timesys和风河等公司提供商业Linux发行版,这些发行版在多个行业(医疗、汽车、航空航天等)都经过了严格的法规验证和确认。
这些构成了一个链条,你的项目通常在最后,这意味着你不能自由选择组件。你不能简单地从https://www.kernel.org/,除非在少数情况下,因为它不支持你正在使用的芯片或电路板。
这是嵌入式开发中的一个持续问题。理想情况下,链条上每个环节的开发者都会把他们的变化推到上游,但他们没有这样做。发现一个内核有成千上万的补丁没有被合并的情况并不少见。此外,SoC供应商倾向于只为其最新的芯片积极开发开源组件,这意味着对任何超过几年前的芯片的支持将被冻结,不会收到任何更新。
其结果是,大多数嵌入式设计是基于旧版本的软件。他们不会收到安全修复、性能增强或较新版本的功能。诸如Heartbleed(OpenSSL库中的一个错误)和ShellShock(bash shell中的一个错误)等问题都没有得到修复。我将在本章后面的安全主题下进一步讨论这个问题。
你能做什么呢?首先,向你的供应商(恩智浦、德州仪器和赛灵思,仅举几例)提问:他们的更新政策是什么,他们多久修改一次内核版本,当前的内核版本是什么,之前的版本是什么,他们向上游合并变化的政策是什么?一些供应商在这方面正在取得长足进步。你应该首选他们的芯片。
第二,你可以采取措施使自己更加自给自足。第1节中的各章更详细地解释了依赖性,并告诉你在哪些方面可以帮助自己。不要只是接受SoC或电路板供应商提供给你的软件包,而不考虑其他选择,盲目地使用它。
在项目生命周期中前进
本书分为四个部分,反映了一个项目的各个阶段。这些阶段不一定是有顺序的。通常情况下,它们是重叠的,你需要跳回去重新审视以前做过的事情。然而,它们代表了开发人员在项目进展过程中所关注的问题:
- 嵌入式Linux的要素(第1至8章)将帮助你建立开发环境,并为后面的阶段创建一个工作平台。它通常被称为 “板块建立 “阶段。
- 系统结构和设计选择(第9章到第15章)将帮助你研究一些你必须做出的设计决定,涉及程序和数据的存储,如何在内核设备驱动程序和应用程序之间进行分工,以及如何初始化系统。
- 编写嵌入式应用程序(第16至18章)展示了如何打包和部署Python应用程序,有效利用Linux进程和线程模型,以及如何在一个资源受限的设备中管理内存。
- 调试和优化性能(第19章到21章)描述了如何在应用程序和内核中跟踪、剖析和调试你的代码。最后一章解释了在需要时如何设计实时行为。
嵌入式Linux的四个要素
每个项目都是从获得、定制和部署这四个要素开始的:工具链、引导程序、内核和根文件系统。这就是本书第一节的主题。
- 工具链: 为你的目标设备创建代码所需的编译器和其他工具。
- Bootloader: 初始化电路板并加载Linux内核的程序。
- 内核: 这是系统的核心,管理系统资源和与硬件的接口。
- 根文件系统: 包含库和程序,一旦内核完成初始化就会运行。
当然,还有一个第五元素,这里没有提到。这就是专门针对你的嵌入式应用的程序集合,使设备做任何它应该做的事情,无论是称杂货、显示电影、控制机器人,还是驾驶无人机。
通常情况下,当你购买SoC或电路板时,你会被提供一些或所有这些元素作为一个包。但是,由于前一段中提到的原因,它们可能不是你的最佳选择。我将在前八章中给你提供背景,让你做出正确的选择,我还将向你介绍两个工具,为你自动完成整个过程: Buildroot和Yocto项目。
浏览开放源代码
嵌入式Linux的组件都是开源的,所以现在是考虑这意味着什么的好时机,为什么开源以它们的方式工作,以及这对你将用它创建的通常是专有的嵌入式设备有什么影响。
许可证
在谈论开源时,经常使用自由这个词。刚接触这个话题的人往往认为它意味着无需付费,而开源软件许可证确实保证你可以免费使用该软件来开发和部署系统。然而,这里更重要的含义是自由,因为你可以自由地获得源代码,以任何你认为合适的方式修改它,并在其他系统中重新部署它。这些许可证给了你这个权利。与免费软件许可证相比,后者允许你免费复制二进制文件,但不给你源代码,或者其他许可证允许你在某些情况下免费使用该软件,例如,用于个人用途,但不用于商业。这些都不是开源的。
为了帮助你理解使用开放源码许可证的意义,我将提供以下评论,但我想指出,我是一名工程师,而不是一名律师。以下是我对许可证的理解和对它们的解释方式。
开源许可证大致分为两类:自由复制许可证,如GNU通用公共许可证(GPL),以及允许性许可证,如BSD和MIT许可证。
允许性许可证实质上是说,只要你不以任何方式修改许可证的条款,你就可以修改源代码并在你自己选择的系统中使用它。换句话说,有了这一限制,你就可以随心所欲地使用它,包括将它构建到可能的专有系统中。
GPL许可证是类似的,但有条款迫使你把获得和修改软件的权利转给你的最终用户。换句话说,你要分享你的源代码。一种选择是把它放到一个公共服务器上,使其完全公开。另一种方法是通过书面形式提供给你的最终用户,当他们要求时,你可以提供代码。GPL进一步指出,你不能将GPL代码纳入专有程序。任何这样做的尝试都会使GPL适用于整体。换句话说,你不能在一个程序中结合GPL和专有代码。除了Linux内核,GNU编译器集和GNU调试器以及许多其他与GNU项目相关的免费工具都属于GPL的保护范围。
那么,库呢?如果它们获得了GPL的许可,任何与之链接的程序也将获得GPL的许可。然而,大多数库是以GNU小公共许可证(LGPL)授权的。如果是这种情况,你可以从一个专有程序中与它们链接。
重要提示
所有前面的描述都与GPL v2和LGPL v2.1特别相关。我应该提到最新版本的GPL v3和LGPL v3。这些都是有争议的,我承认我并不完全了解其含义。然而,其目的是确保任何系统中的GPL v3和LGPL v3组件都可以被终端用户替换,这符合开放源码软件对所有人的精神。
不过,GPL v3和LGPL v3也有其问题。有安全方面的问题。如果一个设备的所有者能够访问系统代码,那么不受欢迎的入侵者也可能会访问。通常的防御措施是拥有由权威机构(如供应商)签署的内核镜像,这样未经授权的更新就不可能发生。这是否侵犯了我修改设备的权利?众说纷纭。
重要说明
TiVo机顶盒是这场辩论的一个重要部分。它使用的是Linux内核,该内核在GPL v2下获得许可。TiVo已经发布了他们的内核版本的源代码,因此遵守了该许可。TiVo也有一个引导程序,只能加载由他们签名的内核二进制文件。因此,你可以为TiVo盒子建立一个修改过的内核,但你不能在硬件上加载它。自由软件基金会(FSF)认为这不符合开源软件的精神,并将这种程序称为Tivo化。GPL v3和LGPL v3的编写是为了明确地防止这种情况的发生。一些项目,特别是Linux内核,一直不愿意采用GPL第3版许可证,因为它们会给设备制造商带来限制。
为嵌入式Linux选择硬件
如果你正在为一个嵌入式Linux项目设计或选择硬件,你要注意什么?
首先,一个内核支持的CPU架构–当然,除非你打算自己添加一个新的架构 看一下Linux 5.4的源代码,有25个架构,每个架构都由arch/目录下的一个子目录代表。它们都是
32位或64位架构,大多数有MMU,但有些没有。嵌入式设备中最常见的是Arm、MIPS、PowerPC和x86,它们都有32位和64位的变体,都有内存管理单元(MMU)。
本书的大部分内容都是针对这类处理器而写的。还有一类处理器没有MMU(memory management units),运行Linux的一个子集,称为微控制器Linux或uClinux。这些处理器架构包括ARC(Argonaut RISC Core)、Blackfin、MicroBlaze和Nios。我将不时地提到uClinux,但我不会详细介绍,因为这是一个相当专业的话题。
第二,你将需要合理数量的内存。16 MiB是一个很好的最低限度,尽管使用一半的内存也很有可能运行Linux。如果你准备对系统的每个部分进行优化,甚至有可能用4MB来运行Linux。甚至有可能更低,但有一点是,它不再是Linux了。
第三,有非易失性存储,通常是闪存。对于一个简单的设备,如网络摄像头或一个简单的路由器,8兆字节就足够了。与内存一样,如果你真的想,你可以用更少的存储空间创建一个可行的Linux系统,但你走得越低,就越难。Linux对闪存设备有广泛的支持,包括原始的NOR和NAND闪存芯片,以及SD卡、eMMC芯片、USB闪存等形式的管理闪存。
第四,一个串行端口是非常有用的,最好是基于UART的串行端口。它不一定要安装在生产板上,但可以使板子的安装、调试和开发更容易。
第五,从头开始时,你需要一些加载软件的手段。许多微控制器板都配备了一个联合测试行动组(JTAG)接口,用于这一目的。现代SoC也有能力直接从可移动媒体,特别是SD和微型SD卡,或UART或USB等串行接口加载启动代码。
除了这些基本要素外,还有与你的设备所需的特定硬件位的接口,以完成其工作。主线Linux为数以千计的不同设备配备了开源驱动程序,还有来自SoC制造商和可能包含在设计中的第三方芯片的OEM厂商的驱动程序(质量不一),但记得我对一些制造商的承诺和能力的评论。作为一个嵌入式设备的开发者,你会发现你花了相当多的时间来评估和调整第三方代码(如果你有的话),或者与制造商联系(如果你没有的话)。最后,你将不得不为设备特有的接口编写设备支持,或者找人帮你写。
获得本书的硬件
本书中的例子是通用的,但为了使它们具有相关性并易于学习,我不得不选择特定的硬件。我选择了三个示范设备:Raspberry Pi 4、BeagleBone Black和QEMU。第一个是迄今为止市场上最流行的基于Arm的单板计算机。第二种是广泛提供的、廉价的开发板,可用于严重的嵌入式硬件。第三种是一个机器模拟器,可以用来创建一系列典型的嵌入式硬件的系统。完全使用QEMU是很诱人的,但是,像所有的仿真器一样,它与真实的东西不太一样。使用Raspberry Pi 4和BeagleBone Black,你就有了与真正的硬件互动和看到真正的LED灯闪烁的满足。虽然BeagleBone Black已经有几年历史了,但它仍然是开源硬件(与树莓派不同)。这意味着板子的设计材料是免费提供的,任何人都可以将BeagleBone Black或其衍生品制作成他们的产品。
在任何情况下,我鼓励你使用这三个平台中的任何一个,或者你手头可能有的任何嵌入式硬件,尽可能多地尝试这些例子。
Raspberry Pi 4
在写这篇文章的时候,Raspberry Pi 4 Model B是Raspberry Pi基金会生产的旗舰型小型双显示器台式电脑。他们的网站是
https://raspberrypi.org/。Pi 4的技术规格包括以下内容:
- 博通BCM2711 1.5 GHz四核Cortex-A72(Arm® v8)64位SoC
- 2、4或8 GiB DDR4内存
- 2.4 GHz和5.0 GHz 802.11ac无线网络、蓝牙5.0、BLE
- 一个用于调试和开发的串行端口
- 一个MicroSD插槽,可作为启动设备使用
- 一个USB-C接口,用于为电路板供电
- 2个全尺寸的USB 3.0和2个全尺寸的USB 2.0主机端口
- 一个千兆位以太网端口
- 2个用于视频和音频输出的micro-HDMI端口
此外,还有一个40针的扩展头,有各种各样的子板,被称为HATs(硬件附加在上面),允许你调整板子做许多不同的事情。然而,在本书的例子中,你将不需要任何HATs。相反,你将利用Pi 4的内置Wi-Fi和蓝牙(BeagleBone Black缺乏这种功能)。
除了电路板本身之外,你还需要以下东西:
- 一个5V的USB-C电源,能够提供3A或更大的电流
- 一条带有3.3V逻辑电平引脚的USB至TTL串行电缆,如Adafruit 954
- 一张MicroSD卡,以及从你的开发电脑或笔记本电脑上写入该卡的方法,这将需要将软件加载到电路板上。
- 一根以太网电缆和一个路由器,因为有些例子需要网络连接。
BeagleBone Black
BeagleBone和后来的BeagleBone Black是由CircuitCo LLC生产的小型信用卡大小的开发板的开放硬件设计。主要的信息库是在https://beagleboard.org/。规格的要点如下:
- 一个TI AM335x 1 GHz Arm® Cortex-A8 Sitara SoC
- 512 MiB DDR3 RAM
- 2或4 GiB 8位eMMC板载闪存
- 一个用于调试和开发的串行端口
- 一个MicroSD插槽,可作为启动设备使用
- 一个迷你USB OTG客户端/主机端口,也可用于为电路板供电
- 一个全尺寸的USB 2.0主机端口
- 一个10/100以太网端口
- 一个用于视频和音频输出的HDMI端口
此外,还有两个46针的扩展头,有各种各样的子板,被称为 “披风”,使你可以使板子做许多不同的事情。然而,在本书的例子中,你不需要安装任何capes。
除了板子本身之外,你还需要以下东西:
- 一条Mini-USB到USB-A的电缆(随板提供)。
- 一条能与板子提供的6针3.3V TTL电平信号连接的串行电缆。
- 一条可以与板子提供的6针3.3V TTL电平信号连接的串行电缆。BeagleBoard网站有兼容电缆的链接。
- 一张MicroSD卡和一种从你的开发电脑或笔记本上写入卡的方法,这将需要将软件加载到板子上。
- 一根以太网电缆和一个连接路由器,因为有些例子需要网络连接。
- 一个能够提供1 A或更多的5V电源。
除了上述内容外,第12章 “用突破板进行原型开发 “还需要
以下物品:
- 一块SparkFun公司的GPS-15193分线板。
- 一个Saleae Logic 8逻辑分析器。这个仪器将被用来探测BeagleBone Black和NEO-M9N之间的SPI通信的引脚。
QEMU
QEMU是一个机器仿真器。它有许多不同的版本,每个版本都可以模拟一个处理器架构和一些使用该架构的板子。例如,我们有以下几种:
- qemu-system-arm: 32位Arm
- qemu-system-mips: MIPS
- qemu-system-ppc: PowerPC
- qemu-system-x86: x86和x86_64
对于每个架构,QEMU都模拟了一系列的硬件,你可以通过使用-machine帮助选项查看。每台机器都模拟了该板上通常会有的大部分硬件。有一些选项可以将硬件与本地资源联系起来,比如使用一个本地文件来模拟磁盘驱动器。
下面是一个具体的例子:
$ qemu-system-arm -machine vexpress-a9 -m 256M -drive file=rootfs.ext4,sd -net nic -net use -kernel zImage -dtb vexpress- v2p-ca9.dtb -append "console=ttyAMA0,115200 root=/dev/mmcblk0" -serial stdio -net nic,model=lan9118 -net tap,ifname=tap0
前面的命令行中使用的选项如下:
- -machine vexpress-a9: 创建一个带有Cortex A-9处理器的Arm Versatile Express开发板的仿真系统。
- -m 256M: 用256MB的内存填充它
- -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-utilites:
$ sudo tunctl -u $(whoami) -t tap0
这将创建一个名为tap0的网络接口,连接到模拟的QEMU机器的网络控制器上。你配置tap0的方式与配置其他接口的方式完全一样。
所有这些选项在下面的章节中都有详细描述。我将在我的大多数例子中使用Versatile Express,但使用不同的机器或架构应该是很容易的。
配置你的开发环境
我只使用了开源软件,无论是开发工具还是目标操作系统和应用程序。我假设你将在你的开发系统上使用Linux。我使用Ubuntu 20.04 LTS测试了所有的主机命令,所以对这个特定的版本有一点偏爱,但任何现代的Linux发行版都可能工作得很好。