虚拟内存
在我们编写程序的时候,我们使用的是虚拟内存布局,它是建立在真实的物理内存之上,虚拟内存一般是比物理内存要大,并且每个进程都享有独立的虚拟内存
所以我们要明白我们在程序中使用的是虚拟内存,虚拟内存经过一些内存映射,才能被映射到真实的物理内存
局部性
空间局部性(Spatial locality):是指程序倾向于访问在最近访问过的内存地址附近的内存(由于指令是顺序执行的,且有时会按顺序处理数据结构)。
时间局部性(Temporal locality):是指程序倾向于在不久的将来再次访问最近刚访问过的内存地址(由于循环)。
由于这两种局部性特征,即访问局部性,使得程序即使只有部分地址空间在物理内存中,也能正常执行,然后就有了虚拟内存这种抽象,竟然程序能够正常执行,那我就让他认为他占有整个地址空间,这方便了程序的编写,我们无需关心到底哪些物理地址可用
虚拟内存映射
虚拟内存的规划之一是将每个程序使用的内存切割成小型的、固定大小的“页”(page)单元。相应地,将 RAM 划分成一系列与虚存页尺寸相同的页帧。任一时刻,每个程序仅有部分页需要驻留在物理内存页帧中。这些页构成了所谓驻留集(resident set)。程序未使用的页拷贝保存在交换区(swap area)内—这是磁盘空间中的保留区域,作为计算机 RAM 的补充— 仅在需要时才会载入物理内存。
简单来说,我们将虚拟内存划分为 页, 将物理内存划分为 页帧,这两者大小相同,并且能够从 页 映射到 页帧,然后仅有一部分页驻留在物理内存中,其他的页保留在磁盘的swap分区,当程序在物理内存中找不到所需要的页时,会发生页面错误,并进行页面替换
虚拟内存到物理内存的映射就通过数据结构 页表 实现,内核为每个进程维护一张页表
页表中的每个条目要么指出一个虚拟页面在 RAM 中的所在位置,要么
表明其当前驻留在磁盘上。
以下这段话相当重要,后面的进程内存布局会说到
在进程虚拟地址空间中,并非所有的地址范围都需要页表条目。通常情况下,由于可能存在大段的虚拟地址空间并未投入使用,故而也无必要为其维护相应的页表条目。若进程试图访问的地址并无页表条目与之对应,那么进程将收到一个 SIGSEGV 信号。
进程内存布局
进程内存布局是建立在虚拟内存之上的
图中灰色的部分表示页表中没有对应的条目,即没有在物理内存或磁盘中,不可用
文本段
文本段包含了进程运行的程序机器语言指令。文本段具有只读属性,以防止进程通过错误指针意外修改自身指令。
你可以理解为这里存放的是程序的代码
数据段
初始化数据段(用户初始化数据段)
初始化数据段包含显式初始化的全局变量和静态变量。
初始化数据段中的数据会被存储在磁盘空间中,即存放在可执行文件中
未初始化数据段(bss) (零初始化数据段)
未初始化数据段包含了未进行显式初始化的全局变量和静态变量。程序启动之前,系统将本段内所有内存初始化为 0。出于历史原因,此段常被称为 BSS 段。
这句话说明,没有进行显示初始化的全局变量或静态变量 在程序启动之前 会自动初始化为0(不是编译阶段),编译阶段只需记录未初始化数据段的位
置及所需大小,直到运行时再由程序加载器来分配这一空间,所以bss段的数据没有存储到磁盘空间中
栈段
栈(stack)是一个动态增长和收缩的段,由栈帧(stack frames)组成。系统会为每个当前调用的函数分配一个栈帧。栈帧中存储了函数的局部变量(所谓自动变量)、实参和返回值。
栈的增长是从高地址项低地址增长的,也就是向堆的方向增长
如下,是一个栈帧的例子,最开始是main函数的栈帧,在main函数中调用其他函数,会一次将其加入栈帧,栈指针指向栈顶,程序在栈顶的栈帧中执行
栈帧中包含的信息:
- 函数实参和局部变量,这些会在栈帧销毁后自动销毁,即函数结束后自动销毁
- (函数)调用的链接信息,保留当前函数执行的寄存器的副本,方便返回后继续执行
堆
堆(heap)是可在运行时(为变量)动态进行内存分配的一块区域。堆顶端称作program break
堆的顶端叫做 program break,指明堆的边界,可以通过brk()或者sbrk()系统调用进行调整
标签:初始化,程序,内存,linux,进程,虚拟内存,页表 From: https://www.cnblogs.com/dylaris/p/18460345