当前位置: 首页 > news >正文

进程的内存映像,只读区,可读写区,堆,共享库,栈详解

我们把一个进程的虚拟地址空间想象成一栋专门为它建造的摩天大楼

  • 虚拟地址空间:这栋摩天大楼的设计图纸。在32位系统上,图纸上规划了从0层到大约40亿层(4GB)的巨大空间。每个进程都拿到一份一模一样的图纸,它们都以为自己独占了整栋大楼。
  • 物理内存:现实世界中真正可用的建筑材料。所有进程的“大楼”实际上都是用这些有限的材料搭建的。

摩天大楼的楼层规划(从低地址到高地址)

这栋4GB高的大楼,被严格地分成了两个大区:用户区(低3GB) 和 内核区(高1GB)

内核区 (Kernel Space) - “顶层豪华总统套房” (高地址区域)
  • 位置:大楼的最顶层,大约从3GB到4GB的位置。
  • 住户操作系统内核
  • 存放内容
    • 进程的“身份证” (PCB):每个租户(进程)的个人档案都存放在这里,由大楼管理员(OS)统一保管。
    • 大楼的“管理规定” (OS代码):比如“租户调度规则”、“安保条例”等,都存放在这里。
    • 大楼的“总地图” (页表):记录了每个租户的“设计图纸”是如何映射到“现实建材”上的。
  • 权限戒备森严。普通租户(用户进程)绝对禁止入内。只有在需要请求管理员服务时(通过系统调用),才能在管理员的陪同下进入一小部分指定区域。

用户区 (User Space) - “租户的私人生活空间” (低地址区域)

这是分配给每个进程自由使用的区域。它也被精心划分成了几个功能区。

1. 只读区(代码段 .text + 只读数据段 .rodata) - “图书馆和展览馆”
  • 位置:用户区的最底层。
  • 存放内容
    • 代码段 (.text): 你写的C语言代码被编译成的机器指令(程序的“说明书”),就存放在这里。这些指令是规定好的,运行时不能被修改。
    • 只读数据段 (.rodata): 存放常量。比如C语言里用 const 关键字定义的全局变量,或者字符串字面量(如"Hello, World!")。它们就像展览馆里的展品,只能看,不能摸(不能修改)。
  • 宏定义 vs. const常量
    • #define PI 3.14:这只是一个“文本替换”的宏。在预编译阶段,代码里所有的PI都会被直接换成3.14。它不会在内存中单独占据一个格子,而是融入了指令之中。
    • const float pi = 3.14;:这定义了一个真正的常变量。系统会为pi在只读数据区分配一个格子,并把3.14存进去。你可以获取它的地址,但不能修改它的值。
2. 可读写数据区(数据段 .data + .bss段) - “储藏室”
  • 位置:只读区的上方。
  • 存放内容:那些在程序整个生命周期内都存在的、且可以被修改的变量。
    • 数据段 (.data): 存放已初始化的全局变量和静态变量。比如 int global_var = 10;。程序启动时,这个10就会被加载到这里。
    • BSS段 (.bss): 存放未初始化的全局变量和静态变量。比如 static int static_var;。为了节省可执行文件的大小,这些变量的值(默认为0)不会被存在文件里,只是记录了需要多大的空间。程序启动时,操作系统会在这里清出一片内存,并全部填充为0。
  • 特点:这片区域的大小在程序加载时就固定了,运行时不会改变。
3. 堆 (Heap) - “可自由搭建的乐高区”
  • 位置:数据区的上方。
  • 生长方向:从低地址向高地址增长。
  • 存放内容:由程序员手动管理的内存。在C语言中,通过 malloc() 申请的内存就在堆上;通过 free() 释放。
  • 特点:大小动态变化。你需要多少,就申请多少。忘了释放,就会造成内存泄漏。堆是程序中最灵活、也最容易出问题的地方。
4. 共享库 (Shared Libraries) - “公共设施层”
  • 位置:在堆和栈之间的一大片区域。
  • 存放内容:存放一些系统提供的、多个进程可以共享的库函数代码。比如 printfscanf 等函数的机器指令。
  • 特点:为了节省内存,这些公共的库函数在物理内存中可能只存在一份,但被映射到了多个进程的虚拟地址空间中。
5. 栈 (Stack) - “临时工作间”
  • 位置:用户区的最顶层。
  • 生长方向:从高地址向低地址增长。这种设计可以使堆和栈有最大的发展空间,只要它们不“撞车”就行。
  • 存放内容:与函数调用相关的一切。
    • 局部变量:在函数内部定义的变量。
    • 函数参数:调用函数时传递的参数。
    • 返回地址:函数执行完后,应该跳回到哪里继续执行。
  • 特点自动管理。调用一个函数时,系统会自动在栈顶“压入”一块内存(称为一个栈帧)来存放这个函数相关的所有数据。当函数返回时,这块内存会被自动“弹出”并销毁。如果函数调用嵌套太深(比如无限递归),栈会不停地向低地址增长,最终可能会用尽所有空间,导致栈溢出 (Stack Overflow)

必会题与详解

