自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Linux下被我誤解的gcc,軟件可執(zhí)行文件的跨系統(tǒng)版本兼容性沒有那么差,如果你也是這樣處理

開發(fā) 前端
因為C/C++開發(fā)的軟件占比很大,即使開發(fā)業(yè)務中不直接使用,也會在運行中依賴到C/C++的庫或可執(zhí)行文件的功能。

以上是近期發(fā)布的這3篇文章,圍繞Linux下的可執(zhí)行文件究竟是采用靜態(tài)鏈接還是動態(tài)鏈接方式發(fā)布,從不同角度做了分析??雌饋淼?、2篇的觀點是相反的?,F(xiàn)在對這兩個看似矛盾的說法加以解釋,以期讀者能對Linux的動態(tài)庫、靜態(tài)編譯可執(zhí)行文件的理解,和我一樣在學習進步,形成總體符合實際的認知。

也得到了讀者的很多反饋,現(xiàn)舉例其一:

為什么要盯住gcc呢?因為Linux的軟件生態(tài)中,C/C++開發(fā)的軟件,特別是基礎型軟件、需要高性能的軟件,往往歷史原因都以這兩種語言為主,在此不贅述。

總之,因為C/C++開發(fā)的軟件占比很大,即使開發(fā)業(yè)務中不直接使用,也會在運行中依賴到C/C++的庫或可執(zhí)行文件的功能。搞懂動態(tài)庫的特性,對于解決一些軟件依賴問題、開發(fā)編譯鏈接失敗問題,都有幫助。所以開發(fā)者和運維仍然有必要增進這方面的理解。

1.動態(tài)鏈接適合插件化開發(fā)、插件化升級、希望打包發(fā)布的可執(zhí)行文件盡量小的場景;而靜態(tài)鏈接方式適合易部署、不想處理第三方動態(tài)庫依賴問題的場景。

2.gcc/g++ 作為Linux下主要的編譯器,支持動態(tài)鏈接、靜態(tài)鏈接方式。如最基本的main.c 代碼可通過gcc -o main_dynamic_link main.c 和 gcc -static -o main_static main.c 分別得到兩類可執(zhí)行文件。這是大學生在學校初學Linux下的gcc C/C++編程的時候就了解的。

3.gcc 動態(tài)鏈接生成的可執(zhí)行文件,因為代碼必然使用到c/c++的標準庫提供的函數(shù),那么可執(zhí)行文件必然要與libc.so庫動態(tài)庫鏈接(如下圖)。

4.gcc編譯得到的可執(zhí)行文件,運行時會以進程方式在用戶態(tài)、內(nèi)核態(tài)的內(nèi)存中布局。如果可執(zhí)行文件是動態(tài)鏈接方式的,則運行時由 Linux內(nèi)核負責載入ELF格式的可執(zhí)行文件后,內(nèi)核通過 ld-linux.so (64位系統(tǒng)下則為 ld-linux-x86-64.so ) 分析可執(zhí)行文件依賴的其他動態(tài)庫信息,由ld-linux.so 負責逐個載入其他動態(tài)庫到該進程的虛擬內(nèi)存的代碼段位置中。這里就發(fā)揮了動態(tài)庫和虛擬內(nèi)存的優(yōu)勢:熱門的動態(tài)庫被很多其他進程依賴,那么這種可執(zhí)行文件實際只占用物理內(nèi)存的一塊空間,無論被多少個進程依賴。

所以達到了提高內(nèi)存利用率的效果,這對于需要運行大量軟件的場景(如Linux桌面),收益還是可觀的??蓤?zhí)行文件從被調(diào)起到執(zhí)行完畢,我們可以用 strace 命令看到全過程,包括需要讀取的其他庫文件的過程。比如下面的可執(zhí)行文件可以用strace 看到執(zhí)行全流程(功能只調(diào)用printf函數(shù)打印字符串)。

root@localhost:~# file  ./main
./main: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=82e7afc31da1cdbdd374658c2724dce983ccedab, for GNU/Linux 3.2.0, not stripped


root@localhost:~# ldd  ./main
        not a dynamic executable   #說明當前是靜態(tài)鏈接的
        
        
