2026/4/17 1:02:53
网站建设
项目流程
代销网站源码,商务网站的功能,本科自考科目有哪些,flash网站什么意思目录x86-32 地址空间用户地址空间保留区代码段 .text初始化数据段 .data未初始化数据段 .bss堆 heap内存映射段(mmap)栈 Stack内核地址空间直接映射区 896M高端内存(HIGH_MEMORY)VMALLOC_OFFSETVMALLOC物理内存x86-32 地址空间
Linux内核一般将处理器的虚拟地址空间划分为两个…目录x86-32 地址空间用户地址空间保留区代码段 .text初始化数据段 .data未初始化数据段 .bss堆 heap内存映射段(mmap)栈 Stack内核地址空间直接映射区 896M高端内存(HIGH_MEMORY)VMALLOC_OFFSETVMALLOC物理内存x86-32 地址空间Linux内核一般将处理器的虚拟地址空间划分为两个部分底部比较大的部分用于用户进程顶部则专用于内核虽然在两个用户进程之间的上下文切换期间会改变下半部分但虚拟地址空间的内核部分总是保持不变也就是在进程切换的过程中页表的用户部分会被更新反映出新的内存布局内核部分的页表项保持不变因此无需刷新TLBTranslation Lookaside Buffer中与内核相关的条目Linux将虚拟地址空间划分为0-3G为用户空间3-4G为内核空间用户地址空间保留区位于虚拟地址空间的最低部分未赋予物理地址任何对它的引用都是非法的用于捕捉使用空指针和小整数型指针引用内存的异常情况它并不是一个单一的内存区域而是对地址空间中受到操作系统保护而禁止用户进程访问的地址区域的总称大多数操作系统中极小的地址通常都是不允许访问的如NULLC语言将无效指针赋值为0也是因为0地址上正常情况下不会存放有效的可访问数据因为历史原因前面128.28125MB属于保留空间代码段 .text代码段也称正文段或文本段通常用于存放程序执行代码(即CPU执行的机器指令)一般C语言执行语句都编译成机器代码保存在代码段通常代码段是可共享的因此频繁执行的程序只需要在内存中拥有一份拷贝即可代码段通常属于只读以防止其他程序意外地修改其指令(对该段的写操作将导致段错误) 某些架构也允许代码段为可写即允许修改程序代码段指令根据程序设计流程依次执行对于顺序指令只会执行一次(每个进程)若有反复则需使用跳转指令若进行递归则需要借助栈来实现代码段指令中包括操作码*和操作对象**(或对象地址引用)若操作对象是立即数(具体数值)将直接包含在代码中若是局部数据将在栈区分配空间然后引用该数据地址若位于BSS段和DATA数据段同样引用该数据地址代码段最容易受优化措施影响初始化数据段 .data数据段通常用于存放程序中已初始化且初值不为0的全局变量和静态局部变量数据段属于静态内存分配(静态存储区)可读可写数据段保存在目标文件中(在嵌入式系统里一般固化在镜像文件中)其内容由程序初始化DATA段与BSS段的区别如下BSS段不占用物理文件尺寸但占用内存空间数据段占用物理文件也占用内存空间。对于大型数组如int ar0[10000] {1 2 3 ...}和int ar1[10000]ar1放在BSS段只记录共有10000*4个字节需要初始化为0而不是像ar0那样记录每个数据1、2、3…运行时数据段和BSS段的整个区段通常称为数据区某些资料中数据段指代数据段 BSS段 堆未初始化数据段 .bssBSS(Block Started by Symbol)段中通常存放程序中以下符号:未初始化的全局变量和静态局部变量初始值为0的全局变量和静态局部变量(依赖于编译器实现)未定义且初值不为0的符号(该初值即common block的大小)C语言中未显式初始化的静态分配变量被初始化为0(算术类型)或空指针(指针类型)由于程序加载时BSS会被操作系统清零所以未赋初值或初值为0的全局变量都在BSS中BSS段仅为未初始化的静态分配变量预留位置在目标文件中并不占据空间这样可减少目标文件体积但程序运行时需为变量分配内存空间故目标文件必须记录所有未初始化的静态分配变量大小总和(通过start_bss和end_bss地址写入机器代码)当加载器(loader)加载程序时将为BSS段分配的内存初始化为0在嵌入式软件中进入main()函数之前BSS段被C运行时系统映射到初始化为全零的内存(效率较高)注意bss段通过start_bss和end_bss地址和代码段相关联注意尽管均放置于BSS段但初值为0的全局变量是强符号而未初始化的全局变量是弱符号若其他地方已定义同名的强符号(初值可能非0)则弱符号与之链接时不会引起重定义错误但运行时的初值可能并非期望值(会被强符号覆盖)因此定义全局变量时若只有本文件使用则尽量使用static关键字修饰否则需要为全局变量定义赋初值(哪怕0值)保证该变量为强符号以便链接时发现变量名冲突而不是被未知值覆盖某些编译器将未初始化的全局变量保存在common段链接时再将其放入BSS段在编译阶段可通过-fno-common选项来禁止将未初始化的全局变量放入common段此外由于目标文件不含BSS段故程序烧入存储器(Flash)后BSS段地址空间内容未知U-Boot启动过程中将U-Boot的Stage2代码(通常位于lib_xxxx/board.c文件)搬迁(拷贝)到SDRAM空间后必须人为添加清零BSS段的代码而不可依赖于Stage2代码中变量定义时赋 0 值堆 heap堆用于存放进程运行时动态分配的内存段可动态扩张或缩减堆中内容是匿名的不能按名字直接访问只能通过指针间接访问当进程调用malloc/new等函数分配内存时新分配的内存动态添加到堆上(扩张)当调用free/delete等函数释放内存时被释放的内存从堆中剔除(缩减)分配的堆内存是经过字节对齐的空间以适合原子操作堆管理器通过链表管理每个申请的内存由于堆申请和释放是无序的最终会产生内存碎片堆内存一般由应用程序分配释放回收的内存可供重新使用若程序员不释放程序结束时操作系统可能会自动回收堆的末端由break指针标识当堆管理器需要更多内存时可通过系统调用br k和 sbrk来移动break指针以扩张堆一般由系统自动调用使用堆时经常出现两种问题释放或改写仍在使用的内存(内存破坏)未释放不再使用的内存(内存泄漏)当释放次数少于申请次数时可能已造成内存泄漏内存映射段(mmap)此处内核可以将文件的内容直接映射到内存任何应用程序都可通过Linux的mmap()系统调用或Windows的CreateFileMapping/MapViewOfFile请求这种映射内存映射是一种方便高效的文件I/O方式 因而被用于装载动态共享库用户也可创建匿名内存映射该映射没有对应的文件可用于存放程序数据在Linux中若通过malloc请求一大块内存C运行库将创建一个匿名内存映射而不使用堆内存大块意味着比阈值MMAP_THRESHOLD还大缺省为128KB可通过mallopt()调整栈 Stack栈又称堆栈由编译器自动分配释放行为类似数据结构中的栈(先进后出)堆栈主要有三个用途为函数内部声明的非静态局部变量(C语言中称自动变量)提供存储空间记录函数调用过程相关的维护性信息称为栈帧(Stack Frame)或过程活动记录(Procedure Activation Record) 它包括函数返回地址不适合装入寄存器的函数参数及一些寄存器值的保存除递归调用外堆栈并非必需因为编译时可获知局部变量参数和返回地址所需空间并将其分配于BSS段(也就是函数返回地址不适合装入寄存器的函数参数寄存器的保存值)临时存储区用于暂存长算术表达式部分计算结果或alloca()函数分配的栈内内存持续地重用栈空间有助于使活跃的栈内存保持在CPU缓存中从而加速访问进程中的每个线程都有属于自己的栈向栈中不断压入数据时若超出其容量就会耗尽栈对应的内存区域从而触发一个页错误此时若栈的大小低于堆栈最大值RLIMIT_STACK(通常是8M)则栈会动态增长程序继续运行映射的栈区扩展到所需大小后不再收缩Linux中ulimit -s命令可查看和设置堆栈最大值当程序使用的堆栈超过该值时 发生栈溢出(Stack Overflow)程序收到一个段错误(Segmentation Fault)内核地址空间直接映射区 896M所谓的直接映射区就是这一块空间是连续的和物理内存是非常简单的映射关系其实就是虚拟内存地址减去3G就得到物理内存的位置__pa(vaddr)返回与虚拟地址vaddr相关的物理地址__va(paddr)则计算出对应于物理地址paddr的虚拟地址// PAGE_OFFSET 3G 0x0c0000000#define__va(x)((void*)((unsignedlong)(x)PAGE_OFFSET))#define__pa(x)__phys_addr((unsignedlong)(x))#define__phys_addr(x)__phys_addr_nodebug(x)#define__phys_addr_nodebug(x)((x)-PAGE_OFFSET)这896M还需要仔细分解在系统启动的时候物理内存的前1M已经被占用了从1M开始加载内核代码段然后就是内核的全局变量、BSS等也是ELF里面涵盖的这样内核的代码段全局变量BSS也就会被映射到3G后的虚拟地址空间里面。具体的物理内存布局可以查看cat/proc/iomem.....00100000-bffdbfff:System RAM01000000-01a02fff:Kernel code01a03000-021241bf:Kernel data02573000-02611fff:Kernel bss25000000-34ffffff:Crash kernel.....在内核运行的过程中如果碰到系统调用创建进程内核的进程管理代码会将实例创建在3G至3G896M的虚拟空间中当然也会被放在物理内存里面的前896M里面相应的页表也会被创建如果涉及内核栈的分配内核的进程管理的代码会将内核栈创建在3G至3G896M的虚拟空间中当然也就会被放在物理内存里面的前896M里面相应的页表也会被创建高端内存(HIGH_MEMORY)x86-32下特有的x64下没有这个东西因为内核虚拟空间只有1G无法管理全部的内存空间当内核想访问高于896MB物理地址内存时从0xF8000000 ~ 0xFFFFFFFF地址空间范围内找一段相应大小空闲的逻辑地址空间借用一会借用这段逻辑地址空间建立映射到想访问的那段物理内存即填充内核PTE页面表临时用一会用完后归还这样别人也可以借用这段地址空间访问其他物理内存实现了使用有限的地址空间访问所有所有物理内存VMALLOC_OFFSET系统会在low memory和VMALLOC区域留8M防止访问越界。因此假如理论上vmalloc size有300M实际可用的也是只有292Minclude/asm-x86/pgtable_32.h#defineVMALLOC_OFFSET(8*1024*1024)这个缺口可用作针对任何内核故障的保护措施如果访问越界地址即无意地访问物理上不存在的内存区则访问失败并生成一个异常报告该错误如果vmalloc区域紧接着直接映射那么访问将成功而不会注意到错误在稳定运行的情况下肯定不需要这个额外的保护措施但它对开发尚未成熟的新内核特性是有用的VMALLOC虚拟内存中连续、但物理内存中不连续的内存区可以在vmalloc区域分配该机制通常用于用户过程内核自身会试图尽力避免非连续的物理地址通常会成功因为大部分大的内存块都在启动时分配给内核那时内存的碎片尚不严重但在已经运行了很长时间的系统上在内核需要物理内存时就可能出现可用空间不连续的情况此类情况主要出现在动态加载模块时include/asm-x86/pgtable_32.h#defineVMALLOC_START(((unsignedlong)high_memory\2*VMALLOC_OFFSET-1)~(VMALLOC_OFFSET-1))#ifdefCONFIG_HIGHMEM#defineVMALLOC_END(PKMAP_BASE-2*PAGE_SIZE)#else#defineVMALLOC_END(FIXADDR_START-2*PAGE_SIZE)#endifvmalloc区域的起始地址取决于在直接映射物理内存时使用了多少虚拟地址空间内存因此也依赖于上文的high_memory变量内核还考虑到下述事实即两个区域之间有至少为VMALLOC_OFFSET的一个缺口而且vmalloc区域从可被VMALLOC_OFFSET整除的地址开始VMALLOC_START到VMALLOC_END之间称为内核动态映射空间也即内核想像用户态进程一样malloc申请内存在内核里面可以使用vmalloc假设物理内存里面896M到1.5G之间已经被用户态进程占用了并且映射关系放在了进程的页表中内核vmalloc的时候只能从分配物理内存1.5G开始就需要使用这一段的虚拟地址进行映射映射关系放在专门给内核自己用的页表里面物理内存物理内存的前4KiB是第一个页帧一般会忽略通常保留给BIOS使用接下来的640KiB原则是可以用的但是也不能用于内核加载原因是 该区域之后紧邻的区域由系统保留用于映射各种ROM通常是系统BIOS和显卡ROM因为不能向映射的ROM区域写入数据但是内核一定会装载在一个连续的内存区中如果要从4KiB处作为起始位置装载内核镜像则要求内核必须要小于640KiB在x86架构中内存有三种区域ZONE_DMAZONE_NORMALZONE_HIGHMEM 不同类型的区域适合不同需要在32位系统中结构中1G(内核空间)/3G(用户空间) 地址空间划分时三种类型的区域如下:ZONE_DMA内存开始的16MBZONE_NORMAL16MB ~ 896MBZONE_HIGHMEM896MB~ 结束ZONE_DMA该区域的物理页面专门供I/O设备的DMA使用之所以需要单独管理DMA的物理页面是因为DMA使用物理地址访问内存不经过MMU并且需要连续的缓冲区所以为了能够提供物理上连续的缓冲区必须从物理地址空间专门划分一段区域用于DMADMA技术就是我们在主板上放一块独立的芯片。在进行内存和I/O设备的数据传输的时候我们不再通过CPU来控制数据传输而直接通过DMA控制器DMA Controller简称DMACZONE_NORMALZONE_NORMAL的范围是16M~896M该区域的物理页面是内核能够直接使用的属于直接映射区ZONE_HIGHMEM896M~结束是系统中剩下的可用内存但因为内核的地址空间有限这部分内存不直接映射到内核