在多任务操作系统中,每个进程都运行在属于自己的内存沙盘中 。这个沙盘就是虚拟地址空间(Virtual Address Space),在32位模式下它是一个4GB的内存地址块 。在linux系统中, 内核进程和用户进程所占的虚拟内存比例是1:3,而windows系统为2:2(通过设置Large-Address-Aware Executables标志也可为1:3) 。这并不意味着内核使用那么多物理内存,仅表示它可支配这部分地址空间,根据需要将其映射到物理内存 。
虚拟地址通过页表(Page Table)映射到物理内存,页表由操作系统维护并被处理器引用 。内核空间在页表中拥有较高特权级,因此用户态程序试图访问这些页时会导致一个页错误(page fault) 。在Linux中,内核空间是持续存在的,并且在所有进程中都映射到同样的物理内存 。内核代码和数据总是可寻址,随时准备处理中断和系统调用 。与此相反,用户模式地址空间的映射随进程切换的发生而不断变化 。
Linux进程在虚拟内存中的标准内存段布局如下图所示:
文章插图
其中,用户地址空间中的蓝色条带对应于映射到物理内存的不同内存段,灰白区域表示未映射的部分 。这些段只是简单的内存地址范围,与Intel处理器的段没有关系 。
上图中Random stack offset和Random mmap offset等随机值意在防止恶意程序 。Linux通过对栈、内存映射段、堆的起始地址加上随机偏移量来打乱布局,以免恶意程序通过计算访问栈、库函数等地址 。execve(2)负责为进程代码段和数据段建立映射,真正将代码段和数据段的内容读入内存是由系统的缺页异常处理程序按需完成的 。另外,execve(2)还会将BSS段清零 。
用户进程部分分段存储内容如下表所示(按地址递减顺序):
名称
存储内容
栈
局部变量、函数参数、返回地址等
堆
动态分配的内存
BSS段
未初始化或初值为0的全局变量和静态局部变量
数据段
已初始化且初值非0的全局变量和静态局部变量
代码段
可执行代码、字符串字面值、只读变量
在将应用程序加载到内存空间执行时,操作系统负责代码段、数据段和BSS段的加载,并在内存中为这些段分配空间 。栈也由操作系统分配和管理;堆由程序员自己管理,即显式地申请和释放空间 。
BSS段、数据段和代码段是可执行程序编译时的分段,运行时还需要栈和堆 。
以下详细介绍各个分段的含义 。
1 内核空间内核总是驻留在内存中,是操作系统的一部分 。内核空间为内核保留,不允许应用程序读写该区域的内容或直接调用内核代码定义的函数 。
2 栈(stack)栈又称堆栈,由编译器自动分配释放,行为类似数据结构中的栈(先进后出) 。堆栈主要有三个用途:
- 为函数内部声明的非静态局部变量(C语言中称“自动变量”)提供存储空间 。
- 记录函数调用过程相关的维护性信息,称为栈帧(Stack Frame)或过程活动记录(Procedure Activation Record) 。它包括函数返回地址,不适合装入寄存器的函数参数及一些寄存器值的保存 。除递归调用外,堆栈并非必需 。因为编译时可获知局部变量,参数和返回地址所需空间,并将其分配于BSS段 。
- 临时存储区,用于暂存长算术表达式部分计算结果或alloca()函数分配的栈内内存 。
Linux中ulimit -s命令可查看和设置堆栈最大值,当程序使用的堆栈超过该值时, 发生栈溢出(Stack Overflow),程序收到一个段错误(Segmentation Fault) 。注意,调高堆栈容量可能会增加内存开销和启动时间 。
堆栈既可向下增长(向内存低地址)也可向上增长, 这依赖于具体的实现 。本文所述堆栈向下增长 。
堆栈的大小在运行时由内核动态调整 。
3 内存映射段(mmap)此处,内核将硬盘文件的内容直接映射到内存, 任何应用程序都可通过Linux的mmap()系统调用或Windows的CreateFileMApping()/MapViewOfFile()请求这种映射 。内存映射是一种方便高效的文件I/O方式,因而被用于装载动态共享库 。用户也可创建匿名内存映射,该映射没有对应的文件, 可用于存放程序数据 。在 Linux中,若通过malloc()请求一大块内存,C运行库将创建一个匿名内存映射,而不使用堆内存 。”大块” 意味着比阈值 MMAP_THRESHOLD还大,缺省为128KB,可通过mallopt()调整 。
推荐阅读
- 一文看懂“超融合”和“虚拟化”的区别
- Linux 升级gcc g++ gdb glibc教程
- 这27个常用Linux命令,我建议大家都熟练掌握,工作中够用了
- linux中的重定向
- linux如何设置主机名称
- linux内核设备树及编译
- CentOS继任者Rocky Linux 8.5新特性
- 小白黑客如何使用Kali Linux中间人获取内网计算机图片信息
- Intel|首次使用EUV工艺 Intel “4nm”酷睿点亮:成功运行Win/Linux/Chrome三大系统
- Linux 中 ss 命令的使用实例