動態連結庫和靜態連結庫
使用我們按照之前幾節配置好的 vim 輸入以下程式碼:
// 檔名
t.c
#include <stdio.h>
int main()
{
printf("hello embedTime ");
return 0;
}
這段程式碼包含了 stdio 標頭檔案,呼叫了庫函式 printf,所以編譯它肯定會使用連結庫。linux 系統有兩種連結庫,一種常常被稱為“靜態連結庫(static library)”,還有一種常被稱作“動態連結庫(shared library)”。
動態連結是應用非常廣泛的方式。動態連結庫的英文字面意思可以翻譯為“共享的庫”,的確如此,使用動態連結庫的程式在載入時,linux 核心會檢查程式用到的庫是否已經在記憶體中,如果在,則 linux 核心不再重新載入庫,直接就執行程式了。所以,多個程式可以共享一個庫,這實際上可以節約資源。
對於靜態連結庫來說,程式連結時會將其作為程式的一部分,因此最終生成的可執行程式相比於動態連結方式,會更大一點。
編譯上面的程式:
這條編譯語句使用的是動態連結方式。為 gcc 命令附加 -static 命令,可以以靜態連結方式編譯程式:
現在我們檢視一下這兩種連結方式生成的可執行程式大小對比:
# 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
很容易看出,使用靜態連結方式生成的可執行程式,要比使用動態連結方式生成的可執行程式大 100 多倍。雖然幾百 KB 對於大多數 linux 主機來說不算什麼,但是嵌入式系統資源一般都非常緊缺,這時再輕易使用靜態連結就非常奢侈了。
靜態連結和動態連結的可執行程式,執行過程有哪些不同
先分析 shared.out,我們輸入 strace ./shared.out,會發現有一大堆輸出資訊:
這些輸出資訊即為 linux 執行程式的過程。每一個函式,都可以透過 man 命令查詢其手冊。幾個主要的過程如下:
就是載入庫到記憶體,再執行程式,最後呼叫系統呼叫 exit 結束程式。
現在再來看看靜態連結的程式 static.out,同樣使用 strace 命令檢視:
可以看出,因為連結時,編譯器直接把靜態庫作為程式的一部分了,所以這裡相比於動態連結的程式,少了很多將庫對映到記憶體的操作:
到這裡,動態連結和靜態連結程式執行時的不同點,就清楚了。
動態連結庫和靜態連結庫
使用我們按照之前幾節配置好的 vim 輸入以下程式碼:
// 檔名
t.c
#include <stdio.h>
int main()
{
printf("hello embedTime ");
return 0;
}
這段程式碼包含了 stdio 標頭檔案,呼叫了庫函式 printf,所以編譯它肯定會使用連結庫。linux 系統有兩種連結庫,一種常常被稱為“靜態連結庫(static library)”,還有一種常被稱作“動態連結庫(shared library)”。
動態連結是應用非常廣泛的方式。動態連結庫的英文字面意思可以翻譯為“共享的庫”,的確如此,使用動態連結庫的程式在載入時,linux 核心會檢查程式用到的庫是否已經在記憶體中,如果在,則 linux 核心不再重新載入庫,直接就執行程式了。所以,多個程式可以共享一個庫,這實際上可以節約資源。
對於靜態連結庫來說,程式連結時會將其作為程式的一部分,因此最終生成的可執行程式相比於動態連結方式,會更大一點。
編譯上面的程式:
# gcc t.c -o shared.out這條編譯語句使用的是動態連結方式。為 gcc 命令附加 -static 命令,可以以靜態連結方式編譯程式:
# gcc t.c -static -o static.out現在我們檢視一下這兩種連結方式生成的可執行程式大小對比:
# 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
很容易看出,使用靜態連結方式生成的可執行程式,要比使用動態連結方式生成的可執行程式大 100 多倍。雖然幾百 KB 對於大多數 linux 主機來說不算什麼,但是嵌入式系統資源一般都非常緊缺,這時再輕易使用靜態連結就非常奢侈了。
使用靜態連結也是有好處的,生成的可執行程式能夠脫離庫獨立執行,而使用動態連結的可執行程式則不能脫離庫獨立執行。靜態連結和動態連結的可執行程式,執行過程有哪些不同
先分析 shared.out,我們輸入 strace ./shared.out,會發現有一大堆輸出資訊:
# 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 執行程式的過程。每一個函式,都可以透過 man 命令查詢其手冊。幾個主要的過程如下:
就是載入庫到記憶體,再執行程式,最後呼叫系統呼叫 exit 結束程式。
現在再來看看靜態連結的程式 static.out,同樣使用 strace 命令檢視:
# strace static.out可以看出,因為連結時,編譯器直接把靜態庫作為程式的一部分了,所以這裡相比於動態連結的程式,少了很多將庫對映到記憶體的操作:
到這裡,動態連結和靜態連結程式執行時的不同點,就清楚了。