root@localhost:~# strace ./main
execve("./main", ["./main"], 0x7ffe6fd2a090 /* 25 vars */) = 0
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffe7ada0370) = -1 EINVAL (Invalid argument)
brk(NULL)                               = 0x1a12000
brk(0x1a12dc0)                          = 0x1a12dc0
arch_prctl(ARCH_SET_FS, 0x1a123c0)      = 0
set_tid_address(0x1a12690)              = 27080
set_robust_list(0x1a126a0, 24)          = 0
rseq(0x1a12d60, 0x20, 0, 0x53053053)    = 0
uname({sysname="Linux", nodename="localhost", ...}) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
readlink("/proc/self/exe", "/root/main", 4096) = 10
getrandom("\xa1\xe6\x48\xa4\x1d\x32\xef\x0e", 8, GRND_NONBLOCK) = 8
brk(0x1a33dc0)                          = 0x1a33dc0
brk(0x1a34000)                          = 0x1a34000
mprotect(0x4c1000, 16384, PROT_READ)    = 0
newfstatat(1, "", {st_mode=S_IFCHR|0600, st_rdev=makedev(0x88, 0), ...}, AT_EMPTY_PATH) = 0
write(1, "111", 3111)                      = 3
exit_group(3)                           = ?
+++ exited with 3 +++

## 下面按動態(tài)鏈接生成可執(zhí)行文件
root@localhost:~# gcc  -o main main.c


