第八章存储管理_第1页
第八章存储管理_第2页
第八章存储管理_第3页
第八章存储管理_第4页
第八章存储管理_第5页
已阅读5页,还剩120页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第八章存储管理第八章存储管理n本章主要内容:本章主要内容:mmummu(memory management unit) 第第 8 章章 目录目录1 缺少缺少mmu支持的内存管理支持的内存管理2 flat平模式内存管理平模式内存管理 2.1 3种内存管理模型种内存管理模型 2.2 标准标准linux的内存管理的内存管理 2.3 uclinux内存管理内存管理 2.4 uclinux内存管理的局限性内存管理的局限性3 内存管理模块的启动过程内存管理模块的启动过程4 可执行程序的加载可执行程序的加载 4.1 用户程序的内存分布用户程序的内存分布 4.2 reloc段机制段机制 4.3 flat可执行

2、文件格式可执行文件格式 4.4 执行文件加载流程执行文件加载流程 第第 8 章章 目录目录1 缺少缺少mmu支持的内存管理支持的内存管理2 flat平模式内存管理平模式内存管理 2.1 3种内存管理模型种内存管理模型 2.2 标准标准linux的内存管理的内存管理 2.3 uclinux内存管理内存管理 2.4 uclinux内存管理的局限性内存管理的局限性3 内存管理模块的启动过程内存管理模块的启动过程4 可执行程序的加载可执行程序的加载 4.1 用户程序的内存分布用户程序的内存分布 4.2 reloc段机制段机制 4.3 flat可执行文件格式可执行文件格式 4.4 执行文件加载流程执行文

3、件加载流程8.1 缺少缺少mmu支持的内存管理支持的内存管理 内存管理是操作系统中非常重要的子模内存管理是操作系统中非常重要的子模块。块。 如同普通操作系统一样,在嵌入式操作如同普通操作系统一样,在嵌入式操作系统中,内存管理实现的好坏对系统性能有系统中,内存管理实现的好坏对系统性能有决定性的作用决定性的作用。8.1 缺少缺少mmu支持的内存管理支持的内存管理8.1 缺少缺少mmu支持的内存管理支持的内存管理 目前的目前的linuxlinux内核已经被移植到大量的内核已经被移植到大量的非非x86x86平台上,包括平台上,包括armarm,m68km68k,ppcppc,alphaalpha,sp

4、arcsparc等。等。 其中其中uclinuxuclinux主要针对缺少主要针对缺少mmummu内存内存管理的优秀嵌入式管理的优秀嵌入式linuxlinux操作系统。操作系统。8.1 缺少缺少mmu支持的内存管理支持的内存管理8.1 缺少缺少mmu支持的内存管理支持的内存管理 8.1 缺少缺少mmu支持的内存管理支持的内存管理 第第 8章章 目录目录1 缺少缺少mmu支持的内存管理支持的内存管理2 flat平模式内存管理平模式内存管理 2.1 3种内存管理模型种内存管理模型 2.2 标准标准linux的内存管理的内存管理 2.3 uclinux内存管理内存管理 2.4 uclinux内存管理

5、的局限性内存管理的局限性3 内存管理模块的启动过程内存管理模块的启动过程4 可执行程序的加载可执行程序的加载 4.1 用户程序的内存分布用户程序的内存分布 4.2 reloc段机制段机制 4.3 flat可执行文件格式可执行文件格式 4.4 执行文件加载流程执行文件加载流程8.2.1 3种内存管理模型1. 单一程序模型单一程序模型 这是没有硬件地址转换的内存管理模型。一个应用程序可以对所有的物理内存地址进行寻址 应用程序始终在物理内存中的同一地址空间运行,一个时刻只有一个应用程序被加载运行,程序加载器把应用程序加载到内存低端,将操作系统加载到高端 2. 多程序模型多程序模型 这也是没有硬件地址

6、转换的内存管理模型。 即使没有硬件地址转换功能,多个程序也可以共享相同的物理地址。 在程序被加载到内存的时候,改变程序中寻址指令(load,store,jump)所使用的地址值为当前被加载到的位置。 正是使用了这种模型。uclinux 应用程序使用的是虚拟地址,cpu实际执行程序所使用的是物理地址,从虚拟地址到物理地址的转换需要操作系统和mmu硬件的参与 标准linux以及大多数现代操作系统都使用这种内存管理模型3. 具有地址转换硬件的内存管理模型 第第 8章章 目录目录1 缺少缺少mmu支持的内存管理支持的内存管理2 flat平模式内存管理平模式内存管理 2.1 3种内存管理模型种内存管理模

