嵌入式Linux:存储(二)

姓名:陈方园    学号:19020100239    学院:电子工程学院

转自:

https://max.book118.com/html/2021/0122/6011000144003053.shtm

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

【嵌牛鼻子】虚拟内存、交叉开发环境

【嵌牛提问】如何实现存储的内存空间?

【嵌牛正文】

2.3.5 内存空间

老式嵌入式操作系统通常将系统内存看做一大块线性地址空间,并进行管理。也就说,微处理器的地址空间的下限是0,上限是其物理地址范围的顶部。举例来说,如果一个微处理器有24条物理地址线,其内存范围的上限就是16MB。因此,其地址范围可以用十六进制表示为从0x00000000到0x00ffffff。硬件设计常常将DRAM放置在这个地址范围的底部,并将闪存放置在顶部。位于DRAM顶部和闪存底部之间的那些未使用的地址范围常常被分配给板上的各种外围设备芯片,用于对它们进行寻址。这种设计方法一般是由所选择的微处理器决定的。图2-5显示了一个简单嵌入式系统中的典型内存布局。

图2-5 典型的嵌入式系统内存布局

在基于老式操作系统的嵌入式设备中,操作系统和所有的任务[12]具有相同的权限,能够访问系统的所有资源。某个进程中的一个故障可能会改写系统中任意一块内存的内容,这块内存可能属于这个进程本身、操作系统、其他任务,甚至是地址空间中的一个硬件寄存器。虽然这种内存管理方式有个最大的优点:简单,但它会导致一些很难诊断的故障。

高性能的微处理器中都包含一个复杂的硬件引擎,称为内存管理单元(MemoryManagement Unit,MMU)。MMU的作用是使操作系统能够在很大程度上管理和控制地址空间,包括操作系统自身的地址空间和分配给进程的地址空间。这种控制主要体现为两种形式:访问权限控制(access right)和内存地址转换(memory translation)。访问权限控制允许操作系统将特定的内存访问权分配给特定的进程。内存地址转换允许操作系统将其地址空间虚拟化,从而带来很多好处。Linux内核利用这些硬件MMU实现了一个虚拟内存操作系统。虚拟内存所带来的最大的一个好处是,它可以让系统的内存看起来比实际的物理内存多,这样能够更加有效地利用物理内存。其他的好处是,内核在为任务或进程分配系统内存时,可以指定这块内存的访问权限,从而防止某个进程错误地访问属于另一个进程或内核自身的内存或其他资源。

下一节将更详细地讨论MMU的工作原理。复杂的虚拟内存系统的内容超出了本书的范围[13]。实际上,我们会从嵌入式系统开发者的角度来考查虚拟内存系统。

2.3.6 执行上下文

系统引导时,Linux最先要完成一项琐碎工作,即配置处理器中的硬件MMU以及相应的数据结构,并使之能够进行地址转换。这一步完成后,内核运行于自己的虚拟内存空间中,这个空间称为内核空间。在当前的Linux内核版本中,这个虚拟内存空间的起始地址是由内核开发者选择的,其默认值为0xC0000000[14]。对于大多数硬件架构,这是个可以配置的参数[15]。在内核符号表中,可以看到内核符号的链接地址都是以0xC0xxxxxx开头的。所以,当内核在内核空间中执行代码时,处理器的指令指针(程序计数器)所包含的值都在这个范围之内。

Linux中有两个明显分隔开的运行上下文,由线程[16]的执行环境所决定。那些完全在内核中执行的线程被认为运行在内核上下文中,而应用程序运行在用户空间上下文中。用户空间进程只能访问它自己拥有的内存,如果它要访问文件或设备I/O等特权资源,则必须使用内核系统调用。下面举个例子,让你更好地理解这一点。

假设一个应用程序打开一个文件并读取其中的内容,如图2-6所示。对读函数的调用是从用户空间开始的,由应用程序调用C库中的read()函数。接着,C库向内核发起一个读请求。这个读请求造成一次上下文的切换,从用户程序切换到内核,以服务这个请求并读取文件中的数据。在内核中,这个读请求最终转变成对硬盘驱动器的访问,从包含文件内容的扇区中读取相应数据。

图2-6 简单的文件读请求

通常,这个对硬盘驱动器的读请求是以异步的形式发往硬件自身的。也就是说,处理器将这个请求发给硬件,并不会等待其完成请求。硬件收到请求后读取数据,当数据准备好的时候,通过中断的方式来告知处理器读请求已经完成了。等待数据的应用程序会阻塞在一个等待队列中,直到有数据可用。当硬盘准备好数据时,它将向处理器发送一个硬件中断(这里只是描述了一个简化的过程)。当内核接收到这个硬件中断时,它会挂起正在执行中的任何进何进程,并从硬盘驱动器中读取应用程序所等待的数据。

下面对我们的讨论做一个概括,我们学习了两个通用的执行上下文——用户空间和内核空间。当应用程序执行系统调用,造成上下文的切换而进入内核时,内核会代表这个进程执行内核代码。你会经常听到,这种情况称为内核运行于进程上下文中。相反,处理IDE驱动器的中断处理程序(ISR)也是内核代码,但在运行时并不代表任何特定的进程。这种情况通常被称为内核运行于中断上下文中。

内核运行于中断上下文中时会受到一些限制,包括中断处理程序不能够阻塞(睡眠)或调用任何可能造成阻塞的内核函数。如果想要更多地了解这些概念,请阅读本章末尾的参考文献。

2.3.7 进程虚拟内存

