天天看点

《嵌入式Linux基础教程(第2版)》——2.2 剖析嵌入式系统

本节书摘来自异步社区《嵌入式linux基础教程(第2版)》一书中的第2章,第2.2节,作者:【美】christopher hallinan(克里斯托弗 哈利南)著,更多章节内容可以访问云栖社区“异步社区”公众号查看

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

《嵌入式Linux基础教程(第2版)》——2.2 剖析嵌入式系统

通常,嵌入式系统的处理器完成很多功能,不仅仅是处理传统的核心指令流。图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发行版中获得。

《嵌入式Linux基础教程(第2版)》——2.2 剖析嵌入式系统

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

《嵌入式Linux基础教程(第2版)》——2.2 剖析嵌入式系统

2.2.2 启动目标板

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

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

《嵌入式Linux基础教程(第2版)》——2.2 剖析嵌入式系统

当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内核

《嵌入式Linux基础教程(第2版)》——2.2 剖析嵌入式系统

代码清单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基础教程(第2版)》——2.2 剖析嵌入式系统

其余引导信息是由linux内核产生的。对于这一点,我们在后续章节还要详细说明,但我们需要注意u-boot是在哪儿离开的以及linux内核是在哪儿取得控制权的。

2.2.4 内核初始化:概述

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

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

《嵌入式Linux基础教程(第2版)》——2.2 剖析嵌入式系统

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中的这一行:

《嵌入式Linux基础教程(第2版)》——2.2 剖析嵌入式系统

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

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

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

继续阅读