7、型 2.2 标准标准linux的内存管理的内存管理 2.3 uclinux内存管理内存管理 2.4 uclinux内存管理的局限性内存管理的局限性3 内存管理模块的启动过程内存管理模块的启动过程4 可执行程序的加载可执行程序的加载 4.1 用户程序的内存分布用户程序的内存分布 4.2 reloc段机制段机制 4.3 flat可执行文件格式可执行文件格式 4.4 执行文件加载流程执行文件加载流程8.2.2 标准linux的内存管理linux使用了上述的第三个模型(地址转换硬件的内存管理模型)。 为了理解uclinux对标准linux的裁减,首先必须清楚标准linux中内存管理的各种基本概念。这里

8、对相关于虚拟内存的各个概念作一个总结。 8.2.2 标准linux的内存管理8.2.2 标准linux的内存管理内核主要通过页目录和页表的地址转换功能将内核主要通过页目录和页表的地址转换功能将应用程序的虚拟地址转换成物理地址。应用程序的虚拟地址转换成物理地址。这个过程中可能将应用程序中使用的超过了实这个过程中可能将应用程序中使用的超过了实际物理内存大小的虚拟地址映射到适当的真实物理际物理内存大小的虚拟地址映射到适当的真实物理地址,从而使应用程序可以随心所欲地使用巨大的地址,从而使应用程序可以随心所欲地使用巨大的虚拟存储空间虚拟存储空间( (对对linux 2.4linux 2.4内核来说为内核

9、来说为4gb)4gb)。8.2.2 标准linux的内存管理 但是只通过地址映射还不能解决有限的物理但是只通过地址映射还不能解决有限的物理内存被虚拟地址空间所使用的问题,操作系统还内存被虚拟地址空间所使用的问题,操作系统还必须使用必须使用页面交换机制页面交换机制将那些暂时不再使用的内将那些暂时不再使用的内存空间交换到外存中以使其他程序,能够使用物存空间交换到外存中以使其他程序,能够使用物理内存。理内存。 linuxlinux没有将整个进程所使用的空间都交换到没有将整个进程所使用的空间都交换到外存中,而是对部分不再使用的大小为外存中,而是对部分不再使用的大小为4kb 4kb 的页的页面进行交换,

10、这样就获得了更多的灵活性面进行交换,这样就获得了更多的灵活性。 应用程序在标准应用程序在标准linuxlinux中的加载使用了中的加载使用了“按按需调页需调页”的策略,也就是说,应用程序在开始被的策略,也就是说,应用程序在开始被加载的时候并没有一次被全部装载到内存中,只加载的时候并没有一次被全部装载到内存中,只有那些现在必需的代码和数据在开始进行了加载。有那些现在必需的代码和数据在开始进行了加载。 8.2.2 标准linux的内存管理 在程序执行的过程中,如果遇到了不在内存在程序执行的过程中,如果遇到了不在内存中的程序部分将产生页面错误,操作系统在处理中的程序部分将产生页面错误,操作系统在处理

11、这个错误中断的时候会到外存中找到相应的应用这个错误中断的时候会到外存中找到相应的应用程序部分进行加载。程序部分进行加载。 8.2.2 标准linux的内存管理 这种设计是基于计算机科学中著名的90-10规则的:90的程序执行时间花费在整个程序10的代码上。 所以只要保持我们用到的程序在内存中,就可以既满足程序的执行速度又节约物理内存空间。 标准linux中的内存管理模型如图如图8-1所示所示。 图图8-1 linux的内存管理模型的内存管理模型第第 8章章 目录目录1 缺少缺少mmu支持的内存管理支持的内存管理2 flat平模式内存管理平模式内存管理 2.1 3种内存管理模型种内存管理模型 2

12、.2 标准标准linux的内存管理的内存管理 2.3 uclinux内存管理内存管理 2.4 uclinux内存管理的局限性内存管理的局限性3 内存管理模块的启动过程内存管理模块的启动过程4 可执行程序的加载可执行程序的加载 4.1 用户程序的内存分布用户程序的内存分布 4.2 reloc段机制段机制 4.3 flat可执行文件格式可执行文件格式 4.4 执行文件加载流程执行文件加载流程8.2.3 uclinux的内存管理 由于由于m68k系列微处理器中没有分段的系列微处理器中没有分段的概念,所以标准概念,所以标准linux内核中从虚拟地址到内核中从虚拟地址到线性地址的映射已经不必存在了。线性

