.bss section:C 語言所種下的因

jollen 發表於 January 10, 2007 10:44 PM

由於當初 C 語言標準提到「未初始化的全域變數(un-initialized global variables)其初始值為零(zero)」,所以得到的結果便是「程式執行時,必須將未初始化的全域變數都初始化成零」。

Linux 針對這種狀況的解決方式是「配置 zeroed pages 給 .bss section」,因此 un-initialized global variables 的值(value)便會為零。

註:Un-initialized global variables 會被編譯器放到 .bss section,可參考 Jollen 的「BSS Section 觀念教學」專欄。

此外,global variable 被初始化為零時,也會被 GCC 放到 .bss section 裡。所以,以下的寫法:

int foo;

main()
{
...
}

會等於:

int foo = 0;

main()
{
...
}

以上二種寫法都會讓 foo 被放到 .bss section。由此可知,以下二種狀況,變數都會被放在 .bss section:

1. 當 global variable 未被初始化時;
2. 或是 global variable 被初始化成零時。

-fno-zero-initialized-in-bss

如果(但是確實有這種應用場合)我們不想讓 variable 被放到 .bss section 呢?做法有二。第一種方式是「傳統做法」,程式設計師只要將 global variable 初始化為「非零」值即可,舉以下程式為例:

#include <stdio.h>

int foo = 1;

int main(void)

{

printf("foo is %d.\n", foo);

return 0;

}

將此程式編譯:

$ gcc -O2 -o bss bss.c

利用 objdump 來觀察後,會發現 foo 變數被 GCC 放到 .data section 裡了。這種做法是基於 coding 時的做法。

第二種做法是 GCC 3.4.x 後所支援的「-fno-zero-initialized-in-bss」最佳化選項。將程式修改如下:

#include <stdio.h>

int foo = 0;

int main(void)

{

printf("foo is %d.\n", foo);

return 0;

}

請特別留意,global variable 仍要做初始化,所以「foo」一定要做「assignment」為零值的動作。將程式編譯:

$ gcc -fno-zero-initialized-in-bss -O2 -o bss bss.c

GCC 會把 foo 放到 .data section。同樣可利用 objdump 來觀察。

良好的 Embedded Linux 程式寫作習慣

了解以上的觀念後,我要來說明一個重要的觀念。由於某些特定應用,或是 target 需要將變數放在 .data section 裡,因此若是 Embedded Linux 的應用,建議應對全域變數做初始化,例如:

int x;
int y;
int z;

int main(void)
{
...
}

應將這種傳統 C 的寫作習慣調整為:

int x = 0;
int y = 0;
int z = 0;

int main(void)
{
...
}

良好的習慣養成,未來將會得到許多好處。

這種寫法並非傳統 C 語言所講的「多此一舉」,應該以 Linux systems software 的角度來思考:這種寫法便能搭配 GCC 的「-fno-zero-initialized-in-bss」或是「-fzero-initialized-in-bss(預設)」選項,來決定 global variable 要放到 .bss section 或是 .data section。

Jollen's Blog 使用 Github issues 與讀者交流討論。請點擊上方的文章專屬 issue,或 open a new issue

您可透過電子郵件 jollen@jollen.org,或是 Linkedin 與我連絡。更歡迎使用微信,請搜尋 WeChat ID:jollentw