分享
嵌入式Linux06.ppt
下载文档

ID:3453282

大小:282KB

页数:38页

格式:PPT

时间:2024-05-08

收藏 分享赚钱
温馨提示:
1. 部分包含数学公式或PPT动画的文件,查看预览时可能会显示错乱或异常,文件下载后无此问题,请放心下载。
2. 本文档由用户上传,版权归属用户,汇文网负责整理代发布。如果您对本文档版权有争议请及时联系客服。
3. 下载前请仔细阅读文档内容,确认文档内容符合您的需求后进行下载,若出现内容与标题不符可向本站投诉处理。
4. 下载文档时可能由于网络波动等原因无法下载或下载错误,付费完成后未能成功下载的用户请联系客服处理。
网站客服:3074922707
嵌入式 Linux06
第6章 嵌入式Linux的内存管理,本章教学目的及要求了解Linux内存管理的基本概念及相关数据结构了解虚存空间的管理掌握共享内存的操作理解嵌入式操作系统内存管理理解动态内存分配及malloc/free的实现了解Linux内存的使用,内存管理机制是嵌入式系统中的一个重点和难点,它必须满足以下几个特性:实时性可靠性高效性在嵌入式实时系统中,内存管理根据不同的系统,有不同的策略,对于有些系统支持的虚拟内存管理机制,对于另外一些系统,可能只有flat式的简单内存管理机制。,6.1 Linux内存管理的基本概念及相关数据结构,6.1.1 嵌入式操作系统内存管理机制,1.嵌入式操作系统内存管理机制分类,(1)虚拟内存管理机制有些嵌入式处理器提供了MMU,MMU具备内存地址映射和寻址功能,它使操作系统的内存管理更加方便。如果存在MMU,操作系统会使用它完成从虚拟地址到物理地址的转换,所有的应用程序只需要使用虚拟地址寻址数据。这种使用虚拟地址寻址整个系统的主存和辅存的方式在现代操作系统中被称为虚拟内存。MMU 是实现虚拟内存的必要条件。(2)非虚拟内存管理机制由于虚拟内存机制会导致不确定性的 I/O阻塞时间,使得程序运行时间不可预期。在实时性要求较高时,很多嵌入式系统不需要虚拟内存机制。另外,从嵌入式处理器的成本考虑,大多采用不装配MMU 的嵌入式微处理器。采用实存储器管理策略时,对于内存的访问是直接的,它对地址的访问不需要经过MMU,而是直接送到地址线上输出,所有程序中访问的地址都是实际的物理地址;同时,大多数嵌入式操作系统对内存空间没有保护,各个进程实际上共享一个运行空间。,2内存在系统中的生命期,(1)在启动 阶段,内存以临时分配的形式出现,当完成系统启动后,这些内存会回收供以后系统使用。(2)在正常运行阶段,内存以两种方式存在:系统为代码,数据分配的永久内存,这些内存在系统运行过程中是不会改变的,有的硬件的I/O等外设也把相应的地址映射到固定的内存空间。动态内存分配空间:这些内存不会固定分配,而是根据系统需要而动态分配的,如果利用非虚拟内存管理机制,一般需要改造动态内存分配机制以提高性能。,3Linux系统的内存管理机制,Linux内核的设计并没有全部采用Intel所提供的段机制,仅仅是有限度地使用了分段机制。这不仅简化了Linux内核的设计,而且为把Linux移植到其他平台创造了条件,因为很多RISC处理器并不支持段机制。所有段的基地址均为0。由此可以得出,每个段的逻辑地址空间范围为0-4GB。因为每个段的基地址为0,因此,逻辑地址到线性地址映射保持不变,在Linux中所提到的逻辑地址和线性地址(或虚拟地址)指的也就是同一地址。Linux巧妙地把段机制给绕过去了,而完全利用了分页机制。,4Linux系统虚拟内存机制的屏蔽,屏蔽虚拟内存机制的思路:实现虚拟内存的机制有:地址映射机制、内存分配和回收机制,缓存和刷新机制、请页机制、交换机制、内存共享机制,将实现这些机制的数据结构和函数屏蔽或修改,还要修改与之相关的文件。,虚拟内存管理机制在为进程安全提供很好保证的同时,也为开发人员提供了一个管理内存的方法,使开发人员更多的关注其他的方面。但是它也带来了时间不确定性的缺陷。根据不同的系统需求,可以选取相应的内存管理策略。现在大多数的实时系统中,非虚拟内存管理机制用得比较多,这样保证了系统的实时性,但是增加了开发的难度,任务内存操作不当,可能引起系统崩溃。,内存管理程序提供以下功能:大地址空间:用户程序使用的内存数量可以超过物理上实际所有的内存数量。内存保护:进程的内存是私有的,不能被其他进程所读取和修改。而且,内存管理程序可以防止进程覆盖代码和只读数据。内存映射:可以把一个文件映射到虚拟内存区域,并把该文件当做内存来访问。对物理内存的公平访问:内存管理程序确保所有的进程都能公平地访问计算机的内存资源,这样可以确保理想的系统性能。共享虚拟内存:内存管理程序允许进程共享它们内存的一部分。,6.1.2 Linux内存管理基础知识,内存管理的基本概念,(1)存储空间在32位嵌入式系统中,存储空间的地址范围从0 x00000000到0 xFFFFFFFF。这4GB存储范围内可以包括以下几种存储空间.设备空间、内部高速SRAM空间、内部mini cache空间、低端中断向量空间、高端中断向量空间、RAM内存空间、ROM(flash)空间。(2)内存空间系统的内存空间特指上面的RAM内存空间。(3)内存页(page)Linux是以页(page)为单位来管理物理内存的,一页大小一般等于4096(字节)。页容量越大,系统中可能存在的内存碎片就越多。(4)内存区段(bank)一个内存bank表示一块连续内存区域,一个bank一般对应处理器的一个RAM片选管脚上连接的RAM芯片内存空间。(5)内存节点(node)内存节点是指有一个或者多个内存bank组成的内存集合,如果一个内存节点由多个内存bank组成,这些内存bank之间可以地址连续,也可以不连续,即内存节点内可以存在内存孔洞。,内存管理的基本概念,(6)内存页区(zone)内存页区是定义在内存节点(node)内的概念,每个内存节点可分为3个内存页区,即DMA页区(ZONE_DMA=0)、Normal页区(ZONE_NORMAL=1)和HighMem页区(ZONE_HIGHMEM=2),三个页区的含义如下:DMA页区:可以进行DMA操作的RAM内存区域。Normal页区:不能进行DMA操作的RAM内存区域。HighMem页区:属于高端内存的区域,高端内存是指系统中的物理内存容量太大,其中高于一定域值的RAM内存页区就是高端内存页区。(7)空闲内存区域(free area)对应内存空闲区域的重要数据结构有struct free_area。(8)NUMA(Non-Uniform Memory Access)非一致内存访问。NUMA是一种分布式存储器访问方式,处理器可以同时访问不同的存储器地址,大幅提高并行性。,如:内存页描述符结构如下,struct page page_flags_t flags;页标志字 atomic_t _count;atomic_t _mapcount;unsigned long private;struct address_space*mapping;pgoff_t index;struct list_head lru;#if defined(WneANT_PAGE_VIRTUAL)void*virtual;#endif/*WANT_PAGE_VIRTUAL*/;,6.1.3Linux内存管理相关数据结构,1硬件物理内存相关的数据结构与函数,(1)虚存区间 vm_area_struct,变量名通常为vma。(2)Vm_area_struct中的vm_mm指向一个mm_struct数据结构,变量名通常为mm;mm-segments为指向该进程的局部段描述符表LDT。(3)Find_vma()找到一个包含addr的虚存页面。(4)Insert_vm_struct向进程的虚拟页面队列中插入一个虚存页面(linux内核的链表插入采用双重链表指针)。(5)Swap_info_struct 相当于page结构。(6)Swap_entry_t 相当于pte_t结构。(7)SWP_TYPE(x)页面所在的文件。(8)SWP_OFFSET(x)页面在文件中的偏移。(9)SWP_ENTRY(type,offset)获得swap_entry_t。,6.1.3Linux内存管理相关数据结构,2虚拟内存相关的数据结构与函数,(1).text存放 CPU 执行的机器指令,代码区通常是只读的,防止程序意外地修改了它的指令。(2).data该区包含被初始化的全局变量、静态变量.它们是在编译阶段被编译器存放在可执行目标文件的.data段中的.(3).BSS未被初始化的全局变量和静态局部变量,编译的时候并未被分配空间,仅仅在.bss段中标记它们,当程序运行的时候才为它们在内存中分配空间,并把它们初始化为零或空指针(NULL)。(4).rodata该区包含常量数据(如字符串常量)在编译阶段被编译器存放在可执行目标文件的.rodata段中的。,6.2 Linux的进程与内存管理,6.2.1 进程内存管理,可执行程序存储结构,程序运行时内存结构,(1)代码区(text)代码区指令根据程序设计流程依次执行,对于顺序指令,则只会执行一次(每个进程),如果反复,则需要使用跳转指令,如果进行递归,则需要借助栈来实现。(2)全局初始化数据区/静态数据区(data):只初始化一次(3)未初始化数据区(BSS):在运行时改变其值。(4)栈区(stack):由编译器自动分配释放,存放函数的参数值、局部变量的值等。(5)堆区(heap):用于动态内存分配。一般由程序员分配和释放,若程序员不释放,程序结束时有可能由 OS 回收。,程序执行时的内存分配情况,int a=0;/a 在全局已初始化数据区 char*p1;/p1 在 BSS 区(未初始化全局变量)main()int b;/b 在栈区 char s=abc;/s 为数组变量,存储在栈区,/“abc”为字符串常量,存储在已初始化数据区 char*p1,p2;/p1、p2 在栈区 char*p3=123456;/1234560 data数据区,p3 在栈区.static int c=0;/c为局部静态数据,data数据区 p1=(char*)malloc(10);/分配得来的 10 个字节的区域在堆区 p2=(char*)malloc(20);/分配得来的 20 个字节的区域在堆区 free(p1);free(p2);,栈和堆的区别,(1)管理方式不同 栈编译器自动管理,无需程序员手工控制;而堆空间的申请释放工作由程序员控制,容易产生内存泄漏。(2)空间大小不同。栈是向低地址扩展的数据结构,是一块连续的内存区域。因此,用户从栈获得的空间较小。堆是向高地址扩展的数据结构,是不连续的内存区域。堆获得的空间较灵活,也较大。,进程内存空间,Linux操作系统采用虚拟内存管理技术,使得每个进程都有独立的进程地址空间,该空间是大小为3G,用户看到和接触的都是虚拟地址,无法看到实际的物理地址。利用这种虚拟地址不但能起到保护操作系统的作用,而且更重要的是用户程序可使用比实际物理内存更大的地址空间。Linux将4G的虚拟地址空间划分为两个部分用户空间与内核空间。用户空间从0到0 xbfffffff,内核空间从3G到4G。用户进程通常情况下只能访问用户空间的虚拟地址,不能访问内核空间。例外情况是用户进程通过系统调用访问内核空间。用户空间对应进程,所以每当进程切换,用户空间就会跟着变化。每个进程的用户空间都是完全独立、互不相干的。,内存分配方式,(1)静态分配:编译器在处理程序源代码时分配,静态对象是有名字的变量,可以直接对其进行操作,静态对象的分配与释放由编译器自动处理。(2)动态分配:程序在执行时调用 malloc 库函数申请分配,动态对象需要通过指针间接地进行操作,分配与释放必须由程序员显式地管理。,内存分配方式,创建进程fork()、程序载入execv()、动态内存分配malloc()等进程相关操作都需要分配内存给进程。这时进程申请和获得的不是物理地址,仅仅是虚拟地址。实际的物理内存只有当进程真的去访问新获取的虚拟地址时,才会由“请页机制”产生“缺页”异常,从而进入分配实际页框的程序。该异常是虚拟内存机制赖以存在的基本保证它会告诉内核去为进程分配物理页,并建立对应的页表,这之后虚拟地址才实实在在地映射到了物理地址上。,6.2.2系统物理内存管理,Linux 2.6.29内核为每种CPU提供统一的界面,采用了四级页管理架构,来兼容二级、三级、四级管理架构的CPU。,Linux页式管理,这四级分别为:(1).页全局目录(Page Global Directory):即pgd,是多级页表的抽象最高层。(2).页上级目录(Page Upper Directory):即pud。(3).页中间目录(Page Middle Directory):即pmd,是页表的中间层。(4).页表(Page Table Entry):即pte。Linux通过伙伴算法管理和分配页。,用户空间和内核空间内存使用,Linux内核内存使用,1.Slab分配器Linux内核中有许多内存动态分配的需求,而其中的对象大小也参差不齐,Linux内核提供了slab层,扮演了通用数据结构缓存层的角色。slab层根据对象的类型来分组不同的cache,每个cache存放不同类型的对象,例如一个cache被用来存储task_struct,而另一个存放inode等。这些cache包含几个slab,而slab由一个或多个物理上连续的page组成。对于一般的数据结构,每个slab只有一个页即可。每个slab都包含一些对象成员,即被管理的数据结构。系统分配对象时就从slab中取得。首先从这个cache中部分满的slab中分配,如果没有这样的slab,便从空的slab中分配,如果也没有,就创建一个新的slab来分配即可。因为每个slab是包含同一种对象的cache块,它对对象的分配和释放会变得更为容易和快速。另外,由于每个对象在释放时几乎处于分配好并且初始化好的状态,还可以节省不少初始化的时间。比如说分配inode变量,首先要一块malloc(sizeof(inode)大小的内存,然后初始化inode中的数据成员,在使用完毕后用free释放分配的内存。实际上,在free之后内存中的内容和刚刚初始化时差不多,比如说inode的引用计数count一定是降为零等等。,Linux内核内存使用,1.Slab分配器kernel有许多数据结构都是还原到和初始化时一样的时候才会free掉,例如一个mutex lock初始化时和释放时都处于unlock状态。因此,只要在cache初始化的时候就将对象置于合法状态,以后每次分配对象的这些field一定是确定的,从而不必重复初始化,可以节省不少开销。为了这个目的,linux的kmem_cache_create中有一个参数ctor初始化函数,可以被用作这一目的,但linux似乎并没有使用slab的这一特性(因为它没用调用ctor函数)。每个cache都用struct kmem_cache_s表示,其中有一个重要的成员变量struct kmem_list3 lists。这个结构体中存放了该cache中空、部分满、满的slab的队列:slab描述符(struct slab对象)本身也要占用内存。它们可以放在slab自身开始的地方,或者在slab之外另行分配。slab分配器创建新的slab正是通过上面的页分配器进行的。,Linux内核内存使用,2.kmalloc核心的kmalloc内存分配函数和应用层的malloc函数很相似,只是这个函数运行得很快一除非它被阻塞。它不清零它获得的内存空间,分配给它的区域仍存放着原有的数据。kmalloc函数在中定义,实际上,它是通过下一层的_kmalloc完成内存分配的。static void*kmalloc(size_t size,int flags)void*_kmalloc(size_t size,int flags)kmalloc函数的第一个参数是size(大小),第二个参数,flags是优先权参数,它会使kmalloc函数在寻找空闲页较困难时改变函数的行为。最常用的优先权是GFP_KERNEL,它的意思是该内存分配在内部是通过调用get_free_pages来实现的,所以名字中带GFP是由运行在内核态的进程调用的。当空闲内存太少时,kmalloc函数会使当前进程进入睡眠,等待空闲页的出现。新的页面可以通过以下几种途径获得。一种方法是换出其他页:因为对换需要时间,进程会等待它完成,这时内核可以调度执行其他的任务。,Linux内核内存使用,3.Vmalloc在申请和释放较小且连续的内存空间时,使用kmalloc()和kfree()在物理内存中进行分配。申请较大的内存空间时,使用vmalloc()。由vmalloc()申请的内存空间在虚拟内存中是连续的,它们映射到在物理内存时,可以使用不连续的物理页面,而且仅把当前访问的部分放在物理页面中。尽管这段区域在物理上可能是不连续的(要访问其中的每个页面都必须独立地调用函数_get_free_page),内核却认为它们在地址上是连续的。分配的内存空间被映射进入内核数据段中,从用户空间是不可见的这一点上与其他分配技术不同。vmalloc发生错误时返回0(NULL地址),成功时返回一个指向一个大小为size的线性地址空间的指针。,Linux内核内存使用,3.Vmalloc使用vmalloc时应将包含进来。与其他内存分配函数不同的是,vmalloc返回很“高”的地址值这些地址要高于物理内存的顶部。正确使用vmalloc函数的场合是为软件分配一大块连续的用于缓冲的内存区域。注意vmalloc的开销要比_get_free_pages大,因为它处理获取内存还要建立页表。因此,不值得用vmalloc函数只分配一页的内存空间。vmalloc分配的内核虚拟内存与kmalloc/_get_free_pages分配的内核逻辑内存位于不同的区间,不会重叠。因为内核空间被分区管理,各司其职。用户空间被分配在03G,3G之后紧跟着是物理内存映射区间,再后面才是vmalloc_start开始的用于vmalloc分配内存的地址空间。,Linux内核内存使用,3.Vmalloc内存分配区间示意图用vmalloc分配得到的内存空间用vfree函数来释放,这就像是要用kfree函数来释放kmalloc函数分配得到的内存空间。,6.3 虚拟空间的管理,内核在“系统态”执行,在这一级任何操作都可以执行。而应用程序则执行在最低级,所谓的“用户态”,在这一级处理器禁止对硬件的直接访问和对内存的未授权访问。模块是在所谓的“内核空间”中运行的,而应用程序则是在“用户空间”中运行的。它们分别引用不同的内存映射,也就是程序代码使用不同的“地址空间”。Linux通过系统调用和硬件中断完成从用户空间到内核空间的控制转移。,6.3.1 内核空间和用户空间,6.3 虚拟空间的管理,一个虚存区域是虚存空间中一个连续的区域,在这个区域中的信息具有相同的操作和访问特性。每个虚拟区域用一个vm_area_struct结构体进行描述,它定义在/include/linux/mm.h中。struct vm_area_struct struct mm_struct*vm_mm;unsigned long vm_start;unsigned long vm_end;pgprot_t vm_page_prot;unsigned short vm_flags;short vm_avl_height;系统为每个进程提供了4GB的虚拟内存空间。各个进程的虚拟内存彼此独立。,6.3.2 进程的虚拟区域,6.4 共享内存,共享内存区域是被多个进程共享的一部分物理内存。如果多个进程都把该内存区域映射到自己的虚拟地址空间,则这些进程就都可以直接访问该共享内存区域,从而可以通过该区域进行通信。共享内存是进程间共享数据的一种最快的方法,一个进程向共享内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。这块共享虚拟内存的页面,出现在每一个共享该页面的进程的页表中。但是它不需要在所有进程的虚拟内存中都有相同的虚拟地址。Linux通过系统调用和硬件中断完成从用户空间到内核空间的控制转移。每一个新创建的共享内存对象都用一个shmid_kernel数据结构来表达。系统中所有的shmid_kernel数据结构都保存在shm_segs向量表中,该向量表的每一个元素都是一个指向shmid_kernel数据结构的指针。,6.4.1 共享内存的数据结构,6.4 共享内存,Linux为共享内存提供了四种基本的操作1.共享内存对象的创建或获得进程在使用共享内存区域以前,必须通过系统调用sys_ipc(call值为SHMGET)创建一个键值为key的共享内存对象,或获得已经存在的键值为key的某共享内存对象的引用标识符。以后对共享内存对象的访问都通过该引用标识符进行。对共享内存对象的创建或获得由函数sys_shmget完成2.映射在创建或获得某个共享内存区域的引用标识符后,还必须将共享内存区域映射(粘附)到进程的虚拟地址空间,然后才能使用该共享内存区域。系统调用 sys_ipc(call值为SHMAT)用于共享内存区到进程虚拟地址空间的映射。,6.4.2 共享内存的操作,6.4 共享内存,Linux为共享内存提供了四种基本的操作3.分离当进程不再需要共享虚拟内存的时候,它们与之分离。只要仍旧有其它进程在使用这块内存,这种分离就只会影响当前的进程,而不会影响其它进程。当共享这块内存的最后一个进程与之分离时,共享内存页被释放4.控制控制操作包括获得共享内存对象的状态,设置共享内存对象的参数(如uid、gid等),将共享内存对象在内存中锁定和释放,释放共享内存对象资源等。,6.4.2 共享内存的操作,6.5 动态内存分配及malloc/free的实现,静态内存分配动态内存分配,6.6 Linux内存的使用,Linux在内存管理上分为两级,一级是线性区,对应于虚拟内存,它实际上不占用实际物理内存;一级是具体的物理页面,它对应计算机上的物理内存。内存的延迟分配。Linux内核在用户申请内存的时候,只是给它分配了一个线性区(也就是虚存),并没有分配实际物理内存;只有当用户使用这块内存的时候,内核才会分配具体的物理页面给用户,这时候才占用物理内存。,本章小结,本章介绍了Linux内存管理的基本概念及相关数据结构、虚存空间的管理、共享内存的操作及动态内存分配及malloc/free的实现等内容。通过本章的学习,要理解Linux内存管理的基本原理,了解虚存空间的管理,掌握共享内存的操作,理解动态内存分配及malloc/free的实现,了解Linux内存的使用。,6作业P184习题一1、3、4,

此文档下载收益归作者所有

下载文档
收起
展开