13、地址的映射已经不必存在了。 而且由于缺少了而且由于缺少了mmu硬件的支持,硬件的支持,uclinux不能支持虚拟内存管理和内存保护。不能支持虚拟内存管理和内存保护。 8.2.3 uclinux的内存管理n这就意味着它完全不使用标准linux内核中的分页管理机制,也就没有了页表和页目录对线性地址的映射,从而线性地址到物理地址的转换也是不需要进行任何工作的。n换句话说,uclinux中所使用的都是直接物理地址。 8.2.3 uclinux的内存管理n而且,由于没有了虚拟内存管理功能,uclinux不再使用“按需调页”算法。 这样在程序载入内存执行的时候需要将程序的全部映像都一次装入。n 那些比物理

14、内存还大的程序将无法执行。8.2.3 uclinux的内存管理n 尽管如此,uclinux还是将整个物理内存划分成大小为4kb的页面。n 由数据结构page管理,有多少页面就有多少page结构,它们又作为元素组成一个数组mem_map。n 物理页面可以作为进程的代码、数据和堆栈的一部分,还可以存储装入的文件,也可以当作缓冲区。8.2.3 uclinux的内存管理 uclinux仍然使用标准linux内核中的变型buddy system机制来管理空闲的物理页面,bitmap表和free_area数组,以及相关的函数或宏_get_free_pages()、free_pages()、_get_fre

15、e_page()现在仍然在被使用。8.2.3 uclinux的内存管理n 进程可以向核心申请使用物理内存。这仍然通过使用传统的kmalloc()和kfree()实现。n 这些内存块来自于free_area数组,由blocksize表、size表、page_descriptor结构和block_header结构共同管理。 8.2.3 uclinux的内存管理 而过去的vmalloc()和vfree()由于是从虚拟空间3gb以上的虚拟空间的最高端分配内存,所以现在对它们的实现只是简单地调用kmalloc()和kfree(),实际上分配的也是从空闲物理页面链表中获得的页面。8.2.3 uclinux

16、的内存管理n 不使用虚拟空间的概念,虚存段结构vm_area_struct以及由它构成的线性链表和avl树都不再使用。n 没有了虚拟内存的应用,也就不再有将页面换出到外存中的机制。n 所以过去的kswapd页面换出守护进程和交换空间的页面管理数据结构在uclinux中都被删除。第第 8章章 目录目录1 缺少缺少mmu支持的内存管理支持的内存管理2 flat平模式内存管理平模式内存管理 2.1 3种内存管理模型种内存管理模型 2.2 标准标准linux的内存管理的内存管理 2.3 uclinux内存管理内存管理 2.4 uclinux内存管理的局限性内存管理的局限性3 内存管理模块的启动过程内存

17、管理模块的启动过程4 可执行程序的加载可执行程序的加载 4.1 用户程序的内存分布用户程序的内存分布 4.2 reloc段机制段机制 4.3 flat可执行文件格式可执行文件格式 4.4 执行文件加载流程执行文件加载流程8.2.4 uclinux内存管理的局限性 由于缺少了mmu硬件的支持,uclinux的多任务管理功能受到一定限制:1. uclinux中无法实现fork()而只能使用vfork() 这并不意味着uclinux不具有多任务功能,而是父进程在调用vfork()之后必须在子进程调用exec()或者exit()之前阻塞。8.2.4 uclinux内存管理的局限性2. 标准linux中

18、的内存分段为应用程序提供了接近无限的堆空间和栈空间,而uclinux为可执行程序在紧随它的数据段结束处分配堆栈空间。 这样如果堆增长的太大,它将可能覆盖程序的静态数据段和代码段。8.2.4 uclinux内存管理的局限性3. uclinux中没有自动扩展的栈,也没有brk()调用。 用户必须通过使用mmap()来分配内存空间。 可以在程序的编译过程中指定它所使用的栈大小。4. 不具有内存保护。任何程序都有可能导致内核崩溃。 8.2.4 uclinux内存管理的局限性 uclinux与标准linux的主要区别在于它针对没有mmu的处理器进行改造。 uclinux所做的修改最大的部分理所当然位于内

19、存管理部分,而内存管理上最大变化就是uclinux没有使用虚拟内存机制。 8.2.4 uclinux内存管理的局限性 这样,标准linux的内存管理模块中的许多功能实际上都被抛弃了,诸如对页目录和页表的管理,对于交换空间的维护,页交换内核守护进程和页面换出功能,缺页中断和页面保护机制等。 8.2.4 uclinux内存管理的局限性 而为了解决由此产生的新问题,uclinux同时设计了一种新的可执行文件格式:flat,以及修改了部分进程管理的代码:如用vfork()来代替了fork()调用,实现了无法共享页面的do_mmap()等。 uclinux下的多任务管理远比linux简单,因为没有进程的

