赞
踩
text | data | bss | dec | hex | filename | sum | |
---|---|---|---|---|---|---|---|
1366 | 4616 | 4032 | 10014 | 271e | main | 21184 | |
1366 | 8616 | 8 | 9990 | 2706 | main1 | 25184 | |
变化main1-main | 0 | 4000 | -4024 | -24 | -24 | 4000 |
bss 段不占磁盘空间
源代码(source coprede)→预处理器(processor)→编译器(compiler)→汇编程序(assembler)→目标程序(object code)→链接器(Linker)→可执行程序(executables)
分配程序执行所需的栈空间、代码段、静态存储区、映射堆空间地址等,操作系统会创建一个进程结构体来管理进程,然后将进程放入就绪队列,等待CPU调度运行。参考1.2
代码段(.text)
,也称文本段(TextSegment)
,存放着程序的机器码
和只读数据
,可执行指令就是从这里取得的。如果可能,系统会安排好相同程序的多个运行实体共享这些实例代码。这个段在内存中一般被标记为只读,任何对该区的写操作都会导致段错误(Segmentation Fault)
。2、数据段,
包括已初始化的数据段(.data)
和未初始化的数据段(.bss)
,
3、堆栈段分为堆和栈:
堆(Heap)
:用来存储程序运行时分配的变量。
malloc()、new()
等这类实时内存分配函数来实现。malloc
等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);free
等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)栈(Stack)
是一种用来存储函数调用时的临时信息
的结构,如函数调用所传递的参数
、函数的返回地址
、函数的局部变量
等。在程序运行时由编译器在需要的时候分配,在不需要的时候自动清除。
堆和栈的区别:
1、首先介绍各段的关系:
.text
部分是编译后程序的主体,也就是程序的机器指令。.data 和 .bss
保存了程序的全局变量,.data保存有初始化的全局变量,.bss保存只有声明没有初始化的全局变量。text和data段
都在可执行文件中(在嵌入式系统里一般是固化在镜像文件中),由系 统从可执行文件中加载;bss段
不在可执行文件中,由系统初始化。2、.bss的简单说明
BSS 是“Block Started by Symbol”的缩写,意为“以符号开始的块”。BSS是Unix链接器产生的未初始化数据段
。
BSS段的变量只有名称和大小却没有值
。此名后来被许多文件格式使用,包括PE。“以符号开始的块” 指的是编译器处理未初始化数据的地方。BSS节不包含任何数据,只是简单的维护开始和结束的地址,以便内存区能在运行时被有效地清零。BSS节在应用程序 的二进制映象文件中并不存在。
3、将.data和.bss分开的理由是为了节约磁盘空间,.bss不占实际的磁盘空间,为什么.bss不占磁盘空间呢?
#include <stdio.h>
int a[1000];
int b[1000] = {1};
int main()
{
printf("123\n");
return 0;
}
这里编写了一个test.c文件,gcc编译gcc test.c -o test
之后,使用ls -l test
命令就可以得到可执行文件的信息,文件的大小为12696字节
ls -l test
-rwxrwxr-x 1 xxx xxx 12696 Dec 1 01:04 test
size test
text data bss dec hex filename
1174 4568 4032 9774 262e test
接着我们修改源程序:
#include <stdio.h>
int a[1000] = {1};
int b[1000] = {1};
int main()
{
printf("123\n");
return 0;
}
同样ls -l test
ls -l test
-rwxrwxr-x 1 xxx xxx 16696 Dec 1 01:09 test
size test
text data bss dec hex filename
1174 8568 8 9750 2616 test
仅仅是初始化了a[1000]
,让这个数组的所在段从.bss段
改到了.data段
。size test
命令查看bss段的大小也减小了。这就证明了.bss段中的数据并没有占据磁盘空间
,从而节约了磁盘的空间。
linux环境下的c语言,初始值为零和没有赋初始值的变量放在BSS段,因为这些值都是零,所以就不需要放到文件里面,等程序加载的时候再赋值就好了。
int a[1000]既然不占据实际的磁盘空间(是指不占据应该分配的内存大小),那么它的大小和符号存在哪呢?
readelf -S test
来查看test可执行文件的段表。$ readelf -S test.o There are 13 section headers, starting at offset 0x2d0: Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .text PROGBITS 0000000000000000 00000040 0000000000000017 0000000000000000 AX 0 0 1 [ 2] .rela.text RELA 0000000000000000 00000220 0000000000000030 0000000000000018 I 10 1 8 [ 3] .data PROGBITS 0000000000000000 00000057 0000000000000000 0000000000000000 WA 0 0 1 [ 4] .bss NOBITS 0000000000000000 00000057 0000000000000000 0000000000000000 WA 0 0 1 [ 5] .rodata PROGBITS 0000000000000000 00000057 0000000000000010 0000000000000000 A 0 0 1 [ 6] .comment PROGBITS 0000000000000000 00000067 000000000000002a 0000000000000001 MS 0 0 1 [ 7] .note.GNU-stack PROGBITS 0000000000000000 00000091 0000000000000000 0000000000000000 0 0 1 [ 8] .eh_frame PROGBITS 0000000000000000 00000098 0000000000000038 0000000000000000 A 0 0 8 [ 9] .rela.eh_frame RELA 0000000000000000 00000250 0000000000000018 0000000000000018 I 10 8 8 [10] .symtab SYMTAB 0000000000000000 000000d0 0000000000000120 0000000000000018 11 9 8 [11] .strtab STRTAB 0000000000000000 000001f0 000000000000002b 0000000000000000 0 0 1 [12] .shstrtab STRTAB 0000000000000000 00000268 0000000000000061 0000000000000000 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), I (info), L (link order), O (extra OS processing required), G (group), T (TLS), C (compressed), x (unknown), o (OS specific), E (exclude), l (large), p (processor specific)
总结:为什么需要.bss段?
1、.data部分:
2、 .bss部分:
1、一方面是当程序被装载后,数据和指令分别被映射到两个虚存区域
。由于数据区域对于进程来说是可读写的
,而指令区域对于进程来说是只读的
,所以这两个虚存区域的权限可以被分别设置成可读写和只读。这样可以防止程序的指令被有意或无意地改写。
2、另外一方面是对于现代的CPU来说,它们有着极为强大的缓存(Cache)体系。指令区和数据区的分离有利于提高程序的局部性。现代CPU的缓存一般都被设计成数据缓存和指令缓存分离,所以程序的指令和数据被分开存放对CPU的缓存命中率提高有好处。
3、,其实也是最重要的原因,就是当系统中运行着多个该程序的副本时,它们的指令都是一样的,所以内存中只须要保存一份改程序的指令部分。对于指令 这种只读的区域来说是这样,对于其他的只读数据也一样,比如很多程序里面带有的图标、图片、文本等资源也是属于可以共享的。当然每个副本进程的数据区域是不一样的,它们是进程私有的。不要小看这个共享指令的概念,它在现代的操作系统里面占据了极为重要的地位,特别是在有动态链接的系统中,可以节省大量的内存。比如我们常用的Windows Internet Explorer 7.0运行起来以后,它的总虚存空间为112 844 KB,它的私有部分数据为15 944 KB,即有96 900 KB的空间是共享部分。如果系统中运行了数百个进程,可以想象共享的方法来节省大量空间。关于内存共享的更为深入的内容我们将在装载这一章探讨。
1、https://www.zhihu.com/search?type=content&q=.data%E5%92%8C.bss
2、https://zhuanlan.zhihu.com/p/145263213
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。