为什么需要.bss段?


120

我知道的是,全局变量和静态变量存储在.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 ?** */
}

3
您可以将BSS阅读为更好的节省空间
smwikipedia

Answers:


89

原因是减小程序大小。想象一下,您的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可能是一些非常高效的内联汇编程序,这意味着可以更快地执行启动复制。


7
需要说明的是:.data和.bss之间的唯一区别是在启动时,“ copy-down”可以顺序运行,因此速度更快。如果未将其分为两个部分,则初始化将不得不跳过属于未初始化变量的RAM点,从而浪费了时间。
CL22

80

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

在第一个程序,a将在.data链段和b将在.bss可执行文件的段。加载程序后,区别就不再重要了。在运行时,b占用20 * sizeof(int)字节。

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


16
例如,考虑具有许多长度为4096字节的未初始化的缓冲区。您是否希望所有这些4k缓冲区都有助于二进制文件的大小?那会浪费很多空间。
Jeff Mercado 2012年

1
@jonathen杀手::为什么整个bss段用单个数字描述?
Suraj Jain

@JonathanLeffler我的意思是所有零初始化的静态变量都放在bss中。因此,它的值不应该只是零吗?为什么不给它们.data节上的空格,这又怎么会使它变慢呢?
Suraj Jain

2
@SurajJain:存储的数字是要用零填充的字节数。除非没有这样的未初始化变量,否则bss节的长度将不会为零,即使加载程序后bss节的所有字节均为零。
乔纳森·莱夫勒

1
可执行文件中的.bss部分只是一个数字。内存过程映像中的.bss节通常是与.data节相邻的内存,并且通常将运行时.data节与.bss组合在一起;在运行时存储器中没有区别。有时,您可以找到bss的起始位置(edata)。实际上,一旦完成过程映像,.bss便不会在内存中存在;因此,.bss不存在。置零的数据是.data节的简单部分。但细节取决于使用/ s等。o
乔纳森·莱弗勒

15

汇编语言的Step-by-步:使用Linux编程杰夫Duntemann,有关。数据部分:

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

关于.data节要记住的重要一点是,您定义的初始化数据项越多,可执行文件将越大,并且在运行它时将其从磁盘加载到内存所需的时间也越长。

.bss部分:

在程序开始运行之前,并非所有数据项都需要具有值。例如,当您从磁盘文件中读取数据时,需要有一个放置数据的位置,以便将数据从磁盘中导入。程序的.bss部分中定义了类似的数据缓冲区。您为缓冲区留出了一定数量的字节,并为缓冲区指定了名称,但是您没有说缓冲区中将出现什么值。

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


9

好吧,首先,您的示例中的那些变量并未被初始化。C指定未初始化的静态变量初始化为0。

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

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


5

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

.bss本节包含有助于程序存储映像的未初始化数据。根据定义,当程序开始运行时,系统将数据初始化为零。该节不占用文件空间,如节类型所示SHT_NOBITS

表示节名称.bss是保留的,具有特殊效果,特别是它不占用文件空间,因此具有的优势.data

不利的一面是,0当操作系统将所有字节放入内存时,必须将所有字节都设置为字节,这是限制性更强的方法,但是一种常见的使用情况,并且对于未初始化的变量也可以正常工作。

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

sh_size该成员提供节的大小(以字节为单位)。除非节类型为SHT_NOBITS,否则节将占用sh_size 文件中的字节。类型的节的SHT_NOBITS大小可能不为零,但在文件中不占空间。

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


3

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

过去,每一点都是宝贵的,因此任何用信号通知保留的空白空间的方法都是有用的。这个(.bss)是被卡住的。

.data节用于不为空的空间,而是将(您)定义的值输入其中。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.