20、页表项和保护机制要处理。 8.2.4 uclinux内存管理的局限性 内核的调度器不需要进行修改,唯一需要完成的工作就是进行程序上下文的正确保存和恢复。 这些上下文包括所有的在进程被中断的时候必须保存的寄存器的值。8.2.4 uclinux内存管理的局限性 这些机制的缺少归根结底都在于微处理芯片没有mmu的支持。 例如,在没有mmu的处理器上不可能实现内存共享和保护,这是由于各种unix的内存共享都是需要mmu中的页表和页目录管理功能支持的。 8.2.4 uclinux内存管理的局限性 标准linux中的内存共享以页面共享的形式实现,共享该页的各个进程的页表项直接指向共享页,当共享状态发生变化

21、的时候共享该页的各进程的页表都进行修改。 另一个例子是内存保护机制的缺少。 8.2.4 uclinux内存管理的局限性 标准linux中对内存的加锁保护是在虚拟内存段vma的基础上进行的, 也就是每一个vma段由一个自己的保护权限标志vm_flags,将这个标志设置成prot_read,prot_ write和prot_exec就可以实现保护机制, 但实际上在vma操作的最底层,保护的实现还是要依靠各个页面本身的页表项标志。第第 8章章 目录目录1 缺少缺少mmu支持的内存管理支持的内存管理2 flat平模式内存管理平模式内存管理 2.1 3种内存管理模型种内存管理模型 2.2 标准标准lin

22、ux的内存管理的内存管理 2.3 uclinux内存管理内存管理 2.4 uclinux内存管理的局限性内存管理的局限性3 内存管理模块的启动过程内存管理模块的启动过程4 可执行程序的加载可执行程序的加载 4.1 用户程序的内存分布用户程序的内存分布 4.2 reloc段机制段机制 4.3 flat可执行文件格式可执行文件格式 4.4 执行文件加载流程执行文件加载流程8.3 内存管理模块的启动过程内存管理模块的启动过程 在标准linux的启动过程中要进行许多与内存管理相关的功能模块的初始化工作,诸如页目录和页表映射的建立等。 在把它改造成不使用mmu的系统的过程中必然要对这些功能进行修改。 所

23、以在这一部分中将展开对uclinux中内存模块的启动初始化分析。8.3 内存管理模块的启动过程内存管理模块的启动过程 arch/armnommu/kernel/entry_armv.s是一个汇编文件,它包含了一个kernel_entry的定义,这是整个内核的进入点。 在完成某些平台相关的初始化工作之后,执行流程跳转到start_kernel()处。 从这里开始考察uclinux的内存模块启动初始化是如何实现的。8.3 内存管理模块的启动过程内存管理模块的启动过程 start_kernel()中与内存模块相关的函数调用流程如下:1.setup_arch() 2. paging_init() 3.

24、free_area_init() 4.mem_init()下面分别分析这些函数各自的功能以及uclinux对它们的改造。1.setup_arch() setup_arch()首先根据目前内核所配置的平台向某些特定地址写入特殊字符序列,以完成对特定硬件的初始化, 比如工作状态发光二极管、错误和报警发光二极管等。1.setup_arch() 存储了可用物理内存起始地址的变量memory_start的初始化是通过一个ld脚本中定义的变量_end进行的。 ld脚本是用来控制gnu ld连接器在连接内核各个目标文件部分的时候的配置动作,比如这样一个脚本:1.setup_arch()sections .=

25、0 x10000; .text : *(.text) .=0 x8000000; .data : *(.data) .bss : * (.bss) 1.setup_arch() 用来配置ld连接目标文件的时候将所有目标文件中的存储程序正文的. text段(section)连接到一起,并且映射到输出文件的地址0 x10000处,将所有目标文件中已初始化的数据 1.setup_arch() data段连接到一起并放置到输出可执行文件的0 x8000000地址处,而所有目标文件中还未初始化的数据段. bss连接起来后映射到输出文件中紧跟在.data段之后的位置。1.setup_arch() 这个ld

26、配置脚本文件对每个平台都是不同的。 如为micetek上所使用的uclinux版本使用的ld配置文件为arch/armnommu/vmlinux.lds。 可以通过修改某个平台上的ld脚本配置文件中的_end变量来达到配置其可用物理内存起始地址的目的。1.setup_arch() setup_arch()在完成对memory_start变量的初始化之后,通过某些特定手段检测不同类型的内存分布情况。 比如为检测某段地址范围是否为ram的方法是通过将某个地址的数据读出来,将它加1后写回内存地址中,然后再读出来和原始数据比较看看其值是否成功增加了1,这样反复操作两次,最后将原数据恢复。 1.setu

