bss段不佔據磁碟空間的理解
elf檔案中.bss段: 存放未初始化的全域性變數,將.data和.bss分開的理由是為了節約磁碟空間,.bss不佔實際的磁碟空間。這句話該怎麼理解呢? 可以看下面的例子:
#include <stdio.h>
int a[1000];
int b[1000] = {1};
int main()
{
printf("123\n");
return 0;
}
這裡編寫了一個test.c
的檔案,gcc編譯gcc test.c -o test
之後,使用ls -l test
命令可以得到可執行檔案的資訊,我們只關注檔案的大小為12608。
使用命令size test
檢視各個段的大小(不包含stack和heap段):
#include <stdio.h>
int a[1000] = {1};
int b[1000] = {1};
int main()
{
printf("123\n");
return 0;
}
編譯之後,使用ls -l test
命令再次檢視可執行檔案的資訊: 使用命令size test
檢視段的大小:
可以看到大小從12608變成了16608,與之前相對比,該檔案佔據的大小漲了4000位元組,這不就是我們的陣列a[1000]
的大小嗎?我們所在的改動僅僅是初始化了a[1000]
,讓這個陣列的所在段從.bss
段改到了.data
段。通過size test
命令檢視bss
段的大小也減小了。這就證明了.bss
當程式載入執行時,就會為.bss
段中的資料分配記憶體已經進行初始化了。
下面還有兩個疑問,那就是int a[1000]
既然不佔據實際的磁碟空間(是指不佔據應該分配的記憶體大小),那麼它的大小和符號存在哪呢? .bss
段佔據的大小存放在ELF檔案格式中的段表(Section Table)中,段表存放了各個段的各種資訊,比如段的名字、段的型別、段在elf檔案中的偏移、段的大小等資訊。
我們可以通過命令readelf -S test
來檢視test
可執行檔案的段表(這裡只截取了一部分):
這裡再額外說明一個很有趣的地方,在elf檔案結構中,有一個字串表.strtab
.strtab
中對應字串的偏移。
.bss
段所佔空間的大小存在哪裡解決了,那麼就剩下符號了。
符號當然對應的存在符號表.symtab
中了。
我們可以通過命令readelf -s test
來檢視:
在第49行,我們看到了定義的全域性陣列b[1000]
,4000
那一項表明資料的大小是4000
位元組,OBJECT
代表是一個變數,GLOBAL
代表是作用域是全域性的。
最後我們總結一下: .bss
不佔據實際的磁碟空間,只在段表中記錄大小,在符號表中記錄符號。當檔案載入執行時,才分配空間以及初始化。