## strace 顯示可執(zhí)行文件 執(zhí)行時需要加載 `/lib/x86_64-linux-gnu/libc.so.6` 文件。
root@localhost:~# strace ./main
execve("./main", ["./main"], 0x7ffec2fb69c0 /* 25 vars */) = 0
brk(NULL)                               = 0x55fd3f1bc000
root@localhost:~#
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa3491dd000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=87359, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 87359, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fa3491c7000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\237\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0 \0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0"..., 48, 848) = 48
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0 =\340\2563\265?\356\25x\261\27\313A#\350"..., 68, 896) = 68
newfstatat(3, "", {st_mode=S_IFREG|0755, st_size=2216304, ...}, AT_EMPTY_PATH) = 0
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
mmap(NULL, 2260560, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fa348f9f000
mmap(0x7fa348fc7000, 1658880, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x28000) = 0x7fa348fc7000
mmap(0x7fa34915c000, 360448, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bd000) = 0x7fa34915c000
mmap(0x7fa3491b4000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x214000) = 0x7fa3491b4000
mmap(0x7fa3491ba000, 52816, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fa3491ba000
close(3)                                = 0
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa348f9c000
arch_prctl(ARCH_SET_FS, 0x7fa348f9c740) = 0
set_tid_address(0x7fa348f9ca10)         = 27089
set_robust_list(0x7fa348f9ca20, 24)     = 0
rseq(0x7fa348f9d0e0, 0x20, 0, 0x53053053) = 0
mprotect(0x7fa3491b4000, 16384, PROT_READ) = 0
mprotect(0x55fd3e9e1000, 4096, PROT_READ) = 0
mprotect(0x7fa34921d000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
munmap(0x7fa3491c7000, 87359)           = 0
newfstatat(1, "", {st_mode=S_IFCHR|0600, st_rdev=makedev(0x88, 0), ...}, AT_EMPTY_PATH) = 0
getrandom("\x84\x8c\x06\x16\x25\xe5\x97\x97", 8, GRND_NONBLOCK) = 8
brk(NULL)                               = 0x55fd3f1bc000
brk(0x55fd3f1dd000)                     = 0x55fd3f1dd000
write(1, "111", 3111)                      = 3
exit_group(3)                           = ?
+++ exited with 3 +++
root@localhost:~#

可以看到 動態(tài)鏈接的C可執(zhí)行文件運行時確實需要加載 /lib/x86_64-linux-gnu/libc.so.6 文件。而靜態(tài)鏈接的可執(zhí)行文件沒有加載任何.so文件,包括libc.so也不需要。因為靜態(tài)文件的代碼指令已完整在文件中。

root@localhost:~# gcc --version
gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.


root@localhost:~# gcc -static -o main_static main.c  # 靜態(tài)鏈接
root@localhost:~# gcc  -o main_shared_link main.c    # 動態(tài)鏈接
root@localhost:~# ls -lht
total 1.8M
-rwxr-xr-x 1 root root  16K Dec 17 23:58 main_shared_link
-rwxr-xr-x 1 root root 880K Dec 17 23:58 main_static
-rw-r--r-- 1 root root   54 Dec 17 22:30 main.c

root@localhost:~# file main_static   # 查看靜態(tài)鏈接文件的屬性
main_static:      ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=82e7afc31da1cdbdd374658c2724dce983ccedab, for GNU/Linux 3.2.0, not stripped

root@localhost:~# main_shared_link  # 查看動態(tài)鏈接文件的屬性
main_shared_link: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=f14401e673b52624535874f2e1a8488a0edbc891, for GNU/Linux 3.2.0, not stripped

root@localhost:~# ls -lht /lib/x86_64-linux-gnu/libc.so.6
-rwxr-xr-x 1 root root 2.2M Nov 22 21:18 /lib/x86_64-linux-gnu/libc.so.6

這里看到:880K Dec 17 23:58 main_static靜態(tài)文件大小880K,而libc.so大小2.2M 。靜態(tài)文件大小比C標準庫動態(tài)文件的大小還小很多。

說明gcc并不只是把libc.so的所有函數(shù)完全包含進靜態(tài)可執(zhí)行文件,而這是我之前對gcc的誤解。

實際gcc對靜態(tài)的鏈接是做了精簡優(yōu)化的,只保留了有被調(diào)用的函數(shù)代碼到最終可執(zhí)行文件內(nèi)。

5.所以本文最初提到的《為什么不建議交付靜態(tài)鏈接的可執(zhí)行文件給用戶?》 中的不建議,是只從靜態(tài)文件的分發(fā)方式降低了系統(tǒng)基礎庫的重用率的角度出發(fā)的。當然隨著技術的進步,也許以后內(nèi)核的可執(zhí)行文件加載器能做到識別出靜態(tài)文件中的某些可替代的公共部分就自動剔除對物理內(nèi)存的占用,而借助系統(tǒng)已有的已載入內(nèi)存的公共代碼實現(xiàn)替代,而不影響軟件功能,就結合了動態(tài)和靜態(tài)的兩種優(yōu)勢。這只是一個猜想,并非合理完美的方案。

6.而《為什么Golang開發(fā)的軟件單文件直接丟到各種Linux系統(tǒng)就能運行?》中golang能做到的,gcc也能做到,所以C/C++靜態(tài)鏈接的可執(zhí)行文件的跨系統(tǒng)版本兼容性沒有那么差,跟Golang生成的靜態(tài)可執(zhí)行文件是一樣能丟到各種Linux發(fā)行版的運行的。

7.但跨系統(tǒng)兼容性的前提是32位、64位系統(tǒng)和軟件的數(shù)位,要匹配。除非32位的系統(tǒng)內(nèi)核支持PAE特性或Linux系統(tǒng)額外安裝了 multilib 庫,以實現(xiàn)32位系統(tǒng)下運行64位的軟件,或64位系統(tǒng)下運行32位的軟件。

責任編輯:趙寧寧 來源: 深入理解Linux
相關推薦

2010-02-22 18:04:27

CentOS mpla

2012-01-05 10:37:40

Java

2015-02-02 11:03:12

2009-06-20 09:21:37

UNIXLINUX

2024-08-12 16:42:50

二進制工具系統(tǒng)

2021-01-06 05:29:57

虛擬內(nèi)存文件

2022-05-11 14:50:34

Python解包執(zhí)行文件

2011-08-09 10:24:19

可執(zhí)行文件病毒病毒

2021-01-12 10:10:41

shell腳本Linux命令

2021-01-08 08:06:19

腳本Shell文件

2017-02-07 10:22:53

2023-09-04 07:14:36

2009-04-16 10:37:17

Javaexejar

2024-05-21 12:01:39

.NET 6開發(fā)

2023-03-31 23:31:06

.go文本文件

2024-05-06 00:00:00

Go文件瘦身代碼

2021-01-14 22:17:09

PythonLinux工具

2014-11-04 14:33:33

WebService

2023-12-18 09:21:22

開發(fā)靜態(tài)編譯Linux

2024-04-07 10:16:57

Python代碼six模塊
點贊
收藏

51CTO技術棧公眾號