为什么需要.bss段?

Who*_*ami 107 c linux compiler-construction

我所知道的是全局和静态变量存储在.data段中,未初始化的数据存在于.bss段中.我不明白的是为什么我们有未初始化变量的专用段?如果未初始化的变量在运行时分配了值,那么该变量是否仅存在于.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 ()
{
   ;
}  
Run Code Online (Sandbox Code Playgroud)

另外,请考虑以下程序,

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

Lun*_*din 81

原因是减少了程序的大小.想象一下,您的C程序在嵌入式系统上运行,其中代码和所有常量都保存在真正的ROM(闪存)中.在这样的系统中,在调用main()之前,必须执行初始"copy-down"以设置所有静态存储持续时间对象.它通常会像这样伪:

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

memset(.bss, 
       0, 
       all_implicitly_initialized_objects);
Run Code Online (Sandbox Code Playgroud)

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

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

此外,memset可能是一些非常高效的内联汇编程序,这意味着可以更快地执行启动副本.

  • 澄清一下:.data和.bss之间的唯一区别是,在启动时,"copy-down"可以按顺序运行,因此更快.如果它没有被分成两个段,那么初始化将不得不跳过属于未初始化变量的RAM点,因此浪费时间. (5认同)

Jon*_*ler 74

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

在第一个程序中,a将在.data段中并且b将位于.bss可执行文件的段中.加载程序后,区别变得无关紧要.在运行时,b占用20 * sizeof(int)字节.

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

  • 例如,考虑使用许多未初始化的缓冲区,长度为4096字节.您是否希望所有这些4k缓冲区对二进制文件的大小有贡献?这将浪费很多空间. (12认同)
  • @SurajJain:存储的数字是要填充零的字节数。除非没有这样的未初始化变量,否则 bss 部分的长度不会为零,即使加载程序后 bss 部分的所有字节都为零。 (3认同)
  • @jonathen 杀手:为什么整个 bss 段都用单个数字描述? (2认同)

mih*_*hai 12

汇编语言循序渐进: Jeff Duntemann 使用Linux进行编程,关于.data部分:

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

关于.data部分要记住的重要事项是,您定义的数据项越多,可执行文件就越大,运行时将它从磁盘加载到内存所需的时间就越长.

.bss部分:

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

.data部分中定义的数据项与.bss部分中定义的数据项之间存在重要差异:.data部分中的数据项会增加可执行文件的大小..bss部分中的数据项不是.可以在.bss中定义占用16,000字节(或更多,有时甚至更多)的缓冲区,并且几乎不添加任何内容(对于描述大约50个字节)到可执行文件大小.


jan*_*neb 9

好吧,首先,你的例子中的那些变量不是未初始化的; C指定未初始化的静态变量初始化为0.

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

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


Phi*_*ley 5

维基百科文章.bss提供了一个很好的历史解释,因为该术语来自 1950 年代中期(yippee 我的生日;-)。

在过去,每一比特都是宝贵的,因此任何表示预留空闲空间的方法都是有用的。这个(.bss)是卡住的那个。

.data部分用于非空的空间,而是将输入(您的)定义的值。


Cir*_*四事件 5

系统V 4.1 ABI(1997)(AKA ELF规范),也包含了答案:

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

说节名.bss是保留的,有特殊效果,特别是不占用文件空间,因此比.data.

缺点当然是0当操作系统将它们放在内存中时,所有字节都必须设置为,这更具限制性,但很常见,并且适用于未初始化的变量。

SHT_NOBITS部分类型的文档重复那肯定:

sh_size该成员以字节为单位给出节的大小。除非节类型是SHT_NOBITS,否则该节sh_size 在文件中占用字节。type 的部分SHT_NOBITS可能具有非零大小,但它不占用文件中的空间。

C标准只字未提的部分,但我们可以很容易地验证对变量的存储在Linux中objdumpreadelf,并得出结论,未初始化的全局其实都是存储在.bss。例如,请参阅此答案:C 中声明的未初始化变量会发生什么?