27、p_arch() 如果是可读可写的ram,那么这个测试的结果就是每次比较都是成功的,否则就不能将这个地址当作ram。1.setup_arch() 在setup_arch()中还可能根据所用平台进行对flashmemory和rom的测试。 在这些平台相关的工作完成以后,setup_arch()将对系统运行的第一个进程init_task的mm_struct结构中描述地址空间分布的变量start_code,end_code,end_data和brk进行初始化,start_code为0,其他三个数值分别为来自于ld脚本配置文件中定义的相关变量_etext、_edata和_end。1.setup_arc

28、h() 此后setup_arch()将根据linux中为系统中的第块rom/flash memory card所分配的固定的主从设备号(可以从documentdevicestxt中得到)来创建根文件系统的设备号,并存储在后来将要用到的全局变量root_dev中。1.setup_arch() setup_arch()最后完成对系统启动参数的保存。 在调用setup_arch()返回之后,start_kernel()中得到了系统可用物理内存的起始和结束地址,以及系统启动时的命令行参数。2. paging_init() 在linux中,paging_init()的一项主要功能是建立页目录和页表,而且

29、将linux移植到不同平台的过程中非常重要的一个步骤就是修改这个函数来适应新的硬件平台的虚拟内存体系。 2. paging_init() 但是由于在uclinux中不再使用虚拟内存机制,也就不再需要维护页目录和页表数据结构了,所以paging_init()在这里只是为系统启动的时候保留一部分特殊用途的内存区间。 它返回后,从可以使用的内存区间开始,依次是如下的数据结构:2. paging_init()empty_bad_page_table 占用1页 empty_bad_page 占用1页 empty_zero_page 占用1页,并且被初始化为全0 mem_map bitmap2. pagi

30、ng_init() paging_init()函数在返回前通过调用free_area_init(start_mem,end_mem)进行建立buddy system的映射位图关系,以及建立空闲物理页面链表的操作。3. free_area_init() 这个函数用于建立管理物理页帧的数据结构mem_map,有多少物理页帧就有多少mem_map_t类型的结构体与之相对应。 每个页面的mem_map_t结构中的flags被标明为pg_dma和pg_reserved,并且页帧号被赋给相应的数值。 同时建立了管理空闲页面的bitmap映射表,并且所有的位都被清0。4. mem_init() mem_in

31、it()函数遍历整个可用物理内存地址空间,将每个页面相对应的struct page结构中flags的pg_reserved标志位清除,标志用户个数的count计数器置1,并同时统计可用物理页面数量,然后打印系统的各个内存参数,如可用ram和rom的大小、内核代码段和数据段大小等。第第 8章章 目录目录1 缺少缺少mmu支持的内存管理支持的内存管理2 flat平模式内存管理平模式内存管理 2.1 3种内存管理模型种内存管理模型 2.2 标准标准linux的内存管理的内存管理 2.3 uclinux内存管理内存管理 2.4 uclinux内存管理的局限性内存管理的局限性3 内存管理模块的启动过程内

32、存管理模块的启动过程4 可执行程序的加载可执行程序的加载 4.1 用户程序的内存分布用户程序的内存分布 4.2 reloc段机制段机制 4.3 flat可执行文件格式可执行文件格式 4.4 执行文件加载流程执行文件加载流程8.4 可执行程序的加载可执行程序的加载 在普通的linux中,虚拟内存技术的使用使我们不必关心一个应用程序是从什么地址开始的。 即使所有的应用程序都使用同一个连接脚本配置。 8.4 可执行程序的加载可执行程序的加载n 也就是说,即使它们使用的虚拟地址是重叠的,经过页表和页目录的转换之后它们也可以被映射到不同的物理地址。n 但是在uclinux中,由于缺少了mmu的硬件支持,

33、在内核中不会发生地址的映射转换,这样就必须解决应用程序的加载问题。8.4 可执行程序的加载可执行程序的加载n uclinux系统使用flat可执行文件格式,gcc的编译器不能直接形成这种文件格式,n 但是可以首先编译生成coff可执行格式或者elf可执行格式的文件,n 然后使用格式转化工具(coff2flt或者elf2flt)将这些中间代码转换成flat文件格式。8.4 可执行程序的加载可执行程序的加载n当用户执行一个flat格式的可执行程序时,内核的执行文件加载器将对flat文件进行进一步处理,主要是对reloc段进行修正。n下面对do_load_flat_binary()函数的分析将详细描

