由於當初 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