linux 为什么需要.bss段?

zpgglvta  于 2022-12-29  发布在  Linux
关注(0)|答案(6)|浏览(186)

我所知道的是全局变量和静态变量都存储在.data段中,而未初始化的数据则存储在.bss段中。我不明白的是,为什么我们要为未初始化的变量设置一个专用段?如果一个未初始化的变量在运行时被赋值,那么该变量是否仍然只存在于.bss段中?
在下面的程序中,a.data段中,b.bss段中;对吗?2如果我理解错了,请指正。

#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 ?** */
}
alen0pnh

alen0pnh1#

原因是为了减小程序的大小。假设你的C程序运行在一个嵌入式系统上,代码和所有常量都保存在真正的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可能是某种非常高效的内联汇编程序,这意味着启动时的向下拷贝可以执行得更快。

q3qa4bjr

q3qa4bjr2#

.bss段是一个优化。整个.bss段由一个数字描述,可能是4字节或8字节,它给出了它在运行进程中的大小,而.data部分与初始化变量大小之和一样大。因此,.bss使可执行文件更小,加载更快。否则,变量可以在.data段中,显式初始化为零;程序很难区分(详细地说,.bss中对象的地址可能与.data段中的地址不同)。
在第一个程序中,a位于可执行文件的.data段中,b位于.bss段中。一旦程序被加载,区别就变得不重要了。在运行时,b占用20 * sizeof(int)字节。
在第二个程序中,var被分配了空间,main()中的赋值修改了该空间,碰巧var的空间是在.bss段而不是.data段中描述的,但这并不影响程序运行时的行为。

lymnna71

lymnna713#

来自Jeff Duntemann的Assembly Language Step-by-Step: Programming with Linux,关于**.数据**部分:

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

关于.data节,需要记住的重要一点是,定义的初始化数据项越多,可执行文件就越大,运行时从磁盘加载到内存所需的时间也就越长。
和**.bss部分:
在程序开始运行之前,并不是所有的数据项都需要有值。例如,当你从磁盘文件阅读数据时,你需要有一个地方让数据从磁盘进入后去。像这样的数据缓冲区在程序的
.bss**部分定义。你为缓冲区留出一定数量的字节,并给予缓冲区一个名字。但你没有说明缓冲区中有什么值。
在.data节中定义的数据项和在.bss节中定义的数据项之间有一个关键的区别:.data节中的数据项会增加可执行文件的大小。2.bss节中的数据项不会。3占用16,000字节(或更多,有时甚至更多)的缓冲区可以在.bss中定义,并且几乎不会增加可执行文件的大小(说明大约为50字节)。

ckocjqey

ckocjqey4#

首先,示例中的那些变量不是未初始化的; C指定将未初始化的静态变量初始化为0。
所以.bss的原因是有更小的可执行文件,节省空间,并允许更快地加载程序,因为加载程序可以只分配一堆零,而不必从磁盘复制数据。
当运行程序时,程序加载器会将.data.bss加载到内存中。写入驻留在.data或.bss中的对象只会进入内存,它们不会在任何时候刷新到磁盘上的二进制文件中。

jtw3ybtb

jtw3ybtb5#

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

.bss此段保存构成程序内存映像的未初始化数据。根据定义,当程序开始运行时,系统用零初始化数据。此段不占用文件空间,如段类型SHT_NOBITS所示。
表示节名.bss被保留并具有特殊效果,特别是它不占用文件空间,因此优于.data
当然,缺点是当操作系统将字节放入内存时,所有字节都必须设置为0,这限制性更强,但这是一个常见的用例,对于未初始化的变量来说效果很好。
SHT_NOBITS节类型文档重复了该确认:
sh_size此成员以字节为单位给出节的大小。除非节类型为SHT_NOBITS,否则节在文件中占用sh_size字节。类型为SHT_NOBITS的节可能具有非零大小,但它在文件中不占用任何空间。
C标准没有提到节,但是我们可以很容易地用objdumpreadelf验证变量在Linux中的存储位置,并得出结论,未初始化的全局变量实际上存储在.bss中。What happens to a declared, uninitialized variable in C?

js81xvg6

js81xvg66#

维基百科文章.bss提供了一个很好的历史解释,因为这个术语是从20世纪50年代中期开始的(yippee my birthday; -).
在过去,每一个比特都是宝贵的,所以任何一种通知保留的空空间的方法都是有用的。这个(. bss)是一个沿用至今的方法。

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

相关问题