标签:tac new 关系 显示 main 通过 目标 符号表 用户空间
现代操作系统的内存分配以页为单位进行管理,而页通过段进行管理,组成了段页式内存管理。
本文对C++程序的各段进行简单的区分,并厘清各段在可执行程序与进程中的状态关系。
程序大体被划分为两部分,只读部分和读/写部分,这源于历史上ROM和RAM两类存储器的划分。尽管现代存储器的发展早就突破了这种分类方式,程序内存的某些部分不应该被修改的想法却被保留了下来。
代码段(code segment/text segment)用于存放可执行程序,也就是对应架构下程序的指令序列。代码段包含在可执行程序中,大小是确定的;加载到进程中之后,所在内存区域通常被MMU设置为只读,以保护代码段不会被意外改写(如出错时)。当然,没有MMU的系统,就没有这种写保护。代码段也可能包含一些只读的常量,如字符串常量等。
注意,并不是所有的常量都会被保存到rodata段,存在一些特殊情况:
分别是针对text段和data段的重定位表。
堆和栈都是进程中的概念,可执行程序中并不包括这两个段,更不会预先指定其大小。堆和栈的大小都受到具体的机器限制,当堆和栈用尽系统内存的时候,分别会发生常见的 Stack Overflow
、Out of Memory
错误。
ptmalloc(glibc标配)
、tcmalloc(google)
、jemalloc(facebook)
来切换不同的内存分配算法,以提高内存分配效率。可执行程序中确定包含的段有:text/code段、data段、rodata段,可能包含的段有:符号表和重定位表。
编译时gcc -g
选项可以打开调试信息。重定位分为链接时重定位和装载时重定位,链接时重定位是把各个不同.o文件的符号合并到目标地址,从而形成一个统一的可执行文件;装载时重定位是因为可能引用外部的动态链接库.so文件中的变量或函数,无法预先确定偏移量。如果没有引用外部.so,则无需装载时重定位。
#include <stdio.h>
int a[100]; // case 1
// int a[100] = {1}; // case 2
// int a[100] = {0}; // case 3
int main() {
printf("%d\n", a[0]);
return 0;
}
在本地对于以上三种情况分别进行编译,并使用 ls -al
查看文件大小显示分别为 8328、8744、8328。显然,未初始化和初始化为零的大小一样,都是作为bss段处理,并不占用可执行程序大小;初始化为非零值之后,除了100个int元素应占的400B空间之外,另有16B额外空间开销(可能是用于strtab或者rel.data开销?)。
标签:tac new 关系 显示 main 通过 目标 符号表 用户空间
原文地址:https://www.cnblogs.com/zhcpku/p/14437940.html