为什么需要.bss段?

时间:2012-03-02 14:51:18

标签: 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 ()
{
   ;
}  

另外,请考虑以下程序,

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

6 个答案:

答案 0 :(得分:77)

原因是减少了程序大小。想象一下,您的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);

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

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

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

答案 1 :(得分:74)

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

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

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

答案 2 :(得分:11)

Jeff Duntemann致Assembly Language Step-by-Step: Programming with Linux关于 .data 部分:

  

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

     

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

.bss 部分:

  

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

     

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

答案 3 :(得分:9)

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

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

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

答案 4 :(得分:3)

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

在当天,每一点都很珍贵,所以任何信号保留空白空间的方法都很有用。这个( .bss )就是卡住的那个。

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

答案 5 :(得分:3)

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

  

.bss此部分包含有助于实现此目标的未初始化数据   程序的记忆图像。根据定义,系统初始化   程序开始运行时带零的数据。该部分不占用文件空间,如部分类型SHT_NOBITS所示。

表示部分名称.bss是保留的并且具有特殊效果,特别是不占用文件空间,因此优于.data

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

SHT_NOBITS部分类型文档重复了这一肯定:

  

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

C标准对部分没有任何说明,但是我们可以使用objdumpreadelf轻松验证变量在Linux中的存储位置,并得出结论:未初始化的全局变量实际上存储在{{1例如,请参阅此答案:https://stackoverflow.com/a/36725211/895245