文章目录

  • 1.虚拟地址空间简介
  • 2.虚拟地址空间布局
  • 参考文献

1.虚拟地址空间简介

虚拟地址空间(Virtual Address Space)是每一个程序被加载运行起来后,操作系统为进程分配的虚拟内存,它为每个进程提供了一个假象,即每个进程都在独占地使用主存。

每个进程所能访问的最大的虚拟地址空间由计算机的硬件平台决定,具体地说是由 CPU 的位数决定的。比如 32 位的 CPU 决定了虚拟地址空间的大小为 0 - 2322^{32}232-1,即 0x00000000 - 0xFFFFFFFF,也就是我们常说的 4 GB 虚拟内存空间。如果是 64 位的CPU,那么寻址范围是 0 - 2642^{64}264-1,即 0x0000000000000000 - 0xFFFFFFFFFFFFFFFF,共有 17 179 864 184 GB。

假设我们使用的是 32 位的硬件平台,4GB 的虚拟内存空间可以被用户程序完全占用吗?很显然,不行。因为除了用户进程,操作系统会独占一部分虚拟内存空间,用户进程只能使用操作系统分配给进程的地址空间,如果用户进程访问未经允许的地址空间,则会被操作系统判为非法请求,结果就是程序被操作系统强制结束。比如 Windows 下的“进程因非法操作需要关闭” 和 Linux 下的 “Segmentation fault”,一般都是由于进程访问了非法的内存地址。

对于 Linux,4GB 的虚拟地址空间的默认分配状态如下:

2.虚拟地址空间布局

C/C++程序为编译链接后生成可执行的二进制文件,由多个段组成,一般包含代码段、数据段和 BSS 段等。由于可执行文件段的数量较多,映射到虚拟地址空间时,由于段的大小往往并不是系统页大小的整数倍,多余部分也会占用一个页,这就会造成内存空间的浪费。当操作系统装载程序时,会进行优化,将多个相同属性的段合并成一个段进行装入,比如将相同权限的段合并成一个段进行映射。段的权限一般分为如下三种:
(1)以代码段 .text 为代表的可读可执行的段;
(2)以数据段 .data 和未初始化数据段 .bss 为代表的可读可写的段;
(3)以只读数据段 .rodata 为代表的只读的段。

比如 .text 和 .init 段,分别包含程序的可执行代码和初始化代码,操作系统在装载程序时可以将这两个段合并成一个段(Segment)进行映射,以节省内存空间。

说到合并后的段(Segment)和合并之前的段(Section),虽然中文叫法相同,但对于英文称谓是不同的。合并后的段是 Segment,是程序装载时的概念,合并之前的段是 Section,是程序链接时的概念,需要加以区分。系统按照 Segment 进行装载映射可执行文件而不是 Section。

可执行文件载入内存运行时,在 Linux 环境下的虚拟地址空间由一般有代码段、初始化数据段、未初始化数据段、堆和栈构成,如果程序使用了内存映射文件(比如共享库、共享文件),那么包含映射段。Linux 环境程序典型的内存布局如下图所示。

有时候,把 BSS 段与 Data 段看做成一个可读写的数据段也是可以的,这里做了区分。下面简要说明程序装载时相关的段。

代码段(Text Segment),用户存放 CPU 执行的机器指令,为防止指令被其它程序修改,代码段一般只读不可更改。比如,源码中的字符串常量存储于代码段,不可修改。

初始化数据段(Data Segment)又称为数据段,用于存储初始化的全局变量和Static变量,段大小在编译时确定,所以内存的分配属于静态内存分配。

未初始化数据段(BSS Segment,Block Started by Symbol),又称为BSS段,通常用来存放程序中未初始化的全局变量和 Static 变量。虽未显示初始化,但在程序载入内存执行时,由内核清 0,所以未显示初始化则默认为 0。BSS 段的大小也是在编译时确定,内存分配属于静态内存分配。

堆(Heap),用于保存程序运行时动态申请的内存空间,由开发人员手动申请,手动释放,若不手动释放,程序结束后由系统回收,生命周期是整个程序运行期间,比如使用malloc()或new申请的内存空间。堆的地址空间“向上增加”,即当堆上保存的数据越多,堆的地址就越高。堆的内存分配属于动态分配,一般运行时才知道分配的内存大小,并且堆可分配存活于函数之外的内存,在未显示调用free()或delete释放时,其生命周期为进程的生命周期。

映射段(Memory Mapping Segment),该区域内核将文件内容直接映射到内存。任何应用程序都可以请求该区域。Linux中通过mmap()系统调用,Windows中通过creatFileMapping()/MapViewOfFile()创建。文件I/O时内存映射方便并且高效,所以,它常用来加载动态库,还可以创建一种匿名映射,并不对应于文件,而用于程序数据。在Linux中,如果使用malloc()申请一块过大的内存,C库函数便会创建这种内存映射段,而不是使用堆内存。“过大”的内存指超过M_MMAP_THRESHOLD字节,默认128KB,可以通过mallopt()函数调整。映射段也属于动态分配。

