嵌入式课程设计报告范例Word下载.docx
- 文档编号:18403853
- 上传时间:2022-12-16
- 格式:DOCX
- 页数:17
- 大小:415.38KB
嵌入式课程设计报告范例Word下载.docx
《嵌入式课程设计报告范例Word下载.docx》由会员分享,可在线阅读,更多相关《嵌入式课程设计报告范例Word下载.docx(17页珍藏版)》请在冰豆网上搜索。
3.2、系统处理流程8
3.3、各功能模块分析实现8
4、系统实现12
4.1、总体界面与程序流程分析12
4.2、流程图16
5、使用说明18
5.1.源程序的编译18
5.2程序的运行18
6、课程设计总结20
参考文献21
附录:
重要代码21
1、系统概论
1.1内存管理技术理论
1.1.1为什么必须管理内存
内存管理是计算机编程最为基本的领域之一。
在很多脚本语言中,您不必担心内存是如何管理的,这并不能使得内存管理的重要性有一点点降低。
对实际编程来说,理解您的内存管理器的能力与局限性至关重要。
在大部分系统语言中,比如C和C十十,您必须进行内存管理。
追溯到在Apple11上进行汇编语言编程的时代,那时内存管理还不是个大问题。
您实际上在运行整个系统。
系统有多少内存,您就有多少内存。
您甚至不必费心思去弄明白它有多少内存,因为每一台机器的内存数量都相同。
所以,如果内存需要非常固定,那么您只需要选择一个内存范围并使用它即可。
不过,即使是在这样一个简单的计算机中,您也会有问题,其是当您不知道程序的每个部分将需要多少内存时。
如果您的空间有限,而内存需求是变化的,那么您需要一些方法来满足这些需求:
确定您是否有足够的内存来处理数据。
从可用的内存中获取一部分内存。
向可用内存池(pool)中返回部分内存,以使其可以由程序的其他部分或者其他程序使用。
实现这些需求的程序库称为分配程序(allocators),因为它们负责分配和回收内存。
程序的动态性越强,内存管理就越重要,您的内存分配程序的选择也就更重要。
1.1.2C风格的内存分配程序
在大部分操作系统中,内存分配由以下两个简单的函数来处理:
Void*malloc(longnumbytes):
该函数负责分配numbytes大小的内存,并返回指向第一个字节的指针。
Voidfree(void*firstbyte):
如果给定一个由先前的malloc返回的指针,那么该函数会将分配的空间归还给进程的“空闲空间”
Malloc_init将是初始化内存分配程序的函数。
它要完成以下三件事:
将分配程序标识为己经初始化,找到系统中最后一个有效内存地址,然后建立起指向我们管理的内存的指针。
malloc()的实现有很多,这些实现各有优点与缺点。
在设计一个分配程序时,要面临许多需要折衷的选择,其中包括:
分配的速度;
回收的速度;
有线程的环境的行为;
内存将要被用光时的行为:
局部缓存;
簿记(Bookkeeping)内存开销;
虚拟内存环境中的行为;
小的或者大的对象;
实时保证。
基于malloc()的内存管理的缺点:
不只是我们的内存管理器有缺点,基于malloc()的内存管理器仍然也有很多缺点,不管您使用的是哪个分配程序。
对于那些需要保持长期存储的程序使用malloc()来管理内存可能会非常令人失望。
如果您有大量的不固定的内存引用,经常难以知道它们何时被释放。
生存期局限于当前函数的内存非常容易管理,但是对于生存期超出该范围的内存来说,管理内存则困难得多。
1.2嵌入式系统对内存管理的要求
1.快速性:
嵌入式系统中对实时性的保证,要求内存分配要尽可能地快。
因此在嵌入式系统中,不可能采用通用操作系统中地复杂而完善地内存策略,一般都采取简单、快速地内存管理策略。
2.可靠性:
嵌入式内存分配地请求必须得到满足,如果分配失败可能会带来灾难性地后果。
嵌入式系统应用环境千变万化,其中有一些是对可靠性要求极高地。
比如,汽车地自动驾驶系统中,系统检测到即将撞车,如果因为内存分配失败而不能执行相应的操作,就会发生车毁人亡的惨剧,这是不能容忍的。
3.高效性:
内存分配要尽可能减少浪费。
不可能为了保证满足所有的内存分配请求而将内存配置的无限大。
一方面,嵌入式系统对成本的要求使得内存在其中只是一种有限得资源;
另一方面,即使不考虑成本得因素,系统有限得空间决定了可配置得内存容量是很有限的。
1.3嵌入式系统的内存管理特点
许多软件使用虚拟内存,程序过大,可以交换到虚拟存储器上。
然而在嵌入式系统中特别要求实时性很强的系统来说,这个功能的确不那么重要,因为这个机制会浪费时间,所以,嵌入式系统的应用程序还是在固定的地方运行比较好。
考虑到一些CPU有这方面的特点,也可以保留虚拟存储器的代码,这样能够使得不同进程使用相同代码。
如果没有了这个功能,每个程序都需要自己的运行库,在内存中就会有库的很多拷贝。
其实只需把交换空间的长度设置为零,就可以关闭虚拟内存的页面换入和换出功能。
对一些CPU,虚拟存储器的功能提供内存管理,防止不同程序占用相同的地址空间。
而在嵌入式系统中,使用简单、绝对的地址空间,因此这种功能也不会起作用。
不过这倒提供了防止误写以至于使系统崩溃的功能。
很多嵌入式系统习惯使用“全局变量”,让进程共享数据。
1.4内存管理策略概述
内存管理策略大致可分为静态分配和动态分配两大类。
静态分配的可用内存大小在编译时确定。
系统初始化阶段预先分配好同定数量的内存,用于存放所有需使用的对象和数据结构。
程序运行过程中能且只能使用这内存。
动态分配的内存在程序运行时根据需要向系统申请后获得,只有在需要的时候.才分配或归还。
由于嵌入式系统的特殊性,开发人员必须在编译内核和开发应用时都参与系统的内存管理。
1.5内存管理模块策略及实现考量
1.5.1简单内存模型
设计之初。
最简单直接的一种技术其实就是“不管理”,即所有静态分配在栈中的、动态分配在堆中的变量和数据结构.这二者的分配、释放,全交给编译器、链接程序和运行时库去完成,开发者只需负责合理设置系统栈、堆及任务栈。
该模型的一例如图1.
这种方法一般用于操作系统原型的设计.易于配置的、或内存相对宽裕且实时性要求不高的系统的初始设计。
1.5.2静态方式
为简单、特殊考虑或安全计算,可以只使用静态技术。
静态内存分配在程序执行前进行,效率较高;
编译时可精确预估系统内存需求状况;
所有分配动作所需时间同定且很小;
分配的内存一般不归还。
外部碎片很少;
程序易测试,可靠性高。
这些优点使得静态分配尤其适用于实时应用和高安全系统。
另一方面.静态分配必须考虑可能发生的最坏情况,因此往往分配比实际用量更多的内存。
预先分配内存也增加了系统的启动时间。
使用静态分配,常需限制系统中同时容纳的进程数,结束旧任务之后才能开始新任务。
为限制容量,必须指定程序将存储的最大数据量。
这些均会降低系统的可用性。
要求在程序执行前就知道所需的内存类型和数量,显然缺少灵活性。
但在只采用静态分配的内存管理方式中,不仅操作系统内核本身。
用户进程也必须全部采用静态分配。
所以,只能通过以下几种方式设置运行前已指定了大小的任务栈:
由用户传递一个静态分配的数组首地址参数给任务构造函数;
使用模板参数;
继承通用任务类。
实现自己的特定任务子类,在子类构造函数中给予其静态分配的栈。
1.5.3动态方式
大部分面向过程和面向对象语言都提供在堆中创建和回收对象的动态管理机制,并在缺省状态下使用。
本系统就是采用这样的方式来进行设计的。
静态分配注重效率和安全,动态分配则偏重于灵活性。
它避免分配不被使用的内存,降低内存总体需求;
改善设计质量,使系统更易编写、扩展和维护:
运行时间分配内存减少了系统的启动时间。
当然,灵活性也需付出代价:
动态内存管理需要额外区块头部的开销;
内存分配和归还需花费处理器时间。
典型情况和最坏情况下的内存需求难以估计;
内存耗尽的处理会增加代码的复杂度;
易造成内存碎片,增加内存的额外开销和不可预测性。
动态分配的内存必须由操作系统管理。
通常使用的是链表法。
它维持一个已分配和空闲的内存段的链表。
段是进程或是两个进程间的空洞。
段链表通常按照地址排序。
在进程结束或被换出时更新十分简单、直接。
使用双链表结构更易于找到上一个表项以检查是否可以合并。
进程和空洞按照地址顺序存放在链表中时。
在系统知道要分配的内存大小的前提下,可用于为新创建和换进的进程分配空间的算法中,首次适配不仅是最简单的,而且是最好和最快的。
首次适配算法从头开始扫描存储器并选择大小足够的第一个可用块。
把进程和空洞放在不同链表中,能提高这几个算法的速度。
但会提高复杂度和使内存释放速度变慢,因为被释放的内存段必须从进程链表中删除并插入空洞链表。
将空洞按大小排序将导致查看是否可以合并邻接块的操作非常费时。
但若不作合并.内存将很快充满大量的、进程无法使用的碎片。
所以。
空洞列表一般按照内存地址排列,需要分配内存时.用首次适配算法对空洞列表进行搜索,以找出一个足够大的空洞并装入之。
在必须使用动态方式的系统中,也应同时运用静态方式,把所有可以事先确定的内存用量都预先分配好.以尽量减少动态分配和回收带来的不确定性。
2、需求分析
2.1需求背景
随着嵌入式实时系统在通信领域的广泛应用,嵌入式软件的开发也越来越受到更多的瞩目,诸如VxWorks,pSOS等嵌入式实时操作系统的应用带来了人们对嵌入式软件开发的新思路和新需求,在屏蔽不同的硬件平台甚至不同操作系统之上,中间件、虚拟机等概念的引用形成了符合通信设备上应用软件不同需求的一个支撑平台,为上层的嵌入式应用软件的开发提供一种统一接口,也为嵌入式软件提供了一种新的开发模式和实现思想。
在目前公司的各大主流产品的设备级入式单板软件中均采用了操作系统的封装支撑平台,该平台对应用层提供的统一的接口和支撑功能,对操作系统和不同的硬件特性进行屏蔽,使得嵌入式应用软件的开发着重于产品的具体需求和功能,该支撑平台以一致的消息机制、多任务多进程的功能划分模式、统一管理的静态、动态内存分配和定时机制、还有特定的系统控制和异常处理、出错处理等功能为应用层的开发提供了一种可遵循的实现方法和标准接口,在遵循这个统一模式的前提下,如何为嵌入式应用提供一套开发的标准模板和规范成为我们这项课题的焦点。
其意义在于具备可观的经济效益和社会效益
2.2功能需求
内存管理是操作系统的中心任务之一.内存管理模块通常是操作系统内核的一部分。
其主要任务是组织内存以容纳内核和各待执行进程,跟踪当前内存使用状况,在需要时为进程分配内存,其使用完毕后释放并回收内存。
内存管理与操作系统和底层硬件体系结构密切相关,不同平台的内存管理策略往往大不相同。
较之普通桌面型操作系统,嵌入式操作系统中的内存管理有其独特之处:
嵌入式系统受成本、设备体积和电池技术等因素制约,内存容量一般相当有限,需要高效地使用;
嵌入式系统一般都具有实时需求。
至少是软实时的.内存分配、回收必须迅速.时间应可预测;
很多嵌入式系统事关重大,必须安全地运行,为避免灾难性后果,内存管理必须十分可靠。
桌面系统的微处理器大多带有存储管理单元(MMU),所以桌面操作系统如Windows、Linux都使用虚拟存储器,实际存储器和程序都被分成大小相同的页面,程序运行时,只将要运行的部分页面载入内存即可。
MMU的作用是将虚地址映射为物理地址,保护地址越界“。
大多数嵌入式系统的处理器没有MMU(即使系统中含有这些硬件也没采用),因此不能使用虚存管理技术,只能采用实存管理,直接访问实际的物理地址。
每个任务运行前,必须为它分配足够的连续地址空间,运行时全部载入;
嵌入式操作系统没有内存保护,所有任务共享一个运行空间,任何一个任务都可能破坏其它任务的代码、数据或堆栈,甚至破坏内核代码或数据结构,导致整个系统工作异常,或使系统崩溃。
由此可见,开发嵌入式系统时内存管理非常重要。
内存如何分配和释放,才能保证内存碎片少,且不会导致内存丢失:
每个任务的堆栈如何安排,如何保证不侵犯其它程序(包括系统程序和数据)的地址空间,才能保证程序不会破坏系统或其它程序的正常工作,这些都是内存管理所要考虑的问题。
3、系统设计
3.1、程序的开发、运行平台
开发平台:
linux系统的操作环境
GCC编译器
运行平台:
cpu500M以上
内存128MB以上
15寸显示器以上
3.2、系统处理流程
3.3、各功能模块分析实现
1./*函数原型*/
RECT*assignment(RECT*head,intapplication);
voidacceptment1(RECT*head,RECT*back1);
voidacceptment2(RECT*head,RECT*back1);
intbackcheck(RECT*head,RECT*back1);
voidprint(RECT*head);
2./*变量声明*/
RECT*head,*back,*assign1,*p;
intapplication1,maxblocknum;
charway;
3..实现内存分配功能,选择适应算法(首次适应算法,最佳适应算法)。
基本原理分析:
1)最佳适应算法:
将空闲分区按大小从大到小排序,从头找到大小合适的分区。
它从全部空闲区中找出能满足作业要求的、且大小最小的空闲分区,这种方法能使碎片尽量小。
为适应此算法,空闲分区表(空闲区链)中的空闲分区要按大小从小到大进行排序,自表头开始查找到第一个满足要求的自由分区分配。
该算法保留大的空闲区,但造成许多小的空闲区。
主要函数算法如下:
voidacceptment2(RECT*head,RECT*back1)
{
RECT*before,*after;
intinsert;
insert=0;
before=head;
after=head->
next;
if(head->
next==NULL)/*如果可利用区表为空*/
{
head->
size=back1->
size;
next=back1;
maxblocknum++;
back1->
next=NULL;
}
2)首次适应算法:
将空闲分区按大小从小到大排序,从头找到大小合适的分区。
从空闲分区表的第一个表目起查找该表,把最先能够满足要求的空闲区分配给作业,这种方法目的在于减少查找时间。
为适应这种算法,空闲分区表(空闲区链)中的空闲分区要按地址由低到高进行排序。
该算法优先使用低址部分空闲区,在低址空间造成许多小的空闲区,在高地址空间保留大的空闲区。
voidacceptment1(RECT*head,RECT*back1)/*首先适应*/
intinsert;
while(!
insert)/*将回收区插入空闲区表*/
if((after==NULL)||
((back1->
address<
=after->
address)&
&
(back1->
address>
=before->
address)))
before->
next=after;
insert=1;
else
before=before->
after=after->
4.typedefstructnode/*设置分区描述器*/
5.RECT*assignment(RECT*head,intapplication)/*分配函数*/
6.voidprint(RECT*head)/*输出链表*/
7.intbackcheck(RECT*head,RECT*back1)/*检查回收块的合法性,back1为要回收的结点地址*/
8.voidprint(RECT*head)/*输出链表*/
9.voidacceptment2(RECT*head,RECT*back1)/*最佳适应,back1为回收结点的地址*/
10.voidacceptment1(RECT*head,RECT*back1)/*首先适应*/
内存作为最重要的系统资源,其分配和释放策略对系统的运行效率起着至关重要的作用。
系统内核和所有进程通过共享有限的物理内存来运行,一个系统的高效性与稳定性往往取决于它内存管理机制。
因此,一个高效的内存管理系统不仅要能够有效地管理系统内存,减少频繁分配和回收内存而导致的内存碎片,还要尽量提高分配和回收的速度来提高系统的运行效率。
此外,内存管理系统还应该保证内存分配和回收的公平性。
4、系统实现
4.1、总体界面与程序流程分析
在内存初始化完成以后,内存中就常驻有内核映象(内核代码和数据)。
以后,随着用户程序的执行和结束,就需要不断地分配和释放物理页面。
内核应该为分配一组连续的页面而建立一种稳定、高效的分配策略。
为此,必须解决一个比较重要的内存管理问题,即外碎片问题。
频繁地请求和释放不同大小的一组连续页面,必然导致在已分配的内存块中分散许多小块的空闲页面。
由此带来的问题是,即使这些小块的空闲页面加起来足以满足所请求的页面,但是要分配一个大块的连续页面可能就根本无法满足。
Linux采用著名的伙伴(Buddy)系统算法来解决外碎片问题。
但是请注意,在Linux中,CPU不能按物理地址来访问存储空间,而必须使用虚拟地址;
因此,对于内存页面的管理,通常是先在虚存空间中分配一个虚存区间,然后才根据需要为此区间分配相应的物理页面并建立起映射,也就是说,虚存区间的分配在前,而物理页面的分配在后,
1、操作流程分析
由于本系统采用动态方式,所以首先确定内存空间分配表;
然后采用最佳适应算法或首先适应算法完成主存空间的分配,完成内存空间的回收。
内存管理预先不将主存划分成几个区域,而把主存除操作系统占用区域外的空间看作一个大的空闲区。
当作业要求装入主存时,根据作业需要的主存空间的大小查询主存内各个空闲区,当从主存空间中找到一个大于或等于该作业大小的主存空闲区时,选择其中一个空闲区,按作业需求量划出一个分区装入该作业。
作业执行完后,它所占的主存分区被收回,成为一个空闲区。
如果该空闲区的相邻分区也是空闲区,则需要将相邻空闲区合并成一个空闲区。
(1)选择分配任务,输入申请空间大小,如下图分别申请两个大小不同的空闲分区。
(2)当再次申请内存大小超过剩余空闲内存时,则会提示分配不成功。
(3)当要分配的内存大小不足够时,可以对内存进行回收,则选择“ac”进行内存回收。
(4)当回收的内存大小大于剩余已分配了的内存大小,则系统会提示输入错误的信息,提示重新输入正确的内存地址和合理的内存大小。
(5)由于任务开始执行时主存分配算法采用“最佳适应”算法。
最佳适应算法是按作业要求挑选一个能满足作业要求的最小空闲区,这样保证可以不去分割一个大的区域,使装入大作业时比较容易得到满足。
但是最优适应算法容易出现找到的一个分区可能只比作业所要求的长度略大一点的情况,这时,空闲区分割后剩下的空闲区就很小,这种很小的空闲区往往无法使用,却影响了主存的使用。
动态分区方式下回收主存空间时,应该检查是否有与归还区相邻的空闲区。
若有,则应该合并成一个空闲区。
一个归还区可能有上邻空闲区,也可能有下邻空闲区,或者既有上邻空闲区又有下邻空闲区,或者既无上邻空闲区也无下邻空闲区。
在实现回收时,首先将作业归还的区域在已分配表中找到,将该栏目的状态变为“空”,然后检查空闲区表中标志为“未分配”的栏目,查找是否有相邻空闲区;
最后,合并空闲区,修改空闲区表。
(6)当要装入一个作业时,从空闲区表中查找标志为“未分配”的空闲区,从中找出一个能容纳该作业的空闲区。
如果找到的空闲区正好等于该作业的长度,则把该分区全部分配给作业。
这时应该把该空闲区登记栏中的标志改为“空”,同时在已分配区表中找到一个标志为“空”的栏目登记新装入作业所占用分区的起始地址、长度。
如果找到的空闲区大于作业长度,则把空闲区分成两部分,一部分用来装入作业,另外一部分仍为空闲区。
这时只要修改原空闲区的长度,且把新装入的作业登记到已分配区表中。
4.2、流程图
(1)主程序流程图:
(2)分配算法流程图
5、使用说明
5.1.源程序的编译
在Linux下面,如果要编译一个C语言源程序,我们要使用GNU的gcc编译器.首先我们知道这个文件的文件名是memory,所以要编译这个程序,我们只要在命令行下执行:
gcc-omemorymemory.c
gcc编译器就会为我们生成一个memory的可执行文件.执行./hello就可以看到程序的输出结果了.
命令行中gcc表示我们是用gcc来编译我们的源程序,-o选项表示我们要求编译器给我们输出的可执行文件名为hello,而memory.c是我们的源程序文件.gcc编译器有许多选项,一般来说我们只要知道其中的几个就够了.
-o选项我们已经知道了,表示我们要求输出的可执行文件名.
-c选项表示我们只要求编译器输出目标代码,而不必要输出可执行文件.
-g选项表示我们要求编译器在编译的时候提供我们以后对程序进行调试的信息.知道了这三个选项,我们就可以编译我们自己所写的的源程序了
5.2程序的运行
本次的嵌入式操作系统的内存管理模拟主要采用两个内存分配算法:
最先适应分配算法,最优适应分配算法
首先用户要先选择适应算法,然后用户提出内存空间的申请;
系统根据申请者的要求,按照一定的分配策略分析内存空间的使用情况,找出能满足请求的空闲区,分给申请者;
当程序执行完毕或主动归还内存资源时,系统要收回它所占用的内存空间或它归还的部分内存空间。
1.程序运行时首先接收输入:
首先选择需要适应算法的要求,输入b时,表示最优适应分配算法,输入f时,表示最先适应分配算法
2.然后程序提示要进行分配还是回收的工作,输入as表示进行内存分配,输入ac表示进行内存的回收。
3.当要进行内存分配时,则要求输入要申请内存的大小,然后程序会根据剩余的内存大小进行具体分配。
4.建立空闲区表并在屏幕上显示输出空闲区表内容,空闲区表中记录了内存中可供分配的空闲区的始址和长度,用标志位指出该分区是否是未分配的空闲区。
5.在屏幕上显示输出新的空闲区表和已分配区表的内容。
6.当要进行内存的回收时,从用户界面根据程序提示接收一个内存回收申请,格式为:
地址、申请空间的大小。
7.在屏幕上显示输出新的空闲区表和已分配区表的内容。
6、课程设计总结
为了使嵌入式系统能够在不同的平台上进行移植,使之适用与各种应用环境,嵌入式系统内存管理方案在充分考虑实时性、可靠性和高效性的基础上,应该提供比较丰富的管理机制。
本文分析了嵌入式系统中内存管理的要求、存在的问题,介绍了静态和动态两种分配策略;
以动态的方式来分配内存为例,介绍了嵌入式系统中常用的内
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 嵌入式 课程设计 报告 范例