BSS段为什么需要初始化

文章来自博客:http://www.cnblogs.com/amanlikethis/p/3384743.html

我们都知道bss段需要初始化,但是这是为什么呢?

       通过浏览资料,我们都会发现,bss段是不会出现在程序下载文件(*.bin *.hex)中的,因为全都是0。如果把它们出现在程序下载文件中,会增加程序下载文件的大小。实际应用中,通常只需要把bss段的起始地址和结束地址保存起来,而不需要将程序下载文件中出现bss段(一堆0)将来真正运行程序的时候,再根据这两个数据进行bss段的初始化就行了。

       以上这段文字是网上的资料说的。但是,我可不可以让bss段出现在程序下载文件中呢?如果这样可以的话,当程序由存储器(例如nandflash)拷贝到内存中时,捎带着会把bss段像data段那样初始化。

       实际上是可以这样做的。请看下边的两个链接脚本。

链接脚本一:

复制代码
SECTIONS {
    . =  0x00000000;
    .init : AT( 0){ head.o init.o nand.o}
    . =  0x30000000;
    .text : AT( 4096) { *(.text) }
    .rodata ALIGN( 4) : AT((LOADADDR(.text)+SIZEOF(.text)+ 3)&~( 0x03)) {*(.rodata*)} 
    .data ALIGN( 4)   : AT((LOADADDR(.rodata)+SIZEOF(.rodata)+ 3)&~( 0x03)) { *(.data) }
    __bss_start = .;
    .bss ALIGN( 4)  : { *(.bss)  *(COMMON) }
    __bss_end = .;
}
复制代码

链接脚本二:

复制代码
SECTIONS {
    . =  0x00000000;
    .init : AT( 0){ head.o init.o nand.o}
    . =  0x30000000;
    .text : AT( 4096) { *(.text) }
    .rodata ALIGN( 4) : AT((LOADADDR(.text)+SIZEOF(.text)+ 3)&~( 0x03)) {*(.rodata*)} 
    .bss ALIGN( 4)  : AT((LOADADDR(.rodata)+SIZEOF(.rodata)+ 3)&~( 0x03)){ *(.bss) }
    .data ALIGN( 4) : AT((LOADADDR(.bss)+SIZEOF(.bss)+ 3)&~( 0x03)) { *(.data) }
}
复制代码

    链接脚本一,把bss段放在最后边,arm-linux-gcc编译器默认的会把bss段给忽略掉,也即不会让bss段出现在程序下载文件中(可以通过Jlink软件查看编译后的bin文件)。这种链接脚本也是我们通常见到的方式。

    链接脚本二,把bss段放在了rodata段和data段中间,这个时候,arm-linux-gcc编译器并不会把bss段在程序下载文件中删除,也即会把bss段保留下来,最终出现在程序下载文件中。我考虑原因可能是这样的:编译后的地址rodata段、bss段、data段是连续的,也即程序运行时这几个段是连续的;倘若把bss段在程序下载文件中删除,那么程序下载文件中rodata段后边紧接着的是data段;这就要求程序的这两个段需要分别处理,而不能一次性将它们连续拷贝过去。

链接脚本二的方法可以让bss段出现在程序下载文件中。但是,通常都不会这样做,这里之所以这样深钻,只不过是在探究bss段初始化的必要性。我们通常采用的链接脚本一,由于最终程序下载文件中没有bss段,所以必须在应用程序运行前,根据bss段的起始地址和结束地址将bss段初始化。

下边,着重讲一下链接脚本中与初始化bss段相关的几句话。

    ( 1) __bss_start = .;
    ( 2).bss ALIGN( 4)  : { *(.bss)  *(COMMON) }
    ( 3)__bss_end = .;

     这里,实际上句(1)是在bss段的起始地址处定义了一个int类型的全局变量__bss_start。虽然,bss段的起始地址处肯定是一个未初始化的全局变量,但是这里算是编译器又在这个位置上又重新定义了一个全局变量。就是说,一个地址有两个名字,它们都能访问这个地址空间。句(3)的解释同句(1)。

    接着我们再看一下用C语言写的初始化bss段的程序。

 

复制代码
1void clean_bss( void)
2){
3)    extern  int __bss_start, __bss_end;
4)    int *p = &__bss_start;
5) 
6)    for (; p < &__bss_end; p++)
7)     *p =  0;
8)}
复制代码

      首先,句(3)对编译器产生的两个全局变量进行声明。句(4)通过__bss_start取出bss段的起始地址,句(6)通过__bss_end取出bss段的结束地址。


  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值