栈(Stack),用于保存函数的局部变量(但不包括static声明的静态变量,静态变量存放在数据段或BSS段)、参数、返回值、函数返回地址以及调用者环境信息(比如寄存器值)等信息,由系统进行内存的管理,在函数完成执行后,系统自行释放栈区内存,不需要用户管理。整个程序的栈区的大小可以由用户自行设定,Windows默认的栈区大小为1M,可通过Visual Studio更改编译参数手动更改栈的大小。64bits的Linux默认栈大小为10MB,可通过命令ulimit -s临时修改。栈是一种“后进先出”(Last In First Out,LIFO)的数据结构,这意味着最后入栈的数据,将会是第一个出栈的数据。对于那些暂时存贮无需长期保存的信息来说,LIFO这种数据结构非常理想。在调用函数后,系统通常会清除栈上保存的信息。栈另外一个重要的特征是,它的地址空间“向下减少”,即当栈上保存的数据越多,栈的地址就越低。

内核空间(Kernel Space), 用于存储操作系统和驱动程序,用户空间用于存储用户的应用程序,二者不能简单地使用指针传递数据。当一个进程执行系统调用而陷入内核空间执行内核代码时,我们称进程处于内核运行态(或简称为内核态)。此时处理器处于特权级最高的(0级)内核代码中执行。当进程处于内核态时,执行的内核代码会使用当前进程的内核栈。每个进程都有自己的内核栈。当进程在执行用户自己的代码时,则称其处于用户运行态(用户态),即此时处理器在执行最低特权级(3级)用户代码。当正在执行用户程序而突然被中断程序中断时,此时用户程序也可以象征性地称为处于进程的内核态。因为中断处理程序将使用当前进程的内核栈,这与处于内核态进程的状态有些类似。

内存段的特点和区别如下。

段名 存储内容 分配方式 生长方向 读写特点 运行态
代码段 程序指令、字符串常量、虚函数表 静态分配 由低到高 只读 用户态
数据段 初始化的全局变量和静态变量 静态分配 由低到高 可读可写 用户态
BSS段 未初始化的全局变量和静态变量 静态分配 由低到高 可读可写 用户态
动态申请的数据 动态分配 由低到高 可读可写 用户态
映射段 动态链接库、共享文件、匿名映射对象 动态分配 由低到高 可读可写 用户态
局部变量、函数参数与返回值、函数返回地址、调用者环境信息 静态+动态分配 由高到低 可读可写 用户态
内核空间 操作系统、驱动程序 静态+动态分配 由低到高+由高到低 不能直接访问 内核态

由于内核空间包含内核栈和内核的数据段,所以内存地址生长方向既有由低到高(内核数据段),也有由高到低(内核栈)。关于读写的特点,由内核进行读写,用户程序不可直接访问。

下面以 C++ 为例,看一下常见变量所属的内存段。

#include <string.h>int a = 0;                         // a在数据段,0为文字常量,在代码段
char *p1;                       // BSS段,系统默认初始化为NULL
void main() {int b;                         // 栈char *p2 = "123456";         // 字符串"123456"在代码段,p2在栈上static int c = 0;             // c 在数据段const int d = 0;          // 栈static const int d;         // 数据段p1 = (char*)malloc(10);      // 分配的10字节在堆strcpy(p1,"123456");      // "123456"放在代码段,编译器可能会将它与p2所指向的"123456"优化成一个地方
}

以上所有代码,编译成二进制机器指令存放于代码段,不可修改。


参考文献

[1] linux内核空间和用户空间详解
[2] 程序或-内存区域分配(五个段)–终于搞明白了
[3] 进程内存分布剖析
[4] 深入理解计算机系统中文版[M].C1.7.3虚拟内存.P12-P14
[5] 深入理解计算机系统中文版[M].C9.7.2.Linux虚拟内存系统.P580-P581
[6] 俞甲子,石凡,等.程序员的自我修养——链接、装载与库[M].北京:电子工业出版社,2009-04.C6.4 进程虚存空间分布.P161-173

