编程高手必学的内存知识
海纳
华为编译器高级专家,原 Huawei JDK 团队负责人
20674 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 33 讲
编程高手必学的内存知识
15
15
1.0x
00:00/00:00
登录|注册

03 | 内存布局:应用程序是如何安排数据的?

你好,我是海纳。
在前边的课程里,我们学习了计算机物理地址和虚拟地址的概念。有了虚拟地址之后,运行在系统里的用户进程看到的地址空间范围,都是虚拟地址空间范围(32 位计算机的地址范围是 4G;64 位计算机的地址范围是 256T)。这样的话,就不用再担心内存地址不够用,以及与其他进程之间产生内存地址冲突的问题了。
前面几节课,我们关注的是如何解决进程之间的冲突,从这节课起,我们一起来看下进程内部的虚拟内存布局,或者说单一进程是如何安排自己的各种数据的。
学习了这节课,你将理解全局变量和 static 变量在内存中的位置以及初始化时机,在这个基础上,你还将明白在栈上创建对象和在堆上创建对象有什么不同等问题。这些问题的核心都可以归结到“内存是如何布局的”这个问题上,所以只有深刻地掌握了内存布局的知识,你才能做到以不变应万变,面对各种具体问题才有了分析的方向和思路,进而,你才能写出更加“内存安全”的代码。
首先,我们来看一下,对于一个典型的进程来说,它的内存空间是由哪些部分组成的?每个部分又被安置在空间的什么位置?

抽象内存布局

