{eval=Array;=+count(Array);}
恰好之前我寫(xiě)過(guò)一篇文章討論這個(gè)問(wèn)題,下面摘錄一部分。
使用我們按照之前幾節(jié)配置好的 vim 輸入以下代碼:
// 文件名
t.c
#include <stdio.h>
int main()
{
printf("hello embedTime ");
return 0;
}
這段代碼包含了 stdio 頭文件,調(diào)用了庫(kù)函數(shù) printf,所以編譯它肯定會(huì)使用鏈接庫(kù)。linux 系統(tǒng)有兩種鏈接庫(kù),一種常常被稱(chēng)為“靜態(tài)鏈接庫(kù)(static library)”,還有一種常被稱(chēng)作“動(dòng)態(tài)鏈接庫(kù)(shared library)”。
動(dòng)態(tài)鏈接是應(yīng)用非常廣泛的方式。動(dòng)態(tài)鏈接庫(kù)的英文字面意思可以翻譯為“共享的庫(kù)”,的確如此,使用動(dòng)態(tài)鏈接庫(kù)的程序在加載時(shí),linux 內(nèi)核會(huì)檢查程序用到的庫(kù)是否已經(jīng)在內(nèi)存中,如果在,則 linux 內(nèi)核不再重新加載庫(kù),直接就執(zhí)行程序了。所以,多個(gè)程序可以共享一個(gè)庫(kù),這實(shí)際上可以節(jié)約資源。
對(duì)于靜態(tài)鏈接庫(kù)來(lái)說(shuō),程序鏈接時(shí)會(huì)將其作為程序的一部分,因此最終生成的可執(zhí)行程序相比于動(dòng)態(tài)鏈接方式,會(huì)更大一點(diǎn)。
編譯上面的程序:
# gcc t.c -o shared.out
這條編譯語(yǔ)句使用的是動(dòng)態(tài)鏈接方式。為 gcc 命令附加 -static 命令,可以以靜態(tài)鏈接方式編譯程序:
# gcc t.c -static -o static.out
現(xiàn)在我們查看一下這兩種鏈接方式生成的可執(zhí)行程序大小對(duì)比:
# ls -ahltotal 888Kdrwxr-xr-x 3 root root 4.0K Dec 17 22:40 .drwxr-xr-x 8 root root 4.0K Dec 11 10:28 ..drwxr-xr-x 2 root root 4.0K Dec 17 22:39 his-rwxr-xr-x 1 root root 8.4K Dec 17 22:40 shared.out-rwxr-xr-x 1 root root 857K Dec 17 22:40 static.out-rw-r--r-- 1 root root 76 Dec 17 21:37 t.c
很容易看出,使用靜態(tài)鏈接方式生成的可執(zhí)行程序,要比使用動(dòng)態(tài)鏈接方式生成的可執(zhí)行程序大 100 多倍。雖然幾百 KB 對(duì)于大多數(shù) linux 主機(jī)來(lái)說(shuō)不算什么,但是嵌入式系統(tǒng)資源一般都非常緊缺,這時(shí)再輕易使用靜態(tài)鏈接就非常奢侈了。
使用靜態(tài)鏈接也是有好處的,生成的可執(zhí)行程序能夠脫離庫(kù)獨(dú)立運(yùn)行,而使用動(dòng)態(tài)鏈接的可執(zhí)行程序則不能脫離庫(kù)獨(dú)立運(yùn)行。
靜態(tài)鏈接和動(dòng)態(tài)鏈接的可執(zhí)行程序,執(zhí)行過(guò)程有哪些不同
上面討論了 linux 中程序鏈接的兩種方式,既然可執(zhí)行程序體積相差這么多,那它們的執(zhí)行過(guò)程也應(yīng)該有所差異了?的確如此,現(xiàn)在我們一起來(lái)分析下。在linux中分析程序的執(zhí)行過(guò)程,可以使用 strace 命令。
先分析 shared.out,我們輸入 strace ./shared.out,會(huì)發(fā)現(xiàn)有一大堆輸出信息:
# strace ./shared.outexecve("./shared.out", ["./shared.out"], [/* 22 vars */]) = 0brk(0) = 0x1a66000access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3fstat(3, {st_mode=S_IFREG|0644, st_size=33518, ...}) = 0mmap(NULL, 33518, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fe241ff2000close(3) = 0access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3read(3, "ELF>P "..., 832) = 832fstat(3, {st_mode=S_IFREG|0755, st_size=1857312, ...}) = 0mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe241ff1000mmap(NULL, 3965632, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fe241a10000mprotect(0x7fe241bce000, 2097152, PROT_NONE) = 0mmap(0x7fe241dce000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1be000) = 0x7fe241dce000mmap(0x7fe241dd4000, 17088, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fe241dd4000close(3) = 0mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe241fef000arch_prctl(ARCH_SET_FS, 0x7fe241fef740) = 0mprotect(0x7fe241dce000, 16384, PROT_READ) = 0mprotect(0x600000, 4096, PROT_READ) = 0mprotect(0x7fe241ffb000, 4096, PROT_READ) = 0munmap(0x7fe241ff2000, 33518) = 0fstat(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(136, 2), ...}) = 0mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe241ffa000write(1, "hello embedTime ", 16hello embedTime) = 16exit_group(0) = ?+++ exited with 0 +++
這些輸出信息即為 linux 執(zhí)行程序的過(guò)程。每一個(gè)函數(shù),都可以通過(guò) man 命令查詢(xún)其手冊(cè)。幾個(gè)主要的過(guò)程如下:
就是加載庫(kù)到內(nèi)存,再執(zhí)行程序,最后調(diào)用系統(tǒng)調(diào)用 exit 結(jié)束程序。
現(xiàn)在再來(lái)看看靜態(tài)鏈接的程序 static.out,同樣使用 strace 命令查看:
# strace static.out
可以看出,因?yàn)殒溄訒r(shí),編譯器直接把靜態(tài)庫(kù)作為程序的一部分了,所以這里相比于動(dòng)態(tài)鏈接的程序,少了很多將庫(kù)映射到內(nèi)存的操作:
到這里,動(dòng)態(tài)鏈接和靜態(tài)鏈接程序執(zhí)行時(shí)的不同點(diǎn),就清楚了。
歡迎在評(píng)論區(qū)一起討論,質(zhì)疑。文章都是手打原創(chuàng),每天最淺顯的介紹C語(yǔ)言、linux等嵌入式開(kāi)發(fā),喜歡我的文章就關(guān)注一波吧,可以看到最新更新和之前的文章哦。
0
回答0
回答0
回答0
回答0
回答0
回答0
回答0
回答10
回答0
回答