题目:给定以下C语言代码,请指出变量g_varg_uninit_varPIc_vars_varl_var 以及 p_var 所指向的内存,分别存储在进程内存映像的哪个区域?

#include <stdio.h>
#include <stdlib.h>#define PI 3.14159const int c_var = 10;
int g_var = 20;
int g_uninit_var;void func() {int l_var = 40;printf("Local variable: %d\n", l_var);
}int main() {static int s_var = 30;int *p_var = (int*)malloc(sizeof(int));*p_var = 50;func();free(p_var);return 0;
}

答案详解

  • g_var (全局已初始化变量):存储在 数据段 (.data)。它在程序整个生命周期都存在,并且有初始值。

  • g_uninit_var (全局未初始化变量):存储在 BSS段 (.bss)。它也在整个程序生命周期存在,但没有初始值,程序加载时会被初始化为0。

  • PI (宏定义常量)不存储在内存的任何区域。它是一个宏,在预编译阶段,代码中所有出现PI的地方都会被直接替换为文本3.14159。它最终会成为CPU指令的一部分(比如 mov rax, 3.14159)。

  • c_var (const全局变量):存储在 只读数据段 (.rodata)。它是一个常量,程序运行时不能被修改。

  • s_var (静态局部变量):存储在 数据段 (.data)。虽然它定义在main函数内部,但static关键字改变了它的生命周期,使其与程序的生命周期一样长。因为它有初始值,所以存放在.data段。

  • l_var (局部变量):存储在 栈 (Stack) 上。它是func函数的局部变量,当func被调用时,l_varfunc的栈帧中被创建;当func返回时,它随着栈帧的销毁而被释放。

  • p_var 所指向的内存:变量p_var本身是一个指针,它是一个局部变量,所以p_var这个指针变量本身存储在栈 (Stack) 上。但是,它所指向的那块通过malloc分配的、大小为sizeof(int)的内存,是存储在 堆 (Heap) 上的。


文章转载自:
http://baggageman.wanhuigw.com
http://antifluoridationist.wanhuigw.com
http://apa.wanhuigw.com
http://ataraxia.wanhuigw.com
http://bolshevistic.wanhuigw.com
http://avidly.wanhuigw.com
http://bemazed.wanhuigw.com
http://charpit.wanhuigw.com
http://bibitory.wanhuigw.com
http://annates.wanhuigw.com
http://belying.wanhuigw.com
http://bruno.wanhuigw.com
http://amphimacer.wanhuigw.com
http://centner.wanhuigw.com
http://barracuda.wanhuigw.com
http://calamographer.wanhuigw.com
http://acquit.wanhuigw.com
http://advertise.wanhuigw.com
http://celeriac.wanhuigw.com
http://aeneas.wanhuigw.com
http://badman.wanhuigw.com
http://auditress.wanhuigw.com
http://aciform.wanhuigw.com
http://abreaction.wanhuigw.com
http://benzophenone.wanhuigw.com
http://carnose.wanhuigw.com
http://chengchow.wanhuigw.com
http://botfly.wanhuigw.com
http://bail.wanhuigw.com
http://callipers.wanhuigw.com
http://www.dtcms.com/a/281370.html

相关文章:

  • 23.将整数转换为罗马数字
  • 磁悬浮轴承的“眼睛”:位移测量核心技术深度解析
  • 【监控实战】Grafana自动登录如何实现
  • 关于tresos Studio(EB)的MCAL配置之FEE
  • dataLoader是不是一次性的
  • 文心一言4.5企业级部署实战:多模态能力与Docker容器化测评
  • 告别手动迁移:使用 PowerShell 一键导出 IIS 配置,让服务器迁移更轻松
  • LSA链路状态通告
  • QT——文件选择对话框 QFileDialog
  • Transformer是什么 - 李沐论文《Attention Is All You Need》精读
  • 内网穿透实例:在 NAT 环境下通过 FRP 配置 ThinLinc 远程桌面 实现外网登录
  • zynq串口的例子
  • 自己训练大模型?MiniMind 全流程解析 (一) 预训练
  • 如何科学做好企业软件许可优化?
  • Day03_C语言网络编程20250715
  • Datawhale AI 夏令营第一期(机器学习方向)Task2 笔记:用户新增预测挑战赛 —— 从业务理解到技术实现
  • 如何理解flex: 1 1 50%
  • 【Unity基础】Unity中元素的层级排序
  • WPF,Winform,HTML5网页,哪个UI开发速度最快?
  • 线程(一) linux
  • 前端医疗生命体征
  • MIPI DSI(四) video 和 command 模式
  • 比较vue和react框架
  • Windows 下 Visual Studio 开发 C++ 项目的部署流程
  • Spring Boot 启动原理揭秘:从 main 方法到自动装配
  • 判断QMetaObject::invokeMethod()里的函数是否调用成功
  • Process Lasso:提升电脑性能的得力助手
  • C++20 协程参考手册详解 - 源自 cppreference.com
  • Expression 类的静态方法
  • PostgreSQL 大数据量(超过50GB)导出方案