为什么需要.bss段?

我所知道的是,全局variables和静态variables存储在.data段中,未初始化的数据在.bss段中。 我不明白的是为什么我们有未初始化variables的专用段? 如果未初始化的variables在运行时分配了一个值,该variables是否仍然存在于.bss段中?

在下面的程序中, a.data段中,而b.bss段中; 那是对的吗? 如果我的理解错误,请纠正我。

 #include <stdio.h> #include <stdlib.h> int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9}; int b[20]; /* Uninitialized, so in the .bss and will not occupy space for 20 * sizeof (int) */ int main () { ; } 

另外,请考虑下面的程序,

 #include <stdio.h> #include <stdlib.h> int var[10]; /* Uninitialized so in .bss */ int main () { var[0] = 20 /* **Initialized, where this 'var' will be ?** */ } 

原因是减less程序大小。 想象一下,你的C程序运行在embedded式系统上,代码和所有的常量都保存在真正的ROM(闪存)中。 在这样的系统中,在main()被调用之前,必须执行初始的“拷贝”来设置所有的静态存储持续时间对象。 它通常会像这样伪:

 for(i=0; i<all_explicitly_initialized_objects; i++) { .data[i] = init_value[i]; } memset(.bss, 0, all_implicitly_initialized_objects); 

其中.data和.bss存储在RAM中,但init_value存储在ROM中。 如果它是一个段,那么ROM必须被填满大量的零,从而显着增加ROM的大小。

基于RAM的可执行文件的工作方式类似,当然他们没有真正的ROM。

此外,memset可能是一个非常有效的内联汇编器,这意味着启动复制可以更快地执行。

.bss段是一个优化。 整个.bss段由一个单一的数字描述,大概是4个字节或8个字节,它在运行过程中给出了它的大小,而.data段则和初始化variables的大小总和一样大。 因此, .bss使可执行文件更小更快加载。 否则,variables可以在.data段中显式初始化为零; 该计划将难以分辨。 ( .bss如果.bss中的对象位于.data段中,则地址可能与地址不同。)

在第一个程序中, a将在.data段中,而b将在可执行文件的.bss段中。 一旦程序被加载,区分变得不重要。 在运行时, b占用20 * sizeof(int)个字节。

在第二个程序中, var被分配了空间, main()的赋值修改了这个空间。 恰巧var的空间是在.bss段而不是.data段中描述的,但是不影响程序在运行时的行为方式。

那么,首先,你的例子中的那些variables不是未初始化的; C指定未被初始化的静态variables被初始化为0。

所以.bss的原因是有更小的可执行文件,节省空间,并允许更快的加载程序,因为加载器可以分配一堆零而不必从磁盘复制数据。

运行程序时,程序加载器将把.data和.bss加载到内存中。 写入到.data或.bss中的对象因此只能进入内存,在任何时候都不会刷新到磁盘上的二进制文件。

从汇编语言一步一步:使用Linux编程 Jeff Duntemann,关于.data部分:

.data部分包含初始化数据项的数据定义。 初始化的数据是在程序开始运行之前具有一个值的数据。 这些值是可执行文件的一部分。 当可执行文件被加载到内存中执行时,它们被加载到内存中。

关于.data部分要记住的重要一点是,您定义的初始化数据项越多,可执行文件就越大,运行它时从磁盘加载到内存中的时间就越长。

.bss部分:

在程序开始运行之前,并不是所有的数据项都需要有值。 例如,当您从磁盘文件读取数据时,您需要在数据从磁盘进入之后有一个空间。 像这样的数据缓冲区是在程序的.bss部分定义的。 您为缓冲区留出了一定数量的字节,并为缓冲区指定了一个名称,但是您没有说明缓冲区中将出现什么值。

.data节中定义的数据项与.bss节中定义的数据项之间存在着至关重要的区别:.data节中的数据项增加了可执行文件的大小。 .bss部分中的数据项不包含。 可以在.bss中定义占用16,000字节(或更多,有时更多)的缓冲区,并且对可执行文件大小几乎不添加(大约50字节用于描述)。

维基百科的文章.bss提供了一个很好的历史解释,因为这个词是从二十世纪五十年代中期(yippee我的生日;-)。

当天,每一位都是宝贵的,所以任何用于发信号保留空白空间的方法都是有用的。 这( .bss )是卡住的那个。

.data节是空的,而不是空的,而是将(你)定义的值input到它。

System V ABI 4.1(1997) (AKA ELF规范)也包含答案:

.bss本节保存对程序内存映像有贡献的未初始化的数据。 根据定义,系统在程序开始运行时用零来初始化数据。 该部分不占用文件空间,如节typesSHT_NOBITS

说段名.bss是保留的,具有特殊效果,特别是它不占用文件空间 ,因此优于.data

不利的一面是,当操作系统把它们放在内存上的时候,所有的字节必须设置为0 ,这是一个比较常见的用例,对于未初始化的variables来说工作正常。

SHT_NOBITS节types文档重复肯定:

sh_size该成员以字节为单位给出该部分的大小。 除非段types是SHT_NOBITS ,否则该段占用文件中的sh_size字节。 SHT_NOBITStypes的SHT_NOBITS可能有一个非零大小,但它不占用文件中的空间。

C标准没有提到关于section的内容,但是我们可以使用objdumpreadelf很容易地validationvariables在Linux中的存储readelf ,并且得出结论:未初始化的全局variables事实上存储在.bss ,例如: https:// stackoverflow的.com /一个/895245分之36725211

我不是操作系统和内存pipe理问题的专家,但是我认为你已经混淆了:段只存在于程序 (即可执行文件)中。 请记住,程序是存在的被动实体,仅此而已,它们什么都不做。

因此,在初始化之后询问存储在.bss段中的单元化variables会发生什么变化没有意义:一旦程序被加载到计算机的存储器中并开始运行,它就成为一个进程 (一个活动的实体,你知道,那就运行!)。

在专用于该进程的(可能是虚拟的)内存空间中,不会有任何段,只有堆栈,文本/代码和堆区(后者用于分配variables)。 在这方面,请查看Mats Petersson 在这里提供的答案。

来自巴西的问候!