linux arm的存储分布那些事之一
linux arm 内存分布总览
上图是linux的arm的虚拟地址分布总览,我们按从低地址到高地址的顺序逐个描述,每项的描述包括如下的内容的组和:
地址范围大小,虚拟转物理的接口函数,各个区域对应的分配函数,该区域有什么作用,使用场合等等。
首先开始第一个区域:CPUvector page null pointer trap
该区域的大小是一个page页的大小,对于那些不支持中断向量重映射的cpu,该区域用来存储对应的中断向量表;
对于那些支持中断向量重映射的cpu,该区域用来扑获0地址的非法访问,即null指针。针对arm体系,他是支持中断向量重映射,该区域一般保留不用,用来扑获null指针。
第二个区域:应用程序地址空间
地址大小范围属于[0x1000, 0xbf000000],我知道每个应用进程都有如下几个段:text段即存储代码段,data段即存储初始化的数据段,bss段即存储未初始化的数据段,堆(malloc,free),栈(往下生长)。他们的地址分布如下:
图1
在应用程序加载到内存后,会为每个段,分一个vma的内核结构体,并且为每个段都分配了虚拟地址(虚拟地址和大小都存储在vma结构体中),当可执行
程序的各个段在加载的时候,就会给其分配虚拟地址,每个段对应内核的一个vma结构,程序所有段对应的vma,都挂在程序对应的进程的struct mm结构中,但并未给他分配实际的物理地址,待cpu实际去访问它时,才会去实际建立物理到vma指定的虚拟地址映射,并且将对应的段内容从elf文件中拷贝到相应的物理内存中。
譬如当cpu要访问text段时,这个时候并未建立相应的映射表,所以会产生page fault异常,从而在异常处理中,linux的内存管理系统会为其分配物理内存, 并从二进制可执行程序的elf文件读取text段到物理内存,并且为该进程对应的页表建立该物理页到虚拟地址的映射,这样cpu就可以访问该进程的text段,并且执行对应的指令了。
stack跟heap都一样,在cpu有实际的访问时,才会分配物理内存,并建立物理到对应的虚拟地址(在程序加载时,vma中就已经分配了虚拟地址)映射。这样做,就可以节省程序运行时实际物理内存的使用。而不是程序一开始就建立了所有物理到虚拟的映射,从而导致物理内存被大量不必要的消耗。
第三个区域:模块地址
该区域用来为内核模块分配地址,譬如在insmod一个驱动模块时,会通过如下的流程:sysinit_module-->load_module-->layout_and_allocate-->move_module-->module_alloc_update_bounds-->module_alloc来为模块的各个段分配虚拟地址
图2
line42可见:就指定了模块的虚拟地址范围为:[MODULES_VADDR,MODULES_END] = [0xbf000000,0xbfe00000],总计14MB。注意此时__vmalloc_node_range进行了实际的物理内存分配,并且建立了物理到虚拟地址的映射。
第四个区域: