V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
fffang
V2EX  ›  问与答

关于 Linux c 程序的五大内存分区的疑问

  •  
  •   fffang · 2020-11-09 11:01:11 +08:00 · 1098 次点击
    这是一个创建于 1503 天前的主题,其中的信息可能已经有所发展或是发生改变。

    一个典型的 C 程序的内存布局是以下几个分区(section)组成:

    1. 代码段(Text segment)
    2. 数据段(Initialized data segment)
    3. 未初始化的数据段(Uninitialized data segment)
    4. 堆(Stack)
    5. 栈(Heap)

    对于这些分区的理解是:

    代码段顾名思义储存的是二进制的可执行指令。数据段储存的是已初始化的内容,例如字符串,数字等,ex.char s [] =“ hello world”;。未初始化的数据段,声明一个全局变量但不赋值,将存储在 bss 段,static int i;。程序员 malloc/new 的区域会在堆上,堆可伸缩,同时会产生大量的碎片内存。栈的结构是 LIFO 的结构,存储着临时变量,指针,由 OS 进行管理。数据段和代码段是在可执行文件中,运行时由 OS 读取到内存中的。BSS 段不在可执行文件中,可执行文件中 BSS 段仅仅是一个数字,标记所有未初始化的变量占用空间的总和。在读取可执行文件时,OS 在数据段旁边,开辟这些区域并将它们赋值为 0 。

    这是我的理解,有错误的地方还请指教。以下是我的问题:

    如果一个未初始化的变量定义如,static int i;,然后用 malloc 进行分配,这块内存位于何处呢?还有如char s [];,从硬盘读取文件内容到内存,应该会在堆区,然后赋值给s,这块内存位于何处呢?

    9 条回复    2020-11-09 12:39:33 +08:00
    codehz
        1
    codehz  
       2020-11-09 11:36:05 +08:00
    1. 全局变量不是从 malloc 获取的,malloc 拿到的是新的指针。。。
    2. char s[]; 是非法的变量声明,直接编译不过
    chinuno
        2
    chinuno  
       2020-11-09 11:37:23 +08:00 via Android
    1.静态变量编译好位置就是在 bss 里,malloc 要怎么给他分配? malloc 出来的是个指针,顶多取值给他赋值
    2.char s[];你试试能不能编译。后面带字符串的情况下才可以省略长度,因为静态字符串数据以及固定了,编译的时候确定下来了。假设你制定了 s 的长度特别长够你用了,那你只能把堆里的文件数据复制到 s 本身的内存空间( bss 段)内
    codehz
        3
    codehz  
       2020-11-09 11:52:34 +08:00
    3. 硬盘读取文件内容到内存的过程,不会帮你分配内存,所以你可以直接传递一个够大的全局变量数组。。。
    fffang
        4
    fffang  
    OP
       2020-11-09 11:56:06 +08:00
    fffang
        5
    fffang  
    OP
       2020-11-09 11:57:43 +08:00
    @chinuno
    @codehz
    想了下应该这么描述:定义一个未初始化的全局变量,那它显然应该在 BSS 段内,但是长度不确定,在运行时通过才能知道它的长度,这时会如何分配 BSS 段呢?
    XiaoxiaoPu
        6
    XiaoxiaoPu  
       2020-11-09 12:04:10 +08:00
    @fffang 全局变量定义时就确定了长度,不可能运行时才确定长度
    codehz
        7
    codehz  
       2020-11-09 12:06:31 +08:00 via Android
    @fffang 不能做到,bss 段的东西长度必须是编译的时候已知的,没有任何例外。。。
    但是这不妨碍你放一个指针,指针的大小是固定的,但指向的内容可以不确定大小(也不确定是哪个内存区域,或者无效)
    这时候就可以 malloc 了
    raaaaaar
        8
    raaaaaar  
       2020-11-09 12:07:45 +08:00 via Android
    有书专门讲这些东西么,要看内存管理还是啥,我以前也看过这个,不过不是很透彻
    sockpuppet9527
        9
    sockpuppet9527  
       2020-11-09 12:39:33 +08:00   ❤️ 1
    1. malloc 分出来的东西是逻辑地址,给到你返回的是一个指向该逻辑地址的指针,具体硬件上的地区要经过 MMU 。
    2. 硬盘读取的内容你可以放到堆,也可以放到栈。栈是要先声明长度,比如 char s[100]; ,这就是栈和堆最大的不同,栈的深浅是固定的,堆是动态的,就算到了汇编层面,你写一个方法,栈里面变量的使用也需要先声明长度,越了栈就违法了。

    另外关于内存(逻辑地址)在何处,这个是动态的,参考一本内核书上的 hello world 例子:

    ```
    08048368 <greeting>:
    8048368: 55 push %ebp
    8048369: 89 e5 mov %esp,%ebp
    804836b: 83 ec 08 sub $0x8,%esp
    804836e: 83 ec 0c sub $0xc,%esp
    8048371: 68 84 84 04 08 push $0x8048484
    8048376: e8 35 ff ff ff call 80482b0 <printf@plt>
    804837b: 83 c4 10 add $0x10,%esp
    804837e: c9 leave

    804837f: c3 ret
    08048380 <main>:
    8048380: 55 push %ebp
    8048381: 89 e5 mov %esp,%ebp
    8048383: 83 ec 08 sub $0x8,%esp
    8048386: 83 e4 f0 and $0xfffffff0,%esp
    8048389: b8 00 00 00 00 mov $0x0,%eax
    804838e: 83 c0 0f add $0xf,%eax
    8048391: 83 c0 0f add $0xf,%eax
    8048394: c1 e8 04 shr $0x4,%eax
    8048397: c1 e0 04 shl $0x4,%eax
    804839a: 29 c4 sub %eax,%esp
    804839c: e8 c7 ff ff ff call 8048368 <greeting>
    80483a1: c9 leave
    80483a2: c3 ret
    80483a3: 90 nop
    ```

    你可以看到 main 的入口是 0x8048380,这其实是给到 CPU 的 EIP 寄存器使用的,指向的虚拟地址。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2694 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 05:58 · PVG 13:58 · LAX 21:58 · JFK 00:58
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.