嵌入式系统内存管理
1、概述
操作系统的内存管理功能用于向操作系统提供一致的地址映射功能和内存页面的申请、释放操作。在嵌入式实时系统中,内存管理根据不同的系统,有不同的策略,对于有些系统支持的虚拟内存管理机制,对于另外一些系统,可能只有flat式的简单内存管理机制。
2、内存管理机制:
大体上来说,嵌入式系统所用到的内存管理机制主要有以下两种:
(1) 虚拟内存管理机制:
有一些嵌入式处理器提供了MMU,在MMU具备内存地址映射和寻址功能,它使操作系统的内存管理更加方便。如果存在MMU ,操作系统会使用它完成从虚拟地址到物理地址的转换, 所有的应用程序只需要使用虚拟地址寻址数据。 这种使用虚拟地址寻址整个系统的主存和辅存的方式在现代操作系统中被称为虚拟内存。MMU 便是实现虚拟内存的必要条件。
虚拟内存的管理方法使系统既可以运行体积比物理内存还要大的应用程序,也可以实现“按需调页”策略,既满足了程序的运行速度,又节约了物理内存空间。
在L inux系统中,虚拟内存机制的实现实现为我们提供了一个典型的例子:在不同的体系结构下, 使用了三级或者两级页式管理,利用MMU 完成从虚拟地址到物理地址之间的转换。基于虚拟内存管理的内存最大好处是:由于不同进程有自己单独的进程空间,十分有效的提高了系统可靠性和安全性。
(2) 非虚拟内存管理机制
在实时性要求比较高的情况下,很多嵌入式系统并不需要虚拟内存机制:因为虚拟内存机制会导致不确定性的 I/O阻塞时间, 使得程序运行时间不可预期,这是实时嵌入式系统的致命缺陷;另外,从嵌入式处理器的成本考虑,大多采用不装配MMU 的嵌入式微处理器。所以大多嵌入式系统采用的是实存储器管理策略。因而对于内存的访问是直接的,它对地址的访问不需要经过MMU,而是直接送到地址线上输出,所有程序中访问的地址都是实际的物理地址;而且,大多数嵌入式操作系统对内存空间没有保护,各个进程实际上共享一个运行空间。一个进程在执行前,系统必须为它分配足够的连续地址空间,然后全部载入主存储器的连续空间。
由此可见,嵌入式系统的开发人员不得不参与系统的内存管理。从编译内核开始,开发人员必须告诉系统这块开发板到底拥有多少内存;在开发应用程序时,必须考虑内存的分配情况并关注应用程序需要运行空间的大小。另外,由于采用实存储器管理策略,用户程序同内核以及其它用户程序在一个地址空间,程序开发时要保证不侵犯其它程序的地址空间,以使得程序不至于破坏系统的正常工作,或导致其它程序的运行异常;因而,嵌入式系统的开发人员对软件中的一些内存操作要格外小心。
UCOS就是使用非虚拟内存管理的一个例子,在UCOS中,所有的任务共享所有的物理内存,任务之间没有内存保护机制,这样能够提高系统的相应时间,但是任务内存操作不当,会引起系统崩溃。
3、内存在系统中的生命期:
对于内存在整个嵌入式运行过程中,以3中方式存在:
1)、 在bootstraping 阶段,内存以临时内存分配的形式出现,当完成系统启动后,这些内存会回收供以后系统使用。
2)、 在正常运行阶段,内存以两种方式存在:
( 1) 系统为代码,数据分配的永久内存,这些内存在系统运行过程中是不会改变的,有的硬件的I/O等外设也把相应的地址映射到固定的内存空间。
( 2) 动态内存分配空间:这些内存不会固定分配,而是根据系统需要而动态分配的,如果利用非虚拟内存管理机制,一般需要改造动态内存分配机制以提高性能。
4、内存管理的具体应用:
A、Linux系统的内存管理机制
Linux内存管理机制中,X86体系结构是利用虚拟内存管理的典型,在i386CPU上,首先要进行段式映射,Linux 没有用到段式管理,它的做法是把GDT中段描述符段的大小定义为4GB, 也就是说只分了一段, 从而使段式映射没有起作用。在页式映射中,对于嵌入式i386芯片来说,实际上是两层映射, 跳过中间的PMD层次。对于程序来说,并非所有虚存都映射都到物理空间了,而是动态映射,如果程序运行时内核发现虚拟页面没有映射或映射的是磁盘页面, 会作相应的缺页处理——分配内存页面并建立映射,然后恢复程序运行。
在程序运行的过程中,涉及到的内存操作主要有内存分配、内存使用、内存回收、内存页面换出、页面换入。内存分配会在管理区的空闲区进行, 通过Buddy算法在管理区的free_area 中获得需要的内存块。如果内存不足, 则会启动Kswapd这个守护进程腾出部分物理内存。除了被调用, Kswapd 进程还会定时启动。Kswapd 的工作分两部分:
( 1) 检测物理内存剩余的情况, 如果短缺, 则按LRU策略断开active_list 队列中部分可交换页面的映射, 使页面变为不活跃状态,链入inactive_clean_list队列或者inactive_dirty_list队列, 为换出做准备。
( 2) 每次都执行, 把inactive_ dirty_list 中的页面写入交换设备,并且回收一部分inactive_clean_list 中的页面。
Linux系统虚拟内存机制的屏蔽
由于虚拟内存在时间上的不可预期性,对于实时性要求很高的系统,必须屏蔽虚拟内存机制。在uCLinux中就利用了这种技术一保证系统的实时性,下面是屏蔽虚拟内存机制的思路:
为了满足在工业控制中一些任务的实时性要求,必须屏蔽内核的虚拟内存管理机制以增强Linux的实时性。当要更改内核的某项机制时,一般不必大规模的改写代码,可采用条件编译的方法。思路是用#ifdef或 #ifndef屏蔽现有语句,在#else宏编译语句中包括自己编写的代码。实现虚拟内存的机制有:地址映射机制、内存分配和回收机制,缓存和刷新机制、请页机制、交换机制、内存共享机制,将实现这些机制的数据结构和函数屏蔽或修改,还要修改与之相关的文件。需要改动的文件主要在 /include/linux、/mm、/drivers/char、/fs、/ipc/kernel、/init目录下。
主要的改动如下:与虚存有关的主要的数据结构是vm_area_struct,将进程的mm_struct结构中的vm_area_struct去掉,vm_area_struct利用了vm_ops来抽象出对虚拟内存的处理方法,屏蔽与虚拟内存操作有关的函数。内存映射主要由do_mmap()实现,改写此函数的代码。取消交换操作,屏蔽用于交换的结构和函数声明,以及实现交换的代码。取消内核守护进程kswapd。
B、UCOS的内存管理:
UCOS的内存管理与大多数嵌入式系统一样,是flat内存,但在此flat内存的基础上进行了优化,使在动态内存分配的时候,减少了内存粹片,提高了系统性能。
UCOS的具体方法是:把连续的大块内存进行分区,每个分区包含整数个大小相同的内存块,在一个系统中有多个不同内存大小的分区。这样,应用程序根据不同的需求,从不同大小的内存分区中分配相应大小的内存。对于不用的内存,又重新释放回原来的分区。通过这样的内存管理算法,解决了内存粹片的问题,提高了系统性能。UCOS具体的实现可以参看源码。主要由以下几个函数实现:
OSMemCreate():创建内存分区。
OSMenGet() :为应用程序分配一段内存。
OSMenPut():回收应用程序不再使用的内存。
5、结论:
内存管理是嵌入式系统的一个重要方面,虚拟内存管理机制在为进程安全提供很好保证的同时,也为开发人员提供了一个管理内存的方法,使开发人员更多的关注其他的方面。但是它也带来了时间不确定性的缺陷。根据不同的系统需求,我们可以选取相应的内存管理策略。在现在大多数的实时系统中,非虚拟内存管理机制用得比较多,这样保证了系统的实时性,但是增加了开发的难度,任务内存操作不当,可能引起系统崩溃。
由于虚拟内存管理的请求换页机制在很大程度上影响了系统的实时性能,现在有些开发人员提出了一个折衷方案,即不用虚拟内存管理的请求换页机制,只考虑进程保护、内存映射、共享虚拟内存等功能,这样既能提高系统的实时性,又能提高系统的安全性,具体请参考文献4。
参考文献:
1、基于Linux的嵌入式系统在测控系统中的设计.http://article.ednchina.com/Embeded/20070630090542.htm
2、The Linux kernel Primer.
3、嵌入式L inux 操作系统的研究. 刘文峰, 李程远, 李善平
4、嵌入式软件虚拟内存管理技术的研究和实现. 钱 静, 芦东昕, 谢 鑫, 徐立锋
5、Understanding the Linux kernel.
作者:Bluestorm's Space
来源:博客园