一个进程产生时——例如,当用户在Linux的命令提示符后面输入ls的时候——内核就会为这个进程分配内存及相应的虚拟内存地址范围。这些地址与内核中的地址或其他正在运行的进程的地址没有固定的关系。此外,这些进程所看到的虚拟地址跟目标板上的物理内存的地址也没有直接的关系。实际上,由于系统中存在分页(paging)和交换(swapping)机制,一个进程在其生命周期中常常会占用内存的多个不同的物理地址。

代码清单2-4是程序员所熟知的“Hello World”程序,这里做了点修改来说明刚刚讨论的一些概念。这个例子的目的是解释说明内核分配给进程的地址空间。这段代码编译后,在一个拥有256MB DRAM内存的嵌入式系统上运行。

代码清单2-4 嵌入式风格的Hello World

代码清单2-5显示了运行编译后的程序hello时,控制台输出的信息。注意,hello进程认为它的运行地址位于高地址内存的某个地方,刚好超过256MB的边界(0x10000418)。还需注意,栈的地址大概处于32位地址空间一半的地方,远远超过了内存的大小256MB(0x7ff8ebb0)。怎么会这样呢?在这种系统中,DRAM通常是一块连续的内存。乍一看,我们几乎有将近2 GB的DRAM可以使用。这些虚拟地址是由内核分配的,并且有嵌入式目标板上的256MB的物理内存在背后支持。

代码清单2-5 Hello的输出

虚拟内存系统的一个特点是当可用的物理内存的数量低于某个指定的阈值时,内核可以将内存页面交换到大容量存储媒介中,通常是硬盘驱动器。内核检查正在使用中的内存区域,并判断哪些区域最近使用得最少,然后将这些内存区域交换到磁盘中,并释放这些内存区域给当前进程使用。嵌入式系统的开发者常常会因为性能原因或资源限制而禁用嵌入式系统中的交换功能。多数情况下,使用慢速且写寿命有限的闪存设备作为交换设备是很不明智的。如果没有交换设备可用,就必须仔细地设计应用程序,使其能够运行在有限的物理内存中。

2.3.8 交叉开发环境

开发嵌入式系统应用和设备驱动之前,需要一套工具(编译器、实用工具等)来生成适合目标系统的二进制可执行文件。考虑一个在桌面PC上编写的简单应用,比如传统的“Hello World”。你在电脑上编写好代码后,会使用电脑操作系统自带的编译器(通常是GNU的gcc编译器)来编译代码,以生成一个可执行的二进制镜像文件。这个可执行文件的格式与编译代码的电脑兼容,可以在该电脑上运行。这被称为本地(native)编译。也就是说,使用本机系统中的编译器生成可以在本机上运行的程序。

需要注意的是,本地编译并不意味着我们就能知道用于编译和运行程序的系统架构。其实,如果你有一个可以在目标板上运行的工具链,就可以在目标板上本地编译生成适合此目标板架构的应用程序。实际上,要对一个新的嵌入式内核和定制单板进行压力测试,一个好办法就是在上面反复编译Linux内核。

在交叉开发环境中开发软件要求编译器运行于开发主机上,但生成的二进制可执行文件的格式与开发主机不兼容,不能在上面运行。这类工具存在的主要原因是,在资源(一般指内存大小和CPU性能)受限的嵌入式系统上本地开发和编译代码常常是不现实或不可能的。

这种开发方式隐藏着很多陷阱,嵌入式开发的新手稍不留神就会中招。当编译一个程序时,编译器一般都知道怎样找到所需要的头文件和正确编译代码必需的程序库。为了说明这些概念,我们再看一下“Hello World”。代码清单2-4中的示例代码是使用下面的命令行进行编译的:

在代码清单2-4中,我们看到这个程序代码包含了一个头文件stdio.h。这个文件和我们在gcc命令行中指定的文件hello.c不在同一个目录中。那么,编译器是如何找到它的呢?另外,函数printf()也不是在文件hello.c中定义的。因此,编译hello.c后,它会包含一个对此符号的未解析的引用(unresolved reference)。链接器在链接时是怎样解析这个引用的呢?

编译器使用一些默认的搜索路径来定位头文件。在代码中引用某个头文件时,编译器在默认的几个搜索路径中查找这个文件。类似地,链接器也是以这种方式来解析对外部符号printf()的引用。链接器知道默认在C库(libc-*)中搜索未解析的引用,并且知道在系统中的哪些位置可以找到这些程序库。再说明一下,这种默认行为是内置于工具链中的。

现在假设你为某个采用Power架构的嵌入式系统编写应用程序。显然,你需要一个交叉编译器,用于生成兼容Power架构处理器的二进制可执行文件。如果你使用交叉编译器,并采用类似的编译命令来编译前面的hello.c程序,在解析对外部符号printf()的引用时,链接器很可能会意外地将二进制可执行文件链接到一个x86版本的C库。当然,由于生成的可执行程序混合了Power架构和x86二进制指令,如果运行这个错误的混合体[17],其结果是可以预见的,那就是系统崩溃!

摆脱这个困境的方法是指引交叉编译器在非标准路径中进行查找,以使用针对目标架构的头文件和程序库。我们将在第12章中详细讨论这个主题。这个例子旨在说明两种开发环境的区别,即本地开发环境和嵌入式系统所需的交叉编译开发环境。这只是交叉开发环境复杂性的一个方面。交叉调试中也会出现相同的问题和解决方案,从第14章开始,你会了解到这些内容。正确地搭建交叉开发环境对于成功至关重要,你将在第12章中看到,这不仅仅涉及编译器,还包括其他很多内容。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容