Linux 进程虚拟地址空间布局相关推荐

  1. linux进程--虚拟地址空间布局(十一)

    在多任务操作系统中,每个进程都运行在属于自己的内存沙盘中.这个沙盘就是虚拟地址空间(Virtual Address Space),在32位模式下它是一个4GB的内存地址块.在Linux系统中, 内核进 ...

  2. 【Linux 内核 内存管理】虚拟地址空间布局架构 ③ ( 内存描述符 mm_struct 结构体成员分析 | mmap | mm_rb | task_size | pgd | mm_users )

    文章目录 一.mm_struct 结构体成员分析 1.mmap 成员 2.mm_rb 成员 3.get_unmapped_area 函数指针 4.task_size 成员 5.pgd 成员 6.mm_ ...

  3. 【Linux 内核 内存管理】虚拟地址空间布局架构 ① ( 虚拟地址空间布局架构 | 用户虚拟地址空间划分 )

    文章目录 一.虚拟地址空间布局架构 二.用户虚拟地址空间划分 一.虚拟地址空间布局架构 在 646464 位的 Linux 操作系统中 , " ARM64 架构 " 并 不支持 6 ...

  4. 【Linux内核】虚拟地址空间布局架构

    虚拟地址空间布局架构(Linux内核学习) 1.Linux内核整体架构及子系统 内核对下管理硬件,对上通过运行时库对应用提供服务 用户空间 使用malloc()分配内存通过free()释放内存 内核空 ...

  5. Linux虚拟地址空间布局

     在多任务操作系统中,每个进程都运行在属于自己的内存沙盘中.这个沙盘就是虚拟地址空间(Virtual Address Space),在32位模式下它是一个4GB的内存地址块.在Linux系统中, ...

  6. linux的虚拟内存是4G,而每个进程都有自己独立的4G内存空间,怎么理解?进程虚拟地址4G指拥有4G的寻址能力,需要页表转换为实际物理地址,每个进程用到的内核是直接映射,地址的进程地址-3G的关系

    linux的虚拟内存是4G,而每个进程都有自己独立的4G内存空间,怎么理解? 问: linux的虚拟内存是4G,而每个进程都有自己独立的4G内存空间,怎么理解? 每个进程所拥有的4G独立的虚拟内存空间 ...

  7. Linux虚拟地址空间布局 - clover_toeic

    http://www.th7.cn/system/lin/201405/58078.shtml 在多任务操作系统中,每个进程都运行在属于自己的内存沙盘中.这个沙盘就是虚拟地址空间(Virtual Ad ...

  8. 用户的虚拟地址 linux 0 4gb,Linux驱动虚拟地址和物理地址的映射

    一般情况下,Linux系统中,进程的4GB内存空间被划分成为两个部分------用户空间和内核空间,大小分别为0~3G,3~4G. 用户进程通常情况下,只能访问用户空间的虚拟地址,不能访问到内核空间. ...

  9. 007-2虚拟地址空间布局

    二.虚拟地址空间布局 ARM64处理器不支持64位完全虚拟地址.在ARM64结构的linux内核中,内核虚拟地址和用户虚拟地址都是48位,并没有占用前面的16位.所有进程共享内核虚拟地址:ffff 0 ...

最新文章

  1. MapReduce DataJoin 链接多数据源
  2. go二维map_mirrorlang——从0设计二维内存寻址语言及vm(五.内存管理的思考)
  3. 【MM模块】Report 标准报表简介
  4. python多进程的使用方法
  5. centos6.8 如何编译php,centos 6.8 安装编译php7.1.2
  6. php操作xml文件,php xml文件操作代码(一)
  7. 自动文本摘要(automatic text summarization)目前的研究方法分类
  8. Matplotlib - 柱状图、直方图、条形图 bar() barh() 所有用法详解
  9. java线程池执行器_Java线程池ThreadPoolExecutor的使用
  10. Asp.net 面向接口可扩展框架之业务规则引擎扩展组件
  11. 安装inotify-tools,用inotifywait命令监听文件或目录的访问信息
  12. 【收藏】华三交换机配置方法及操作实例
  13. 【单目测距和双目测距比较】
  14. SPSS实战:多因素方差分析(以随机区组设计为例)
  15. 【历史上的今天】12 月 26 日:现代计算机鼻祖诞生;手机之父出生;美国史上最大信息盗窃案
  16. java百度地图坐标_腾讯地图坐标与百度地图坐标互相转换(PHP版、Java版)
  17. 电商运营中京东运营法则
  18. php中的,理解php中的print EOT
  19. 神经网络训练不起来,怎么办?
  20. STM32串口中断接收标记USART_RX_STA 学习

热门文章

  1. MySQL集群架构:MHA+MySQL-PROXY+LVS实现MySQL集群架构高可用/高性能-技术流ken
  2. spring boot 2.0.3+spring cloud (Finchley)6、配置中心Spring Cloud Config
  3. 在Ubuntu中部署并测试Fabric 1.0 Beta
  4. linux 自学系列:touch 命令
  5. erlang在mac上安装
  6. 电商价格战 谁才是最大受益者
  7. 从自卑的阴影中走出来
  8. H3C SecPath F100-C 防火墙默认配置
  9. Python 定义源码编码 (Source Encoding)
  10. matlab波形反白,基于MATLAB的海岸污染物浓度扩散实验分析