34、述其实现过程。 第第 8章章 目录目录1 1 缺少缺少mmummu支持的内存管理支持的内存管理2 flat2 flat平模式内存管理平模式内存管理 2.1 32.1 3种内存管理模型种内存管理模型 2.2 2.2 标准标准linuxlinux的内存管理的内存管理 2.3 uclinux2.3 uclinux内存管理内存管理 2.4 uclinux2.4 uclinux内存管理的局限性内存管理的局限性3 3 内存管理模块的启动过程内存管理模块的启动过程4 4 可执行程序的加载可执行程序的加载 4.1 4.1 用户程序的内存分布用户程序的内存分布 4.2 reloc4.2 reloc段机制段机制

35、4.3 flat4.3 flat可执行文件格式可执行文件格式 4.4 4.4 执行文件加载流程执行文件加载流程8.4.1 用户程序的内存分布 1.堆堆 标准linux上用户程序的动态内存分配是通过调用glibc库的malloc函数从程序的堆空间中获得内存页面。 在虚拟内存系统中malloc是使用sbrk调用将程序的数据段向后扩展得到。 应用程序的内存使用分布图如图8-2所示。图 8-2 flat可执行文件格式8.4.1 用户程序的内存分布 而在uclinux的平地址模式中,堆空间是通过mmap调用获得的。 uclibc中的malloc函数是一个非常简单的实现,它将所有分配内存的细节都交给了内核

36、。 2. 栈 uclinux中的栈紧随着用户程序的数据段,而堆是从栈底向下扩张的,如图8-2所示。 由于uclinux中没有内存保护机制,这样必须在程序编译连接的时候就确保为栈保留了足够的空间使它不会覆盖程序的数据段和代码段。图 8-2 flat可执行文件格式第第 8章章 目录目录1 缺少缺少mmu支持的内存管理支持的内存管理2 flat平模式内存管理平模式内存管理 2.1 3种内存管理模型种内存管理模型 2.2 标准标准linux的内存管理的内存管理 2.3 uclinux内存管理内存管理 2.4 uclinux内存管理的局限性内存管理的局限性3 内存管理模块的启动过程内存管理模块的启动过程

37、4 可执行程序的加载可执行程序的加载 4.1 用户程序的内存分布用户程序的内存分布 4.2 reloc段机制段机制 4.3 flat可执行文件格式可执行文件格式 4.4 执行文件加载流程执行文件加载流程8.4.2 reloc段机制 一个可执行程序通常包含代码段、数据段、堆栈段、未初始化数据段。 在普通的linux系统上,程序连接的时候,连接器ld都要受一个叫做连接器脚本的配置文件所控制,这个脚本用连接器命令语言写成,连接器会根据它来决定将程序的各个段加载到内存中的什么位置。8.4.2 reloc段机制 但是由于uclinux中没有使用虚拟内存机制,也就没有了从虚拟地址到物理地址之间的映射, 所

38、以程序连接时所指定的程序运行空间在uclinux中成为实际的物理地址而不是像linux那样可以通过内核维护的页目录和页表来映射到任意物理地址空间。 8.4.2 reloc段机制 在后面的分析中可以看到,程序加载的时候它所加载的地址是由内核的页面分配机制所决定的,在程序加载之前不可能被预知。 这样就形成了一个矛盾:程序连接时连接器所假定的程序运行空间与实际程序加载到的内存空间可能不同。 比如下面的一条指令: bl app_start; 这一条指令采用直接寻址,跳转到app_start地址处执行,连接程序将在编译完成时按照连接器1d的配置文件计算出app_start的实际地址(设若实际地址为oxl

39、0000), 这个实际地址是根据ld的配置文件做出的假设(因为连接器总是假定该程序将被加载到由ld配置文件指明的内存空间)。 8.4.2 reloc段机制注:但实际上操作系统在加载程序时根本没有考虑到要按照ld配置文件规定的地址进行加载。 这时如果程序仍然跳转到绝对地址oxl0000处执行,通常情况这是不正确的。 uclinux采用的解决办法是在连接生成的可执行文件中增加一个变量用于在程序加载的时候动态修正app_start的实际地址8.4.2 reloc段机制 这实际上是一块32位的空间,前30位用来纪录程序中的那些标明绝对地址的变量在数据段中离数据段的开始位置的偏移量, 后2位用来标明这个