我们知道,CPU 运行一个程序,实质就是在顺序执行该程序的机器码。一个程序的机器码会被组织到同一个地方,这个地方就是代码段
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入介绍了计算机内存布局的相关知识,重点讨论了进程内部的虚拟内存布局以及在Linux系统下的内存布局。文章首先介绍了进程内存空间的基本组成部分,包括代码段、数据段、BSS段、堆空间和栈空间。随后详细解释了32位机器上Linux进程的内存布局,包括保留区、代码段、BSS段、堆空间、栈空间和内存映射区域。此外,还介绍了内存地址随机化的选项及其影响。文章通过图示和实际命令展示了内存布局的具体情况,帮助读者深入理解了内存布局的概念和实际应用。 在64位系统下,地址空间被分割成用户空间和内核空间,其中用户空间占据了低128T的空间。文章还介绍了堆空间的管理,包括sbrk和mmap系统调用的使用方法及其功能。sbrk函数通过增加内核的brk变量来改变堆的大小,而mmap函数则可以用于创建共享内存、文件映射区域以及申请堆内存。对于mmap函数的参数和功能进行了详细解释,使读者能够更好地理解其使用场景和方法。 总的来说,本文通过清晰的图示和详细的解释,使读者对进程内存布局有了全面的认知。读者可以从中了解到进程内存空间的基本组成和在Linux系统下的内存布局情况,为进一步深入学习和应用相关知识提供了基础和指导。文章还介绍了mmap系统调用的其他应用场景,包括共享匿名映射在父子进程间通信的运用,为读者提供了更多实际应用的例子和思考。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《编程高手必学的内存知识》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(29)

  • 最新
  • 精选
  • êwěn
    老师,文章里说mmap也可以修改堆大小,那映射的区域为啥不属于堆呢?

    作者回复: 很有意思的问题:)我们课里也介绍了mmap的几个作用。其中私有匿名映射用于分配空间,这样分配的空间还是从映射区域里分配的,但是从功能上说呢,它和使用sbrk所得到的内存区域又是相同的。所以这一部分内存我们还是倾向于称呼它为堆。但你要知道的是,其实严格来说,这是从文件映射区“偷”来的。

    2021-11-02
    3
    13
  • 大脑壳
    您这里所描述的linux进程的内存布局,和JVM的内存布局是一个维度吗?JVM内部的堆栈和linxu系统的堆栈是一个什么样的关系?请老师指教

    作者回复: 不是。JVM把很多东西都托管了。它和linux进程布局很不相同。但是JVM仍然有很多设计深受传统的进程内存使用的影响。先学好Linux进程布局吧。JVM我们慢慢讲。

    2021-11-17
    6
  • 慢动作
    从执行视角那张图,代码段是可读可执行,数据段是可读可写,把链接器视角的.bss对应为数据段了,后面的图又把bss单独列出来,这是为什么?bss存了未初始化的数据,只有大小,那初始化后数据放哪里?

    作者回复: bss段在磁盘上的时候,只记录了大小,比如100字节,在磁盘上不需要真的保留100字节,只要记下来就行了。加载进内存的时候要先为他分配100字节,然后这100字节再和data段合并到一起,成为内存里的data segment。带有初始值的变量,是放到data section的。这个文章里面有的。再仔细看一下~

    2021-10-29
    2
    5
  • 送过快递的码农
    我说下我的猜想,代码段,可读可执行,数据段:可读可写。至于堆:这个我猜想所在,如果单从我们写一个Hello World程序的时候,堆看起来有读写权限就行。但是,由于我们有实时编译技术,就从我知道的Java来说,有jit。虽然我从来没接触过这个大神,但是它本质上无非就是把一个动态字符穿实时编译成一个可执行字节码。由于通常字符串是在堆中的,所以编译成的可执行字节码是存在堆里面的。所以我猜想,堆里面肯定是有执行权限的。这个技术像Java可以进行字节码技术进行动态代理,大大增加了程序的灵活性,但是,也让程序有了被攻击的可能性,比如很多反序列化技术比如阿里巴巴的fastjson的bug应该和即时编译是有关系的。 老师我有个问题,就是之前在别的专栏里看的内容,说Linux进程和线程是在内核里面是一样的数据结构,那么进程的子进程和线程应该也是可区分的把,至少进程之前,内存除了共享内存,彼此之间是不能通讯的吧,但是线程是无条件使用进程的内存权限吧。内核还是能够区分这个任务是进程还是线程的吧

    作者回复: 很全面!思考得很深入!非常好

    2021-10-29
    2
    4
  • =
    代码段:可读可执行 数据段:可读可写 堆:可读可写可执行

    作者回复: 堆一般也是可写可读,要让他可执行,得用mmap或者mprotect接口主动去修改

    2022-01-10
    3
  • 坚定的抢手
    打开随机化的模式,会让内核在加载的过程中,对这些区域的起始地址增加一些随机的偏移值,这能增加缓冲区溢出的难度。 这个地方不太明白,为什么增加随机便宜值之后会增加缓冲区溢出的难度。希望老师能抽空解答一下。 网上查了一下关于随机化的作用,一般性的回答都是说,提高程序的安全防止攻击。

    作者回复: 好的。这个问题比较复杂,放在评论里篇幅太长了。我准备在最后开一个专题文章专门讲一下,就不在这里回答了。

    2021-12-13
    2
  • 姑射仙人
    老师,mmap和Page Cache的使用有关系吗?像RocketMQ中写文件使用的内存映射文件。

    作者回复: 是工作在不同层次上的。mmap其实只负责将文件与内存页对应起来。至于这个页是缺页状态,还是在Cache里,或者在swap区域,都是操作系统关心的,mmap不需要知道这些的。有很多开源组件使用mmap来加速IO,这是很常见的技巧。更常见的是驱动开发里使用这个技巧。因为这部分的资料比较多了,而且和我们这个专栏的主题关联度也不是那么强,所以我就没写这部分内容。

    2021-11-07
    2
  • 我是内存
    请问下: 1.IA-32的内存布局中kernel占1G,用户空间占3G,这个比例是可以随意调整的吗? 2.64bit的内存布局中,深色的空洞部分是表示它占着地方,但是没把它占据的空间添加到虚拟地址空间里面吧,相当于是一个黑户?

    作者回复: 1. 不行的哦,这个是linux内核源码写死的,要改就得改linux的源代码再重新编译。2. 你的比喻很直观,非常好,页表就是管户口的,没在页表里注册的,就是黑户,👍

    2021-11-02
    2
    2
  • WA自动机
    老师,前面的文章提到过:通过虚拟内存,每个进程有独立的页表。用fork创建父子进程应该都是有自己独立的页表啊,他们是怎么做到共享内存的呢

    作者回复: 继续向后看,学习完第十课可以自己再思考一下。如果还是没能解答你的疑问,可以加入微信群继续提问。

    2021-12-20
    4
    1
  • void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset); 第一个形参名是start,而不是addr, 形参名 见名知意。

    作者回复: linux内核代码里的声明就是addr。形参就是个名字而已。

    2021-11-02
    1
收起评论
显示
设置
留言
29
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部