16

我正在编写一个嵌入式应用程序,几乎所有的 RAM 都被全局字节数组使用。当我的固件启动时,它首先用零覆盖 RAM 中的整个 BSS 部分,这在我的情况下是完全没有必要的。

有什么方法可以指示编译器不需要对某些数组进行零初始化?我知道这也可以通过将它们声明为指针并使用 malloc() 来解决,但有几个原因我想避免这种情况。

4

7 回答 7

11

问题是标准 C强制对静态对象进行零初始化。如果编译器跳过它,它将不符合 C 标准。

在嵌入式系统编译器上,通常有一个非标准选项“紧凑启动”或类似选项。启用后,程序中的任何位置都不会发生静态/全局对象的初始化。如何做到这一点取决于您的编译器,或者在这种情况下,取决于您的 gcc 端口。

如果您提及您正在使用哪个系统,那么有人可能会为该特定编译器端口提供解决方案。

这意味着您显式初始化的任何静态/全局(静态存储持续时间)变量将不再被初始化。您必须在运行时初始化它,也就是说,static int x=1;您必须编写static int x; x=1;. 以这种方式编写嵌入式 C 程序是相当普遍的,以使它们与禁用静态初始化的编译器兼容。

于 2013-02-04T12:06:03.333 回答
10

事实证明,我的工具链中包含的链接器脚本有一个特殊的“noinit”部分。

__attribute__ ((section (".noinit")))

/** 强制编译器在启动时不自动将给定的全局变量归零,以便保留当前 RAM 内容。在大多数情况下,由于断电后易失性存储器的行为,该值将是随机的,但可以在某些特定情况下使用,例如在系统看门狗重置后传回值。

因此,所有标有该属性的全局变量在启动期间都不会被初始化为零。

于 2013-02-04T15:33:53.960 回答
2

C 标准要求将全局数据初始化为零。

一些嵌入式系统制造商可能会提供一种绕过此选项的方法,但如果“初始化为零”没有完成,肯定有许多典型的应用程序会失败。

一些编译器还允许您有更多的部分,这些部分可能具有除“bss”部分之外的其他特征。

另一种选择当然是“自己分配”。由于它是一个嵌入式系统,我想您可以控制应用程序和数据如何加载到 RAM 中,特别是使用哪些地址。

因此,您可以使用指针,并简单地使用您自己的机制将指针分配给为您需要大型数组的任何内容保留的内存区域。这避免了相当复杂的使用malloc- 并且它为您提供或多或少的永久地址,因此您不必担心稍后尝试查找数据的位置。这当然会对性能产生很小的影响,因为它增加了另一个间接级别,但在大多数情况下,一旦数组用作函数的参数,它就会消失,因为无论如何它都会衰减到那个点的指针.

于 2013-02-04T12:09:43.613 回答
2

有一些解决方法,例如:

  • 从二进制文件中删除 BSS 部分或将其大小设置为 0 或 1。如果加载程序必须为所有部分显式分配内存,这将不起作用。如果加载程序只是将数据复制到 RAM,这将起作用。
  • 像在 C 代码中一样声明数组extern并在单独的汇编文件中的汇编代码或链接描述文件中定义符号(连同它们的地址)。同样,如果必须显式分配内存,这将不起作用。
  • 在加载程序或之前在程序中执行的启动代码中修补或删除相关的 BSS 归零代码main()
于 2013-02-04T12:09:51.640 回答
2

所有嵌入式编译器都应该允许 noinit 段。使用 IAR AVR 编译器,您不想被初始化的变量只需声明如下:

__no_init uint16_t foo;

最有用的原因是允许变量在看门狗或掉电复位时保持其值,这在基于计算机的 C 程序中当然不会发生,因此它从标准 C 中省略。

只需在您的编译器手册中搜索“noinit”或类似内容即可。

于 2015-08-17T11:18:25.303 回答
1

您确定二进制格式实际上包含二进制文件中的 BSS 部分吗?在我使用过的二进制格式中,BSS 只是一个整数,它告诉内核/加载器要分配多少内存并将其归零。

C 中绝对没有通用的方法来获取未初始化的全局变量。这将是您的编译器/链接器/运行时系统的一个功能,并且非常具体。

于 2013-02-04T11:45:47.197 回答
1

使用 gcc,-fno-zero-initialized-in-bss

于 2017-07-30T11:34:03.263 回答