40、变量究竟是指示一个什么地址:分为代码地址,数据地址和未初始化数据地址三种, 以便于在程序加载的时候,相应程序的代码段,数据段和bss段被加载到内存中的实际位置对这个地址变量做出相应调整。 在上面的例子中,对于程序中的标明地址值的变量app_start就需要这样一个flat_reloc结构的变量。 设若使用变量addr表示这个变量的存储空间,则addr的前30位记录了app_start离数据段开始位置的偏移量。 因为这个地址app_start是跳转代码执行地址,所以addr的后2位被赋值为flat_reloc_type_text(数值为0)。 8.4.2 reloc段机制 增加的4字节变量add

41、r被存放在称为reloc的段内。 程序中的所有的这样的变量被连续存放在可执行文件的头部reloc段中。 连接器连接程序的时候,变量app_start存储的是根据ld配置脚本计算出的地址,通常只是距离代码段、数据段或者bss段头的相对偏移量。 8.4.2 reloc段机制 在可执行文件加载时,可执行文件加载器遍历可执行文件头部的整个reloc段,对每一个其中的flat_reloc类型的变量首先根据offset的值得到它所对应的地址变量在数据段中的位置8.4.2 reloc段机制 然后根据type的值将这个数据段中的地址变量加上程序在物理内存中的实际的代码段,数据段和bss段的起始地址进行修正。

42、这样程序中的地址变量中保存的就是正确的物理地址。第第 8章章 目录目录1 缺少缺少mmu支持的内存管理支持的内存管理2 flat平模式内存管理平模式内存管理 2.1 3种内存管理模型种内存管理模型 2.2 标准标准linux的内存管理的内存管理 2.3 uclinux内存管理内存管理 2.4 uclinux内存管理的局限性内存管理的局限性3 内存管理模块的启动过程内存管理模块的启动过程4 可执行程序的加载可执行程序的加载 4.1 用户程序的内存分布用户程序的内存分布 4.2 reloc段机制段机制 4.3 flat可执行文件格式可执行文件格式 4.4 执行文件加载流程执行文件加载流程8.4.3

43、 flat可执行文件格式 为了适应uclinux中新的内存管理模式而引入了一种专为它所使用的flat可执行文件格式。 可执行文件头是前面描述的reloc段,紧接着是程序的文本段、数据段、未初始化数据段。 可执行文件加载到内存之后程序的堆栈段紧随在bss段的后面。8.4.3 flat可执行文件格式 flat可执行文件格式如图如图8-2所示所示。 但是注意在可执行文件被加载到内存的时候reloc段仍然在外存中。图 8-2 flat可执行文件格式8.4.3 flat可执行文件格式n 每实现一个可执行二进制文件格式,只要通过调用内核文件系统中的register_ binfmt( )函数将一个struc

44、t linux_binfmt类型的结构注册到内核中。n 这个结构中包含了这种文件格式的执行文件加载器,共享库加载器和core dump内存镜像文件生成器:include/linux/binfmts.h:struct linux_binfmt struct linux_binfmt *next; long *use_count; int (*load_binary)(struct linux_binprm *, struct pt_regs * regs); int (*load_shlib)(int fd); int (*core_dump)(long signr,struct pt_regs

45、 * regs); 其中load_binary函数是每一个二进制文件格式的实现都必须提供的,而其他的两个函数指针可以为空, 比如在uclinux中,由于内存管理的特定方式决定了不能使用代码的共享,所有的可执行程序都在编译时进行了静态库的连接 8.4.3 flat可执行文件格式 所以没有必要实现运行时动态加载连接库的功能,而且uclinux也没有实现core dump机制的支持, 所以它的flat可执行文件格式注册过程是这样的:static struct linux_binfmt flat_format = #ifndef module null,null,load_flat_binary,nu

46、ll,null#else null,&mode_use_count_,load_flat_binary,null,null#endif;register_binfmt(&flat_format); 注册完成后,内核文件系统在加载flat binary格式的可执行程序的时候自动调用load_flat_binary( )函数完成加载。第第 8章章 目录目录1 1 缺少缺少mmummu支持的内存管理支持的内存管理2 flat2 flat平模式内存管理平模式内存管理 2.1 32.1 3种内存管理模型种内存管理模型 2.2 2.2 标准标准linuxlinux的内存管理的内存管理 2.

47、3 uclinux2.3 uclinux内存管理内存管理 2.4 uclinux2.4 uclinux内存管理的局限性内存管理的局限性3 3 内存管理模块的启动过程内存管理模块的启动过程4 4 可执行程序的加载可执行程序的加载 4.1 4.1 用户程序的内存分布用户程序的内存分布 4.2 reloc4.2 reloc段机制段机制 4.3 flat4.3 flat可执行文件格式可执行文件格式 4.4 4.4 执行文件加载流程执行文件加载流程8.4.4 执行文件加载流程n 下面考察一个磁盘上的可执行文件是如何被加载到内存中并执行的。n 在已注册的flat可执行文件加载器被调用的时候,load_fl

48、at_binary()通过调用do_load_flat_binary()函数完成加载功能。8.4.4 执行文件加载流程 每一个应用程序在内核中对应一个进程,由一个进程控制块描述,也就是task_struct结构。 在调用do_load_flat_binary()的时候,已经为这个任务的运行创建了进程上下文环境,包括task_struct结构空间的分配和初始化。 8.4.4 执行文件加载流程 do_load_flat_binary()所要完成的就是从磁盘文件中按照可执行文件的格式读取可执行文件的各个段到ram中(正文段可以在rom中执行, 所以可能并不加载到ram内存中,而数据段、未初始化数据段

49、bss必须被加载到ram中,程序的堆栈空间也必须在ram中被分配)。 8.4.4 执行文件加载流程 并且由于flat binary的特性,还要在必要的时候进行reloc段的内存偏移量修正。 函数do_load_flat_binary()负责实际加载二进制文件的工作,其定义如下:inline int do_load_flat_binary(struct linux_binprm * prm,struct pt_regs * regs); 其中函数do_load_flat_binary( )的参数bprm是一个如下类型的结构变量: include/linux/binfmts.h: struct l

50、inux_binprm char buf128; #ifndef no_mm unsigned long pagemax_arg_pages; #else /*!no_mm */ char * envp,*argv;#endif /* !no_mm */ unsigned long p; int sh_bang; struct inode * inode; int e_uid,e_gid; int argc,envc; char * filename; /*name of binary */ unsigned long loader,exec; int dont_iput; /*binfmt

51、handler has put inode */;8.4.4 执行文件加载流程 它用来在加载各种可执行文件的时候存储参数信息。 在加载flat_bin格式的可执行文件的情况下,开始的buf域实际上存储了一个struct flat_hdr类型的结构变量(只使用了64个字节)。 envp和argv是执行程序时的环境变量指针列表和参数指针列表。 inode是可执行文件在文件系统中的inode指针,通过它可以得到这个文件的所有相关信息。 filename是这个可执行文件在磁盘上的文件名的字符串指针等。 其中buf域中保持的flat_hdr结构用于每个flat binary格式的磁盘文件的格式描述,定义

52、如下:include/asm-armnommu/flat.hstruct flat_hdr char magic4; unsigned long rev; unsigned long entry; /* offset of first executable instruction with text segment from beginning of file */unsigned long data_start; /*offset of data segment from beginning of file*/unsigned long bss_end; /*offset of end of

53、 bss segment from beginning of file */ /*(it is assumed that data_end through bss_end forms the bss segment.)*/ unsigned long stack_size; /*size of stack,in bytes */ unsigned long reloc_start; /* offset of relocation records from beginning of file */ unsigned long reloc_count; /* number of relocatio

54、n records */ unsigned long flags; unsigned long filler6; /*reserbered,set to zero*/; magic字符数组中必须包含字符串“bflt”,其他的各个变量描述了在这个flatbinary可执行文件中各个段的起始位置离文件头的偏移量和长度,并且说明了这个文件中的reloc结构数组离文件头的偏移量和数组项个数。 do_load_flat_binary()的流程如下: 1. 首先调用flush_o1d_exec()根据当前进程(current)的mm_struct结构创建一个新的mm_struct结构,它的引用计数器cou

55、nt设为1,占用内存页面数rss为0,tblock.rblock和tblock.next置为0, 然后将task_struct中的mm指针指向这个mm_struct结构,将原来的mm_struct以及它所包含的内存空间使用kfree()释放。 这个过程结束后,当前进程只有个新分配的mm_struct结构,它里面不包含任何内存空间。2. 调用do_mmap()尝试将根据调用参数bprm中的inode所代表的文件映射到当前进程管理的内存空间中: error = do_mmap(file, 0, code_len + data_len + bss_len + stack_len, prot_read|prot_exec|(hdr -flags & flat_flag_ram) prot_write : 0), 0 /*map_ * */, 0);0第二步第二步 其中进行映射的文件内容包括正文段、数据段、未初始化数据段,以及预先计算出来的需要保留大小的堆栈段。 映射的保护权限设置为可读、可执行,并且根据hdr中的调用参数flag中是否有flat_flag_ram被设置(表明是否希望在rom中直接执行还是装载到ram中再执行)而决定是否设置可写位。 do_mmap()根据flags参数决定是否真正进行ram映射,

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论